[clang] 80c80e6 - [clang][bytecode] Check const writes more thorougly (#204529)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Jun 19 05:20:41 PDT 2026
Author: Timm Baeder
Date: 2026-06-19T14:20:36+02:00
New Revision: 80c80e6d04515acf7dac8c256a1a3b3dc14cfa4d
URL: https://github.com/llvm/llvm-project/commit/80c80e6d04515acf7dac8c256a1a3b3dc14cfa4d
DIFF: https://github.com/llvm/llvm-project/commit/80c80e6d04515acf7dac8c256a1a3b3dc14cfa4d.diff
LOG: [clang][bytecode] Check const writes more thorougly (#204529)
We used to only have a list of blocks under construction, but now we
have a list of pointers, which gives us more information.
Use this new list to diagnose a case we couldn't previously diagnose.
The test case is from `constant-expression-cxx14.cpp` and shows that a
write to a const member is invalid, even if the parent object is being
constructed right now.
Added:
Modified:
clang/lib/AST/ByteCode/Interp.cpp
clang/test/AST/ByteCode/cxx20.cpp
Removed:
################################################################################
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index e5bf9c0c590ac..60914a2da111a 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -587,11 +587,44 @@ bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
// The This pointer is writable in constructors and destructors,
// even if isConst() returns true.
- if (S.initializingBlock(Ptr.block()))
- return true;
+ for (PtrView V : llvm::reverse(S.InitializingPtrs)) {
+ if (V.block() != Ptr.block())
+ continue;
+ if (!V.getFieldDesc()->IsConst) {
+ // If the pointer being initialized is not declared as const,
+ // Ptr is const because of a parent of V, but that is irrelevant
+ // since V is being initialized and NOT const.
+ // This is fine, so return true.
+ return true;
+ }
+
+ // We know that Ptr is const because of a parent field and we also
+ // know that V is explicitly marked const.
+ // But since V is in InitializingPtrs, the fact that it is const doesn't
+ // matter and it is writable.
+ // What we now need to check is whether there is a pointer between Ptr and V
+ // that is marked const but NOT in InitializingPtrs. If that is the case,
+ // Ptr is currently not writable.
+ bool FoundProblem = false;
+ for (PtrView P = Ptr.view(); P != V; P = P.getBase()) {
+ if (P.getFieldDesc()->IsConst) {
+ FoundProblem = true;
+ break;
+ }
+ }
+
+ // We couldn't find any pointer that's explicitly marked const, so
+ // Ptr is writable right now.
+ if (!FoundProblem)
+ return true;
+ // We only need to find the right block once.
+ break;
+ }
if (!S.checkingPotentialConstantExpression()) {
- const QualType Ty = Ptr.getType();
+ QualType Ty = Ptr.getType();
+ if (!Ptr.getFieldDesc()->IsConst)
+ Ty.addConst();
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_modify_const_type) << Ty;
}
@@ -1803,6 +1836,7 @@ bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
return false;
};
+ bool InstancePtrTracked = false;
if (Func->hasThisPointer()) {
size_t ArgSize = Func->getArgSize() + VarArgSize;
size_t ThisOffset = ArgSize - (Func->hasRVO() ? primSize(PT_Ptr) : 0);
@@ -1845,7 +1879,8 @@ bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
if (Func->isDestructor() && !CheckDestructor(S, OpPC, ThisPtr))
return false;
- if (Func->isConstructor() || Func->isDestructor())
+ InstancePtrTracked = (Func->isConstructor() || Func->isDestructor());
+ if (InstancePtrTracked)
S.InitializingPtrs.push_back(ThisPtr.view());
}
@@ -1872,7 +1907,7 @@ bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
InterpStateCCOverride CCOverride(S, Func->isImmediate());
bool Success = Interpret(S);
// Remove initializing block again.
- if (Func->isConstructor() || Func->isDestructor())
+ if (InstancePtrTracked)
S.InitializingPtrs.pop_back();
if (!Success) {
diff --git a/clang/test/AST/ByteCode/cxx20.cpp b/clang/test/AST/ByteCode/cxx20.cpp
index 625e65c769133..a6409d4a2c268 100644
--- a/clang/test/AST/ByteCode/cxx20.cpp
+++ b/clang/test/AST/ByteCode/cxx20.cpp
@@ -1423,3 +1423,37 @@ namespace FuncPtrRef {
}
static_assert(bullet_five_tests());
}
+
+namespace ConstWrites {
+ struct basic_string {
+ unsigned char a;
+ constexpr basic_string() {
+ a = false;
+ }
+ };
+ struct array {
+ basic_string str;
+ };
+
+ constexpr bool tests() {
+ const array right{};
+ return true;
+ }
+ static_assert(tests());
+
+ struct A {
+ int n;
+ constexpr A() : n(1) { n = 2; }
+ };
+ struct B {
+ const A a;
+ constexpr B(bool mutate) {
+ if (mutate)
+ const_cast<A &>(a).n = 3; // both-note {{modification of object of const-qualified type 'const int'}}
+ }
+ };
+ constexpr B b(false);
+ static_assert(b.a.n == 2, "");
+ constexpr B bad(true); // both-error {{must be initialized by a constant expression}} \
+ // both-note {{in call to 'B(true)'}}
+}
More information about the cfe-commits
mailing list