]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: Do not mark STATEMENT_LISTs as visited in genericization.
authorIain Sandoe <iain@sandoe.co.uk>
Sat, 24 Jan 2026 14:22:43 +0000 (14:22 +0000)
committerIain Sandoe <iain@sandoe.co.uk>
Sun, 25 Jan 2026 22:55:51 +0000 (22:55 +0000)
This addresses a latent issue in C++ genericization (only seen
in development code, so far).

In the following code snippet using facilities from the proposed
C++26 contracts implementation:

    while (!SWAPPER::isFinished()) {
        uc = SWAPPER::swapBytes();
        if (0 != uc) {
        }
    }
    contract_assert( translator.d_capacity >= 1 );

During genericization, a statement list from the while loop is freed.
The expansion of the contract_assert then requires a 'new' statement list.

Since the statment list in the while has been visited, it was marked as
such.

A specific property of statement lists is that they are cached using a
LIFO stack.  So that the statement list picked for the contract_assert
is the one freed from the while loop.  However since that list entry
has already been marked as visited, the newly created contract expansion
is not visited (leading to an ICE).

The solution here is to forgo marking STATEMENT_LISTs as visited in this
code (which is provision for potential future cases, as well as resolving
the specific instance seen).

gcc/cp/ChangeLog:

* cp-gimplify.cc (cp_genericize_r): Do not mark STATEMENT_LISTs
as visited.

Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
gcc/cp/cp-gimplify.cc

index 3c0f415fa87cf50cdd060bec23a24fb16a02b446..30d6a07e376dd2a99cbfad5f8b26454cbb48b7d8 100644 (file)
@@ -2503,12 +2503,24 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
     case OMP_TILE:
     case OMP_UNROLL:
     case OACC_LOOP:
-    case STATEMENT_LIST:
       /* These cases are handled by shared code.  */
       c_genericize_control_stmt (stmt_p, walk_subtrees, data,
                                 cp_genericize_r, cp_walk_subtrees);
       break;
 
+    case STATEMENT_LIST:
+      /* As above, handled by shared code.  */
+      c_genericize_control_stmt (stmt_p, walk_subtrees, data,
+                                cp_genericize_r, cp_walk_subtrees);
+      /* If a statement list is freed as part of genericisation it will be
+        pushed onto the top of a statement list cache stack.  A subsequent
+        action can cause a new statement list to be required - and the one
+        just pushed will be returned.  If that is marked as visited, it can
+        prevent a tail recursion from processing the 'new' statement list,
+        so we do not mark statement lists as visited.  */
+      return NULL_TREE;
+      break;
+
     case BIT_CAST_EXPR:
       *stmt_p = build1_loc (EXPR_LOCATION (stmt), VIEW_CONVERT_EXPR,
                            TREE_TYPE (stmt), TREE_OPERAND (stmt, 0));