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
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
@@ -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. */
@@ -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 */
@@ -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
@@ -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
@@ -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
}
@@ -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} \
@@ -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>\\(\\)>"