[2/5] gdb: Support new DWARF for template type parameters.

Message ID 20251229151518.475440-3-christina.schimpe@intel.com
State New
Headers
Series Template type parameter resolution |

Commit Message

Christina Schimpe Dec. 29, 2025, 3:15 p.m. UTC
  The DWARF Version 5 Debugging Format Standard describes the following for
a template type parameter:

"A template type parameter is represented by a debugging information
entry with the tag DW_TAG_template_type_parameter.".

It lists the following example (Figure D.51):

~~~
! DWARF description
!
11$: DW_TAG_structure_type
	DW_AT_name("wrapper")

12$: DW_TAG_template_type_parameter
	DW_AT_name("T")
	DW_AT_type(reference to "int")

13$: DW_TAG_member
	DW_AT_name("comp")
	DW_AT_type(reference to 12$)

14$: DW_TAG_variable
	DW_AT_name("obj")
	DW_AT_type(reference to 11$)
~~~

Recent compilers like clang and gcc do not support this DWARF feature.
Instead, DIE type references which describe source entities referring
to the formal template parameter reference the underlying type DIE and
not the DW_TAG_template_type_parameter DIE.

This is the corresponding gcc bug:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57006

Currently if we print a type or variable with such DWARF, GDB will print
the following:

~~~
(gdb) ptype var
type = struct A<int, float> [with T1 = int, T2 = float] {
    <unknown type in ..., CU 0x11b, DIE 0x16e> me_tint;
    T1 me_int;
    <unknown type in ..., CU 0x11b, DIE 0x187> me_tfloat;
    T2 me_float;
}
(gdb) print var
$1 = {me_tint = <unknown type in ..., CU 0x11b, DIE 0x16e>, me_int = 0, me_tfloat = <unknown type in ..., CU 0x11b, DIE 0x187>, me_float = 0}
~~~

This patch fixes the errors and adds 2 tests
- gdb.dwarf2/template-type-resolution.exp
- gdb.dwarf2/self-referential-templates.exp
for this DWARF format.
This will allow compilers to enable the DWARF format described above
without breaking debug support of template type parameters in GDB.

In a following commit GDB will be taught to resolve template types
correctly based on this new DWARF format.
---
 gdb/c-typeprint.c                             |  15 +-
 gdb/c-valprint.c                              |   7 +
 gdb/dwarf2/read.c                             |  36 +++++
 gdb/gdbtypes.c                                |   8 +-
 gdb/guile/lib/gdb.scm                         |   1 +
 .../gdb.dwarf2/self-referential-templates.exp | 117 ++++++++++++++
 .../gdb.dwarf2/template-type-resolution.cc    |  37 +++++
 .../gdb.dwarf2/template-type-resolution.exp   | 144 ++++++++++++++++++
 gdb/type-codes.def                            |   3 +
 gdb/typeprint.c                               |  10 +-
 gdb/valprint.c                                |  11 ++
 11 files changed, 385 insertions(+), 4 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/self-referential-templates.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/template-type-resolution.cc
 create mode 100644 gdb/testsuite/gdb.dwarf2/template-type-resolution.exp
  

Comments

Tom Tromey Jan. 6, 2026, 7:31 p.m. UTC | #1
>>>>> "Christina" == Christina Schimpe <christina.schimpe@intel.com> writes:

Christina> This patch fixes the errors and adds 2 tests
Christina> - gdb.dwarf2/template-type-resolution.exp
Christina> - gdb.dwarf2/self-referential-templates.exp
Christina> for this DWARF format.
Christina> This will allow compilers to enable the DWARF format described above
Christina> without breaking debug support of template type parameters in GDB.

Christina> +	  if (sym_type != nullptr
Christina> +	      && sym_type->code () == TYPE_CODE_TEMPLATE_TYPE_PARAM)
Christina> +	    sym_type = sym_type->target_type ();

This and similar snippets left me wondering why a different approach
wasn't taken.

That is, conceptually a template type parameter seems very similar to a
typedef.  And, gdb has many places that unwrap typedefs -- and it seems
like those places should also unwrap template parameter types.

So one idea might be to just reuse TYPE_CODE_TYPEDEF, and if the
typedef/template distinction is ever needed, a flag could be used.

Reading ahead I see you did change check_typedef to handle the new code.
But I wonder if you looked at all the spots checking against
TYPE_CODE_TYPEDEF to see if additional change are needed... this kind of
thing is why I suspect a separate flag might be simpler.

One question I had here is if you can use these types in expressions,
things like 'ptype T' or even a cast, like 'print (T) 25' or whatever.
Like, does your series enable this?

Christina> +      type *arg_type = TYPE_TEMPLATE_ARGUMENT (t, i)->type ();
Christina> +      if (arg_type == nullptr)
Christina> +	{
Christina> +	  /* Skip self-referential template type parameters for the
Christina> +	     hashmap based substitution of template type parameters.  */
Christina> +	  continue;

Rather than a special case here, I tend to think the readers should
ensure that weird types aren't created.  So if a self-referential one is
actually seen, it could be handled like DW_TAG_unspecified_type.

Christina>    type = check_typedef (type);

Christina> +  /* For templated types we want to print the value based on its target
Christina> +     type.  */
Christina> +  if (type->code () == TYPE_CODE_TEMPLATE_TYPE_PARAM
Christina> +      && type->target_type () != nullptr)
Christina> +    type = check_typedef (type->target_type ());

It seems like the original call to check_typedef should have done this.
The normal contract of check_typedef is that it strips all the typedefs
and returns the ultimate referent.

Tom
  
Christina Schimpe Jan. 14, 2026, 12:47 p.m. UTC | #2
Hi Tom,

Thank you for the feedback, please see my comments below.

> -----Original Message-----
> From: Tom Tromey <tom@tromey.com>
> Sent: Dienstag, 6. Januar 2026 20:31
> To: Schimpe, Christina <christina.schimpe@intel.com>
> Cc: gdb-patches@sourceware.org
> Subject: Re: [PATCH 2/5] gdb: Support new DWARF for template type
> parameters.
> 
> >>>>> "Christina" == Christina Schimpe <christina.schimpe@intel.com>
> writes:
> 
> Christina> This patch fixes the errors and adds 2 tests
> Christina> - gdb.dwarf2/template-type-resolution.exp
> Christina> - gdb.dwarf2/self-referential-templates.exp
> Christina> for this DWARF format.
> Christina> This will allow compilers to enable the DWARF format
> Christina> described above without breaking debug support of template type
> parameters in GDB.
> 
> Christina> +	  if (sym_type != nullptr
> Christina> +	      && sym_type->code () ==
> TYPE_CODE_TEMPLATE_TYPE_PARAM)
> Christina> +	    sym_type = sym_type->target_type ();
> 
> This and similar snippets left me wondering why a different approach wasn't
> taken.
> That is, conceptually a template type parameter seems very similar to a
> typedef.  And, gdb has many places that unwrap typedefs -- and it seems like
> those places should also unwrap template parameter types.
> 
> So one idea might be to just reuse TYPE_CODE_TYPEDEF, and if the
> typedef/template distinction is ever needed, a flag could be used.

Hm, so your suggestion is to assign the code TYPE_CODE_TYPEDEF  in
read_template_type, instead of TYPE_CODE_TEMPLATE_TYPE.
I did not consider that.

Just to clarify:

This snippet here is specific to template parameters.  So in that specific scenario
using  TYPE_CODE_TYPEDEF instead would not help, or am I missing something?
 
> Reading ahead I see you did change check_typedef to handle the new code.
> But I wonder if you looked at all the spots checking against
> TYPE_CODE_TYPEDEF to see if additional change are needed... this kind of
> thing is why I suspect a separate flag might be simpler.

I see the following differences:
1) typedefs can have no name, while for anonymous templates GDB will assign one:
    https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=945e66a74d6dd3a94bd5f1ee5b132de42337eb4a
   So for instance this snippet in c_type_print_base_1, we'd need to handle it based on
   the flag that you suggested instead:
    ~~~
    case TYPE_CODE_TYPEDEF:
      /* If we get here, the typedef doesn't have a name, and we
	 couldn't resolve type::target_type.  Not much we can do.  */
      gdb_assert (type->name () == NULL);
      gdb_assert (type->target_type () == NULL);
      fprintf_styled (stream, metadata_style.style (),
		      _("<unnamed typedef>"));
      break;
    ~~~
2) In case the new DWARF format is available, I also don't want to add template type parameters to the
   hash-map (typedef_hash_table) anymore, in contrast to how typedefs are currently handled.
   Please see patch "gdb: Enable correct template type resolution."
   I don't understand yet why we use this map for typedefs.

3) In patch "gdb: Print target type and name of template members.",
    I also improve the printing of template members, so we print them as follows:
    ~~~
    (gdb) ptype a.t_val
    type = T [with T = int]
    ~~~
   Side note, which is probably unrelated to this series: 
   We always print the target type for typedefs, too. Since the DWARF
   has this information for typedefs (at least for gcc), I wonder if we 
   should change the printing here, too.

For all the examples above we must additionally use the flag that you suggested.
Due to that I am not sure if typedefs and template type parameters have enough in
common. But of course, this also relies on some of the patches that I am introducing here.

In which locations do you think that typedefs share the same logic as template type
parameters ?

And if we see places, where we could handle the types similar, couldn't we simply add
"|| TYPE_CODE_TEMPLATE_TYPE_PARAM", or similar ? I'd find it a bit cleaner.

> One question I had here is if you can use these types in expressions, things
> like 'ptype T' or even a cast, like 'print (T) 25' or whatever.
> Like, does your series enable this?

In a template class constructor this works before my patch and after my patch,
and it's independent of the DWARF format:
~~~
Breakpoint 2, A<int, int>::A (this=0x7fffffffe04c) at /tmp/gdb.cp/template-type-parameters.cc:21
21        A () : t_val (), u_val (), i_val (), f_val () { }
(gdb) ptype T
type = int
(gdb) print (T) 25
$1 = 25
~~~

> Christina> +      type *arg_type = TYPE_TEMPLATE_ARGUMENT (t, i)->type ();
> Christina> +      if (arg_type == nullptr)
> Christina> +	{
> Christina> +	  /* Skip self-referential template type parameters for the
> Christina> +	     hashmap based substitution of template type parameters.
> */
> Christina> +	  continue;
> 
> Rather than a special case here, I tend to think the readers should ensure that
> weird types aren't created.  So if a self-referential one is actually seen, it
> could be handled like DW_TAG_unspecified_type.

Do you mean that in case we detect a self-referential template type parameter
in read_template_type, we should call read_unspecified_type in
read_template_type, instead of setting the target type to nullptr ?
For typedefs we assign nullptr in read_typedef, too.

> Christina>    type = check_typedef (type);
> 
> Christina> +  /* For templated types we want to print the value based on its
> target
> Christina> +     type.  */
> Christina> +  if (type->code () == TYPE_CODE_TEMPLATE_TYPE_PARAM
> Christina> +      && type->target_type () != nullptr)
> Christina> +    type = check_typedef (type->target_type ());
> 
> It seems like the original call to check_typedef should have done this.
> The normal contract of check_typedef is that it strips all the typedefs and
> returns the ultimate referent.
> 
> Tom

You mean we should change 

  type = check_typedef (type);

to 
~~~
 if (type->target_type () != nullptr)
   type = check_typedef (type->target_type ()); 
else
   type = check_typedef (type);
~~~
here in general?

Or do you mean we should fix check_typedef (type), so that it will call
~~~
if (type->target_type () != nullptr)
   type = check_typedef (type->target_type ());
~~~
and return the result ?

Note that a few lines below we also do sth. similar for TYPE_CODE_RANGE:
~~~
  /* Widen a subrange to its target type, then use that type's
     printer.  */
  while (type->code () == TYPE_CODE_RANGE)
    {
      type = check_typedef (type->target_type ());
      val = value_cast (type, val);
    }
~~~

Should that be treated different in your opinion, too ?

For my series when we print the type, we don't want to lose
the template type information in c_type_print_base_1 so we
can print it as follows:

    (gdb) ptype a.t_val
    type = T [with T = int]

Before this series and with old DWARF format we always print
the target type for template type parameters (since the DWARF
does not provide this information):

    (gdb) ptype a.t_val
    type = int

So with my series, when calling check_typedef in c_type_print_base_1,
we don't want to use the target type, but the template type.

Christina


Intel Deutschland GmbH
Registered Address: Dornacher Straße 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht München HRB 186928
  
Tom Tromey Jan. 20, 2026, 9:38 p.m. UTC | #3
>>>>> Schimpe, Christina <christina.schimpe@intel.com> writes:

>> So one idea might be to just reuse TYPE_CODE_TYPEDEF, and if the
>> typedef/template distinction is ever needed, a flag could be used.

> Hm, so your suggestion is to assign the code TYPE_CODE_TYPEDEF  in
> read_template_type, instead of TYPE_CODE_TEMPLATE_TYPE.
> I did not consider that.

Yeah.

> This snippet here is specific to template parameters.  So in that
> specific scenario using TYPE_CODE_TYPEDEF instead would not help, or
> am I missing something?

I don't follow, which snippet do you mean?
 
> 1) typedefs can have no name, while for anonymous templates GDB will assign one:
>     https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=945e66a74d6dd3a94bd5f1ee5b132de42337eb4a
>    So for instance this snippet in c_type_print_base_1, we'd need to handle it based on
>    the flag that you suggested instead:

The name would be assigned by the DWARF reader, so I don't think it
would affect anything.

>     case TYPE_CODE_TYPEDEF:
>       /* If we get here, the typedef doesn't have a name, and we
> 	 couldn't resolve type::target_type.  Not much we can do.  */

... since this couldn't be reached for a template type parameter.

> 2) In case the new DWARF format is available, I also don't want to add template type parameters to the
>    hash-map (typedef_hash_table) anymore, in contrast to how typedefs are currently handled.
>    Please see patch "gdb: Enable correct template type resolution."
>    I don't understand yet why we use this map for typedefs.

This is where you'd want to check the flag.

For a pre-existing example of this, most of gdb doesn't distinguish
between 'struct' and 'class'.  Instead there's just a flag that's used
pretty much solely for type printing.

> In which locations do you think that typedefs share the same logic as template type
> parameters ?

Well, it's hard to know.  Grep shows 76 spots that test for
TYPE_CODE_TYPEDEF.  A decent number of them have nothing to do with C++
though.

So I guess the thing to do would be to check these to see if they also
need updating for template type parameters.

Maybe c-lang.c:classify_type, what happens if a template type parameter
is also used as the underlying character type of a string?

Or maybe the ADL code in cp-support.c?  It seems like ADL should apply
via a template type parameter.

> And if we see places, where we could handle the types similar, couldn't we simply add
> "|| TYPE_CODE_TEMPLATE_TYPE_PARAM", or similar ? I'd find it a bit cleaner.

It's fine by me if this is the route, for me it depends on which
requires more special cases.  I tend to think that most code can treat a
template type parameter just like a typedef and that there will just be
a couple spots, in type-printing specifically, where the distinction
matters.

>> One question I had here is if you can use these types in expressions, things
>> like 'ptype T' or even a cast, like 'print (T) 25' or whatever.
>> Like, does your series enable this?

> In a template class constructor this works before my patch and after my patch,
> and it's independent of the DWARF format:

Thanks.  This must be finding it via the symbol.

>> Rather than a special case here, I tend to think the readers should ensure that
>> weird types aren't created.  So if a self-referential one is actually seen, it
>> could be handled like DW_TAG_unspecified_type.

> Do you mean that in case we detect a self-referential template type parameter
> in read_template_type, we should call read_unspecified_type in
> read_template_type, instead of setting the target type to nullptr ?
> For typedefs we assign nullptr in read_typedef, too.

Strange, and this is a spot where the two differ: typedefs can be NULL
due to "stub types".  But this won't affect template type parameters
pretty much by construction.

Anyway my concern is to try to not let weird stuff leak into the core,
where other code has to adapt.  If the DWARF reader sees something
weird, it just either handle or discard it on its own, rather than
letting this touch other spots in gdb.

Christina> type = check_typedef (type);
>> 
Christina> +  /* For templated types we want to print the value based on its
>> target
Christina> +     type.  */
Christina> +  if (type->code () == TYPE_CODE_TEMPLATE_TYPE_PARAM
Christina> +      && type->target_type () != nullptr)
Christina> +    type = check_typedef (type->target_type ());

>> It seems like the original call to check_typedef should have done this.
>> The normal contract of check_typedef is that it strips all the typedefs and
>> returns the ultimate referent.

> You mean we should change 

>   type = check_typedef (type);

> to 
> ~~~
>  if (type->target_type () != nullptr)
>    type = check_typedef (type->target_type ()); 
> else
>    type = check_typedef (type);
> ~~~
> here in general?

No...

> Or do you mean we should fix check_typedef (type), so that it will call
> ~~~
> if (type->target_type () != nullptr)
>    type = check_typedef (type->target_type ());
> ~~~
> and return the result ?

I think check_typedef does do this:

  while (type->code () == TYPE_CODE_TYPEDEF)
[...]
      type = type->target_type ();

(Then there's a bunch of stuff around stub types that isn't applicable.)

> Note that a few lines below we also do sth. similar for TYPE_CODE_RANGE:
> ~~~
>   /* Widen a subrange to its target type, then use that type's
>      printer.  */
>   while (type->code () == TYPE_CODE_RANGE)
>     {
>       type = check_typedef (type->target_type ());
>       val = value_cast (type, val);
>     }

> Should that be treated different in your opinion, too ?

Range types are different though since they are a constraint on an
underlying type, and check_typedef doesn't strip them.

I don't really have an opinion about stripping ranges in check_typedef,
though I tend to think it would be difficult.

> For my series when we print the type, we don't want to lose
> the template type information in c_type_print_base_1 so we
> can print it as follows:

>     (gdb) ptype a.t_val
>     type = T [with T = int]

> Before this series and with old DWARF format we always print
> the target type for template type parameters (since the DWARF
> does not provide this information):

>     (gdb) ptype a.t_val
>     type = int

> So with my series, when calling check_typedef in c_type_print_base_1,
> we don't want to use the target type, but the template type.

Well, that makes sense.  I will take another look at your patch.
Perhaps I should change my mind.

Tom
  
Christina Schimpe Jan. 22, 2026, 9:26 a.m. UTC | #4
> -----Original Message-----
> From: Tom Tromey <tom@tromey.com>
> Sent: Dienstag, 20. Januar 2026 22:39
> To: Schimpe, Christina <christina.schimpe@intel.com>
> Cc: Tom Tromey <tom@tromey.com>; gdb-patches@sourceware.org
> Subject: Re: [PATCH 2/5] gdb: Support new DWARF for template type
> parameters.
> 
> >>>>> Schimpe, Christina <christina.schimpe@intel.com> writes:
> 
> >> So one idea might be to just reuse TYPE_CODE_TYPEDEF, and if the
> >> typedef/template distinction is ever needed, a flag could be used.
> 
> > Hm, so your suggestion is to assign the code TYPE_CODE_TYPEDEF  in
> > read_template_type, instead of TYPE_CODE_TEMPLATE_TYPE.
> > I did not consider that.
> 
> Yeah.
> 
> > This snippet here is specific to template parameters.  So in that
> > specific scenario using TYPE_CODE_TYPEDEF instead would not help, or
> > am I missing something?
> 
> I don't follow, which snippet do you mean?

I meant my changes in c_type_print_template_args in this patch.

> 
> > 1) typedefs can have no name, while for anonymous templates GDB will
> assign one:
> >     https://sourceware.org/git/?p=binutils-
> gdb.git;a=commit;h=945e66a74d6dd3a94bd5f1ee5b132de42337eb4a
> >    So for instance this snippet in c_type_print_base_1, we'd need to handle
> it based on
> >    the flag that you suggested instead:
> 
> The name would be assigned by the DWARF reader, so I don't think it would
> affect anything.
> 
> >     case TYPE_CODE_TYPEDEF:
> >       /* If we get here, the typedef doesn't have a name, and we
> > 	 couldn't resolve type::target_type.  Not much we can do.  */
> 
> ... since this couldn't be reached for a template type parameter.

But if we'd assign TYPE_CODE_TYPEDEF also for template type parameters
and use the flag you suggested to not extract the target type to print template types
as follows (as explained below) 

(gdb) ptype a.t_val
type = T [with T = int]

we can get here. Please see my patch " gdb: Print target type and name of template members.", 
I added "case TYPE_CODE_TEMPLATE_TYPE_PARAM" there.

> > 2) In case the new DWARF format is available, I also don't want to add
> template type parameters to the
> >    hash-map (typedef_hash_table) anymore, in contrast to how typedefs are
> currently handled.
> >    Please see patch "gdb: Enable correct template type resolution."
> >    I don't understand yet why we use this map for typedefs.
> 
> This is where you'd want to check the flag.
> 
> For a pre-existing example of this, most of gdb doesn't distinguish between
> 'struct' and 'class'.  Instead there's just a flag that's used pretty much solely
> for type printing.
> > In which locations do you think that typedefs share the same logic as
> > template type parameters ?
> 
> Well, it's hard to know.  Grep shows 76 spots that test for
> TYPE_CODE_TYPEDEF.  A decent number of them have nothing to do with C++
> though.
> 
> So I guess the thing to do would be to check these to see if they also need
> updating for template type parameters.
> 
> Maybe c-lang.c:classify_type, what happens if a template type parameter is
> also used as the underlying character type of a string?

I did not examine this.

> Or maybe the ADL code in cp-support.c?  It seems like ADL should apply via a
> template type parameter.

or this... will do after my vacation (~3 weeks). 
Just wanted to give you some quick feedback before I am out. 
I'll investigate this in more detail once I'm back. :)

> > And if we see places, where we could handle the types similar,
> > couldn't we simply add "|| TYPE_CODE_TEMPLATE_TYPE_PARAM", or
> similar ? I'd find it a bit cleaner.
> 
> It's fine by me if this is the route, for me it depends on which requires more
> special cases.  I tend to think that most code can treat a template type
> parameter just like a typedef and that there will just be a couple spots, in
> type-printing specifically, where the distinction matters.

Yes, that makes sense to me. I will analyse this in more detail.

> >> One question I had here is if you can use these types in expressions,
> >> things like 'ptype T' or even a cast, like 'print (T) 25' or whatever.
> >> Like, does your series enable this?
> 
> > In a template class constructor this works before my patch and after
> > my patch, and it's independent of the DWARF format:
> 
> Thanks.  This must be finding it via the symbol.
> 
> >> Rather than a special case here, I tend to think the readers should
> >> ensure that weird types aren't created.  So if a self-referential one
> >> is actually seen, it could be handled like DW_TAG_unspecified_type.
> 
> > Do you mean that in case we detect a self-referential template type
> > parameter in read_template_type, we should call read_unspecified_type
> > in read_template_type, instead of setting the target type to nullptr ?
> > For typedefs we assign nullptr in read_typedef, too.
> 
> Strange, and this is a spot where the two differ: typedefs can be NULL due to
> "stub types".  But this won't affect template type parameters pretty much by
> construction.
> 
> Anyway my concern is to try to not let weird stuff leak into the core, where
> other code has to adapt.  If the DWARF reader sees something weird, it just
> either handle or discard it on its own, rather than letting this touch other
> spots in gdb.

Ah ok, so if we don't have to deal with this weird DWARF and may discard it,
the following in this patch is probably redundant:
- the test "gdb.dwarf2/self-referential-templates.exp"
- some changes in the function c_type_print_template_args,  which I added
  for self-referential template types (to print <type unknown>)
- my changes in the function check_typedef.

Regards,
Christina
Intel Deutschland GmbH
Registered Address: Dornacher Straße 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht München HRB 186928
  
Tom Tromey Jan. 22, 2026, 4:44 p.m. UTC | #5
>>>>> Schimpe, Christina <christina.schimpe@intel.com> writes:

>> Well, it's hard to know.  Grep shows 76 spots that test for
>> TYPE_CODE_TYPEDEF.  A decent number of them have nothing to do with C++
>> though.
>> 
>> So I guess the thing to do would be to check these to see if they also need
>> updating for template type parameters.

One additional thought I had is that it seems possible to make a chain
of template type parameters and typedefs (like, a typedef of a template
parameter, which itself holds a typedef, and so on).  So, when a
"concrete" type is desired (like say ABI argument type handling, but
there are many such places really), then it seems like check_typedef
really ought to strip them all.

Tom
  

Patch

diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c
index 7b1fa7f5c7c..12b69ed6ebe 100644
--- a/gdb/c-typeprint.c
+++ b/gdb/c-typeprint.c
@@ -819,7 +819,20 @@  c_type_print_template_args (const struct type_print_options *flags,
 		  styled_string (variable_name_style.style (),
 				 sym->linkage_name ()));
       if (sym->loc_class () == LOC_TYPEDEF)
-	c_print_type (sym->type (), "", stream, -1, 0, language, flags);
+	{
+	  struct type *sym_type = sym->type ();
+
+	  if (sym_type != nullptr
+	      && sym_type->code () == TYPE_CODE_TEMPLATE_TYPE_PARAM)
+	    sym_type = sym_type->target_type ();
+
+	  if (sym_type == nullptr)
+	    fprintf_styled (stream,
+			    metadata_style.style (),
+			    _("<type unknown>"));
+	  else
+	    c_print_type (sym_type, "", stream, -1, 0, language, flags);
+	}
       else
 	print_variable_value (sym, {}, stream, 0, language_def (language));
     }
diff --git a/gdb/c-valprint.c b/gdb/c-valprint.c
index fd31f09ce63..c40ba2304c5 100644
--- a/gdb/c-valprint.c
+++ b/gdb/c-valprint.c
@@ -424,6 +424,13 @@  c_value_print_inner (struct value *val, struct ui_file *stream, int recurse,
   struct type *type = val->type ();
 
   type = check_typedef (type);
+
+  /* For templated types we want to print the value based on its target
+     type.  */
+  if (type->code () == TYPE_CODE_TEMPLATE_TYPE_PARAM
+      && type->target_type () != nullptr)
+    type = type->target_type ();
+
   switch (type->code ())
     {
     case TYPE_CODE_ARRAY:
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 4e2644ba959..1525e8e4d39 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -13392,6 +13392,39 @@  read_subroutine_type (struct die_info *die, struct dwarf2_cu *cu)
   return ftype;
 }
 
+/* Extract all information from a DW_TAG_template_type_parameter DIE and
+   put it in the DIE's type field.  */
+
+static type *
+read_template_type (die_info *die, dwarf2_cu *cu)
+{
+  objfile *objfile = cu->per_objfile->objfile;
+
+  const char *name = dwarf2_full_name (nullptr, die, cu);
+  type_allocator alloc (objfile, cu->lang ());
+  type *this_type = alloc.new_type (TYPE_CODE_TEMPLATE_TYPE_PARAM, 0, name);
+  this_type->set_target_is_stub (true);
+  set_die_type (die, this_type, cu);
+  type *target_type = die_type (die, cu);
+
+  if (target_type != this_type)
+    {
+      this_type->set_target_type (target_type);
+      this_type->set_length (target_type->length ());
+    }
+  else
+    {
+      complaint (_("Self-referential DW_TAG_TEMPLATE_TYPE_PARAM "
+		   "- DIE at %s [in module %s]"),
+		 sect_offset_str (die->sect_off), objfile_name (objfile));
+     /* The target type should be set to nullptr in
+	type_allocator::new_type ().  */
+      gdb_assert (this_type->target_type () == nullptr);
+    }
+
+  return this_type;
+}
+
 static struct type *
 read_typedef (struct die_info *die, struct dwarf2_cu *cu)
 {
@@ -17063,6 +17096,9 @@  read_type_die_1 (struct die_info *die, struct dwarf2_cu *cu)
     case DW_TAG_unspecified_type:
       this_type = read_unspecified_type (die, cu);
       break;
+    case DW_TAG_template_type_param:
+      this_type = read_template_type (die, cu);
+      break;
     case DW_TAG_namespace:
       this_type = read_namespace_type (die, cu);
       break;
diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c
index 975e1246a0d..0bee3fe1bc1 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -3259,9 +3259,13 @@  check_typedef (struct type *type)
 
   if (type->target_is_stub ())
     {
-      struct type *target_type = check_typedef (type->target_type ());
+      struct type *target_type = type->target_type ();
 
-      if (target_type->is_stub () || target_type->target_is_stub ())
+      if (target_type != nullptr)
+	target_type = check_typedef (target_type);
+
+      if (target_type == nullptr
+	  || target_type->is_stub () || target_type->target_is_stub ())
 	{
 	  /* Nothing we can do.  */
 	}
diff --git a/gdb/guile/lib/gdb.scm b/gdb/guile/lib/gdb.scm
index bf554eadcaf..91f02c4c52a 100644
--- a/gdb/guile/lib/gdb.scm
+++ b/gdb/guile/lib/gdb.scm
@@ -425,6 +425,7 @@ 
  TYPE_CODE_BOOL
  TYPE_CODE_COMPLEX
  TYPE_CODE_TYPEDEF
+ TYPE_CODE_TEMPLATE_TYPE_PARAM
  TYPE_CODE_NAMESPACE
  TYPE_CODE_DECFLOAT
  TYPE_CODE_INTERNAL_FUNCTION
diff --git a/gdb/testsuite/gdb.dwarf2/self-referential-templates.exp b/gdb/testsuite/gdb.dwarf2/self-referential-templates.exp
new file mode 100644
index 00000000000..fbe6cad1c00
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/self-referential-templates.exp
@@ -0,0 +1,117 @@ 
+# Copyright 2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test template type resolution for self-referential template types.
+
+load_lib dwarf.exp
+require dwarf2_support allow_cplus_tests
+
+standard_testfile template-type-resolution.cc .S
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	 [list $srcfile] {debug c++}] } {
+    return
+}
+
+if { ![runto_main] } {
+    return
+}
+
+set asm_file [standard_output_file $srcfile2]
+set int_size [get_sizeof "int" -1]
+
+Dwarf::assemble $asm_file {
+    global int_size
+    cu {} {
+	DW_TAG_compile_unit {
+	    DW_AT_language @DW_LANG_C_plus_plus
+	} {
+	    declare_labels A template_type_int template_pointer1
+
+	    DW_TAG_subprogram {
+		MACRO_AT_func "main"
+	    }
+
+	    A: DW_TAG_structure_type {
+		DW_AT_name "A"
+		DW_AT_byte_size $int_size DW_FORM_sdata
+	    } {
+		# Cyclic dependency
+		template_type_int: DW_TAG_template_type_param {
+		    DW_AT_type :$template_type_int
+		    DW_AT_name "T"
+		}
+		DW_TAG_member {
+		    DW_AT_name "me_t"
+		    DW_AT_type :$template_type_int
+		}
+
+		DW_TAG_subprogram {
+		    DW_AT_external 1 DW_FORM_flag
+		    DW_AT_name "t_func"
+		    DW_AT_type :$template_type_int
+		    DW_AT_declaration 1 DW_FORM_flag
+		} {
+		      DW_TAG_formal_parameter {
+			  DW_AT_type :$template_pointer1
+			  DW_AT_artificial 1 DW_FORM_flag
+		      }
+
+		      DW_TAG_formal_parameter {
+			  DW_AT_type :$template_type_int
+		      }
+		  }
+	    }
+
+	    template_pointer1: DW_TAG_pointer_type {
+		DW_AT_type :$A
+	    }
+
+	    DW_TAG_variable {
+		DW_AT_name "var"
+		DW_AT_type :$A
+		DW_AT_location {
+		    DW_OP_addr [gdb_target_symbol "var"]
+		} SPECIAL_expr
+		DW_AT_external 1 DW_FORM_flag
+	    }
+	}
+    }
+}
+
+with_test_prefix "self-referential template types" {
+
+    if { [prepare_for_testing "failed to prepare" ${testfile} \
+	     [list $srcfile $asm_file] {debug c++}] } {
+	return
+    }
+
+    if { ![runto_main] } {
+	return
+    }
+
+    # Setup strings used in the tests below.
+    set str_unknown "<type unknown>"
+
+    gdb_test "ptype var" [multi_line \
+	"type = struct A<T> \\\[with T = $str_unknown\\\] {" \
+	"    T me_t;.*" \
+	"    T t_func\\\(T\\\);.*" \
+    "}"]
+    gdb_test "ptype var.me_t" "type = T"
+
+    gdb_test "print var" "{me_t = $str_unknown}"
+    gdb_test "print var.me_t" "$str_unknown"
+}
diff --git a/gdb/testsuite/gdb.dwarf2/template-type-resolution.cc b/gdb/testsuite/gdb.dwarf2/template-type-resolution.cc
new file mode 100644
index 00000000000..56d9f490fe2
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/template-type-resolution.cc
@@ -0,0 +1,37 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2025 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+template <typename T>
+struct A
+{
+  T me_t;
+
+  T t_func (T tval)
+  {
+    return tval;
+  }
+
+};
+
+A<int> var = {0};
+
+int
+main ()
+{
+  asm ("main_label: .globl main_label");
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/template-type-resolution.exp b/gdb/testsuite/gdb.dwarf2/template-type-resolution.exp
new file mode 100644
index 00000000000..4e9cc70d0fe
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/template-type-resolution.exp
@@ -0,0 +1,144 @@ 
+# Copyright 2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test DWARF format which allows GDB to differentiate between template
+# types and normal types, which have the same target type.
+#
+# This format supports DIE type references that describe source entities
+# referring to the formal template parameter which reference the
+# DW_TAG_template_type_parameter DIE, and not the underlying type DIE.
+#
+# It is described in the DWARF 5 spec:
+# "A template type parameter is represented by a debugging information
+# entry with the tag DW_TAG_template_type_parameter."
+# which also lists the following example (Figure D.51):
+#    ! DWARF description
+#    !
+#    11$: DW_TAG_structure_type
+#	    DW_AT_name("wrapper")
+#
+#    12$: DW_TAG_template_type_parameter
+#	    DW_AT_name("T")
+#	    DW_AT_type(reference to "int")
+#
+#    13$: DW_TAG_member
+#	    DW_AT_name("comp")
+#	    DW_AT_type(reference to 12$)
+#
+#    14$: DW_TAG_variable
+#	    DW_AT_name("obj")
+#	    DW_AT_type(reference to 11$)
+
+load_lib dwarf.exp
+
+require dwarf2_support allow_cplus_tests
+
+standard_testfile .cc .S
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	 [list $srcfile] {debug c++}] } {
+    return
+}
+
+if { ![runto_main] } {
+    return
+}
+
+set int_size [get_sizeof "int" -1]
+
+set asm_file1 [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file1 {
+    global int_size
+    cu {} {
+	DW_TAG_compile_unit {
+	    DW_AT_language @DW_LANG_C_plus_plus
+	} {
+	    declare_labels int
+	    declare_labels A template_type_int template_pointer1
+
+	    int: DW_TAG_base_type {
+		DW_AT_name "int"
+		DW_AT_byte_size $int_size DW_FORM_sdata
+		DW_AT_encoding @DW_ATE_signed
+	    }
+
+	    DW_TAG_subprogram {
+		MACRO_AT_func "main"
+	    }
+
+	    A: DW_TAG_structure_type {
+		DW_AT_name "A"
+		DW_AT_byte_size $int_size DW_FORM_sdata
+	    } {
+		template_type_int: DW_TAG_template_type_param {
+		    DW_AT_type :$int
+		    DW_AT_name "T"
+		}
+		DW_TAG_member {
+		    DW_AT_name "me_t"
+		    DW_AT_type :$template_type_int
+		}
+
+		DW_TAG_subprogram {
+		    DW_AT_external 1 DW_FORM_flag
+		    DW_AT_name "t_func"
+		    DW_AT_type :$template_type_int
+		    DW_AT_declaration 1 DW_FORM_flag
+		} {
+		      DW_TAG_formal_parameter {
+			  DW_AT_type :$template_pointer1
+			  DW_AT_artificial 1 DW_FORM_flag
+		      }
+
+		      DW_TAG_formal_parameter {
+			  DW_AT_type :$template_type_int
+		      }
+		  }
+	    }
+
+	    template_pointer1: DW_TAG_pointer_type {
+		DW_AT_type :$A
+	    }
+
+	    DW_TAG_variable {
+		DW_AT_name "var"
+		DW_AT_type :$A
+		DW_AT_location {
+		    DW_OP_addr [gdb_target_symbol "var"]
+		} SPECIAL_expr
+		DW_AT_external 1 DW_FORM_flag
+	    }
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	 [list $srcfile $asm_file1] {debug c++}] } {
+    return
+}
+
+if { ![runto_main] } {
+    return
+}
+
+gdb_test "ptype var" [multi_line \
+    "type = struct A<int> \\\[with T = int\\\] {" \
+    "    T me_t;.*" \
+    "    T t_func\\\(T\\\);.*" \
+    "}"]
+
+gdb_test "ptype var.me_t" "type = T"
+gdb_test "print var" "\\\{me_t = 0\\\}"
+
diff --git a/gdb/type-codes.def b/gdb/type-codes.def
index e52650ddec9..f6057c705fe 100644
--- a/gdb/type-codes.def
+++ b/gdb/type-codes.def
@@ -102,6 +102,9 @@  OP (TYPE_CODE_COMPLEX)		/**< Complex float */
 
 OP (TYPE_CODE_TYPEDEF)
 
+/* Type for a template type parameter (DW_TAG_template_type_param).  */
+OP (TYPE_CODE_TEMPLATE_TYPE_PARAM)
+
 OP (TYPE_CODE_NAMESPACE)	/**< C++ namespace.  */
 
 OP (TYPE_CODE_DECFLOAT)		/**< Decimal floating point.  */
diff --git a/gdb/typeprint.c b/gdb/typeprint.c
index 92dadd06c90..4ba7a1aabfb 100644
--- a/gdb/typeprint.c
+++ b/gdb/typeprint.c
@@ -223,7 +223,15 @@  typedef_hash_table::add_template_parameters (struct type *t)
 
       tf = XOBNEW (&m_storage, struct decl_field);
       tf->name = TYPE_TEMPLATE_ARGUMENT (t, i)->linkage_name ();
-      tf->type = TYPE_TEMPLATE_ARGUMENT (t, i)->type ();
+      type *arg_type = TYPE_TEMPLATE_ARGUMENT (t, i)->type ();
+      if (arg_type == nullptr)
+	{
+	  /* Skip self-referential template type parameters for the
+	     hashmap based substitution of template type parameters.  */
+	  continue;
+	}
+
+      tf->type = arg_type;
 
       m_table.emplace (tf);
     }
diff --git a/gdb/valprint.c b/gdb/valprint.c
index 91f225c4e60..d29846e6447 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -982,6 +982,12 @@  generic_value_print (struct value *val, struct ui_file *stream, int recurse,
 
   type = check_typedef (type);
 
+  /* For templated types we want to print the value based on its target
+     type.  */
+  if (type->code () == TYPE_CODE_TEMPLATE_TYPE_PARAM
+      && type->target_type () != nullptr)
+    type = check_typedef (type->target_type ());
+
   if (is_fixed_point_type (type))
     type = type->fixed_point_type_base_type ();
 
@@ -1089,6 +1095,11 @@  generic_value_print (struct value *val, struct ui_file *stream, int recurse,
 			      stream);
       break;
 
+    case TYPE_CODE_TEMPLATE_TYPE_PARAM:
+      /* This happens for self-referential template type parameters.  */
+      fprintf_styled (stream, metadata_style.style (), _("<type unknown>"));
+      break;
+
     case TYPE_CODE_UNION:
     case TYPE_CODE_STRUCT:
     default: