On 1/23/26 10:03 PM, Iain Sandoe wrote:
Hi Jason

(I saw Jakub’s reply and will aim to have the revised set ready for Monday)

At present, the individual patches do build incrementally, but maybe it makes
more sense to commit them as just two (remove old, add new).  Do you have
a preference?

Not a strong preference, whatever makes more sense to you.

One immediate reply, the rest I’ll work into v4.

On 23 Jan 2026, at 13:27, Jason Merrill <[email protected]> wrote:

On 1/19/26 10:57 PM, Iain Sandoe wrote:

@@ -2123,6 +2124,32 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void 
*data)
       wtd->bind_expr_stack.pop ();
       break;
+    case ASSERTION_STMT:
+    case PRECONDITION_STMT:
+    case POSTCONDITION_STMT:
+      {
+ tree check = build_contract_check (stmt);
+ if (check)
+  /* We need to genericize the contract independently of everything
+   else we genericized until now.  When recursively genericizing, we
+   keep a track of all the statements that have been seen.  Building a
+   contract check will create new statements, which may reuse an
+   already freed statement.  If such an already freed statement has
+   been cached in p_set, we will fail to correctly genericize newly
+   created contract tree.  */

Isn't this avoided by all the copying done elsewhere?

Also I have trouble imagining how a new contract check would involve already 
freed code.

This is AFAICT a latent issue that happens only to fire for contracts so far.

It only affects statement lists, and in a particular way that is unique to them.

When we free a statement list it gets pushed onto the top of a stack of
statement lists - the next statement list created will be popped from that.

In this case, the non-genericised code contains a statement list that is freed
by the genericisation - but, by the time it is freed, it has already been 
marked as visited.

So now we want a new statement list for the genericised code - and
the underlying support picks the one from the top of the list.

We now exit that level and we should see that the new statement list
needs to be visited - but since it was already marked at the outer
level we do not do so… and things go sideways.

Ah, interesting, that makes sense.

Maybe we should just not add STATEMENT_LIST to p_set, since this seems like a generic issue that the contract code just happens to exercise.

+  cp_genericize_tree (&check, wtd->handle_invisiref_parm_p);
+ else
+  /* If we didn't build a check, replace it with void_node so we don't
+  leak contracts into GENERIC.  */
+  check = void_node;
+ *stmt_p = check;
+ *walk_subtrees = 0;
+ /* Return early and do not add the contract statement into the
+   cache.  */
Likewise, I'm not sure why this is needed.

Not adding the list to the visited set means that the re-use of the
same statement list does not prevent the newly genericised code
from being visited.

The seemed the least invasive way to handle the case.
Avoiding adding STATEMENT_LIST ought to cover this as well.

I tried quite hard to find a reproducer that was non-contracts, but
without sucesss - the contract expansions are relatively complex
perhaps.

Iain


Reply via email to