Canonicalizing debug info for alias templates

(seems I have only myself to blame for this situation - I implemented debug info for alias templates in f1b382e87df86b5fee84c9aeb2d7b6fad6862514)

Came across a bug in Clang’s debug info for alias templates - where non-type template parameters produce distinct entities for the same alias instantiation.
eg:

template<bool>
using x = int;
x<true> v1;
x<1 == 1> v2;
!16 = !DIDerivedType(tag: DW_TAG_typedef, name: "type<true>", file: !1, line: 2, baseType: !13)
!19 = !DIDerivedType(tag: DW_TAG_typedef, name: "type<1 == 1>", file: !1, line: 2, baseType: !13)

It looks like this is because maybe there is no canonical version of an alias template specialization? (the canonical thing is the underlying aliased type)
PrintingPolicy : “PrintCanonicalTypes” is mostly addressing this issue, indirectly - causing the two distinct TemplateSpecializationType to get named the same (if they only have type template parameters) - but if there’s a non-type template parameter there’s no canonicalization, and so every different spelling of a NTTP gets a distinct typedef in debug info which adds more string data.

So - is there some nice way we could print out a canonical name for an alias template instantiation?

@AaronBallman ?

We don’t unique non-canonical TemplateSpecializationTypes, that is one thing.

But even if we did, in you example those are two different expressions, that not only have different as-written forms, but also have different source locations. Expressions have source location embedded in them, instead of storing it out-of-line like we do with Types and TypeLocs, so that would present a problem to unique them, unless we were willing to lose information.

Having a canonical type which is an alias TemplateSpecializationType also doesn’t work. Alias TemplateSpecializationTypes are always sugar.

So I don’t think what you want can reasonably be achieved by changes to the type system alone.

I think that for what you want here to work, one possible solution would be for Clang to keep track of declarations for alias templates specializations, much like we do for class templates and such.

And if it did, then Alias Template Specialization types could reference the new TypeAliasTemplateSpecializationDecl, instead of the TypeAliasTemplateDecl as it does currently.

And for the new TypeAliasTemplateSpecializationDecl, we would keep track by the canonical converted arguments, which don’t involve expressions, just like it works for class template specializations.

Right - sorry, my broad goal is: I’d like to be able to produce a canonical string name for all alias template specializations. It’d sort of be nice (from a “it’s quicker to compare/lookup by pointer than to generate a string and then do string lookup (well, actually generate a whole DIDerivedType node and then do hashing node equivalence)” it’d be sort of nice if there were some canonical AST - but I get that that might not be suitable for all the reasons you mentioned)

So I guess a different/more specific question: Could we support PrintingPolicy::PrintCanonicalTypes here somehow? such that alias<true> and alias<1 == 1> print the same?

Yes, I believe I understood what you intended from the start.

I maintain that what you need is to keep track of Alias Template Specializations, whether you:

  • Do the full job of creating a TypeAliasTemplateSpecializationDecl, which is to a TypeAliasTemplateDecl much like what a ClassTemplateSpecializationDecl is to a ClassTemplateDecl
  • Do some “hack” which is more specific to your needs.

To get you an idea of where to start, this is the most relevant part of the code in Clang for what you want: llvm-project/SemaTemplate.cpp at 34a809591bf97bd8832dbbabd7f0b7f7a3355783 · llvm/llvm-project · GitHub

This is the point where we are going to instantiate an alias template.

Of note here, you are going to have your as-written template arguments which you are already familiar with, and which you can’t do much of anything about because you won’t be able to unique the expressions that appear in them.

But in addition to that, you are going to have the Converted argument list, which are the arguments converted, for example from Expression into Integral, Declaration and other more palatable things.

This Converted list is also already canonicalized, saving you some trouble on the way.

So the very quick hack would be just building a hash table which maps an AliasTemplateDecl + as-witten template arguments, into the canonical converted arguments.

And those canonical converted arguments will be unique in the way you want, they all should print as true for both your examples.

Thanks for all the details - though I’m surprised that this would be the necessary path.

your as-written template arguments which you are already familiar with, and which you can’t do much of anything about because you won’t be able to unique the expressions that appear in them.

That’s perhaps a bit I don’t understand - presumably to canonicalize these, some code somewhere would do the work to convert the expression into a constant. Could I not reuse that code in the pretty printer when it’s asked for a canonical name to evaluate the NTTP expression down to a constant I could print canonically?

CheckTemplateArgumentList does that, although its a bit rough to use it like that, because it’s going to:

  • Perform some semantic analysis. including possibly emitting diagnostics.
  • Perform instantiation of NTTP arguments of dependent type.
  • Perform some constexpr evaluation.
  • Substitute default arguments.
  • Check constraints of concepts.

To name a few.

So it’s a bit heavy handed to use, you might need to setup evaluation contexts, put a SFINAE Trap, and other things which might give you a headache.

One other solution, instead of a hash table, is to just store this converted list in the Alias TemplateSpecializationType.

But it would be a good idea to unique this list somehow so we are not wasting a lot of memory with a bunch of copies of them.