[clang] 6d64753 - [clang][bytecode] Don't ignore discarded `DeclRefExpr`s (#204165)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Jun 25 09:36:32 PDT 2026
Author: Timm Baeder
Date: 2026-06-25T18:36:27+02:00
New Revision: 6d6475328be051b35b22c5e72a0c4aa72c3815c4
URL: https://github.com/llvm/llvm-project/commit/6d6475328be051b35b22c5e72a0c4aa72c3815c4
DIFF: https://github.com/llvm/llvm-project/commit/6d6475328be051b35b22c5e72a0c4aa72c3815c4.diff
LOG: [clang][bytecode] Don't ignore discarded `DeclRefExpr`s (#204165)
Check for the evaluating decl in `GetRefGlobal` so we don't fail too
early. We also need to mark the `APValue` as constexpr-unknown when
returning it, even though the backing `Descriptor` is not marked
constexpr-unknown.
This fixes the last differences in `constant-expression-p2280r4.cpp`.
Added:
Modified:
clang/lib/AST/ByteCode/Compiler.cpp
clang/lib/AST/ByteCode/EvalEmitter.cpp
clang/lib/AST/ByteCode/Interp.h
clang/test/SemaCXX/constant-expression-p2280r4.cpp
Removed:
################################################################################
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 389a0094509a9..20b110b38ff78 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -7672,16 +7672,21 @@ bool Compiler<Emitter>::VisitVectorUnaryOperator(const UnaryOperator *E) {
template <class Emitter>
bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
- if (DiscardResult)
- return true;
-
- if (const auto *ECD = dyn_cast<EnumConstantDecl>(D))
+ if (const auto *ECD = dyn_cast<EnumConstantDecl>(D)) {
+ if (DiscardResult)
+ return true;
return this->emitConst(ECD->getInitVal(), E);
+ }
if (const auto *FuncDecl = dyn_cast<FunctionDecl>(D)) {
+ if (DiscardResult)
+ return true;
const Function *F = getFunction(FuncDecl);
return F && this->emitGetFnPtr(F, E);
}
if (const auto *TPOD = dyn_cast<TemplateParamObjectDecl>(D)) {
+ if (DiscardResult)
+ return true;
+
if (UnsignedOrNone Index = P.getOrCreateGlobal(D)) {
if (OptPrimType T = classify(D->getType())) {
if (!this->visitAPValue(TPOD->getValue(), *T, E))
@@ -7705,10 +7710,19 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
QualType DeclType = D->getType();
bool IsReference = DeclType->isReferenceType();
+ auto maybePopPtr = [&]() -> bool {
+ if (DiscardResult)
+ return this->emitPopPtr(E);
+ return true;
+ };
+
// Function parameters.
// Note that it's important to check them first since we might have a local
// variable created for a ParmVarDecl as well.
if (const auto *PVD = dyn_cast<ParmVarDecl>(D)) {
+ if (DiscardResult)
+ return true;
+
if (Ctx.getLangOpts().CPlusPlus && !Ctx.getLangOpts().CPlusPlus11 &&
!DeclType->isIntegralOrEnumerationType()) {
return this->emitInvalidDeclRef(cast<DeclRefExpr>(E),
@@ -7730,9 +7744,9 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
const unsigned Offset = It->second.Offset;
if (IsReference) {
assert(classifyPrim(E) == PT_Ptr);
- return this->emitGetRefLocal(Offset, E);
+ return this->emitGetRefLocal(Offset, E) && maybePopPtr();
}
- return this->emitGetPtrLocal(Offset, E);
+ return this->emitGetPtrLocal(Offset, E) && maybePopPtr();
}
// Global variables.
if (auto GlobalIndex = P.getGlobal(D)) {
@@ -7741,10 +7755,11 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
return this->emitGetGlobal(classifyPrim(E), *GlobalIndex, E);
if (!Ctx.getLangOpts().CPlusPlus23)
return this->emitGetGlobalUnchecked(classifyPrim(E), *GlobalIndex, E);
- return this->emitGetRefGlobal(*GlobalIndex, E);
+
+ return this->emitGetRefGlobal(*GlobalIndex, E) && maybePopPtr();
}
- return this->emitGetPtrGlobal(*GlobalIndex, E);
+ return this->emitGetPtrGlobal(*GlobalIndex, E) && maybePopPtr();
}
// In case we need to re-visit a declaration.
@@ -7774,8 +7789,8 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
auto [Offset, IsPtr] = It->second;
if (IsPtr)
- return this->emitGetThisFieldPtr(Offset, E);
- return this->emitGetPtrThisField(Offset, E);
+ return this->emitGetThisFieldPtr(Offset, E) && maybePopPtr();
+ return this->emitGetPtrThisField(Offset, E) && maybePopPtr();
}
}
@@ -7786,11 +7801,14 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
}
if (const auto *BD = dyn_cast<BindingDecl>(D))
- return this->visit(BD->getBinding());
+ return this->delegate(BD->getBinding());
// Avoid infinite recursion.
- if (D == InitializingDecl)
+ if (D == InitializingDecl) {
+ if (DiscardResult)
+ return true;
return this->emitDummyPtr(D, E);
+ }
// Try to lazily visit (or emit dummy pointers for) declarations
// we haven't seen yet.
@@ -7804,6 +7822,9 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
DeclType.isConstant(Ctx.getASTContext()) && !VD->isWeak() &&
VD->evaluateValue())
return revisit(VD, /*IsConstexprUnknown=*/false);
+
+ if (DiscardResult)
+ return true;
return this->emitDummyPtr(D, E);
}
@@ -7849,6 +7870,8 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
/*InitializerFailed=*/true, E);
}
+ if (DiscardResult)
+ return true;
return this->emitDummyPtr(
D, E, Ctx.getLangOpts().CPlusPlus23 && DeclType->isReferenceType());
}
diff --git a/clang/lib/AST/ByteCode/EvalEmitter.cpp b/clang/lib/AST/ByteCode/EvalEmitter.cpp
index e862d945e020f..83dac57353944 100644
--- a/clang/lib/AST/ByteCode/EvalEmitter.cpp
+++ b/clang/lib/AST/ByteCode/EvalEmitter.cpp
@@ -281,7 +281,17 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(SourceInfo Info) {
if (!Ptr.isLive() && !Ptr.isTemporary())
return false;
- EvalResult.takeValue(Ptr.toAPValue(Ctx.getASTContext()));
+ // If the variable of this pointer is being evaluated when returning
+ // its value, mark it as constexpr-unknown.
+ APValue V = Ptr.toAPValue(Ctx.getASTContext());
+ if (const Descriptor *DeclDesc = Ptr.getDeclDesc();
+ DeclDesc && S.EvaluatingDecl &&
+ DeclDesc->asVarDecl() == S.EvaluatingDecl &&
+ S.getLangOpts().CPlusPlus23 &&
+ S.EvaluatingDecl->getType()->isReferenceType()) {
+ V.setConstexprUnknown(true);
+ }
+ EvalResult.takeValue(std::move(V));
}
return true;
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index ed640f7325f7e..aa2dffc2b982a 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -2052,6 +2052,14 @@ inline bool GetRefLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
inline bool GetRefGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
Block *B = S.P.getGlobal(I);
+ // If we're currently evaluating this variable, use that in-flight value.
+ // It will otherwise be diagnosed as non-initialized reference and we will
+ // complain about a missing initializer.
+ if (S.EvaluatingDecl && B->getDescriptor()->asVarDecl() == S.EvaluatingDecl) {
+ S.Stk.push<Pointer>(B);
+ return true;
+ }
+
if (isConstexprUnknown(B)) {
S.Stk.push<Pointer>(B);
return true;
diff --git a/clang/test/SemaCXX/constant-expression-p2280r4.cpp b/clang/test/SemaCXX/constant-expression-p2280r4.cpp
index bb5499e57ac78..77a9891e6620c 100644
--- a/clang/test/SemaCXX/constant-expression-p2280r4.cpp
+++ b/clang/test/SemaCXX/constant-expression-p2280r4.cpp
@@ -205,8 +205,8 @@ int f() {
namespace uninit_reference_used {
int y;
constexpr int &r = r; // expected-error {{must be initialized by a constant expression}} \
- // expected-note {{initializer of 'r' is not a constant expression}} \
- // expected-note {{declared here}}
+ // expected-note {{initializer of 'r' is not a constant expression}} \
+ // expected-note {{declared here}}
constexpr int &rr = (rr, y);
constexpr int &g() {
int &x = x; // expected-warning {{reference 'x' is not yet bound to a value when used within its own initialization}} \
@@ -224,12 +224,12 @@ namespace uninit_reference_used {
// expected-note {{in call to 'g2()'}}
constexpr int &g3() {
int &x = (x,y); // expected-warning{{left operand of comma operator has no effect}} \
- // expected-warning {{reference 'x' is not yet bound to a value when used within its own initialization}} \
- // nointerpreter-note {{use of reference outside its lifetime is not allowed in a constant expression}}
+ // expected-warning {{reference 'x' is not yet bound to a value when used within its own initialization}} \
+ // expected-note {{use of reference outside its lifetime is not allowed in a constant expression}}
return x;
}
- constexpr int &gg3 = g3(); // nointerpreter-error {{must be initialized by a constant expression}} \
- // nointerpreter-note {{in call to 'g3()'}}
+ constexpr int &gg3 = g3(); // expected-error {{must be initialized by a constant expression}} \
+ // expected-note {{in call to 'g3()'}}
typedef decltype(sizeof(1)) uintptr_t;
constexpr uintptr_t g4() {
uintptr_t * &x = x; // expected-warning {{reference 'x' is not yet bound to a value when used within its own initialization}} \
More information about the cfe-commits
mailing list