Hi!
On the following testcase, we end up with cxx_printable_name_internal
recursion, in particular
#1 0x00000000008b1059 in cxx_printable_name_internal (decl=<function_decl
0x7fffe9550900 operator()>, v=2, translate=false) at ../../gcc/cp/tree.cc:2839
#2 0x00000000008b1386 in cxx_printable_name (decl=<function_decl
0x7fffe9550900 operator()>, v=2) at ../../gcc/cp/tree.cc:2888
#3 0x00000000009a1278 in c_genericize (fndecl=<function_decl 0x7fffe9550900
operator()>) at ../../gcc/c-family/c-gimplify.cc:818
#4 0x00000000004fbb93 in cp_genericize (fndecl=<function_decl 0x7fffe9550900
operator()>) at ../../gcc/cp/cp-gimplify.cc:2612
#5 0x0000000000590f57 in finish_function (inline_p=true) at
../../gcc/cp/decl.cc:20551
#6 0x000000000060a387 in finish_lambda_function (body=<statement_list
0x7fffe95285a0>) at ../../gcc/cp/lambda.cc:1992
#7 0x00000000007fde45 in tsubst_lambda_expr (t=<lambda_expr 0x7fffe99f1320>,
args=<tree_vec 0x7fffe94b59b0>, complain=3, in_decl=<function_decl
0x7fffe9413f00 operator()>) at ../../gcc/cp/pt.cc:20736
#8 0x000000000080a349 in tsubst_expr (t=<lambda_expr 0x7fffe99f1320>,
args=<tree_vec 0x7fffe94b59b0>, complain=0, in_decl=<tree 0x0>) at
../../gcc/cp/pt.cc:22737
#9 0x0000000000803669 in tsubst_expr (t=<call_expr 0x7fffe941e4e0>,
args=<tree_vec 0x7fffe94b59b0>, complain=0, in_decl=<tree 0x0>) at
../../gcc/cp/pt.cc:21713
#10 0x00000000007e89d7 in tsubst (t=<decltype_type 0x7fffe9426b28>,
args=<tree_vec 0x7fffe94b59b0>, complain=0, in_decl=<tree 0x0>) at
../../gcc/cp/pt.cc:17425
#11 0x00000000005c0fd8 in dump_template_bindings (pp=0x4cb7840
<actual_pretty_printer>, parms=<tree 0x0>, args=<tree_vec 0x7fffe94b59b0>,
typenames=0x7fffe9552758 = {...}) at ../../gcc/cp/error.cc:625
#12 0x00000000005c8320 in dump_substitution (pp=0x4cb7840
<actual_pretty_printer>, t=<template_decl 0x7fffe9424908 __ct >,
template_parms=<tree_list 0x7fffe9422398>, template_args=<tree_vec
0x7fffe94b59b0>, flags=4) at ../../gcc/cp/error.cc:1876
#13 0x00000000005ca277 in dump_function_decl (pp=0x4cb7840
<actual_pretty_printer>, t=<template_decl 0x7fffe9424908 __ct >, flags=4) at
../../gcc/cp/error.cc:2056
#14 0x00000000005c7035 in dump_decl (pp=0x4cb7840 <actual_pretty_printer>,
t=<function_decl 0x7fffe94bda00 __ct >, flags=4) at ../../gcc/cp/error.cc:1602
#15 0x00000000005d1e9f in decl_as_string (decl=<function_decl 0x7fffe94bda00
__ct >, flags=4) at ../../gcc/cp/error.cc:3429
#16 0x00000000005d1f78 in lang_decl_name (decl=<function_decl 0x7fffe94bda00
__ct >, v=2, translate=false) at ../../gcc/cp/error.cc:3464
#17 0x00000000008b12aa in cxx_printable_name_internal (decl=<function_decl
0x7fffe94bda00 __ct >, v=2, translate=false) at ../../gcc/cp/tree.cc:2875
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 following patch fixes it by just not doing any caching if we've
recursed.
Now that I think about it again, another possibility would be to add
after:
/* 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];
following call:
const char *ret = xstrdup (lang_decl_name (decl, v, translate));
and then keep doing what it was with
print_ring[ring_counter] = ret;
instead of
print_ring[ring_counter] = xstrdup (lang_decl_name (decl, v, translate));
Though, I guess we'd still theoretically risk the lang_decl_name call
indirectly adding decl name to print_ring already and thus violating the
requirement that it will be there at most 2 times. So maybe it would
also need to repeat 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])
{
free (ret);
/* yes, so return it. */
return print_ring[i];
}
loop first with the free right after the
const char *ret = xstrdup (lang_decl_name (decl, v, translate));
Anyway, the patch below has been successfully bootstrapped/regtested
on x86_64-linux and i686-linux.
2026-01-14 Jakub Jelinek <[email protected]>
PR c++/123578
* tree.cc (cxx_printable_name_internal): Don't cache during
recursive calls.
* g++.dg/cpp2a/pr123578.C: New test.
--- gcc/cp/tree.cc.jj 2026-01-13 17:06:38.000000000 +0100
+++ gcc/cp/tree.cc 2026-01-13 20:38:16.548635901 +0100
@@ -2832,12 +2832,15 @@ cxx_printable_name_internal (tree decl,
static char *print_ring[PRINT_RING_SIZE];
static bool trans_ring[PRINT_RING_SIZE];
static int ring_counter;
+ static bool recursed;
int i;
/* Only cache functions. */
if (v < 2
|| TREE_CODE (decl) != FUNCTION_DECL
- || DECL_LANG_SPECIFIC (decl) == 0)
+ || DECL_LANG_SPECIFIC (decl) == 0
+ /* And don't cache if called recursively. */
+ || recursed)
return lang_decl_name (decl, v, translate);
/* See if this print name is lying around. */
@@ -2865,7 +2868,9 @@ cxx_printable_name_internal (tree decl,
free (print_ring[ring_counter]);
+ recursed = true;
print_ring[ring_counter] = xstrdup (lang_decl_name (decl, v, translate));
+ recursed = false;
uid_ring[ring_counter] = DECL_UID (decl);
trans_ring[ring_counter] = translate;
return print_ring[ring_counter];
--- gcc/testsuite/g++.dg/cpp2a/pr123578.C.jj 2026-01-13 20:50:26.712119452
+0100
+++ gcc/testsuite/g++.dg/cpp2a/pr123578.C 2026-01-13 22:31:07.751515746
+0100
@@ -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;
+}
Jakub