]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: constexpr call hashing and gc [PR124632]
authorJason Merrill <jason@redhat.com>
Fri, 17 Apr 2026 02:41:24 +0000 (22:41 -0400)
committerJason Merrill <jason@redhat.com>
Fri, 17 Apr 2026 02:41:24 +0000 (22:41 -0400)
In cxx_eval_call_expression, we unshare an argument that goes into
constexpr_call::bindings so it's independent, and then if it's a CONSTRUCTOR
we unshare it again before putting it in the constexpr value hashtable so
any modifications within the function don't affect the bindings.  And then
we free these latter CONSTRUCTOR if they aren't part of the return value.

In explicit-obj-lambda2.C, cl5 is a recursive constexpr lambda that takes a
class (the closure) by value.  If -Wtautological-compare is enabled, we try
to constant-fold cl5(5) to see if has a constant value.  In evaluating the
outer call we have an empty CONSTRUCTOR argument {} that we unshare twice.

When we try to evaluate the recursive call, the second unshare from the
first call initially goes into bindings.  But since this is a recursive call
get_fundef_copy needs need to make a copy of the FUNCTION_DECL, and since
this evaluation is for a warning uid_sensitive_constexpr_evaluation_p is
true, so we can't make a copy, so evaluating the recursive copy fails
without ever unsharing the argument.

Then when we get back to finishing up the outer call we ggc_free the
unshared CONSTRUCTOR that we don't need anymore.  But the recursive call
already added it to the constexpr_call_table, so now the entry there is
referring to freed memory.

The solution is doing the first unshare immediately when we create the entry
in the constexpr_call_table, not later after get_fundef_copy.  This should
also mean less unsharing, as we now only do the first unshare when creating
the constexpr_call_table entry, not on subsequent evaluations.  This only
affects uncacheable calls, but that's a significant subset.

PR c++/124632

gcc/cp/ChangeLog:

* constexpr.cc (cxx_eval_call_expression): Unshare bindings
sooner.

gcc/testsuite/ChangeLog:

* g++.dg/cpp23/explicit-obj-lambda2.C: Do strict GC and -Wall.

gcc/cp/constexpr.cc
gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda2.C

index fb9ae760e92d4ae3d43075d636ddfbb0f048cac8..5b7d385fa9724968e17d3aba38cd41a62b20931d 100644 (file)
@@ -4395,6 +4395,16 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
              *entry = new_call;
              entry->result (ctx->manifestly_const_eval) = unknown_type_node;
              fb.preserve ();
+
+             /* Unshare args going into the hash table to separate them
+                from the caller's context, for better GC and to avoid
+                problems with verify_gimple.  */
+             tree bound = new_call.bindings;
+             for (int i = 0; i < TREE_VEC_LENGTH (bound); ++i)
+               {
+                 tree &arg = TREE_VEC_ELT (bound, i);
+                 arg = unshare_expr_without_location (arg);
+               }
            }
        }
       /* Calls that are in progress have their result set to unknown_type_node,
@@ -4453,13 +4463,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
              tree arg = TREE_VEC_ELT (bound, i);
              if (entry)
                {
-                 /* Unshare args going into the hash table to separate them
-                    from the caller's context, for better GC and to avoid
-                    problems with verify_gimple.  */
-                 arg = unshare_expr_without_location (arg);
-                 TREE_VEC_ELT (bound, i) = arg;
-
-                 /* And then unshare again so the callee doesn't change the
+                 /* Unshare again so the callee doesn't change the
                     argument values in the hash table. XXX Could we unshare
                     lazily in cxx_eval_store_expression?  */
                  arg = unshare_constructor (arg);
index 827197a66674f67ca46a0ca16d34d797bac91276..4979ad57a14da8b8141707e441a415c643367125 100644 (file)
@@ -1,6 +1,9 @@
 // P0847R7
 // { dg-do run { target c++23 } }
 
+// PR c++/124632
+// { dg-additional-options "-Wall --param ggc-min-expand=0 --param ggc-min-heapsize=0" }
+
 // recursive lambdas
 
 inline constexpr int correct_result = 5 + 4 + 3 + 2 + 1;