[1/1] gdb: Print template arguments of function templates.

Message ID 20260319161648.1251301-1-christina.schimpe@intel.com
State New
Headers
Series [1/1] gdb: Print template arguments of function templates. |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gdb_check--master-arm success Test passed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 success Test passed

Commit Message

Schimpe, Christina March 19, 2026, 4:16 p.m. UTC
  For function templates currently no template arguments are printed.

Add the list of template arguments to function types and print them,
similar to struct, class or union types.

From now on we print template functions as follows:
~~~
(gdb) ptype func<int, float>
type = int (int, float) [with T = int, U = float]
(gdb) print func<int, float>
$6 = {int (int, float) [with T = int, U = float]} 0x4012a0 <func<int, float>(int, float)>
~~~

Fix the existing tests for template functions to expect the new output.
---
 gdb/c-lang.c                                  |  6 +++
 gdb/c-lang.h                                  | 10 ++++
 gdb/c-typeprint.c                             | 31 ++++++++-----
 gdb/dwarf2/read.c                             | 20 +++++++-
 gdb/testsuite/gdb.cp/cp-relocate.exp          |  4 +-
 gdb/testsuite/gdb.cp/cpexprs.exp.tcl          | 46 +++++++++----------
 .../template-specification-full-name.exp      |  3 +-
 7 files changed, 82 insertions(+), 38 deletions(-)
  

Comments

Keith Seitz April 1, 2026, 7:18 p.m. UTC | #1
Hi,

On 3/19/26 9:16 AM, Christina Schimpe wrote:
> For function templates currently no template arguments are printed.
> 
> Add the list of template arguments to function types and print them,
> similar to struct, class or union types.
> 
>  From now on we print template functions as follows:
> ~~~
> (gdb) ptype func<int, float>
> type = int (int, float) [with T = int, U = float]
> (gdb) print func<int, float>
> $6 = {int (int, float) [with T = int, U = float]} 0x4012a0 <func<int, float>(int, float)>
> ~~~
> 
> Fix the existing tests for template functions to expect the new output.

Excellent, thank you for the patch! I've taken a good look at
this, and everything looks okay. There are just a few typos
to correct (no need to repost).

Reviewed-By: Keith Seitz <keiths@redhat.com>

Thank you for doing this.

Keith

> diff --git a/gdb/c-lang.c b/gdb/c-lang.c
> index fdb5be7b2b7..69ec47cd215 100644
> --- a/gdb/c-lang.c
> +++ b/gdb/c-lang.c
> @@ -941,6 +941,12 @@ class cplus_language : public language_defn
>   		   const struct type_print_options *flags) const override
>     {
>       c_print_type (type, varstring, stream, show, level, la_language, flags);
> +
> +    /* Print target type or template arguments if TYPE it is a function
> +       template.  */

Typo: "... if TYPE is a function ..."

> +    if (type->code () == TYPE_CODE_FUNC)
> +      c_type_print_template_args (flags, type, stream, la_language);
> +
>     }
>   
>     /* See language.h.  */
> diff --git a/gdb/c-lang.h b/gdb/c-lang.h
> index 7d9eaf8571b..538d007e2e6 100644
> --- a/gdb/c-lang.h
> +++ b/gdb/c-lang.h
> @@ -134,4 +134,14 @@ extern int c_textual_element_type (struct type *, char);
>   
>   extern gdb::unique_xmalloc_ptr<char> c_canonicalize_name (const char *name);
>   
> +/* Display template arguments of TYPE in the style [with T = ..., U = ...]

Please phrase as

   /* Display template arguments of TYPE in the format "[with T = ..., U 
= ...]"

> +   based on FLAGS and LANGUAGE on STREAM.
> +   TYPE is the type whose template arguments are being displayed.
> +   It can be a struct/class/union or a function.  */

I think "It can" is a little weak. Given the assert, it *must* be
a struct/class/union.

> +
> +extern void c_type_print_template_args (const type_print_options *flags,
> +					type *type,
> +					ui_file *stream,
> +					language language);
> +
>   #endif /* GDB_C_LANG_H */
> diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c
> index 821b78becf4..a4a3914909b 100644
> --- a/gdb/c-typeprint.c
> +++ b/gdb/c-typeprint.c
> @@ -786,22 +786,26 @@ c_type_print_varspec_suffix (struct type *type,
>       }
>   }
>   
> -/* A helper for c_type_print_base_struct_union that displays template
> -   parameters.
> -
> -   TYPE is the type whose template arguments are being displayed.
> -
> -   STREAM is the stream on which to print.  */
> +/* See c-lang.h.  */
>   
> -static void
> -c_type_print_template_args (const struct type_print_options *flags,
> -			    struct type *type, struct ui_file *stream,
> -			    enum language language)
> +void
> +c_type_print_template_args (const type_print_options *flags, type *type,
> +			    ui_file *stream, language language)
>   {
> +  gdb_assert (type->code () == TYPE_CODE_STRUCT
> +	      || type->code () == TYPE_CODE_UNION
> +	      || type->code () == TYPE_CODE_FUNC);
> +
>     if (flags->raw || TYPE_N_TEMPLATE_ARGUMENTS (type) == 0)
>       return;
>   
>     stream->wrap_here (4);
> +
> +  /* For functions we have to add a space here, for struct/class/unions
> +     this is already taken care of in c_type_print_base_struct_union.  */
> +  if (type->code () == TYPE_CODE_FUNC)
> +    gdb_puts (" ", stream);
> +
>     gdb_printf (stream, _("[with "));
>   
>     for (int i = 0; i < TYPE_N_TEMPLATE_ARGUMENTS (type); ++i)
> @@ -823,7 +827,12 @@ c_type_print_template_args (const struct type_print_options *flags,
>   	print_variable_value (sym, {}, stream, 0, language_def (language));
>       }
>   
> -  gdb_puts (_("] "), stream);
> +  gdb_puts (_("]"), stream);
> +
> +  /* For struct/class/unions printing of the type is not complete yet, so
> +     we have to add a space here.  */
> +  if (type->code () != TYPE_CODE_FUNC)
> +    gdb_puts (" ", stream);
>   }
>   
>   /* Use 'print_spaces', but take into consideration the
> diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> index 8b87d58dd9c..1eff94de246 100644
> --- a/gdb/dwarf2/read.c
> +++ b/gdb/dwarf2/read.c
> @@ -7915,7 +7915,25 @@ read_func_scope (struct die_info *die, struct dwarf2_cu *cu)
>     /* If we've finished processing a top-level function, subsequent
>        symbols go in the file symbol list.  */
>     if (cu->get_builder ()->outermost_context_p ())
> -    cu->list_in_scope = &cu->get_builder ()->get_file_symbols ();
> +    cu->list_in_scope =  &cu->get_builder ()->get_file_symbols ();
> +

A superfluous space has crept in on the above line.

> +  /* In case of C++ functions, add template parameters to the type, similar
> +     to class/struct/union types handled in process_structure_scope.  */
> +  if (!template_args.empty () && cu->lang () == language_cplus)
> +    {
> +      struct type *type =  get_die_type (die, cu);
> +      gdb_assert (type != nullptr && type->code () == TYPE_CODE_FUNC);
> +
> +      ALLOCATE_CPLUS_STRUCT_TYPE (type);
> +      TYPE_N_TEMPLATE_ARGUMENTS (type) = template_args.size ();
> +      TYPE_TEMPLATE_ARGUMENTS (type)
> +	= XOBNEWVEC (&objfile->objfile_obstack,
> +		     struct symbol *,
> +		     TYPE_N_TEMPLATE_ARGUMENTS (type));
> +      memcpy (TYPE_TEMPLATE_ARGUMENTS (type),
> +	      template_args.data (),
> +	      (TYPE_N_TEMPLATE_ARGUMENTS (type) * sizeof (struct symbol *)));
> +    }
>   }
>   
>   /* Process all the DIES contained within a lexical block scope.  Start
  
Schimpe, Christina April 7, 2026, 9:22 a.m. UTC | #2
Hi Keith, 

Thank you for the review.

In the meantime, I noticed an issue with my patch which requires a v2.
Using ALLOCATE_CPLUS_STRUCT_TYPE in read.c:read_func_scope  is clearly
not the right thing to do: It should be used for structs only. I’m unsure how this
was missed, sorry for this oversight.
IIUC I am basically overwriting function specific values that are configured in
read_subroutine_type, but for whatever reason this did not cause any regressions.

So for the v2 and I'll work on this issue and of course fix all your comments.

Christina

> -----Original Message-----
> From: Keith Seitz <keiths@redhat.com>
> Sent: Mittwoch, 1. April 2026 21:19
> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> patches@sourceware.org
> Subject: Re: [PATCH 1/1] gdb: Print template arguments of function
> templates.
> 
> Hi,
> 
> On 3/19/26 9:16 AM, Christina Schimpe wrote:
> > For function templates currently no template arguments are printed.
> >
> > Add the list of template arguments to function types and print them,
> > similar to struct, class or union types.
> >
> >  From now on we print template functions as follows:
> > ~~~
> > (gdb) ptype func<int, float>
> > type = int (int, float) [with T = int, U = float]
> > (gdb) print func<int, float>
> > $6 = {int (int, float) [with T = int, U = float]} 0x4012a0 <func<int,
> > float>(int, float)> ~~~
> >
> > Fix the existing tests for template functions to expect the new output.
> 
> Excellent, thank you for the patch! I've taken a good look at this, and
> everything looks okay. There are just a few typos to correct (no need to
> repost).
> 
> Reviewed-By: Keith Seitz <keiths@redhat.com>
> 
> Thank you for doing this.
> 
> Keith
> 
> > diff --git a/gdb/c-lang.c b/gdb/c-lang.c index
> > fdb5be7b2b7..69ec47cd215 100644
> > --- a/gdb/c-lang.c
> > +++ b/gdb/c-lang.c
> > @@ -941,6 +941,12 @@ class cplus_language : public language_defn
> >   		   const struct type_print_options *flags) const override
> >     {
> >       c_print_type (type, varstring, stream, show, level, la_language,
> > flags);
> > +
> > +    /* Print target type or template arguments if TYPE it is a function
> > +       template.  */
> 
> Typo: "... if TYPE is a function ..."
> 
> > +    if (type->code () == TYPE_CODE_FUNC)
> > +      c_type_print_template_args (flags, type, stream, la_language);
> > +
> >     }
> >
> >     /* See language.h.  */
> > diff --git a/gdb/c-lang.h b/gdb/c-lang.h index
> > 7d9eaf8571b..538d007e2e6 100644
> > --- a/gdb/c-lang.h
> > +++ b/gdb/c-lang.h
> > @@ -134,4 +134,14 @@ extern int c_textual_element_type (struct type *,
> > char);
> >
> >   extern gdb::unique_xmalloc_ptr<char> c_canonicalize_name (const char
> > *name);
> >
> > +/* Display template arguments of TYPE in the style [with T = ..., U =
> > +...]
> 
> Please phrase as
> 
>    /* Display template arguments of TYPE in the format "[with T = ..., U = ...]"
> 
> > +   based on FLAGS and LANGUAGE on STREAM.
> > +   TYPE is the type whose template arguments are being displayed.
> > +   It can be a struct/class/union or a function.  */
> 
> I think "It can" is a little weak. Given the assert, it *must* be a
> struct/class/union.
> 
> > +
> > +extern void c_type_print_template_args (const type_print_options *flags,
> > +					type *type,
> > +					ui_file *stream,
> > +					language language);
> > +
> >   #endif /* GDB_C_LANG_H */
> > diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c index
> > 821b78becf4..a4a3914909b 100644
> > --- a/gdb/c-typeprint.c
> > +++ b/gdb/c-typeprint.c
> > @@ -786,22 +786,26 @@ c_type_print_varspec_suffix (struct type *type,
> >       }
> >   }
> >
> > -/* A helper for c_type_print_base_struct_union that displays template
> > -   parameters.
> > -
> > -   TYPE is the type whose template arguments are being displayed.
> > -
> > -   STREAM is the stream on which to print.  */
> > +/* See c-lang.h.  */
> >
> > -static void
> > -c_type_print_template_args (const struct type_print_options *flags,
> > -			    struct type *type, struct ui_file *stream,
> > -			    enum language language)
> > +void
> > +c_type_print_template_args (const type_print_options *flags, type *type,
> > +			    ui_file *stream, language language)
> >   {
> > +  gdb_assert (type->code () == TYPE_CODE_STRUCT
> > +	      || type->code () == TYPE_CODE_UNION
> > +	      || type->code () == TYPE_CODE_FUNC);
> > +
> >     if (flags->raw || TYPE_N_TEMPLATE_ARGUMENTS (type) == 0)
> >       return;
> >
> >     stream->wrap_here (4);
> > +
> > +  /* For functions we have to add a space here, for struct/class/unions
> > +     this is already taken care of in c_type_print_base_struct_union.
> > + */  if (type->code () == TYPE_CODE_FUNC)
> > +    gdb_puts (" ", stream);
> > +
> >     gdb_printf (stream, _("[with "));
> >
> >     for (int i = 0; i < TYPE_N_TEMPLATE_ARGUMENTS (type); ++i) @@
> > -823,7 +827,12 @@ c_type_print_template_args (const struct
> type_print_options *flags,
> >   	print_variable_value (sym, {}, stream, 0, language_def (language));
> >       }
> >
> > -  gdb_puts (_("] "), stream);
> > +  gdb_puts (_("]"), stream);
> > +
> > +  /* For struct/class/unions printing of the type is not complete yet, so
> > +     we have to add a space here.  */  if (type->code () !=
> > + TYPE_CODE_FUNC)
> > +    gdb_puts (" ", stream);
> >   }
> >
> >   /* Use 'print_spaces', but take into consideration the diff --git
> > a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index
> 8b87d58dd9c..1eff94de246
> > 100644
> > --- a/gdb/dwarf2/read.c
> > +++ b/gdb/dwarf2/read.c
> > @@ -7915,7 +7915,25 @@ read_func_scope (struct die_info *die, struct
> dwarf2_cu *cu)
> >     /* If we've finished processing a top-level function, subsequent
> >        symbols go in the file symbol list.  */
> >     if (cu->get_builder ()->outermost_context_p ())
> > -    cu->list_in_scope = &cu->get_builder ()->get_file_symbols ();
> > +    cu->list_in_scope =  &cu->get_builder ()->get_file_symbols ();
> > +
> 
> A superfluous space has crept in on the above line.
> 
> > +  /* In case of C++ functions, add template parameters to the type, similar
> > +     to class/struct/union types handled in process_structure_scope.
> > + */  if (!template_args.empty () && cu->lang () == language_cplus)
> > +    {
> > +      struct type *type =  get_die_type (die, cu);
> > +      gdb_assert (type != nullptr && type->code () ==
> > + TYPE_CODE_FUNC);
> > +
> > +      ALLOCATE_CPLUS_STRUCT_TYPE (type);
> > +      TYPE_N_TEMPLATE_ARGUMENTS (type) = template_args.size ();
> > +      TYPE_TEMPLATE_ARGUMENTS (type)
> > +	= XOBNEWVEC (&objfile->objfile_obstack,
> > +		     struct symbol *,
> > +		     TYPE_N_TEMPLATE_ARGUMENTS (type));
> > +      memcpy (TYPE_TEMPLATE_ARGUMENTS (type),
> > +	      template_args.data (),
> > +	      (TYPE_N_TEMPLATE_ARGUMENTS (type) * sizeof (struct symbol
> *)));
> > +    }
> >   }
> >
> >   /* Process all the DIES contained within a lexical block scope.
> > Start

Intel Deutschland GmbH
Registered Address: Dornacher Strasse 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 Muenchen HRB 186928
  

Patch

diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index fdb5be7b2b7..69ec47cd215 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -941,6 +941,12 @@  class cplus_language : public language_defn
 		   const struct type_print_options *flags) const override
   {
     c_print_type (type, varstring, stream, show, level, la_language, flags);
+
+    /* Print target type or template arguments if TYPE it is a function
+       template.  */
+    if (type->code () == TYPE_CODE_FUNC)
+      c_type_print_template_args (flags, type, stream, la_language);
+
   }
 
   /* See language.h.  */
diff --git a/gdb/c-lang.h b/gdb/c-lang.h
index 7d9eaf8571b..538d007e2e6 100644
--- a/gdb/c-lang.h
+++ b/gdb/c-lang.h
@@ -134,4 +134,14 @@  extern int c_textual_element_type (struct type *, char);
 
 extern gdb::unique_xmalloc_ptr<char> c_canonicalize_name (const char *name);
 
+/* Display template arguments of TYPE in the style [with T = ..., U = ...]
+   based on FLAGS and LANGUAGE on STREAM.
+   TYPE is the type whose template arguments are being displayed.
+   It can be a struct/class/union or a function.  */
+
+extern void c_type_print_template_args (const type_print_options *flags,
+					type *type,
+					ui_file *stream,
+					language language);
+
 #endif /* GDB_C_LANG_H */
diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c
index 821b78becf4..a4a3914909b 100644
--- a/gdb/c-typeprint.c
+++ b/gdb/c-typeprint.c
@@ -786,22 +786,26 @@  c_type_print_varspec_suffix (struct type *type,
     }
 }
 
-/* A helper for c_type_print_base_struct_union that displays template
-   parameters.
-
-   TYPE is the type whose template arguments are being displayed.
-
-   STREAM is the stream on which to print.  */
+/* See c-lang.h.  */
 
-static void
-c_type_print_template_args (const struct type_print_options *flags,
-			    struct type *type, struct ui_file *stream,
-			    enum language language)
+void
+c_type_print_template_args (const type_print_options *flags, type *type,
+			    ui_file *stream, language language)
 {
+  gdb_assert (type->code () == TYPE_CODE_STRUCT
+	      || type->code () == TYPE_CODE_UNION
+	      || type->code () == TYPE_CODE_FUNC);
+
   if (flags->raw || TYPE_N_TEMPLATE_ARGUMENTS (type) == 0)
     return;
 
   stream->wrap_here (4);
+
+  /* For functions we have to add a space here, for struct/class/unions
+     this is already taken care of in c_type_print_base_struct_union.  */
+  if (type->code () == TYPE_CODE_FUNC)
+    gdb_puts (" ", stream);
+
   gdb_printf (stream, _("[with "));
 
   for (int i = 0; i < TYPE_N_TEMPLATE_ARGUMENTS (type); ++i)
@@ -823,7 +827,12 @@  c_type_print_template_args (const struct type_print_options *flags,
 	print_variable_value (sym, {}, stream, 0, language_def (language));
     }
 
-  gdb_puts (_("] "), stream);
+  gdb_puts (_("]"), stream);
+
+  /* For struct/class/unions printing of the type is not complete yet, so
+     we have to add a space here.  */
+  if (type->code () != TYPE_CODE_FUNC)
+    gdb_puts (" ", stream);
 }
 
 /* Use 'print_spaces', but take into consideration the
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 8b87d58dd9c..1eff94de246 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -7915,7 +7915,25 @@  read_func_scope (struct die_info *die, struct dwarf2_cu *cu)
   /* If we've finished processing a top-level function, subsequent
      symbols go in the file symbol list.  */
   if (cu->get_builder ()->outermost_context_p ())
-    cu->list_in_scope = &cu->get_builder ()->get_file_symbols ();
+    cu->list_in_scope =  &cu->get_builder ()->get_file_symbols ();
+
+  /* In case of C++ functions, add template parameters to the type, similar
+     to class/struct/union types handled in process_structure_scope.  */
+  if (!template_args.empty () && cu->lang () == language_cplus)
+    {
+      struct type *type =  get_die_type (die, cu);
+      gdb_assert (type != nullptr && type->code () == TYPE_CODE_FUNC);
+
+      ALLOCATE_CPLUS_STRUCT_TYPE (type);
+      TYPE_N_TEMPLATE_ARGUMENTS (type) = template_args.size ();
+      TYPE_TEMPLATE_ARGUMENTS (type)
+	= XOBNEWVEC (&objfile->objfile_obstack,
+		     struct symbol *,
+		     TYPE_N_TEMPLATE_ARGUMENTS (type));
+      memcpy (TYPE_TEMPLATE_ARGUMENTS (type),
+	      template_args.data (),
+	      (TYPE_N_TEMPLATE_ARGUMENTS (type) * sizeof (struct symbol *)));
+    }
 }
 
 /* Process all the DIES contained within a lexical block scope.  Start
diff --git a/gdb/testsuite/gdb.cp/cp-relocate.exp b/gdb/testsuite/gdb.cp/cp-relocate.exp
index fb45313466b..81f03ce64e8 100644
--- a/gdb/testsuite/gdb.cp/cp-relocate.exp
+++ b/gdb/testsuite/gdb.cp/cp-relocate.exp
@@ -54,11 +54,11 @@  gdb_file_cmd ${binfile}
 set func1_name ""
 set func2_name ""
 gdb_test_multiple "info functions func<.>" "info functions" {
-    -re "\tint (\[^\r\]*func<1>\[^\r]*);" {
+    -re "\tint (\[^\r\]*func<1>\[^\r]*) \\\[with X = 1\\\];" {
 	set func1_name $expect_out(1,string)
 	exp_continue
     }
-    -re "\tint (\[^\r\]*func<2>\[^\r]*);" {
+    -re "\tint (\[^\r\]*func<2>\[^\r]*) \\\[with X = 2\\\];" {
 	set func2_name $expect_out(1,string)
 	exp_continue
     }
diff --git a/gdb/testsuite/gdb.cp/cpexprs.exp.tcl b/gdb/testsuite/gdb.cp/cpexprs.exp.tcl
index 085dfdd0678..a6c6df959fc 100644
--- a/gdb/testsuite/gdb.cp/cpexprs.exp.tcl
+++ b/gdb/testsuite/gdb.cp/cpexprs.exp.tcl
@@ -449,95 +449,95 @@  add {tclass<base>::do_something} \
     - \
     -
 add {flubber<int, int, int, int, int>} \
-    {void (void)} \
+    {void (void) [with A = int, B = int, C = int, D = int, E = int]} \
     - \
     flubber
 add {flubber<int, int, int, int, short>} \
-    {void (void)} \
+    {void (void) [with A = int, B = int, C = int, D = int, E = short]} \
     - \
     flubber
 add {flubber<int, int, int, int, long>} \
-    {void (void)} \
+    {void (void) [with A = int, B = int, C = int, D = int, E = long]} \
     - \
     flubber
 add {flubber<int, int, int, int, char>} \
-    {void (void)} \
+    {void (void) [with A = int, B = int, C = int, D = int, E = char]} \
     - \
     flubber
 add {flubber<int, int, int, short, int>} \
-    {void (void)} \
+    {void (void) [with A = int, B = int, C = int, D = short, E = int]} \
     - \
     flubber
 add {flubber<int, int, int, short, short>} \
-    {void (void)} \
+    {void (void) [with A = int, B = int, C = int, D = short, E = short]} \
     - \
     flubber
 add {flubber<int, int, int, short, long>} \
-    {void (void)} \
+    {void (void) [with A = int, B = int, C = int, D = short, E = long]} \
     - \
     flubber
 add {flubber<int, int, int, short, char>} \
-    {void (void)} \
+    {void (void) [with A = int, B = int, C = int, D = short, E = char]} \
     - \
     flubber
 add {flubber<int, int, int, long, int>} \
-    {void (void)} \
+    {void (void) [with A = int, B = int, C = int, D = long, E = int]} \
     - \
     flubber
 add {flubber<int, int, int, long, short>} \
-    {void (void)} \
+    {void (void) [with A = int, B = int, C = int, D = long, E = short]} \
     - \
     flubber
 add {flubber<int, int, int, long, long>} \
-    {void (void)} \
+    {void (void) [with A = int, B = int, C = int, D = long, E = long]} \
     - \
     flubber
 add {flubber<int, int, int, long, char>} \
-    {void (void)} \
+    {void (void) [with A = int, B = int, C = int, D = long, E = char]} \
     - \
     flubber
 add {flubber<int, int, int, char, int>} \
-    {void (void)} \
+    {void (void) [with A = int, B = int, C = int, D = char, E = int]} \
     - \
     flubber
 add {flubber<int, int, int, char, short>} \
-    {void (void)} \
+    {void (void) [with A = int, B = int, C = int, D = char, E = short]} \
     - \
     flubber
 add {flubber<int, int, int, char, long>} \
-    {void (void)} \
+    {void (void) [with A = int, B = int, C = int, D = char, E = long]} \
     - \
     flubber
 add {flubber<int, int, int, char, char>} \
-    {void (void)} \
+    {void (void) [with A = int, B = int, C = int, D = char, E = char]} \
     - \
     flubber
 add {flubber<int, int, short, int, int>} \
-    {void (void)} \
+    {void (void) [with A = int, B = int, C = short, D = int, E = int]} \
     - \
     flubber
 add {flubber<int, int, short, int, short>} \
-    {void (void)} \
+    {void (void) [with A = int, B = int, C = short, D = int, E = short]} \
     - \
     flubber
 add {flubber<int, int, short, int, long>} \
-    {void (void)} \
+    {void (void) [with A = int, B = int, C = short, D = int, E = long]} \
     - \
     flubber
 add {flubber<int, int, short, int, char>} \
-    {void (void)} \
+    {void (void) [with A = int, B = int, C = short, D = int, E = char]} \
     - \
     flubber
 add {flubber<int, int, short, short, int>} \
-    {void (void)} \
+    {void (void) [with A = int, B = int, C = short, D = short, E = int]} \
     - \
     flubber
 add {flubber<short, int, short, int, short>} \
-    {void (void)} \
+    {void (void) [with A = short, B = int, C = short, D = int, E = short]} \
     - \
     flubber
 add {flubber<long, short, long, short, long>} \
-    {void (void)} \
+    {void (void) [with A = long, B = short, C = long, D = short, E = long]} \
     - \
     flubber
 add {tclass<base>::do_something} \
diff --git a/gdb/testsuite/gdb.dwarf2/template-specification-full-name.exp b/gdb/testsuite/gdb.dwarf2/template-specification-full-name.exp
index eea7106d701..714a59f3568 100644
--- a/gdb/testsuite/gdb.dwarf2/template-specification-full-name.exp
+++ b/gdb/testsuite/gdb.dwarf2/template-specification-full-name.exp
@@ -81,4 +81,5 @@  if {![runto_main]} {
 }
 
 # Just a sanity check to make sure GDB slurped the symbols correctly.
-gdb_test "print apply<int>" " = {void \\(void\\)} $hex <apply<int>\\(\\)>"
+gdb_test "print apply<int>" \
+    " = {void \\(void\\) \\\[with T = int\\\]} $hex <apply<int>\\(\\)>"