https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/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. >From a665ca1a1990bc3ad5da49e32c217d6b7481ae0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]> Date: Wed, 17 Jun 2026 10:32:17 +0200 Subject: [PATCH] asdf --- clang/lib/AST/ByteCode/Interp.cpp | 45 +++++++++++++++++++++++++++---- clang/test/AST/ByteCode/cxx20.cpp | 34 +++++++++++++++++++++++ 2 files changed, 74 insertions(+), 5 deletions(-) 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)'}} +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
