[clang] [clang][bytecode] diagnose reads from non-constant local variables... (PR #208990)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Sat Jul 11 23:54:52 PDT 2026
https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/208990
... if we're in the bottom frame.
>From 51b228649819fb88f1c0164fac300b77884d061b 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/Interp.cpp | 12 +++++++++++-
clang/test/AST/ByteCode/literals.cpp | 20 ++++++++++++++++----
2 files changed, 27 insertions(+), 5 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 0adcdfe6e78c9..169aab5ed9f3d 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -857,11 +857,12 @@ 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);
@@ -872,6 +873,15 @@ bool CheckLocalLoad(InterpState &S, CodePtr OpPC, const Block *B) {
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