[3/5] gdb: Enable correct template type resolution.

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

Commit Message

Christina Schimpe Dec. 29, 2025, 3:15 p.m. UTC
  This patch addresses the GDB bug:

https://sourceware.org/bugzilla/show_bug.cgi?id=20540

Curently GDB resolves a templated class such as
~~~
template <typename T>
struct A
{
  A () : t_val (), i_val () { }
  T t_val;
  int i_val;
};

int
main ()
{
  A<int> a;
  a.t_val = 1;
  a.i_val = 3; /* Break here.  */

  return 0;
}

~~~

as follows:
~~~
14        a.i_val = 3; /* Break here.  */
(gdb) ptype a
type = struct A<int> [with T = int] {
   T t_val;
   T i_val;

   A(void);
~~~

The DWARF produced by clang
$ clang --version
Ubuntu clang version 14.0.0-1ubuntu1.1
and extracted with
$ llvm-dwarfdump --version
Ubuntu LLVM version 14.0.0

currently looks as follows:
~~~
[...]
0x0000000c: DW_TAG_compile_unit
              DW_AT_producer    ("Ubuntu clang version 14.0.0-1ubuntu1.1")
              DW_AT_language    (DW_LANG_C_plus_plus_14)
[...]
0x0000002b:   DW_TAG_structure_type
                DW_AT_calling_convention        (DW_CC_pass_by_value)
                DW_AT_name      ("A<int>")
                DW_AT_byte_size (0x08)
                DW_AT_decl_file ("/tmp/sample.cc")
                DW_AT_decl_line (2)

0x00000031:     DW_TAG_template_type_parameter
                  DW_AT_type    (0x00000054 "int")
                  DW_AT_name    ("T")

0x00000037:     DW_TAG_member
                  DW_AT_name    ("t_val")
                  DW_AT_type    (0x00000054 "int")
                  DW_AT_decl_file       ("/tmp/sample.cc")
                  DW_AT_decl_line       (5)
                  DW_AT_data_member_location    (0x00)

0x00000040:     DW_TAG_member
                  DW_AT_name    ("i_val")
                  DW_AT_type    (0x00000054 "int")
                  DW_AT_decl_file       ("/tmp/sample.cc")
                  DW_AT_decl_line       (6)
                  DW_AT_data_member_location    (0x04)

0x00000049:     DW_TAG_subprogram
                  DW_AT_name    ("A")
                  DW_AT_decl_file       ("/tmp/sample.cc")
                  DW_AT_decl_line       (4)
                  DW_AT_declaration     (true)
                  DW_AT_external        (true)

0x0000004d:       DW_TAG_formal_parameter
                    DW_AT_type  (0x00000058 "A<int> *")
                    DW_AT_artificial    (true)

0x00000052:       NULL

0x00000053:     NULL
[...]
~~~

The issue is that type references of DIEs that describe source entities
referring to the formal template parameter reference the underlying type DIE
and not the DW_TAG_template_type_parameter DIE.  An example is the DIE
describing t_val.  For t_val there is no way how a debugger could distinguish
this type from a normal member of type int (i_val in the example above).

GDB's current template type resolution is based on a typedef_hash_table-based
substitution of template type parameters in gdb/c-typeprint.c:c_print_type_1.
Due to that map, GDB prints the template type for any member of type int or
of the actual templated type.

This patch enables correct template type resolution for such cases in
case the debuggee supports the new DWARF defined by the DWARF 5 spec.  The
detailed format has already been  described in the former commit "gdb: Support
new DWARF for template type parameters.".

In case GDB detects the old DWARF format, the template type resolution
falls back to the typedef_hash_table-based substitution, so current
template type tests will continue to work as before.  The dwarf tests
gdb.dwarf2/template-type-resolution.exp is extended to verify the correct
template type resolution for this new DWARF.  For better readability a new
test gdb.cp/template-type-parameters.exp is added which focuses on the
resolution of template type parameters only.  It contains approriate xfails,
which refer to the compiler ticket for the new DWARF format:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57006
---
 gdb/c-typeprint.c                             |  10 +-
 gdb/cp-valprint.c                             |   6 +
 gdb/dwarf2/read.c                             |  25 +-
 gdb/gdbtypes.h                                |   8 +
 .../gdb.cp/template-type-parameters.cc        |  95 +++++
 .../gdb.cp/template-type-parameters.exp       | 195 ++++++++++
 .../gdb.dwarf2/template-type-resolution.cc    |   9 +-
 .../gdb.dwarf2/template-type-resolution.exp   | 339 +++++++++++++++++-
 gdb/typeprint.c                               |   9 +-
 gdb/typeprint.h                               |   9 +-
 10 files changed, 684 insertions(+), 21 deletions(-)
 create mode 100644 gdb/testsuite/gdb.cp/template-type-parameters.cc
 create mode 100644 gdb/testsuite/gdb.cp/template-type-parameters.exp
  

Patch

diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c
index 12b69ed6ebe..50dbb47b82c 100644
--- a/gdb/c-typeprint.c
+++ b/gdb/c-typeprint.c
@@ -970,13 +970,15 @@  c_type_print_base_struct_union (struct type *type, struct ui_file *stream,
 
       c_type_print_template_args (&local_flags, type, stream, language);
 
-      /* Add in template parameters when printing derivation info.  */
-      if (local_flags.local_typedefs != NULL)
+      if (local_flags.local_typedefs != nullptr
+	  && !HAS_TEMPLATE_TYPE_MEMBER (type))
 	local_flags.local_typedefs->add_template_parameters (type);
       cp_type_print_derivation_info (stream, type, &local_flags);
 
-      /* This holds just the global typedefs and the template
-	 parameters.  */
+      /* This holds just the global typedefs, and the template type
+	 parameters in case the condition HAS_TEMPLATE_TYPE_MEMBER
+	 (type) is not fulfilled.  */
+
       struct type_print_options semi_local_flags = *flags;
       semi_local_flags.local_typedefs = NULL;
 
diff --git a/gdb/cp-valprint.c b/gdb/cp-valprint.c
index 252072b66dd..ffbf619e0ab 100644
--- a/gdb/cp-valprint.c
+++ b/gdb/cp-valprint.c
@@ -131,6 +131,12 @@  cp_print_value_fields (struct value *val, struct ui_file *stream,
   static int last_set_recurse = -1;
 
   struct type *type = check_typedef (val->type ());
+  
+  /* For templated types we want to print the value based on its target
+     type.  */
+  if (val->type ()->code () == TYPE_CODE_TEMPLATE_TYPE_PARAM
+      && val->type ()->target_type () != nullptr)
+    type = type->target_type ();
 
   if (recurse == 0)
     {
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 1525e8e4d39..297e01a343b 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -11489,8 +11489,31 @@  process_structure_scope (struct die_info *die, struct dwarf2_cu *cu)
       struct field_info fi;
       std::vector<struct symbol *> template_args;
 
+      if (cu->lang () == language_cplus)
+	{
+	  if (!HAVE_CPLUS_STRUCT (type))
+	    ALLOCATE_CPLUS_STRUCT_TYPE (type);
+	  HAS_TEMPLATE_TYPE_MEMBER (type) = false;
+	}
+
       for (die_info *child_die : die->children ())
-	handle_struct_member_die (child_die, type, &fi, &template_args, cu);
+	{
+	  handle_struct_member_die (child_die, type, &fi, &template_args, cu);
+
+	  /* This is a heuristic to detect when DWARF actually references
+	     template type parameter DIEs.  */
+	  if (!template_args.empty () && !HAS_TEMPLATE_TYPE_MEMBER (type))
+	    {
+	      attribute *type_attr = dwarf2_attr (child_die, DW_AT_type, cu);
+	      if (type_attr != nullptr && type_attr->form_is_ref ())
+		{
+		  die_info *type_die
+		    = follow_die_ref (child_die, type_attr, &cu);
+		  if (type_die->tag == DW_TAG_template_type_param)
+		    HAS_TEMPLATE_TYPE_MEMBER (type) = true;
+		}
+	    }
+	}
 
       /* Attach template arguments to type.  */
       if (!template_args.empty ())
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index 3ae5a1ef484..c9a67210cff 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -1783,6 +1783,12 @@  struct cplus_struct_type
        classes.  */
 
     struct symbol **template_arguments;
+
+    /* Flag used in the heuristic to detect a specific DWARF format for
+       type DIEs describing source entities referring to the formal template
+       type parameter.  This flag is true if such type DIEs refer to a
+       DW_TAG_template_type_parameter DIE, otherwise it is set to false.  */
+    bool has_template_type_member;
   };
 
 /* * Struct used to store conversion rankings.  */
@@ -1991,6 +1997,8 @@  extern void set_type_vptr_basetype (struct type *, struct type *);
   TYPE_CPLUS_SPECIFIC (thistype)->n_template_arguments
 #define TYPE_TEMPLATE_ARGUMENTS(thistype) \
   TYPE_CPLUS_SPECIFIC (thistype)->template_arguments
+#define HAS_TEMPLATE_TYPE_MEMBER(thistype) \
+  TYPE_CPLUS_SPECIFIC (thistype)->has_template_type_member
 #define TYPE_TEMPLATE_ARGUMENT(thistype, n) \
   TYPE_CPLUS_SPECIFIC (thistype)->template_arguments[n]
 
diff --git a/gdb/testsuite/gdb.cp/template-type-parameters.cc b/gdb/testsuite/gdb.cp/template-type-parameters.cc
new file mode 100644
index 00000000000..9cb0a25062b
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/template-type-parameters.cc
@@ -0,0 +1,95 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright (C) 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, typename U>
+struct A
+{
+  A () : t_val (), u_val (), i_val (), f_val () { }
+  T t_val;
+  U u_val;
+  int i_val;
+  float f_val;
+
+  T tfunc (T tval, U uval)
+  {
+    return tval;
+  }
+
+  int ifunc (int ival, T tval)
+  {
+    return ival;
+  }
+
+  float ffunc (float fval, U uval)
+  {
+    return fval;
+  }
+};
+
+template<typename T>
+struct B {
+  T t_val;
+
+  template<typename U>
+  struct C {
+    U u_val;
+  };
+
+  C<T> c;
+};
+
+int
+main ()
+{
+  A<int, int> a;
+  a.t_val = 1;
+  a.u_val = 2;
+  a.i_val = 3;
+  a.f_val = 4.0f;
+
+  A<float, float> b;
+  b.t_val = 1;
+  b.u_val = 2.0f;
+  b.i_val = 3;
+  b.f_val = 4.0f;
+
+  A<float, int> c;
+  c.t_val = 1.0;
+  c.u_val = 2;
+  c.i_val = 3;
+  c.f_val = 4.0f;
+
+  A<A<float, int>, A<float, float>> d;
+  d.t_val = c;
+  d.u_val = b;
+  d.i_val = 3;
+  d.f_val = 4.0f;
+
+  A<A<float, float>, A<float, float>> e;
+  e.t_val = b;
+  e.u_val = b;
+  e.i_val = 3;
+  e.f_val = 4.0f;
+
+  B<int> f;
+  f.t_val = 1;
+
+  B<B<int>> g;
+  g.t_val = f;
+
+  return 0; /* Break here.  */
+}
diff --git a/gdb/testsuite/gdb.cp/template-type-parameters.exp b/gdb/testsuite/gdb.cp/template-type-parameters.exp
new file mode 100644
index 00000000000..173a09f4b3c
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/template-type-parameters.exp
@@ -0,0 +1,195 @@ 
+# 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 extended template type resolution for a DWARF format which allows
+# GDB to differentiate between template types and normal types, which have
+# the same target type.
+#
+# This test is only supported in case a compiler is used which supports a
+# specific DWARF format for type references of DIEs that describe source
+# entities referring to the formal template parameter.
+# They must reference the DW_TAG_template_type_parameter DIE, and not the
+# underlying type DIE.
+#
+# This format 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$)
+#
+# For current versions of GCC and Clang this test will fail.
+
+standard_testfile .cc
+
+if { [prepare_for_testing "failed to prepare" $testfile $srcfile {debug} ] } {
+    return
+}
+
+if { ![runto_main] } {
+    return
+}
+
+gdb_breakpoint [gdb_get_line_number "Break here."]
+gdb_continue_to_breakpoint "Break here."
+
+# Test same target type int.
+setup_xfail gcc/57006 "*-*-*"
+gdb_test "ptype a" \
+    [multi_line "type = struct A<int, int> \\\[with T = int, U = int\\\] {" \
+	".*T t_val;.*" \
+	".*U u_val;.*" \
+	".*int i_val;.*" \
+	".*float f_val;.*" \
+	".*A\\(void\\);.*" \
+	".*T tfunc\\(T, U\\);.*" \
+	".*int ifunc\\(int, T\\);.*" \
+	".*float ffunc\\(float, U\\);.*" ]
+
+setup_xfail gcc/57006 "*-*-*"
+gdb_test "ptype a.t_val" "type = T"
+setup_xfail gcc/57006 "*-*-*"
+gdb_test "ptype a.u_val" "type = U"
+gdb_test "ptype a.i_val" "type = int"
+gdb_test "ptype a.f_val" "type = float"
+
+set val "{t_val = 1, u_val = 2, i_val = 3, f_val = 4}"
+gdb_test "print a" "$val"
+
+# Test same target type float.
+setup_xfail gcc/57006 "*-*-*"
+gdb_test "ptype b" \
+    [multi_line "type = struct A<float, float> \\\[with T = float, U = float\\\] {" \
+	".*T t_val;.*" \
+	".*U u_val;.*" \
+	".*int i_val;.*" \
+	".*float f_val;.*" \
+	".*A\\(void\\);.*" \
+	".*T tfunc\\(T, U\\);.*" \
+	".*int ifunc\\(int, T\\);.*" \
+	".*float ffunc\\(float, U\\);.*" ]
+
+setup_xfail gcc/57006 "*-*-*"
+gdb_test "ptype b.t_val" "type = T"
+setup_xfail gcc/57006 "*-*-*"
+gdb_test "ptype b.u_val" "type = U"
+gdb_test "ptype b.i_val" "type = int"
+gdb_test "ptype b.f_val" "type = float"
+
+gdb_test "print b" "$val"
+
+# Test different target types.
+setup_xfail gcc/57006 "*-*-*"
+gdb_test "ptype c" \
+    [multi_line "type = struct A<float, int> \\\[with T = float, U = int\\\] {" \
+	".*T t_val;.*" \
+	".*U u_val;.*" \
+	".*int i_val;.*" \
+	".*float f_val;.*" \
+	".*A\\(void\\);.*" \
+	".*T tfunc\\(T, U\\);.*" \
+	".*int ifunc\\(int, T\\);.*" \
+	".*float ffunc\\(float, U\\);.*" ]
+
+setup_xfail gcc/57006 "*-*-*"
+gdb_test "ptype c.t_val" "type = T"
+setup_xfail gcc/57006 "*-*-*"
+gdb_test "ptype c.u_val" "type = U"
+gdb_test "ptype c.i_val" "type = int"
+gdb_test "ptype c.f_val" "type = float"
+
+gdb_test "print c" "$val"
+
+# Test nested template types: d
+gdb_test "ptype d" \
+    [multi_line "type = struct A<A<float, int>, A<float, float> > \\\[with T = A<float, int>, U = A<float, float>\\\] {" \
+	".*T t_val;.*" \
+	".*U u_val;.*" \
+	".*int i_val;.*" \
+	".*float f_val;.*" \
+	".*A\\(void\\);.*" \
+	".*T tfunc\\(T, U\\);.*" \
+	".*int ifunc\\(int, T\\);.*" \
+	".*float ffunc\\(float, U\\);.*" ]
+
+setup_xfail gcc/57006 "*-*-*"
+gdb_test "ptype d.t_val" "type = T"
+setup_xfail gcc/57006 "*-*-*"
+gdb_test "ptype d.u_val" "type = U"
+gdb_test "ptype d.i_val" "type = int"
+gdb_test "ptype d.f_val" "type = float"
+
+gdb_test "print d" "{t_val = $val, u_val = $val, i_val = 3, f_val = 4}"
+
+# Test nested template types: e
+setup_xfail gcc/57006 "*-*-*"
+gdb_test "ptype e" \
+    [multi_line "type = struct A<A<float, float>, A<float, float> > \\\[with T = A<float, float>, U = A<float, float>\\\] {" \
+	".*T t_val;.*" \
+	".*U u_val;.*" \
+	".*int i_val;.*" \
+	".*float f_val;.*" \
+	".*A\\(void\\);.*" \
+	".*T tfunc\\(T, U\\);.*" \
+	".*int ifunc\\(int, T\\);.*" \
+	".*float ffunc\\(float, U\\);.*" ]
+
+setup_xfail gcc/57006 "*-*-*"
+gdb_test "ptype e.t_val" "type = T"
+setup_xfail gcc/57006 "*-*-*"
+gdb_test "ptype e.u_val" "type = U"
+gdb_test "ptype e.i_val" "type = int"
+gdb_test "ptype e.f_val" "type = float"
+
+gdb_test "print e" "{t_val = $val, u_val = $val, i_val = 3, f_val = 4}"
+
+gdb_test "ptype f" \
+    [multi_line "type = struct B<int> \\\[with T = int\\\] {" \
+	".*T t_val;.*" \
+	".*B<T>::C<T> c;.*" ]
+
+setup_xfail gcc/57006 "*-*-*"
+gdb_test "ptype f.t_val" "type = T"
+
+gdb_test "ptype f.c" \
+    [multi_line "type = struct B<int>::C<int> \\\[with U = int\\\] {" \
+	".*U u_val;.*" ]
+
+gdb_test "ptype g" \
+    [multi_line "type = struct B<B<int> > \\\[with T = B<int>\\\] {" \
+	".*T t_val;.*" \
+	".*B<T>::C<T> c;.*" ]
+
+setup_xfail gcc/57006 "*-*-*"
+gdb_test "ptype g.t_val" "type = T"
+
+gdb_test "ptype g.c" \
+    [multi_line "type = struct B<B<int> >::C<B<int> > \\\[with U = B<int>\\\] {" \
+	".*U u_val;.*" ]
+
diff --git a/gdb/testsuite/gdb.dwarf2/template-type-resolution.cc b/gdb/testsuite/gdb.dwarf2/template-type-resolution.cc
index 56d9f490fe2..881617f8a63 100644
--- a/gdb/testsuite/gdb.dwarf2/template-type-resolution.cc
+++ b/gdb/testsuite/gdb.dwarf2/template-type-resolution.cc
@@ -15,10 +15,13 @@ 
    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>
+template <typename T, typename U>
 struct A
 {
   T me_t;
+  int me_int;
+  U me_u;
+  float me_float;
 
   T t_func (T tval)
   {
@@ -27,7 +30,9 @@  struct A
 
 };
 
-A<int> var = {0};
+A<int, float> var = {0, 0, 0.0f, 0.0f};
+A<A<float, float>, A<float, float>> nestedvar
+  = {{0.0f, 0, 0.0f, 0.0f}, 0, {0.0f, 0, 0.0f, 0.0f}, 0.0f};
 
 int
 main ()
diff --git a/gdb/testsuite/gdb.dwarf2/template-type-resolution.exp b/gdb/testsuite/gdb.dwarf2/template-type-resolution.exp
index 4e9cc70d0fe..82788bdfda2 100644
--- a/gdb/testsuite/gdb.dwarf2/template-type-resolution.exp
+++ b/gdb/testsuite/gdb.dwarf2/template-type-resolution.exp
@@ -57,16 +57,21 @@  if { ![runto_main] } {
 }
 
 set int_size [get_sizeof "int" -1]
+set float_size [get_sizeof "float" -1]
 
 set asm_file1 [standard_output_file $srcfile2]
 Dwarf::assemble $asm_file1 {
-    global int_size
+    global int_size float_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
+	    declare_labels int float
+	    declare_labels A template_type_float template_type_int
+	    declare_labels template_pointer1 template_pointer2 template_pointer3
+	    declare_labels nestedvar_type template_type_floatfloat
+	    declare_labels template_type_float2 template_type_float3
+	    declare_labels template_type_float4 template_type_float5
 
 	    int: DW_TAG_base_type {
 		DW_AT_name "int"
@@ -74,22 +79,44 @@  Dwarf::assemble $asm_file1 {
 		DW_AT_encoding @DW_ATE_signed
 	    }
 
+	    float: DW_TAG_base_type {
+		DW_AT_name "float"
+		DW_AT_byte_size 4 DW_FORM_sdata
+		DW_AT_encoding @DW_ATE_float
+	    }
+
 	    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
+		DW_AT_byte_size 2*$int_size+2*$float_size DW_FORM_sdata
 	    } {
 		template_type_int: DW_TAG_template_type_param {
 		    DW_AT_type :$int
 		    DW_AT_name "T"
 		}
+		template_type_float: DW_TAG_template_type_param {
+		    DW_AT_type :$float
+		    DW_AT_name "U"
+		}
 		DW_TAG_member {
 		    DW_AT_name "me_t"
 		    DW_AT_type :$template_type_int
 		}
+		DW_TAG_member {
+		    DW_AT_name "me_int"
+		    DW_AT_type :$int
+		}
+		DW_TAG_member {
+		    DW_AT_name "me_u"
+		    DW_AT_type :$template_type_float
+		}
+		DW_TAG_member {
+		    DW_AT_name "me_float"
+		    DW_AT_type :$float
+		}
 
 		DW_TAG_subprogram {
 		    DW_AT_external 1 DW_FORM_flag
@@ -109,6 +136,7 @@  Dwarf::assemble $asm_file1 {
 	    }
 
 	    template_pointer1: DW_TAG_pointer_type {
+		DW_AT_byte_size 8 DW_FORM_data8
 		DW_AT_type :$A
 	    }
 
@@ -120,6 +148,117 @@  Dwarf::assemble $asm_file1 {
 		} SPECIAL_expr
 		DW_AT_external 1 DW_FORM_flag
 	    }
+
+	    template_type_floatfloat: DW_TAG_structure_type {
+		DW_AT_name "A"
+		DW_AT_byte_size 2*$int_size+2*$float_size DW_FORM_sdata
+	    } {
+		template_type_float2: DW_TAG_template_type_param {
+		    DW_AT_type :$float
+		    DW_AT_name "T"
+		}
+		template_type_float3: DW_TAG_template_type_param {
+		    DW_AT_type :$float
+		    DW_AT_name "U"
+		}
+		DW_TAG_member {
+		    DW_AT_name "me_t"
+		    DW_AT_type :$template_type_float2
+		}
+		DW_TAG_member {
+		    DW_AT_name "me_int"
+		    DW_AT_type :$int
+		}
+		DW_TAG_member {
+		    DW_AT_name "me_u"
+		    DW_AT_type :$template_type_float3
+		}
+		DW_TAG_member {
+		    DW_AT_name "me_float"
+		    DW_AT_type :$float
+		}
+
+		DW_TAG_subprogram {
+		    DW_AT_external 1 DW_FORM_flag
+		    DW_AT_name "t_func"
+		    DW_AT_type :$template_type_float2
+		    DW_AT_declaration 1 DW_FORM_flag
+		} {
+		      DW_TAG_formal_parameter {
+			  DW_AT_type :$template_pointer2
+			  DW_AT_artificial 1 DW_FORM_flag
+		      }
+
+		      DW_TAG_formal_parameter {
+			  DW_AT_type :$template_type_float2
+		      }
+		  }
+	    }
+
+	    template_pointer2: DW_TAG_pointer_type {
+		DW_AT_byte_size 8 DW_FORM_data8
+		DW_AT_type :$template_type_floatfloat
+	    }
+
+	    nestedvar_type: DW_TAG_structure_type {
+		DW_AT_name "A"
+		DW_AT_byte_size 2*$int_size+2*$float_size DW_FORM_sdata
+	    } {
+		template_type_float4: DW_TAG_template_type_param {
+		    DW_AT_type :$template_type_floatfloat
+		    DW_AT_name "T"
+		}
+		template_type_float5: DW_TAG_template_type_param {
+		    DW_AT_type :$template_type_floatfloat
+		    DW_AT_name "U"
+		}
+		DW_TAG_member {
+		    DW_AT_name "me_t"
+		    DW_AT_type :$template_type_float4
+		}
+		DW_TAG_member {
+		    DW_AT_name "me_int"
+		    DW_AT_type :$int
+		}
+		DW_TAG_member {
+		    DW_AT_name "me_u"
+		    DW_AT_type :$template_type_float5
+		}
+		DW_TAG_member {
+		    DW_AT_name "me_float"
+		    DW_AT_type :$float
+		}
+
+		DW_TAG_subprogram {
+		    DW_AT_external 1 DW_FORM_flag
+		    DW_AT_name "t_func"
+		    DW_AT_type :$template_type_float4
+		    DW_AT_declaration 1 DW_FORM_flag
+		} {
+		      DW_TAG_formal_parameter {
+			  DW_AT_type :$template_pointer3
+			  DW_AT_artificial 1 DW_FORM_flag
+		      }
+
+		      DW_TAG_formal_parameter {
+			  DW_AT_type :$template_type_float4
+		      }
+		  }
+	    }
+
+	    template_pointer3: DW_TAG_pointer_type {
+		DW_AT_byte_size 8 DW_FORM_data8
+		DW_AT_type :$nestedvar_type
+	    }
+
+	     DW_TAG_variable {
+		DW_AT_name "nestedvar"
+		DW_AT_type :$nestedvar_type
+		DW_AT_location {
+		    DW_OP_addr [gdb_target_symbol "nestedvar"]
+		} SPECIAL_expr
+		DW_AT_external 1 DW_FORM_flag
+	    }
 	}
     }
 }
@@ -133,12 +272,196 @@  if { ![runto_main] } {
     return
 }
 
+# Setup strings used in the tests below.
+set var "{me_t = 0, me_int = 0, me_u = 0, me_float = 0}"
+set tfloatfloat "A<float, float>"
+
 gdb_test "ptype var" [multi_line \
-    "type = struct A<int> \\\[with T = int\\\] {" \
-    "    T me_t;.*" \
+"type = struct A<int, float> \\\[with T = int, U = float\\\] {" \
+    "    T me_t;" \
+    "    int me_int;" \
+    "    U me_u;" \
+    "    float me_float;.*" \
     "    T t_func\\\(T\\\);.*" \
-    "}"]
+"}"]
 
 gdb_test "ptype var.me_t" "type = T"
-gdb_test "print var" "\\\{me_t = 0\\\}"
+gdb_test "ptype var.me_int" "type = int"
+gdb_test "ptype var.me_u" "type = U"
+gdb_test "ptype var.me_float" "type = float"
+
+gdb_test "print var" "$var"
+
+gdb_test "ptype nestedvar" [multi_line \
+    "type = struct A<$tfloatfloat, $tfloatfloat > \\\[with T = $tfloatfloat, U = $tfloatfloat\\\] {" \
+    "    T me_t;" \
+    "    int me_int;" \
+    "    U me_u;" \
+    "    float me_float;.*" \
+    "    T t_func\\\(T\\\);.*" \
+"}"]
+gdb_test "ptype nestedvar.me_t" "type = T"
+gdb_test "ptype nestedvar.me_int" "type = int"
+gdb_test "ptype nestedvar.me_u" "type = U"
+gdb_test "ptype nestedvar.me_float" "type = float"
+
+gdb_test "print nestedvar" "{me_t = $var, me_int = 0, me_u = $var, me_float = 0}"
+
+# Tests for unnamed_var1-unnamed_var3 are basically the same as in
+# missing-type-name-for-template, but with the DWARF format described above.
+
+set asm_file_unnamed_templates [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file_unnamed_templates {
+    global int_size float_size
+    cu {} {
+	DW_TAG_compile_unit {
+	    DW_AT_language @DW_LANG_C_plus_plus
+	} {
+	    declare_labels int float
+	    declare_labels template_var1 template_type_float1 template_type_int1
+	    declare_labels template_var2 template_type_float2 template_type_int2
+	    declare_labels template_var3 template_type_float3 template_type_int3
+
+	    int: DW_TAG_base_type {
+		DW_AT_name "int"
+		DW_AT_byte_size 4 DW_FORM_data1
+		DW_AT_encoding @DW_ATE_signed
+	    }
+
+	    float: DW_TAG_base_type {
+		DW_AT_name "float"
+		DW_AT_byte_size 4 DW_FORM_sdata
+		DW_AT_encoding @DW_ATE_float
+	    }
+
+	    DW_TAG_subprogram {
+		MACRO_AT_func "main"
+	    }
+
+	    # A variable whose type is a template instantiation with two
+	    # template parameters, one unnamed.
+	    template_var1: DW_TAG_structure_type {
+		DW_AT_name "template_var1<int, float>"
+		DW_AT_byte_size $int_size+$float_size DW_FORM_sdata
+	    } {
+		template_type_int1: DW_TAG_template_type_param {
+		   DW_AT_type :$int
+		}
+		DW_TAG_member {
+		    DW_AT_name "me"
+		    DW_AT_type :$template_type_int1
+		}
+		template_type_float1: DW_TAG_template_type_param {
+		    DW_AT_name "second"
+		    DW_AT_type :$float
+		}
+		DW_TAG_member {
+		    DW_AT_name "me2"
+		    DW_AT_type :$template_type_float1
+		}
+	    }
+	    DW_TAG_variable {
+		DW_AT_name "unnamed_var1"
+		DW_AT_type :$template_var1
+	    }
+
+	    # A variable whose type is a template instantiation with two
+	    # template parameters, both unnamed.
+	    template_var2: DW_TAG_class_type {
+		DW_AT_name "template_var2<int, float>"
+		DW_AT_byte_size $int_size+$float_size DW_FORM_sdata
+	    } {
+		template_type_int2: DW_TAG_template_type_param {
+		   DW_AT_type :$int
+		}
+		DW_TAG_member {
+		    DW_AT_name "me"
+		    DW_AT_type :$template_type_int2
+		}
+		template_type_float2: DW_TAG_template_type_param {
+		    DW_AT_type :$float
+		}
+		DW_TAG_member {
+		    DW_AT_name "me2"
+		    DW_AT_type :$template_type_float2
+		}
+	    }
+	    DW_TAG_variable {
+		DW_AT_name "unnamed_var2"
+		DW_AT_type :$template_var2
+	    }
+
+	    # A variable whose type is a template instantiation with four
+	    # template arguments, two types, two values, all unnamed.
+	    template_var3: DW_TAG_structure_type {
+		DW_AT_name "template_var3"
+		DW_AT_byte_size $int_size+$float_size DW_FORM_sdata
+	    } {
+		DW_TAG_member {
+		    DW_AT_name "me"
+		    DW_AT_type :$template_type_int3
+		}
+		DW_TAG_template_value_param {
+		   DW_AT_type :$int
+		   DW_AT_const_value 0 DW_FORM_sdata
+		}
+		template_type_int3: DW_TAG_template_type_param {
+		    DW_AT_type :$int
+		}
+		DW_TAG_template_value_param {
+		    DW_AT_type :$int
+		    DW_AT_const_value 11 DW_FORM_sdata
+		}
+		template_type_float3: DW_TAG_template_type_param {
+		    DW_AT_type :$float
+		}
+		DW_TAG_member {
+		    DW_AT_name "me2"
+		    DW_AT_type :$template_type_float3
+		}
+	    }
+	    DW_TAG_variable {
+		DW_AT_name "unnamed_var3"
+		DW_AT_type :$template_var3
+	    }
+	}
+    }
+}
+
+set srcfile3 [file join $srcdir $subdir "missing-type-name-for-templates.cc"]
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+     [list $srcfile3 $asm_file_unnamed_templates] {debug c++}] } {
+    return
+}
+
+if { ![runto_main] } {
+    return
+}
+
+gdb_test "ptype unnamed_var1" [multi_line \
+    "type = struct template_var1<int, float> \\\[with <unnamed0> = int, second = float\\\] {" \
+    "    <unnamed0> me;" \
+    "    second me2;" \
+    "}"]
+
+gdb_test "ptype unnamed_var1.me" "type = <unnamed0>"
+gdb_test "ptype unnamed_var1.me2" "type = second"
+
+gdb_test "ptype unnamed_var2" [multi_line \
+    "type = class template_var2<int, float> \\\[with <unnamed0> = int, <unnamed1> = float\\\] {" \
+    "    <unnamed0> me;" \
+    "    <unnamed1> me2;" \
+    "}"]
+
+gdb_test "ptype unnamed_var2.me" "type = <unnamed0>"
+gdb_test "ptype unnamed_var2.me2" "type = <unnamed1>"
+
+gdb_test "ptype unnamed_var3" [multi_line \
+    "type = struct template_var3<0, int, 11, float> \\\[with <unnamed0> = 0, <unnamed1> = int, <unnamed2> = 11, <unnamed3> = float\\\] {" \
+    "    <unnamed1> me;" \
+    "    <unnamed3> me2;" \
+    "}"]
 
+gdb_test "ptype unnamed_var3.me" "type = <unnamed1>"
+gdb_test "ptype unnamed_var3.me2" "type = <unnamed3>"
diff --git a/gdb/typeprint.c b/gdb/typeprint.c
index 4ba7a1aabfb..a30fe37edc8 100644
--- a/gdb/typeprint.c
+++ b/gdb/typeprint.c
@@ -224,12 +224,11 @@  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 ();
       type *arg_type = TYPE_TEMPLATE_ARGUMENT (t, i)->type ();
+
+      /* Skip self-referential template type parameters for the
+	 hashmap based substitution of template type parameters.  */
       if (arg_type == nullptr)
-	{
-	  /* Skip self-referential template type parameters for the
-	     hashmap based substitution of template type parameters.  */
-	  continue;
-	}
+	continue;
 
       tf->type = arg_type;
 
diff --git a/gdb/typeprint.h b/gdb/typeprint.h
index 21931e7a589..d0d6397070f 100644
--- a/gdb/typeprint.h
+++ b/gdb/typeprint.h
@@ -119,7 +119,14 @@  extern const struct type_print_options type_print_raw_options;
 
 /* A hash table holding decl_field objects.  This is more complicated than an
    ordinary hash because it must also track the lifetime of some -- but not all
-   -- of the contained objects.  */
+   -- of the contained objects.
+   Once common compilers such as gcc and clang support DWARF format with DIE
+   type references describing source entities referring to the formal template
+   parameter that reference the DW_TAG_template_type_parameter, the usage of
+   this table for template type parameters is obsolete and can be removed.
+   However, as long as such DIE type references reference the underlying type
+   DIE this table can still be used, but will result in the issue desribed in
+   https://sourceware.org/bugzilla/show_bug.cgi?id=20540.  */
 
 class typedef_hash_table
 {