Author: Timm Baeder
Date: 2026-04-01T09:49:55+02:00
New Revision: 8edbf230980a038f120e754439a79cec68ad9356

URL: 
https://github.com/llvm/llvm-project/commit/8edbf230980a038f120e754439a79cec68ad9356
DIFF: 
https://github.com/llvm/llvm-project/commit/8edbf230980a038f120e754439a79cec68ad9356.diff

LOG: [clang][bytecode] Revisit reference variables as constexpr-unknown in 
C++26 (#187918)

When revisiting `const T& range = f(T());`, we do mark the variable for
`range` as constexpr-unknown, but not the temporary variable we create
for `T()`. Change that.

Then we also need to ignore constexpr-unknown pointers in
`CheckInvoke()`.

Fixes https://github.com/llvm/llvm-project/issues/187775

Added: 
    

Modified: 
    clang/lib/AST/ByteCode/Compiler.cpp
    clang/lib/AST/ByteCode/Compiler.h
    clang/lib/AST/ByteCode/Interp.cpp
    clang/test/AST/ByteCode/codegen-constexpr-unknown.cpp
    clang/test/AST/ByteCode/cxx26.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/ByteCode/Compiler.cpp 
b/clang/lib/AST/ByteCode/Compiler.cpp
index 75841c4378d21..9d2457a2f35cd 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -4993,15 +4993,14 @@ template <class Emitter>
 unsigned Compiler<Emitter>::allocateLocalPrimitive(DeclTy &&Src, PrimType Ty,
                                                    bool IsConst,
                                                    bool IsVolatile,
-                                                   ScopeKind SC,
-                                                   bool IsConstexprUnknown) {
+                                                   ScopeKind SC) {
   // FIXME: There are cases where Src.is<Expr*>() is wrong, e.g.
   //   (int){12} in C. Consider using Expr::isTemporaryObject() instead
   //   or isa<MaterializeTemporaryExpr>().
   Descriptor *D = P.createDescriptor(Src, Ty, nullptr, 
Descriptor::InlineDescMD,
                                      IsConst, isa<const Expr *>(Src),
                                      /*IsMutable=*/false, IsVolatile);
-  D->IsConstexprUnknown = IsConstexprUnknown;
+  D->IsConstexprUnknown = this->VariablesAreConstexprUnknown;
   Scope::Local Local = this->createLocal(D);
   if (auto *VD = dyn_cast_if_present<ValueDecl>(Src.dyn_cast<const Decl *>()))
     Locals.insert({VD, Local});
@@ -5011,8 +5010,7 @@ unsigned Compiler<Emitter>::allocateLocalPrimitive(DeclTy 
&&Src, PrimType Ty,
 
 template <class Emitter>
 UnsignedOrNone Compiler<Emitter>::allocateLocal(DeclTy &&Src, QualType Ty,
-                                                ScopeKind SC,
-                                                bool IsConstexprUnknown) {
+                                                ScopeKind SC) {
   const ValueDecl *Key = nullptr;
   const Expr *Init = nullptr;
   bool IsTemporary = false;
@@ -5034,7 +5032,7 @@ UnsignedOrNone Compiler<Emitter>::allocateLocal(DeclTy 
&&Src, QualType Ty,
       Init);
   if (!D)
     return std::nullopt;
-  D->IsConstexprUnknown = IsConstexprUnknown;
+  D->IsConstexprUnknown = this->VariablesAreConstexprUnknown;
 
   Scope::Local Local = this->createLocal(D);
   if (Key)
@@ -5137,11 +5135,9 @@ bool Compiler<Emitter>::visitExpr(const Expr *E, bool 
DestroyToplevelScope) {
 }
 
 template <class Emitter>
-VarCreationState Compiler<Emitter>::visitDecl(const VarDecl *VD,
-                                              bool IsConstexprUnknown) {
+VarCreationState Compiler<Emitter>::visitDecl(const VarDecl *VD) {
 
-  auto R = this->visitVarDecl(VD, VD->getInit(), /*Toplevel=*/true,
-                              IsConstexprUnknown);
+  auto R = this->visitVarDecl(VD, VD->getInit(), /*Toplevel=*/true);
 
   if (R.notCreated())
     return R;
@@ -5225,9 +5221,9 @@ bool Compiler<Emitter>::visitDeclAndReturn(const VarDecl 
*VD, const Expr *Init,
 }
 
 template <class Emitter>
-VarCreationState
-Compiler<Emitter>::visitVarDecl(const VarDecl *VD, const Expr *Init,
-                                bool Toplevel, bool IsConstexprUnknown) {
+VarCreationState Compiler<Emitter>::visitVarDecl(const VarDecl *VD,
+                                                 const Expr *Init,
+                                                 bool Toplevel) {
   // We don't know what to do with these, so just return false.
   if (VD->getType().isNull())
     return false;
@@ -5290,8 +5286,7 @@ Compiler<Emitter>::visitVarDecl(const VarDecl *VD, const 
Expr *Init,
   if (VarT) {
     unsigned Offset = this->allocateLocalPrimitive(
         VD, *VarT, VD->getType().isConstQualified(),
-        VD->getType().isVolatileQualified(), ScopeKind::Block,
-        IsConstexprUnknown);
+        VD->getType().isVolatileQualified(), ScopeKind::Block);
 
     if (!Init || Init->getType()->isVoidType())
       return true;
@@ -5309,8 +5304,8 @@ Compiler<Emitter>::visitVarDecl(const VarDecl *VD, const 
Expr *Init,
     return this->emitSetLocal(*VarT, Offset, VD);
   }
   // Local composite variables.
-  if (UnsignedOrNone Offset = this->allocateLocal(
-          VD, VD->getType(), ScopeKind::Block, IsConstexprUnknown)) {
+  if (UnsignedOrNone Offset =
+          this->allocateLocal(VD, VD->getType(), ScopeKind::Block)) {
     if (!Init)
       return true;
 
@@ -7512,10 +7507,13 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl 
*D, const Expr *E) {
   }
 
   // In case we need to re-visit a declaration.
-  auto revisit = [&](const VarDecl *VD) -> bool {
+  auto revisit = [&](const VarDecl *VD,
+                     bool IsConstexprUnknown = true) -> bool {
+    llvm::SaveAndRestore CURS(this->VariablesAreConstexprUnknown,
+                              IsConstexprUnknown);
     if (!this->emitPushCC(VD->hasConstantInitialization(), E))
       return false;
-    auto VarState = this->visitDecl(VD, /*IsConstexprUnknown=*/true);
+    auto VarState = this->visitDecl(VD);
 
     if (!this->emitPopCC(E))
       return false;
@@ -7563,7 +7561,7 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, 
const Expr *E) {
   if (!Ctx.getLangOpts().CPlusPlus) {
     if (VD->getAnyInitializer() && DeclType.isConstant(Ctx.getASTContext()) &&
         !VD->isWeak())
-      return revisit(VD);
+      return revisit(VD, DeclType->isPointerType());
     return this->emitDummyPtr(D, E);
   }
 
@@ -7597,11 +7595,14 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl 
*D, const Expr *E) {
   if (VD->isLocalVarDecl() && typeShouldBeVisited(DeclType) && VD->getInit() &&
       !VD->getInit()->isValueDependent()) {
     if (VD->evaluateValue()) {
+      bool IsConstexprUnknown = !DeclType.isConstant(Ctx.getASTContext()) &&
+                                !DeclType->isReferenceType();
       // Revisit the variable declaration, but make sure it's associated with a
       // 
diff erent evaluation, so e.g. mutable reads don't work on it.
       EvalIDScope _(Ctx);
-      return revisit(VD);
-    }
+      return revisit(VD, IsConstexprUnknown);
+    } else if (Ctx.getLangOpts().CPlusPlus26 && IsReference)
+      return revisit(VD, true);
 
     if (IsReference)
       return this->emitInvalidDeclRef(cast<DeclRefExpr>(E),

diff  --git a/clang/lib/AST/ByteCode/Compiler.h 
b/clang/lib/AST/ByteCode/Compiler.h
index cd14f72b87f9f..4a70db89dba74 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -309,10 +309,8 @@ class Compiler : public 
ConstStmtVisitor<Compiler<Emitter>, bool>,
   bool delegate(const Expr *E);
   /// Creates and initializes a variable from the given decl.
   VarCreationState visitVarDecl(const VarDecl *VD, const Expr *Init,
-                                bool Toplevel = false,
-                                bool IsConstexprUnknown = false);
-  VarCreationState visitDecl(const VarDecl *VD,
-                             bool IsConstexprUnknown = false);
+                                bool Toplevel = false);
+  VarCreationState visitDecl(const VarDecl *VD);
   /// Visit an APValue.
   bool visitAPValue(const APValue &Val, PrimType ValType, const Expr *E);
   bool visitAPValueInitializer(const APValue &Val, const Expr *E, QualType T);
@@ -332,13 +330,11 @@ class Compiler : public 
ConstStmtVisitor<Compiler<Emitter>, bool>,
   /// Creates a local primitive value.
   unsigned allocateLocalPrimitive(DeclTy &&Decl, PrimType Ty, bool IsConst,
                                   bool IsVolatile = false,
-                                  ScopeKind SC = ScopeKind::Block,
-                                  bool IsConstexprUnknown = false);
+                                  ScopeKind SC = ScopeKind::Block);
 
   /// Allocates a space storing a local given its type.
   UnsignedOrNone allocateLocal(DeclTy &&Decl, QualType Ty = QualType(),
-                               ScopeKind = ScopeKind::Block,
-                               bool IsConstexprUnknown = false);
+                               ScopeKind = ScopeKind::Block);
   UnsignedOrNone allocateTemporary(const Expr *E);
 
 private:
@@ -456,6 +452,8 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, 
bool>,
   bool InStmtExpr = false;
   bool ToLValue = false;
 
+  bool VariablesAreConstexprUnknown = false;
+
   /// Flag inidicating if we're initializing an already created
   /// variable. This is set in visitInitializer().
   bool Initializing = false;

diff  --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index 0a13456e6026d..81b9f6d58f8a3 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -882,9 +882,9 @@ bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer 
&Ptr,
 }
 
 static bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
-  if (!CheckLive(S, OpPC, Ptr, AK_MemberCall))
-    return false;
-  if (!Ptr.isDummy()) {
+  if (!Ptr.isDummy() && !isConstexprUnknown(Ptr)) {
+    if (!CheckLive(S, OpPC, Ptr, AK_MemberCall))
+      return false;
     if (!CheckExtern(S, OpPC, Ptr))
       return false;
     if (!CheckRange(S, OpPC, Ptr, AK_MemberCall))

diff  --git a/clang/test/AST/ByteCode/codegen-constexpr-unknown.cpp 
b/clang/test/AST/ByteCode/codegen-constexpr-unknown.cpp
index d5312c09d0bd8..8b70f5a4251ad 100644
--- a/clang/test/AST/ByteCode/codegen-constexpr-unknown.cpp
+++ b/clang/test/AST/ByteCode/codegen-constexpr-unknown.cpp
@@ -1,5 +1,5 @@
 // RUN: %clang_cc1 -triple x86_64-linux -emit-llvm -fcxx-exceptions -o - %s    
                                              | FileCheck %s 
--check-prefix=CHECK
-// RUN: %clang_cc1 -triple x86_64-linux -emit-llvm -fcxx-exceptions -o - %s 
-fexperimental-new-constant-interpreter -DINTERP | FileCheck %s 
--check-prefix=CHECK,INTERP
+// RUN: %clang_cc1 -triple x86_64-linux -emit-llvm -fcxx-exceptions -o - %s 
-fexperimental-new-constant-interpreter -DINTERP | FileCheck %s 
--check-prefix=CHECK
 
 /// CodeGenFunction::ConstantFoldsToSimpleInteger() for the if condition
 /// needs to succeed and return true.
@@ -7,8 +7,6 @@
 /// variable to the topmost scope, otherwise we will pick the call scope
 /// of to_address and de-allocate the local variable at the end of the
 /// to_address call.
-/// FIXME: This is not currently correct since we still mark p as
-/// constexpr-unknown and then reject it when comparing.
 extern void abort2();
 constexpr const int* to_address(const int *a) {
   return a;
@@ -23,7 +21,6 @@ void rightscope() {
 // CHECK-NEXT: entry:
 // CHECK-NEXT: %p = alloca i32
 // CHECK-NEXT: store i32 0, ptr %p
-// INTERP-NEXT: call noundef ptr @_Z10to_addressPKi(ptr noundef %p)
 
 
 /// In the if expression below, the read from s.i should fail.

diff  --git a/clang/test/AST/ByteCode/cxx26.cpp 
b/clang/test/AST/ByteCode/cxx26.cpp
index 8035a2640ff5f..d4349d84996cc 100644
--- a/clang/test/AST/ByteCode/cxx26.cpp
+++ b/clang/test/AST/ByteCode/cxx26.cpp
@@ -62,3 +62,14 @@ namespace ExplicitThisInBacktrace {
   static_assert(test()); // both-error {{not an integral constant expression}} 
\
                          // both-note {{in call to}}
 }
+
+namespace ConstexprUnknownNestedVariables {
+  struct T { constexpr int a() const { return 42; } };
+  constexpr const T& f(const T& t) noexcept { return t; }
+  constexpr int f() {
+      const T& range = f(T());
+      return [&] consteval { return range.a(); }();
+  }
+
+  static_assert(f() == 42);
+}


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to