On 6/4/26 6:00 PM, Cary Coutant via Dwarf-discuss wrote:
template<typename T> struct t1 {
T n1;
};
template<typename T> struct t2 {
T m1;
t1<T> m2;
};
int main()
{
t2<int> v1;
return 0;
}
Based on the current specification, the type of m1 should reference
a template parameter entry. This is a bug in gcc which has already
been described [1]. llvm/clang attempted to fix this in PR [2].
Also for m2 we can see that there is no information that its type was
originally described by a template type parameter of t2. However,
there is no specification addressing this specific case in the DWARF
Standard.
Like Ben, I'm not sure I see why this isn't possible given the current
spec. The type attribute for v1 should point to a type DIE for
t2<int>, which will have a template type parameter and two data
members, m1 and m2. Briefly:
L1: DW_TAG_structure_type (name: t2<int>)
L2: DW_TAG_template_type_parameter (name: T, type: ref to type int)
L3: DW_TAG_member (name: m1, type: ref to L2) [NOTE 1]
L4: DW_TAG_member (name: m2, type: ref to type t1<T>) [NOTE 2]
NOTE 1: This is the bug you point out where gcc and clang don't do
what the spec says, and put a ref to type int instead of to the
template type parameter entry.
NOTE 2: This is the part that may not be so obvious. We want a ref to
a DIE that represents the type t1<T> rather than t1<int>. I think we
can have that, but that new type entry will need to be a child of the
t2<int> type. Otherwise, it won't be able to reference the template
type parameter entry (technically, it *could*, but pointing from one
type DIE into the interior of another is ugly, and could conceivably
cause problems down the road).
L1: DW_TAG_structure_type (name: t2<int>)
L2: DW_TAG_template_type_parameter (name: T, type: ref to type int)
L3: DW_TAG_member (name: m1, type: ref to L2)
L4: DW_TAG_member (name: m2, type: ref to L5)
L5: DW_TAG_structure_type (name: t1<T>)
L6: DW_TAG_template_type_parameter (name: T, type: ref to type int)
L7: DW_TAG_member (name: n1, type: ref to L6)
This is a more compact version of what I came up with. I kept on getting
things jumbled up until I changed one of the names of the template
parameters so that I could keep them straight.
There were two twists that I played with as well. These are probably
obvious to other people:
Say there was also a variable with a t1<int> instance outside of the t2
template. I puzzled for a few minutes about that and thought is that the
same as the t1<T> inside of t2's instantiation. The other one was I made
t3 which also had a t1<T> inside of it.
It took me a few minutes but I eventually realized that they were
different types even though they resolved to the same thing t1<int>.
However, m2 was like t2::t1<int> while my other m2 was really t3::t1<int>
From an ABI (libabigail) perspective they were different but
potentially compatible types.
So if there was a function that took t1<int> and somehow was changed to
t2::t1<int> then I at least should warn about it. However, since the bit
representation was the same it should still work.
I didn't check to see if any current compilers do this but I could
imagine where a compiler could choose to use the same text region for both:
void foo( t1<int> v);
void foo( t2::t1<int> v);
and I considered how that may affect consumers.
Besides missing consumer support (e.g. GDB or LLDB), missing
support for
nested templates was one further reason why the llvm PR [2] was
rejected
by David Blaikie. I discussed this topic with him since I am
working on
the enablement in GDB and submitted a patch series [3] to support
template
type resolution based on the current DWARF specification. David
Blaikie
asked me to submit a DWARF issue for this, since we could not come
up with
a useful specification for the nested case (type of m2 in the example
above).
In the PR, David noted:
That's basically my point, sorry, that v1 must be represented as
"trait::type" (because it has to be canonical) and so if many
uses, like this one, of a type in a template can't reference the
DW_TAG_template_type_parameter, because it's non-canonical - I'm
not sure there's a lot of value in making it work for the subset
of cases where it is workable.
I guess I don't understand the part about "it has to be canonical".
David, can you explain why the approach I used above wouldn't work for
the PR's example with trait::type?
If we need something canonical (and if I understand what that means),
could we also have a canonical instantiation of the type for t1<int>
at the outermost scope, and add an attribute to the entry for t1<T> at
L5 that points to that canonical instantiation — maybe call it
DW_AT_canonical_type?
There's also the concern that the compilers currently don't generate
what the DWARF spec says they should, and if they did, the debuggers
wouldn't be able to handle it.
I'd just say, we file bugs and if they have a compelling reason we
assimilate that into our understanding and THEN if necessary make
changes to DWARF based upon that information.
-ben
And if David's point above is right that we can represent the simple
case, but not the more complex ones, is it worth doing it even for the
simple cases? If the spec is calling for something that can't be done
in the general case, should we remove it from the spec?
-cary
--
Dwarf-discuss mailing list
[email protected]
https://lists.dwarfstd.org/mailman/listinfo/dwarf-discuss