[clang] [clang][bytecode] diagnose reads from non-constant local variables... (PR #208990)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Sun Jul 12 01:36:01 PDT 2026
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/208990
>From f749225e6cc111dc35ca5fc99e78e20f8bdaa8a0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Sun, 12 Jul 2026 08:22:49 +0200
Subject: [PATCH] diagnose reads from non-const locals
---
clang/lib/AST/ByteCode/Compiler.cpp | 6 +++++-
clang/lib/AST/ByteCode/Interp.cpp | 14 ++++++++++++--
clang/test/AST/ByteCode/literals.cpp | 20 ++++++++++++++++----
3 files changed, 33 insertions(+), 7 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 869d3a8767617..394541e78d874 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -5501,6 +5501,7 @@ bool Compiler<Emitter>::visitDeclAndReturn(const VarDecl *VD, const Expr *Init,
return false;
OptPrimType VarT = classify(VD->getType());
+ bool IsReference = VD->getType()->isReferenceType();
if (Context::shouldBeGloballyIndexed(VD)) {
auto GlobalIndex = P.getGlobal(VD);
assert(GlobalIndex); // visitVarDecl() didn't return false.
@@ -5515,7 +5516,10 @@ bool Compiler<Emitter>::visitDeclAndReturn(const VarDecl *VD, const Expr *Init,
auto Local = Locals.find(VD);
assert(Local != Locals.end()); // Same here.
if (VarT) {
- if (!this->emitGetLocal(*VarT, Local->second.Offset, VD))
+ if (IsReference) {
+ if (!this->emitGetRefLocal(Local->second.Offset, VD))
+ return false;
+ } else if (!this->emitGetLocal(*VarT, Local->second.Offset, VD))
return false;
} else {
if (!this->emitGetPtrLocal(Local->second.Offset, VD))
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index e4675978e2a18..20235b07fcc47 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -857,21 +857,31 @@ bool CheckGlobalLoad(InterpState &S, CodePtr OpPC, const Block *B) {
bool CheckLocalLoad(InterpState &S, CodePtr OpPC, const Block *B) {
assert(!B->isExtern());
const auto &Desc = *reinterpret_cast<const InlineDescriptor *>(B->rawData());
+ const Descriptor *BlockDesc = B->getDescriptor();
if (!CheckLifetime(S, OpPC, Desc.LifeState, B, AK_Read))
return false;
if (!Desc.IsInitialized)
return DiagnoseUninitialized(S, OpPC, /*Extern=*/false, B, AK_Read);
- if (B->getDescriptor()->IsVolatile) {
+ if (BlockDesc->IsVolatile) {
if (!S.getLangOpts().CPlusPlus)
return Invalid(S, OpPC);
- const ValueDecl *D = B->getDescriptor()->asValueDecl();
+ const ValueDecl *D = BlockDesc->asValueDecl();
S.FFDiag(S.Current->getLocation(OpPC),
diag::note_constexpr_access_volatile_obj, 1)
<< AK_Read << 1 << D;
S.Note(D->getLocation(), diag::note_constexpr_volatile_here) << 1;
return false;
}
+
+ // A non-const local variable while we don't have a parent frame. This must be
+ // a local variable in a statement expression.
+ if (S.Current->isBottomFrame() && !BlockDesc->IsConst &&
+ !BlockDesc->IsTemporary && !S.checkingPotentialConstantExpression()) {
+ if (const ValueDecl *VD = BlockDesc->asValueDecl())
+ diagnoseNonConstVariable(S, OpPC, VD);
+ return false;
+ }
return true;
}
diff --git a/clang/test/AST/ByteCode/literals.cpp b/clang/test/AST/ByteCode/literals.cpp
index eafab2e1ab64d..3b24b166e39a7 100644
--- a/clang/test/AST/ByteCode/literals.cpp
+++ b/clang/test/AST/ByteCode/literals.cpp
@@ -1326,17 +1326,29 @@ namespace StmtExprs {
constexpr long a(bool x) { return x ? 0 : (intptr_t)&&lbl + (0 && ({lbl: 0;})); }
}
- /// GCC agrees with the bytecode interpreter here.
+ /// FIXME: This should be accepted.
+ /// BUT accepting this breaks test/OpenMP/for_codegen.cpp
void switchInSE() {
- static_assert(({ // ref-error {{not an integral constant expression}}
- int i = 20;
+ static_assert(({ // both-error {{not an integral constant expression}}
+ int i = 20; // expected-note {{declared here}}
switch(10) {
- case 10: i = 300; // ref-note {{a constant expression cannot modify an object that is visible outside that expression}}
+ case 10: i = 300; // ref-note {{a constant expression cannot modify an object that is visible outside that expression}} \
+ // expected-note {{read of non-const variable 'i'}}
}
i;
}) == 300);
}
+ /// We reject the test above because of the read from i, not because of the modification.
+ /// FIXME: The sourcelocation for the broken read is incorrect.
+ void switchInSE2() {
+ static_assert(({ // both-error {{not an integral constant expression}}
+ int i = 20; // expected-note {{read of non-const variable}} \
+ // both-note {{declared here}}
+ i; // ref-note {{read of non-const variable}}
+ }) == 300);
+ }
+
constexpr int f(int k) {
switch (k) {
case 0:
More information about the cfe-commits
mailing list