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

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

Commit Message

Christina Schimpe Dec. 29, 2025, 3:15 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)>
~~~

In case of the new DWARF format as described in the former commit
"gdb: Support new DWARF for template type parameters.", we can display
the correct relationship, too:
~~~
(gdb) ptype func<int, float>
type = T (T, U) [with T = int, U = float]
(gdb) print func<int, float>
$6 = {T (T, U) [with T = int, U = float]} 0x4012a0 <func<int, float>(int, float)>
~~~

Extend the test gdb.cp/template-type-parameters.exp for function templates
and add the apprioriate xfails which refer to the compiler ticket for the
new DWARF format: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57006.
Fix the existing tests for template functions to expect the new output.
---
 gdb/c-lang.c                                  |  8 ++--
 gdb/c-lang.h                                  | 11 +++++
 gdb/c-typeprint.c                             | 36 +++++++++------
 gdb/dwarf2/read.c                             | 18 ++++++++
 gdb/gdbtypes.c                                |  3 +-
 gdb/testsuite/gdb.cp/cp-relocate.exp          |  4 +-
 gdb/testsuite/gdb.cp/cpexprs.exp.tcl          | 46 +++++++++----------
 .../gdb.cp/template-type-parameters.cc        | 10 +++-
 .../gdb.cp/template-type-parameters.exp       |  7 ++-
 .../template-specification-full-name.exp      |  3 +-
 10 files changed, 99 insertions(+), 47 deletions(-)
  

Patch

diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index 045e3cf1244..0a2d1e2f949 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -965,11 +965,13 @@  class cplus_language : public language_defn
   {
     c_print_type (type, varstring, stream, show, level, la_language, flags);
 
-    /* Print target type of TYPE if it is a template type parameter.
-       For struct/class/unions we print it in
-       c_type_print_base_struct_union.  */
+    /* Print target type or template arguments if TYPE it is a template type
+       parameter or function template, respecively.  For struct/class/unions
+       we print it in c_type_print_base_struct_union.  */
   if (type->code () == TYPE_CODE_TEMPLATE_TYPE_PARAM)
     c_type_print_template_type_parameter (type, flags, la_language, stream);
+  else if (type->code () == TYPE_CODE_FUNC)
+    c_type_print_template_args (type, flags, la_language, stream);
   }
 
   /* See language.h.  */
diff --git a/gdb/c-lang.h b/gdb/c-lang.h
index e9a29e3547c..7ae8285a2c7 100644
--- a/gdb/c-lang.h
+++ b/gdb/c-lang.h
@@ -144,4 +144,15 @@  extern void c_type_print_template_type_parameter
    const language language,
    ui_file *stream);
 
+/* 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 *type,
+   const type_print_options *flags,
+   const language language,
+   ui_file *stream);
+
 #endif /* GDB_C_LANG_H */
diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c
index b22068ce9ee..8899dc864f8 100644
--- a/gdb/c-typeprint.c
+++ b/gdb/c-typeprint.c
@@ -816,28 +816,29 @@  c_type_print_template_type_parameter (const type *type,
   gdb_puts (_("]"), stream);
 }
 
+/* See c-lang.h.  */
 
-/* A helper for c_type_print_base_struct_union that displays template
-   parameters.
-
-   TYPE is the type whose template arguments are being displayed.
-   It must be a struct, class or union type.
-
-   STREAM is the stream on which to print.  */
-
-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 *type,
+			    const type_print_options *flags,
+			    const language language,
+			    ui_file *stream)
 {
   gdb_assert (type->code () == TYPE_CODE_STRUCT
 	      || type->code () == TYPE_CODE_UNION
-	      || type->is_declared_class ());
+	      || type->is_declared_class ()
+	      || 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)
@@ -872,7 +873,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
@@ -1003,7 +1009,7 @@  c_type_print_base_struct_union (struct type *type, struct ui_file *stream,
       struct type *basetype;
       int vptr_fieldno;
 
-      c_type_print_template_args (&local_flags, type, stream, language);
+      c_type_print_template_args (type, &local_flags, language, stream);
 
       if (local_flags.local_typedefs != nullptr
 	  && !HAS_TEMPLATE_TYPE_MEMBER (type))
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 297e01a343b..10ef8a89f59 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -8710,6 +8710,24 @@  read_func_scope (struct die_info *die, struct dwarf2_cu *cu)
      symbols go in the file symbol list.  */
   if (cu->get_builder ()->outermost_context_p ())
     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/gdbtypes.c b/gdb/gdbtypes.c
index 0bee3fe1bc1..84e57fb9154 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -1948,7 +1948,8 @@  set_type_vptr_fieldno (struct type *type, int fieldno)
 {
   type = check_typedef (type);
   gdb_assert (type->code () == TYPE_CODE_STRUCT
-	      || type->code () == TYPE_CODE_UNION);
+	      || type->code () == TYPE_CODE_UNION
+	      || type->code () == TYPE_CODE_FUNC);
   if (!HAVE_CPLUS_STRUCT (type))
     ALLOCATE_CPLUS_STRUCT_TYPE (type);
   TYPE_RAW_CPLUS_SPECIFIC (type)->vptr_fieldno = fieldno;
diff --git a/gdb/testsuite/gdb.cp/cp-relocate.exp b/gdb/testsuite/gdb.cp/cp-relocate.exp
index 836f5fb6158..76d4b8d1b4e 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 6766ccea19c..d1b8ab67451 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.cp/template-type-parameters.cc b/gdb/testsuite/gdb.cp/template-type-parameters.cc
index 9cb0a25062b..cf270663dc5 100644
--- a/gdb/testsuite/gdb.cp/template-type-parameters.cc
+++ b/gdb/testsuite/gdb.cp/template-type-parameters.cc
@@ -52,6 +52,12 @@  struct B {
   C<T> c;
 };
 
+template<typename T, typename U>
+static T func (T tval, U uval)
+{
+  return tval;
+}
+
 int
 main ()
 {
@@ -91,5 +97,7 @@  main ()
   B<B<int>> g;
   g.t_val = f;
 
-  return 0; /* Break here.  */
+  int retval = func<int, float> (0, 4.0f);
+
+  return retval; /* Break here.  */
 }
diff --git a/gdb/testsuite/gdb.cp/template-type-parameters.exp b/gdb/testsuite/gdb.cp/template-type-parameters.exp
index 92ddbded065..97c212f860d 100644
--- a/gdb/testsuite/gdb.cp/template-type-parameters.exp
+++ b/gdb/testsuite/gdb.cp/template-type-parameters.exp
@@ -193,7 +193,12 @@  gdb_test "ptype g.c" \
     [multi_line "type = struct B<B<int> >::C<B<int> > \\\[with U = B<int>\\\] {" \
 	".*U u_val;.*" ]
 
+set func_temargs "\\\[with T = int, U = float\\\]"
+
 setup_xfail gcc/57006 "*-*-*"
 gdb_test "ptype func<int, float>" \
-    "type = T \\\(T, U\\\) \\\[with T = int, U = float\\\]"
+    "type = T \\\(T, U\\\) $func_temargs"
 
+setup_xfail gcc/57006 "*-*-*"
+gdb_test "print func<int, float>" \
+    " = {T \\\(T, U\\\) $func_temargs} $hex <func<int, float>\\\(int, float\\\)>"
diff --git a/gdb/testsuite/gdb.dwarf2/template-specification-full-name.exp b/gdb/testsuite/gdb.dwarf2/template-specification-full-name.exp
index 010be287289..b4fd46f111b 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>\\(\\)>"