https://gcc.gnu.org/g:7b60a368fb19b33f8676482a233d389cbc47b85e

commit r17-4235-g7b60a368fb19b33f8676482a233d389cbc47b85e
Author: Joshua Berne <[email protected]>
Date:   Mon Sep 14 10:16:28 2026 -0400

    c++: fix nested instances of modifiable_tracker [PR127282]
    
    PR c++/127282 - [[assume]]: a nested assumption's side effects are not
    rolled back during constant evaluation
    
    During constant evaluation of the predicate of an [[assume]] attribute a
    pointer to a set of modifiable values is stored in the
    constexpr_global_ctx.  The class modifiable_tracker populates this value
    when evaluation of the predicate starts and clears it when it is
    complete, which results in nullptr instead of the original set when an
    [[assume]] is evaluated during the evaluation of the predicate of
    another assume.
    
    Fix this by storing the previous value (nullptr in non-nested cases) and
    restoring it upon destruction instead of clearing the global value.
    
            PR c++/127282
    
    gcc/cp/ChangeLog:
    
            * constexpr.cc (modifiable_tracker::modifiable_tracker): Save the
            current global pointer to the modifiable set.
            (modifiable_tracker::~modifiable_tracker): Restore the original
            pointer to the modifiable set instead of clearing it.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/cpp23/attr-assume10.C: Add a case with a nested assumption.
    
    Signed-off-by: Joshua Berne <[email protected]>

Diff:
---
 gcc/cp/constexpr.cc                        | 10 +++++++---
 gcc/testsuite/g++.dg/cpp23/attr-assume10.C | 19 ++++++++++++++++++-
 2 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index 7219c55c9ceb..18d361c24325 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -1298,7 +1298,9 @@ public:
 /* Helper class for constexpr_global_ctx.  In some cases we want to avoid
    side-effects from evaluation of a particular subexpression of a
    constant-expression.  In such cases we use modifiable_tracker to prevent
-   modification of variables created outside of that subexpression.
+   modification of variables created outside of that subexpression.  Keep and
+   then restore the original value of global->modifiable so that tracker
+   instantiations can nest.
 
    ??? We could change the hash_set to a hash_map, allow and track external
    modifications, and roll them back in the destructor.  It's not clear to me
@@ -1308,8 +1310,10 @@ class modifiable_tracker
 {
   hash_set<tree> set;
   constexpr_global_ctx *global;
+  hash_set<tree> *previous_set;
 public:
-  modifiable_tracker (constexpr_global_ctx *g): global(g)
+  modifiable_tracker (constexpr_global_ctx *g)
+    : global (g), previous_set (g->modifiable)
   {
     global->modifiable = &set;
   }
@@ -1317,7 +1321,7 @@ public:
   {
     for (tree t: set)
       global->clear_value (t);
-    global->modifiable = nullptr;
+    global->modifiable = previous_set;
   }
 };
 
diff --git a/gcc/testsuite/g++.dg/cpp23/attr-assume10.C 
b/gcc/testsuite/g++.dg/cpp23/attr-assume10.C
index 475555ac415d..3f4af3fae95e 100644
--- a/gcc/testsuite/g++.dg/cpp23/attr-assume10.C
+++ b/gcc/testsuite/g++.dg/cpp23/attr-assume10.C
@@ -1,4 +1,4 @@
-// Test that s.i is not modified by the assume.
+// Test that s.i is not modified by assume or a nested assume.
 // { dg-do compile { target c++17 } }
 
 struct string
@@ -20,3 +20,20 @@ constexpr int f()
 }
 
 static_assert (f());
+
+// PR c++/127282, a nested assume also needs to not modify values or allow
+// other expressions within the enclosing assume to modify values.
+constexpr int g()
+{
+  string s ("foobar");
+  [[assume (
+      [&](){[[assume (s.length () > 0)]]; return true; }()
+   && s.length () > 0)
+  ]];
+  if (s.i != 0) __builtin_abort();
+  int len = s.length ();
+  if (s.i != 1) __builtin_abort();
+  return len;
+}
+
+static_assert (g());

Reply via email to