[clang] 1a78ef9 - [clang][bytecode] Allow casts from void* only in std::allocator calls (#136714)

via cfe-commits cfe-commits at lists.llvm.org
Tue Apr 22 23:01:00 PDT 2025


Author: Timm Baeder
Date: 2025-04-23T08:00:57+02:00
New Revision: 1a78ef9a9eddd73de7932f5c33a7a7ad7e8b1806

URL: https://github.com/llvm/llvm-project/commit/1a78ef9a9eddd73de7932f5c33a7a7ad7e8b1806
DIFF: https://github.com/llvm/llvm-project/commit/1a78ef9a9eddd73de7932f5c33a7a7ad7e8b1806.diff

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

Otherwise, add the missing diagnostic.

Added: 
    

Modified: 
    clang/lib/AST/ByteCode/Interp.h
    clang/lib/AST/ByteCode/InterpBuiltin.cpp
    clang/lib/AST/ByteCode/InterpState.cpp
    clang/lib/AST/ByteCode/InterpState.h
    clang/test/AST/ByteCode/c.c
    clang/test/AST/ByteCode/cxx11.cpp
    clang/test/AST/ByteCode/cxx23.cpp
    clang/test/AST/ByteCode/cxx26.cpp

Removed: 
    


################################################################################
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__ ptr
diff _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);


        


More information about the cfe-commits mailing list