[4/5] gdb: Print target type and name of template members.

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

Commit Message

Christina Schimpe Dec. 29, 2025, 3:15 p.m. UTC
  Since commit "gdb: Enable correct template type resolution." GDB is able
to resolve template type parameters correctly in case the DWARF provides
this information.  Currently, when printing the type of a templated
class/struct member GDB shows only the template parameter name (e.g. T)
but is missing the resolved target type.  In contrast, printing the type
of entire class/struct shows both.

This patch changes this for templated members by adding the target type
iformation in a similar style we use already for printing the type of
entire structs or classes.

Example template class:

~~~
template <typename T>
struct A
{
  T t_val;
  int i_val;
};

int
main ()
{
  A<int> a;
  return 0;
}
~~~

with good DWARF support as described above, is currently printes as follows:

~~~
(gdb) ptype a
type = struct A<int> [with T = int] {
   T t_val;
   int i_val;
(gdb) ptype a.t_val
type = T
~~~

Note that for the second ptype command the target type information is
missing.

This patch changes this as follows:
~~~
(gdb) ptype a.t_val
type = T [with T = int]
~~~

For incomplete DWARF (as generated by current GCC/Clang versions), we still
print the underlying type only (e.g. int):
~~~
(gdb) ptype a.t_val
type = int
~~~
---
 gdb/c-lang.c                                  |  6 +++
 gdb/c-lang.h                                  |  9 ++++
 gdb/c-typeprint.c                             | 41 +++++++++++++++++++
 .../gdb.cp/template-type-parameters.exp       | 28 +++++++------
 .../gdb.dwarf2/self-referential-templates.exp |  2 +-
 .../gdb.dwarf2/template-type-resolution.exp   | 20 ++++-----
 6 files changed, 83 insertions(+), 23 deletions(-)
  

Patch

diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index 59db4c4ab6d..045e3cf1244 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -964,6 +964,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 of TYPE if it is a template type parameter.
+       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);
   }
 
   /* See language.h.  */
diff --git a/gdb/c-lang.h b/gdb/c-lang.h
index 48b3f114d50..e9a29e3547c 100644
--- a/gdb/c-lang.h
+++ b/gdb/c-lang.h
@@ -135,4 +135,13 @@  extern int c_textual_element_type (struct type *, char);
 
 extern gdb::unique_xmalloc_ptr<char> c_canonicalize_name (const char *name);
 
+/* Display template type parameter TYPE in the style [with T = ...] based
+   on FLAGS and LANGUAGE on STREAM.  */
+
+extern void c_type_print_template_type_parameter
+  (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 50dbb47b82c..b22068ce9ee 100644
--- a/gdb/c-typeprint.c
+++ b/gdb/c-typeprint.c
@@ -787,10 +787,41 @@  c_type_print_varspec_suffix (struct type *type,
     }
 }
 
+/* See c-lang.h.  */
+
+void
+c_type_print_template_type_parameter (const type *type,
+				      const type_print_options *flags,
+				      const language language,
+				      ui_file *stream)
+{
+
+  gdb_assert (type->code () == TYPE_CODE_TEMPLATE_TYPE_PARAM);
+
+  /* For unnamed templates GDB adds a default name.  */
+  gdb_assert (type->name () != nullptr);
+
+  if (flags->raw)
+    return;
+
+  stream->wrap_here (4);
+  gdb_printf (stream, _(" [with %ps = "),
+	      styled_string (variable_name_style.style (), type->name ()));
+
+  if (type->target_type () == nullptr)
+    fprintf_styled (stream, metadata_style.style (), _("<type unknown>"));
+  else
+    c_print_type (type->target_type (), "", stream, -1, 0, language, flags);
+
+  gdb_puts (_("]"), stream);
+}
+
+
 /* 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.  */
 
@@ -799,6 +830,10 @@  c_type_print_template_args (const struct type_print_options *flags,
 			    struct type *type, struct ui_file *stream,
 			    enum language language)
 {
+  gdb_assert (type->code () == TYPE_CODE_STRUCT
+	      || type->code () == TYPE_CODE_UNION
+	      || type->is_declared_class ());
+
   if (flags->raw || TYPE_N_TEMPLATE_ARGUMENTS (type) == 0)
     return;
 
@@ -1550,6 +1585,12 @@  c_type_print_base_1 (struct type *type, struct ui_file *stream,
       gdb_puts (type->name (), stream);
       break;
 
+    case TYPE_CODE_TEMPLATE_TYPE_PARAM:
+      /* For unnamed templates GDB adds a default name.  */
+      gdb_assert (type->name () != nullptr);
+      c_print_type (type, "", stream, -1, 0, language, flags);
+      break;
+
     default:
       /* Handle types not explicitly handled by the other cases, such
 	 as fundamental types.  For these, just print whatever the
diff --git a/gdb/testsuite/gdb.cp/template-type-parameters.exp b/gdb/testsuite/gdb.cp/template-type-parameters.exp
index 173a09f4b3c..92ddbded065 100644
--- a/gdb/testsuite/gdb.cp/template-type-parameters.exp
+++ b/gdb/testsuite/gdb.cp/template-type-parameters.exp
@@ -73,9 +73,9 @@  gdb_test "ptype a" \
 	".*float ffunc\\(float, U\\);.*" ]
 
 setup_xfail gcc/57006 "*-*-*"
-gdb_test "ptype a.t_val" "type = T"
+gdb_test "ptype a.t_val" "type = T \\\[with T = int\\\]"
 setup_xfail gcc/57006 "*-*-*"
-gdb_test "ptype a.u_val" "type = U"
+gdb_test "ptype a.u_val" "type = U \\\[with U = int\\\]"
 gdb_test "ptype a.i_val" "type = int"
 gdb_test "ptype a.f_val" "type = float"
 
@@ -96,9 +96,9 @@  gdb_test "ptype b" \
 	".*float ffunc\\(float, U\\);.*" ]
 
 setup_xfail gcc/57006 "*-*-*"
-gdb_test "ptype b.t_val" "type = T"
+gdb_test "ptype b.t_val" "type = T \\\[with T = float\\\]"
 setup_xfail gcc/57006 "*-*-*"
-gdb_test "ptype b.u_val" "type = U"
+gdb_test "ptype b.u_val" "type = U \\\[with U = float\\\]"
 gdb_test "ptype b.i_val" "type = int"
 gdb_test "ptype b.f_val" "type = float"
 
@@ -118,9 +118,9 @@  gdb_test "ptype c" \
 	".*float ffunc\\(float, U\\);.*" ]
 
 setup_xfail gcc/57006 "*-*-*"
-gdb_test "ptype c.t_val" "type = T"
+gdb_test "ptype c.t_val" "type = T \\\[with T = float\\\]"
 setup_xfail gcc/57006 "*-*-*"
-gdb_test "ptype c.u_val" "type = U"
+gdb_test "ptype c.u_val" "type = U \\\[with U = int\\\]"
 gdb_test "ptype c.i_val" "type = int"
 gdb_test "ptype c.f_val" "type = float"
 
@@ -139,9 +139,9 @@  gdb_test "ptype d" \
 	".*float ffunc\\(float, U\\);.*" ]
 
 setup_xfail gcc/57006 "*-*-*"
-gdb_test "ptype d.t_val" "type = T"
+gdb_test "ptype d.t_val" "type = T \\\[with T = A<float, int>\\\]"
 setup_xfail gcc/57006 "*-*-*"
-gdb_test "ptype d.u_val" "type = U"
+gdb_test "ptype d.u_val" "type = U \\\[with U = A<float, float>\\\]"
 gdb_test "ptype d.i_val" "type = int"
 gdb_test "ptype d.f_val" "type = float"
 
@@ -161,9 +161,9 @@  gdb_test "ptype e" \
 	".*float ffunc\\(float, U\\);.*" ]
 
 setup_xfail gcc/57006 "*-*-*"
-gdb_test "ptype e.t_val" "type = T"
+gdb_test "ptype e.t_val" "type = T \\\[with T = A<float, float>\\\]"
 setup_xfail gcc/57006 "*-*-*"
-gdb_test "ptype e.u_val" "type = U"
+gdb_test "ptype e.u_val" "type = U \\\[with U = A<float, float>\\\]"
 gdb_test "ptype e.i_val" "type = int"
 gdb_test "ptype e.f_val" "type = float"
 
@@ -175,7 +175,7 @@  gdb_test "ptype f" \
 	".*B<T>::C<T> c;.*" ]
 
 setup_xfail gcc/57006 "*-*-*"
-gdb_test "ptype f.t_val" "type = T"
+gdb_test "ptype f.t_val" "type = T \\\[with T = int\\\]"
 
 gdb_test "ptype f.c" \
     [multi_line "type = struct B<int>::C<int> \\\[with U = int\\\] {" \
@@ -187,9 +187,13 @@  gdb_test "ptype g" \
 	".*B<T>::C<T> c;.*" ]
 
 setup_xfail gcc/57006 "*-*-*"
-gdb_test "ptype g.t_val" "type = T"
+gdb_test "ptype g.t_val" "type = T \\\[with T = B<int>\\\]"
 
 gdb_test "ptype g.c" \
     [multi_line "type = struct B<B<int> >::C<B<int> > \\\[with U = B<int>\\\] {" \
 	".*U u_val;.*" ]
 
+setup_xfail gcc/57006 "*-*-*"
+gdb_test "ptype func<int, float>" \
+    "type = T \\\(T, U\\\) \\\[with T = int, U = float\\\]"
+
diff --git a/gdb/testsuite/gdb.dwarf2/self-referential-templates.exp b/gdb/testsuite/gdb.dwarf2/self-referential-templates.exp
index fbe6cad1c00..cdd45bda7e1 100644
--- a/gdb/testsuite/gdb.dwarf2/self-referential-templates.exp
+++ b/gdb/testsuite/gdb.dwarf2/self-referential-templates.exp
@@ -110,7 +110,7 @@  with_test_prefix "self-referential template types" {
 	"    T me_t;.*" \
 	"    T t_func\\\(T\\\);.*" \
     "}"]
-    gdb_test "ptype var.me_t" "type = T"
+    gdb_test "ptype var.me_t" "type = T \\\[with T = <type unknown>\\\]"
 
     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.exp b/gdb/testsuite/gdb.dwarf2/template-type-resolution.exp
index 82788bdfda2..2f805d7882d 100644
--- a/gdb/testsuite/gdb.dwarf2/template-type-resolution.exp
+++ b/gdb/testsuite/gdb.dwarf2/template-type-resolution.exp
@@ -285,9 +285,9 @@  gdb_test "ptype var" [multi_line \
     "    T t_func\\\(T\\\);.*" \
 "}"]
 
-gdb_test "ptype var.me_t" "type = T"
+gdb_test "ptype var.me_t" "type = T \\\[with T = int\\\]"
 gdb_test "ptype var.me_int" "type = int"
-gdb_test "ptype var.me_u" "type = U"
+gdb_test "ptype var.me_u" "type = U \\\[with U = float\\\]"
 gdb_test "ptype var.me_float" "type = float"
 
 gdb_test "print var" "$var"
@@ -300,9 +300,9 @@  gdb_test "ptype nestedvar" [multi_line \
     "    float me_float;.*" \
     "    T t_func\\\(T\\\);.*" \
 "}"]
-gdb_test "ptype nestedvar.me_t" "type = T"
+gdb_test "ptype nestedvar.me_t" "type = T \\\[with T = $tfloatfloat\\\]"
 gdb_test "ptype nestedvar.me_int" "type = int"
-gdb_test "ptype nestedvar.me_u" "type = U"
+gdb_test "ptype nestedvar.me_u" "type = U \\\[with U = $tfloatfloat\\\]"
 gdb_test "ptype nestedvar.me_float" "type = float"
 
 gdb_test "print nestedvar" "{me_t = $var, me_int = 0, me_u = $var, me_float = 0}"
@@ -445,8 +445,8 @@  gdb_test "ptype unnamed_var1" [multi_line \
     "    second me2;" \
     "}"]
 
-gdb_test "ptype unnamed_var1.me" "type = <unnamed0>"
-gdb_test "ptype unnamed_var1.me2" "type = second"
+gdb_test "ptype unnamed_var1.me" "type = <unnamed0> \\\[with <unnamed0> = int\\\]"
+gdb_test "ptype unnamed_var1.me2" "type = second \\\[with second = float\\\]"
 
 gdb_test "ptype unnamed_var2" [multi_line \
     "type = class template_var2<int, float> \\\[with <unnamed0> = int, <unnamed1> = float\\\] {" \
@@ -454,8 +454,8 @@  gdb_test "ptype unnamed_var2" [multi_line \
     "    <unnamed1> me2;" \
     "}"]
 
-gdb_test "ptype unnamed_var2.me" "type = <unnamed0>"
-gdb_test "ptype unnamed_var2.me2" "type = <unnamed1>"
+gdb_test "ptype unnamed_var2.me" "type = <unnamed0> \\\[with <unnamed0> = int\\\]"
+gdb_test "ptype unnamed_var2.me2" "type = <unnamed1> \\\[with <unnamed1> = float\\\]"
 
 gdb_test "ptype unnamed_var3" [multi_line \
     "type = struct template_var3<0, int, 11, float> \\\[with <unnamed0> = 0, <unnamed1> = int, <unnamed2> = 11, <unnamed3> = float\\\] {" \
@@ -463,5 +463,5 @@  gdb_test "ptype unnamed_var3" [multi_line \
     "    <unnamed3> me2;" \
     "}"]
 
-gdb_test "ptype unnamed_var3.me" "type = <unnamed1>"
-gdb_test "ptype unnamed_var3.me2" "type = <unnamed3>"
+gdb_test "ptype unnamed_var3.me" "type = <unnamed1> \\\[with <unnamed1> = int\\\]"
+gdb_test "ptype unnamed_var3.me2" "type = <unnamed3> \\\[with <unnamed3> = float\\\]"