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

Reply via email to