Page MenuHomePhabricator

Integral template argument suffix and cast printing
Needs ReviewPublic

Authored by reikdas on Apr 6 2020, 3:16 PM.



Added suffixes and casts, where relevant, to integral types.

Diff Detail

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes
reikdas added inline comments.Aug 9 2020, 3:08 AM

@rsmith The tests pass, so that is reassuring at least. Is there any other way to find out whether we need to suppress printing the suffixes for other types in MSVCFormatting mode?

reikdas updated this revision to Diff 284186.Aug 9 2020, 4:08 AM

Can completely replace CanonParamType.

rsmith added a reviewer: rnk.Sep 17 2020, 2:10 PM
rsmith added inline comments.Sep 17 2020, 2:21 PM

@rnk Can you take a look at this? Per rG60103383f097b6580ecb4519eeb87defdb7c05c9 and PR21528 it seems like there might be specific requirements for how template arguments are formatted for CodeView support; we presumably need to make sure we still satisfy those requirements.


I'm uncomfortable with this approach. It's at least theoretically important that we canonicalize the types of template arguments so that template instantiations don't depend on how their arguments were spelled. Also, I don't think this addresses the whole problem: whether we need the type for a template argument depends not only on whether there was a deduced type in the parameter, but also whether the template was selected from an overload set. That's information that whoever is asking to print the template argument should know, but the template argument itself doesn't know in general.

So I'd like us to approach this a different way: change the calls into the template argument printing code to explicitly pass in a flag that says whether the type of the template argument is known from context. We should pass that in as false when:

  1. dumping template arguments
  2. the corresponding parameter contains a deduced type
  3. the template arguments are for a DeclRefExpr that hadMultipleCandidates()
reikdas updated this revision to Diff 293432.Sep 22 2020, 6:15 AM

Attempt an alternate approach, following @rsmith 's inline suggestions. Unfortunately, one of the tests added in this revision unexpectedly fail in this approach :(

reikdas added inline comments.Sep 22 2020, 6:16 AM

@rsmith This test fails with this new diff :( Could you please suggest a possible way to fix this?

rnk added inline comments.Sep 29 2020, 11:13 AM

Probably. This change looks like it preserves behavior from before when MSVCFormatting is set, so I think this is OK. I checked, my version of MSVC still uses 1/0 in debug info for boolean template args.

rsmith added a comment.Nov 5 2020, 2:14 PM

Having looked a bit more at the callers here, I think the pattern we're going to want is:

  • everywhere that prints a template argument list also asks for a list of corresponding template parameters
  • if the list of template parameters is unknown (perhaps because the template name is dependent), or ambiguous from the printed context (because the template was an overloaded function template), then always include types for template arguments
  • otherwise, include types for only template arguments where the parameter's type involved a deduced type

On that basis, I think it would be preferable to change TemplateArgument::print to accept not a bool indicating whether the type was known, but instead a const NamedDecl * for the corresponding template parameter. Then the logic is: include the type if we don't know the corresponding parameter or if its type is deduced. And in the caller, we want to always pass in the corresponding parameter if we know it, except when printing the arguments for an overloaded call to a function template (for which the types of the template arguments could affect which overload we selected). [The comments below assume this refactoring is not done.]


We only know the type if the call wasn't overloaded.


Please capitalize this name as KnownType to be consistent with surrounding code.


Perhaps we should always request that the type is included when printing a DynTypedNode -- someone printing a DynTypedNode can't be assumed to always know the type of the node in general. But this really depends on the intent of the caller -- if the purpose is to provide text that can be inserted by a refactoring tool, whether we include type information or not will depend on the context where the value is used, not only on the value itself.

I'd prefer that we either always pass in false (given that we don't know whether the type is known in the caller's context) or always pass in true (to preserve the old behavior).


What we should do for both this function and the one below is to pass in a flag to indicate whether the template itself (not the template argument) was overloaded, and also pass in the corresponding template parameter list. Then we know the type only if the template was not overloaded and the parameter's type does not contain a deduced type.


We should say we know the type here unless the corresponding template parameter has a deduced type.


I think this should be false: generally we want __PRETTY_FUNCTION__ and friends to uniquely identify the function template specialization.


I'd find it much more intuitive to reverse the sense of this flag so that it's an IncludeType flag or similar.


My concern is that we're changing the behavior for the other cases below in MSVCFormatting mode, not that we're changing the behavior for bool. If we have specific formatting requirements in MSVCFormatting mode, they presumably apply to all types, not only to bool, so we should be careful to not change the output in MSVCFormatting mode for any type.


We'll need to include a cast or similar here for signed char and unsigned char.


I don't think we can reliably determine whether the type was originally a deduced type from here; a canonical TemplateArgument doesn't preserve that information. Instead, we should determine whether the type of the corresponding parameter contained a deduced type, in the callers of TemplateArgument::print.


Please add a FIXME to handle knownType here.


Please add a FIXME to handle knownType here.


We should pass a corresponding template parameter declaration in here, and pass true for KnownType only if we know the corresponding parameter and it doesn't contain a deduced type.

reikdas updated this revision to Diff 306059.EditedNov 18 2020, 5:06 AM

Addresses @rsmith 's comments.

On that basis, I think it would be preferable to change TemplateArgument::print to accept not a bool indicating whether the type was known, but instead a const NamedDecl * for the corresponding template parameter. Then the logic is: include the type if we don't know the corresponding parameter or if its type is deduced.

I decided to continue passing a bool and move the logic for checking if the corresponding parameter's type is deduced to the caller. This is to handle the case where we need to let the template argument printing routine know not to print the cast when we get a parameter list whose size is lesser than the length of the list of arguments.

Unfortunately, there are still some cases as seen in the tests where there are explicit casts when there probably shouldn't be.

reikdas marked 12 inline comments as done.Nov 18 2020, 5:16 AM
reikdas updated this revision to Diff 306097.Nov 18 2020, 6:53 AM
reikdas marked 2 inline comments as done.
rsmith added inline comments.Nov 18 2020, 11:21 AM

This case can't happen. The only way that a template parameter can be a TypeDecl is if it's a TemplateTypeParmDecl, which represents TemplateTypeParmType, never a DeducedType.


Check directly for a NonTypeTemplateParamDecl here.


Don't use getTypePtr() here, just use QualType's operator->. Eg: IncludeType = ParamValueDecl->getType()->getContainedDeducedType();.


You don't need to check for both of these; AutoType derives from DeducedType. Also, you're only looking for a top-level deduced type here; use getContainedDeducedType() instead to properly handle auto* and the like.


No braces here, please.


Class templates are never overloaded.


We don't know in this case, so I think if we're not going to check, then we shouldn't pass the parameter list. This change as-is can lead to us printing out ambiguous names for function templates, both due to argument types and due to omitting arguments equivalent to default arguments.


This is a class template, not a function template, so can't be overloaded. (Also I think this comment is backwards.)


Type templates can't be overloaded. We need the types if and only if we didn't resolve the template name to a specific template decl, which matches what this patch does, so you can just remove the FIXMEs.


I think Record will always be null here, so this dyn_cast will crash. For a dependent template specialization type, I don't think there's any situation in which we can know what the template parameter list is, so we should always include the types for template arguments (that is, pass nullptr as the template parameter list).


I don't think there can ever be such a list: because the scope is dependent, we don't know what template is being named.


Similarly here, because the lookup is unresolved, the template parameter list is unknown.


The cases you need to check for here are VarTemplateSpecializationDecl and FunctionDecl.


We can never have such a list here due to the dependent scope.




E is a ConceptSpecializationExpr. It can't be a DeclRefExpr. What you want here is TPL = E->getNamedConcept()->getTemplateParameters().


We should not include a cast for plain char, only for signed char and unsigned char.


Passing in IncludeType seems appropriate here; I think you can remove the FIXME. Though make sure you have a test for things like:

template<auto ...N> struct X {};
X<1, 1u> x; // should be printed with type suffixes

Debug info shouldn't depend on such things. I think we should never pass the template parameter list here, so we always get complete debug information.


Likewise here, I think we should always produce complete type names in debug info even though that will be more verbose than necessary to uniquely identify the type.


Likewise here


I think this is around the 6th time this code fragment has appeared in this patch. Would it be possible to factor this out? Perhaps into something like

static bool TemplateParameterList::shouldIncludeTypeForArgument(TemplateParameterList *TPL, unsigned Idx)`

This can't be overloaded; you can remove this FIXME.


Hm, are we missing commas between these arguments?

Please consider whether this and some of the other repeated instances of this code can be replaced by calls to printTemplateArgumentList.


I don't think we should ever pass it in this case; we want to print out at least the values of prior arguments that were instantiated from default arguments.


This one is debatable, but I think we should also never pass the argument list here, and always print out the full description of the function template specialization.


In this case, including substituted default argument values seems important.


It would be useful to generate u8'...', u16'...', and u32'...' literals at least whenever we think they'd contain printable characters.

reikdas updated this revision to Diff 307587.Nov 25 2020, 5:06 AM

Address @rsmith 's comments.

reikdas marked 25 inline comments as done.Nov 25 2020, 5:14 AM
reikdas added inline comments.

@rsmith This test fails and we are unable to print the suffix here because the length of the TemplateParameterList is less than the length of the corresponding list of Template Arguments. Do you have any suggestions on how we can fix this?

reikdas marked 2 inline comments as done.Nov 25 2020, 5:17 AM
rsmith added inline comments.Dec 9 2020, 1:36 PM

We should return true in this case; we don't know what the corresponding parameter is so we don't know if the type matters.


May as well go straight to NonTypeTemplateParamDecl here; it can't be any other kind of ValueDecl.


I'm not entirely sure what this is used for, but it looks like the goal is uniqueness, not human-readability, given that the template arguments are separated by newlines not by commas or similar. That being the case, I think we should unconditionally pass true as IncludeType here.


Nit: exactly one space between } and else. (You have two spaces in the first instance here and a newline in the second instance.)


Pass nullptr here; for ObjC @encode we presumably want fully-explicit output always.


We should conservatively assume that it is, rather than potentially printing out an ambiguous AST fragment.


This is a class template specialization, not a function template specialization. It can't be overloaded.


Concepts can't be overloaded; remove this FIXME.


Remove this FIXME: class template specializations can't be overloaded.


This isn't right: getDescribedTemplateParams handles the case where the declaration is the pattern in a template definition, not where it's a template instantiation. What you want here is:

if (auto *FTD = FD->getPrimaryTemplate())
  TPL = FTD->getTemplateParameters();

You should also take into account Node->hadMultipleCandidates() here.

Testcase for this would be something like:

struct A {
  template<long, auto> void f();
void g(A a) { a.f<0L, 0L>(); }

... for which -ast-print should produce f<0, 0L>.


This is likewise not right. In this case we always know there's a template, so it's slightly simpler:

TPL = TD->getSpecializedTemplate()->getTemplateParameters();

Please also rename TD to something more appropriate.

Testcase for this would be something like:

struct A {
  template<long> static int x;
  template<auto> static int y;
int k = A().x<0L> + A().y<0L>;

... for which -ast-print should produce x<0> but y<0L>.


I don't think this is very clear about the purpose of the flag. How about:

\param IncludeType If set, ensure that the type of the expression printed matches the type of the template argument.

@rnk Ping.


This is marked 'done' but not done. Note that plain char is always either a signed or unsigned type, so your check here does not work. You can check T->isSpecificBuiltinType(BuiltinType::SChar) and T->isSpecificBuiltinType(BuiltinType::UChar) instead.


This isn't correct: u8'x' will print as u8'120'. Perhaps you can factor code to do this properly out of StmtPrinter::VisitCharacterLiteral.

Also, the prefix for char16_t literals is u, not u16, and the prefix for char32_t literals is U, not u32.


This is marked as done but not done. (The template parameter object case has a FIXME but the other cases do not and should.)


Please capitalize this, following the naming convention of the surrounding code.


This is wrong; we'll incorrectly assume that element i of the pack corresponds to element i of the original template parameter list. We should use the same template parameter for all elements of the pack. (For example, you could pass in I and instruct the inner invocation of printTo to not increment I as it goes.)


It doesn't make sense to pass a parameter list in here, because we've not selected a template yet.


In this case, the parameter list is TraitTD->getTemplateParameters().


Ping, can this be replaced by a call to printTemplateArgumentList?


Sorry, I wasn't clear: I think we should not include the template parameters here, so that we do print out substituted template arguments even if they're equivalent to the default argument.


Once we hit a template parameter pack, we should use the same parameter for all subsequent arguments.

reikdas updated this revision to Diff 312980.Sun, Dec 20, 7:54 AM

Address @rsmith comments.

reikdas marked 22 inline comments as done.Sun, Dec 20, 8:04 AM
reikdas added inline comments.

This isn't correct: u8'x' will print as u8'120'. Perhaps you can factor code to do this properly out of StmtPrinter::VisitCharacterLiteral.

I partially addressed this comment. I wasn't able to find a suitable example to test u8'x' being printed as u8'120'. @rsmith could you please help me by showing me a reproducer?

rsmith added inline comments.Tue, Jan 5, 12:59 PM

You'll need a C++20 test (or a test using -fchar8_t). Then:

template<auto> struct A {};
A<u8'x'>::B b; // expected-error {{no type named 'B' in 'A<u8'x'>'}}
reikdas updated this revision to Diff 314858.Wed, Jan 6, 4:48 AM
reikdas marked an inline comment as done.

Address @rsmith review

reikdas updated this revision to Diff 314859.Wed, Jan 6, 4:54 AM
reikdas marked 2 inline comments as done.