Skip to content

[clang][bytecode] Allow casts from void* only in std::allocator calls #136714

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 23, 2025

Conversation

tbaederr
Copy link
Contributor

Otherwise, add the missing diagnostic.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:bytecode Issues for the clang bytecode constexpr interpreter labels Apr 22, 2025
@llvmbot
Copy link
Member

llvmbot commented Apr 22, 2025

@llvm/pr-subscribers-clang

Author: Timm Baeder (tbaederr)

Changes

Otherwise, add the missing diagnostic.


Full diff: https://github.com/llvm/llvm-project/pull/136714.diff

8 Files Affected:

  • (modified) clang/lib/AST/ByteCode/Interp.h (+21-5)
  • (modified) clang/lib/AST/ByteCode/InterpBuiltin.cpp (+2-55)
  • (modified) clang/lib/AST/ByteCode/InterpState.cpp (+30)
  • (modified) clang/lib/AST/ByteCode/InterpState.h (+8)
  • (modified) clang/test/AST/ByteCode/c.c (+6-5)
  • (modified) clang/test/AST/ByteCode/cxx11.cpp (+1)
  • (modified) clang/test/AST/ByteCode/cxx23.cpp (+6)
  • (modified) clang/test/AST/ByteCode/cxx26.cpp (+5)
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 866d8e4bf2251..e5300b7cd96a9 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -1113,6 +1113,12 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
             << P.toDiagnosticString(S.getASTContext());
         return false;
       }
+    } else if (BothNonNull && P.isIntegralPointer()) {
+      const SourceInfo &Loc = S.Current->getSource(OpPC);
+      S.FFDiag(Loc, diag::note_constexpr_pointer_constant_comparison)
+          << LHS.toDiagnosticString(S.getASTContext())
+          << RHS.toDiagnosticString(S.getASTContext());
+      return false;
     }
   }
 
@@ -2389,7 +2395,18 @@ static inline bool PtrPtrCast(InterpState &S, CodePtr OpPC, bool SrcIsVoidPtr) {
     bool HasValidResult = !Ptr.isZero();
 
     if (HasValidResult) {
-      // FIXME: note_constexpr_invalid_void_star_cast
+      if (S.getStdAllocatorCaller("allocate"))
+        return true;
+
+      const auto &E = cast<CastExpr>(S.Current->getExpr(OpPC));
+      if (S.getLangOpts().CPlusPlus26 &&
+          S.getASTContext().hasSimilarType(Ptr.getType(),
+                                           E->getType()->getPointeeType()))
+        return true;
+
+      S.CCEDiag(E, diag::note_constexpr_invalid_void_star_cast)
+          << E->getSubExpr()->getType() << S.getLangOpts().CPlusPlus26
+          << Ptr.getType().getCanonicalType() << E->getType()->getPointeeType();
     } else if (!S.getLangOpts().CPlusPlus26) {
       const SourceInfo &E = S.Current->getSource(OpPC);
       S.CCEDiag(E, diag::note_constexpr_invalid_cast)
@@ -2781,10 +2798,9 @@ template <PrimType Name, class T = typename PrimConv<Name>::T>
 inline bool GetIntPtr(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
   const T &IntVal = S.Stk.pop<T>();
 
-  if (Desc)
-    S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_invalid_cast)
-        << diag::ConstexprInvalidCastKind::ThisConversionOrReinterpret
-        << S.getLangOpts().CPlusPlus;
+  S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_invalid_cast)
+      << diag::ConstexprInvalidCastKind::ThisConversionOrReinterpret
+      << S.getLangOpts().CPlusPlus;
 
   S.Stk.push<Pointer>(static_cast<uint64_t>(IntVal), Desc);
   return true;
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 523e471d3c82c..d8b320ff3ba31 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -1526,34 +1526,7 @@ static bool interp__builtin_operator_new(InterpState &S, CodePtr OpPC,
   // A call to __operator_new is only valid within std::allocate<>::allocate.
   // Walk up the call stack to find the appropriate caller and get the
   // element type from it.
-  QualType ElemType;
-  const CallExpr *NewCall = nullptr;
-
-  for (const InterpFrame *F = Frame; F; F = F->Caller) {
-    const Function *Func = F->getFunction();
-    if (!Func)
-      continue;
-    const auto *MD = dyn_cast_if_present<CXXMethodDecl>(Func->getDecl());
-    if (!MD)
-      continue;
-    const IdentifierInfo *FnII = MD->getIdentifier();
-    if (!FnII || !FnII->isStr("allocate"))
-      continue;
-
-    const auto *CTSD =
-        dyn_cast<ClassTemplateSpecializationDecl>(MD->getParent());
-    if (!CTSD)
-      continue;
-
-    const IdentifierInfo *ClassII = CTSD->getIdentifier();
-    const TemplateArgumentList &TAL = CTSD->getTemplateArgs();
-    if (CTSD->isInStdNamespace() && ClassII && ClassII->isStr("allocator") &&
-        TAL.size() >= 1 && TAL[0].getKind() == TemplateArgument::Type) {
-      ElemType = TAL[0].getAsType();
-      NewCall = cast<CallExpr>(F->Caller->getExpr(F->getRetPC()));
-      break;
-    }
-  }
+  auto [NewCall, ElemType] = S.getStdAllocatorCaller("allocate");
 
   if (ElemType.isNull()) {
     S.FFDiag(Call, S.getLangOpts().CPlusPlus20
@@ -1655,33 +1628,7 @@ static bool interp__builtin_operator_delete(InterpState &S, CodePtr OpPC,
     return false;
 
   // This is permitted only within a call to std::allocator<T>::deallocate.
-  bool DeallocateFrameFound = false;
-  for (const InterpFrame *F = Frame; F; F = F->Caller) {
-    const Function *Func = F->getFunction();
-    if (!Func)
-      continue;
-    const auto *MD = dyn_cast_if_present<CXXMethodDecl>(Func->getDecl());
-    if (!MD)
-      continue;
-    const IdentifierInfo *FnII = MD->getIdentifier();
-    if (!FnII || !FnII->isStr("deallocate"))
-      continue;
-
-    const auto *CTSD =
-        dyn_cast<ClassTemplateSpecializationDecl>(MD->getParent());
-    if (!CTSD)
-      continue;
-
-    const IdentifierInfo *ClassII = CTSD->getIdentifier();
-    const TemplateArgumentList &TAL = CTSD->getTemplateArgs();
-    if (CTSD->isInStdNamespace() && ClassII && ClassII->isStr("allocator") &&
-        TAL.size() >= 1 && TAL[0].getKind() == TemplateArgument::Type) {
-      DeallocateFrameFound = true;
-      break;
-    }
-  }
-
-  if (!DeallocateFrameFound) {
+  if (!S.getStdAllocatorCaller("deallocate")) {
     S.FFDiag(Call);
     return true;
   }
diff --git a/clang/lib/AST/ByteCode/InterpState.cpp b/clang/lib/AST/ByteCode/InterpState.cpp
index 70a2e9b62fc3a..d6e6771f0a04f 100644
--- a/clang/lib/AST/ByteCode/InterpState.cpp
+++ b/clang/lib/AST/ByteCode/InterpState.cpp
@@ -115,3 +115,33 @@ bool InterpState::maybeDiagnoseDanglingAllocations() {
   }
   return NoAllocationsLeft;
 }
+
+StdAllocatorCaller InterpState::getStdAllocatorCaller(StringRef Name) const {
+  for (const InterpFrame *F = Current; F; F = F->Caller) {
+    const Function *Func = F->getFunction();
+    if (!Func)
+      continue;
+    const auto *MD = dyn_cast_if_present<CXXMethodDecl>(Func->getDecl());
+    if (!MD)
+      continue;
+    const IdentifierInfo *FnII = MD->getIdentifier();
+    if (!FnII || !FnII->isStr(Name))
+      continue;
+
+    const auto *CTSD =
+        dyn_cast<ClassTemplateSpecializationDecl>(MD->getParent());
+    if (!CTSD)
+      continue;
+
+    const IdentifierInfo *ClassII = CTSD->getIdentifier();
+    const TemplateArgumentList &TAL = CTSD->getTemplateArgs();
+    if (CTSD->isInStdNamespace() && ClassII && ClassII->isStr("allocator") &&
+        TAL.size() >= 1 && TAL[0].getKind() == TemplateArgument::Type) {
+      QualType ElemType = TAL[0].getAsType();
+      const auto *NewCall = cast<CallExpr>(F->Caller->getExpr(F->getRetPC()));
+      return {NewCall, ElemType};
+    }
+  }
+
+  return {};
+}
diff --git a/clang/lib/AST/ByteCode/InterpState.h b/clang/lib/AST/ByteCode/InterpState.h
index 528c1a24e7b05..91e09a911ce37 100644
--- a/clang/lib/AST/ByteCode/InterpState.h
+++ b/clang/lib/AST/ByteCode/InterpState.h
@@ -32,6 +32,12 @@ class InterpStack;
 class InterpFrame;
 class SourceMapper;
 
+struct StdAllocatorCaller {
+  const Expr *Call = nullptr;
+  QualType AllocType;
+  explicit operator bool() { return Call; }
+};
+
 /// Interpreter context.
 class InterpState final : public State, public SourceMapper {
 public:
@@ -116,6 +122,8 @@ class InterpState final : public State, public SourceMapper {
   /// \c true otherwise.
   bool maybeDiagnoseDanglingAllocations();
 
+  StdAllocatorCaller getStdAllocatorCaller(StringRef Name) const;
+
 private:
   friend class EvaluationResult;
   friend class InterpStateCCOverride;
diff --git a/clang/test/AST/ByteCode/c.c b/clang/test/AST/ByteCode/c.c
index fe47f9cab1c9f..a7b1fe07f6d84 100644
--- a/clang/test/AST/ByteCode/c.c
+++ b/clang/test/AST/ByteCode/c.c
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -triple x86_64-linux -fexperimental-new-constant-interpreter -verify=expected,all -std=c11 -Wcast-qual %s
-// RUN: %clang_cc1 -triple x86_64-linux -fexperimental-new-constant-interpreter -pedantic -verify=pedantic,pedantic-expected,all -std=c11 -Wcast-qual %s
-// RUN: %clang_cc1 -triple x86_64-linux -verify=ref,all -std=c11 -Wcast-qual %s
-// RUN: %clang_cc1 -triple x86_64-linux -pedantic -verify=pedantic,pedantic-ref,all -std=c11 -Wcast-qual %s
+// RUN: %clang_cc1 -triple x86_64-linux -verify=expected,all                   -std=c11 -Wcast-qual           %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -triple x86_64-linux -verify=pedantic,pedantic-expected,all -std=c11 -Wcast-qual -pedantic %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -triple x86_64-linux -verify=ref,all                        -std=c11 -Wcast-qual           %s
+// RUN: %clang_cc1 -triple x86_64-linux -verify=pedantic,pedantic-ref,all      -std=c11 -Wcast-qual -pedantic %s
 
 typedef __INTPTR_TYPE__ intptr_t;
 typedef __PTRDIFF_TYPE__ ptrdiff_t;
@@ -231,7 +231,8 @@ int castViaInt[*(int*)(unsigned long)"test"]; // ref-error {{variable length arr
                                               // expected-error {{variable length array}} \
                                               // pedantic-expected-error {{variable length array}}
 
-const void (*const funcp)(void) = (void*)123; // pedantic-warning {{converts between void pointer and function pointer}}
+const void (*const funcp)(void) = (void*)123; // pedantic-warning {{converts between void pointer and function pointer}} \
+                                              // pedantic-expected-note {{this conversion is not allowed in a constant expression}}
 _Static_assert(funcp == (void*)0, ""); // all-error {{failed due to requirement 'funcp == (void *)0'}} \
                                        // pedantic-warning {{expression is not an integer constant expression}}
 _Static_assert(funcp == (void*)123, ""); // pedantic-warning {{equality comparison between function pointer and void pointer}} \
diff --git a/clang/test/AST/ByteCode/cxx11.cpp b/clang/test/AST/ByteCode/cxx11.cpp
index 4c69517304ea7..004f704145afd 100644
--- a/clang/test/AST/ByteCode/cxx11.cpp
+++ b/clang/test/AST/ByteCode/cxx11.cpp
@@ -191,5 +191,6 @@ namespace DynamicCast {
   constexpr S* sptr = &s;
   struct Str {
     int b : reinterpret_cast<S*>(sptr) == reinterpret_cast<S*>(sptr);
+    int g : (S*)(void*)(sptr) == sptr;
   };
 }
diff --git a/clang/test/AST/ByteCode/cxx23.cpp b/clang/test/AST/ByteCode/cxx23.cpp
index d0ade4f5278b1..ce18a9d473302 100644
--- a/clang/test/AST/ByteCode/cxx23.cpp
+++ b/clang/test/AST/ByteCode/cxx23.cpp
@@ -316,3 +316,9 @@ namespace ZeroSizedArray {
   }
   static_assert(foo() == 1);
 }
+namespace VoidCast {
+  constexpr int a = 12;
+  constexpr const int *b = &a;
+  constexpr int *f = (int*)(void*)b; // all-error {{must be initialized by a constant expression}} \
+                                     // all-note {{cast from 'void *' is not allowed in a constant expression}}
+}
diff --git a/clang/test/AST/ByteCode/cxx26.cpp b/clang/test/AST/ByteCode/cxx26.cpp
index cd6b533065010..cd786b17ca9ab 100644
--- a/clang/test/AST/ByteCode/cxx26.cpp
+++ b/clang/test/AST/ByteCode/cxx26.cpp
@@ -31,3 +31,8 @@ namespace ReplaceableAlloc {
   static_assert(foo()); // both-error {{not an integral constant expression}} \
                         // both-note {{in call to}}
 }
+
+constexpr int a = 12;
+constexpr const int *b = &a;
+constexpr int *f = (int*)(void*)b;
+static_assert(*f == 12);

@tbaederr tbaederr merged commit 1a78ef9 into llvm:main Apr 23, 2025
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:bytecode Issues for the clang bytecode constexpr interpreter clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants