https://gcc.gnu.org/g:d341b0d57fc61e58598f072a5c2be1f8a5873ff3

commit r16-7059-gd341b0d57fc61e58598f072a5c2be1f8a5873ff3
Author: Jakub Jelinek <[email protected]>
Date:   Tue Jan 27 10:19:39 2026 +0100

    c++: Fix ICE in cxx_printable_name_internal [PR123578]
    
    On the following testcase, we end up with cxx_printable_name_internal
    recursion, in particular
    +../../gcc/cp/pt.cc:20736
    +../../gcc/cp/error.cc:625
    +template_args=<tree_vec 0x7fffe94b59b0>, flags=4) at 
../../gcc/cp/error.cc:1876
    The ICE is due to double free, that function is doing caching of up to 4
    printed names, but if such a recursion happens, the inner call can change
    ring_counter etc. and the caller will then store the result in a different
    ring element from what was freed and so the freed one can be left
    unmodified.
    
    The patch fixes it by moving the lang_decl_name call earlier, after the:
      /* See if this print name is lying around.  */
      for (i = 0; i < PRINT_RING_SIZE; i++)
        if (uid_ring[i] == DECL_UID (decl) && translate == trans_ring[i])
          /* yes, so return it.  */
          return print_ring[i];
    loop and repeating the loop again just for the theoretical case
    that some recursion would add the same entry.
    
    The ring_counter adjustment and decision which cache entry to reuse
    for the cache is then done without the possibility of ring_counter
    or the cache being changed in the middle.
    
    2026-01-27  Jakub Jelinek  <[email protected]>
    
            PR c++/123578
            * tree.cc (cxx_printable_name_internal): Call lang_decl_name before
            finding the slot to cache it in and repeat search in the cache
            after the call.
    
            * g++.dg/cpp2a/pr123578.C: New test.

Diff:
---
 gcc/cp/tree.cc                        | 11 ++++++++++-
 gcc/testsuite/g++.dg/cpp2a/pr123578.C |  9 +++++++++
 2 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index bb00fff4a772..acc6e31be658 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -2859,6 +2859,15 @@ cxx_printable_name_internal (tree decl, int v, bool 
translate)
       /* yes, so return it.  */
       return print_ring[i];
 
+  const char *ret = lang_decl_name (decl, v, translate);
+
+  /* The lang_decl_name call could have called this function recursively,
+     so check again.  */
+  for (i = 0; i < PRINT_RING_SIZE; i++)
+    if (uid_ring[i] == DECL_UID (decl) && translate == trans_ring[i])
+      /* yes, so return it.  */
+      return print_ring[i];
+
   if (++ring_counter == PRINT_RING_SIZE)
     ring_counter = 0;
 
@@ -2878,7 +2887,7 @@ cxx_printable_name_internal (tree decl, int v, bool 
translate)
 
   free (print_ring[ring_counter]);
 
-  print_ring[ring_counter] = xstrdup (lang_decl_name (decl, v, translate));
+  print_ring[ring_counter] = xstrdup (ret);
   uid_ring[ring_counter] = DECL_UID (decl);
   trans_ring[ring_counter] = translate;
   return print_ring[ring_counter];
diff --git a/gcc/testsuite/g++.dg/cpp2a/pr123578.C 
b/gcc/testsuite/g++.dg/cpp2a/pr123578.C
new file mode 100644
index 000000000000..816a104c6bbe
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/pr123578.C
@@ -0,0 +1,9 @@
+// PR c++/123578
+// { dg-do compile { target c++20 } }
+// { dg-options "-fdump-tree-all" }
+
+namespace {
+  template <typename>
+  struct A { A (decltype ([] { return 0; } ())) {} };
+  A <int> b = 0;
+}

Reply via email to