[clang] 8edbf23 - [clang][bytecode] Revisit reference variables as constexpr-unknown in C++26 (#187918)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 1 00:50:00 PDT 2026
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);
+}
More information about the cfe-commits
mailing list