[RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)

Matthias Kretz m.kretz@gsi.de
Wed Nov 17 09:04:12 GMT 2021


On Wednesday, 17 November 2021 07:09:18 CET Jason Merrill wrote:
> > -  if (CHECKING_P)
> > -    SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, TREE_VEC_LENGTH (a));
> > +  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
> 
> should have been
> 
> if (CHECKING_P || nondefault != TREE_VEC_LENGTH (a))
>    SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);

TBH, I don't understand the purpose of CHECKING_P here, or rather it makes me 
nervous because AFAIU I'm only testing with CHECKING_P enabled. Why make 
behavior dependent on CHECKING_P? I expected CHECKING_P to basically only add 
more assertions.

> > (copy_template_args): Jason?
> 
> Only copy the non-default template args count on TREE_VECs that should
> have it.

Why not simply set the count on all args? Is it a performance concern? The 
INTEGER_CST the TREE_CHAIN has to point to exists anyway, so it's not wasting 
any memory, right?

> 
> > +  /* Pretty print only template instantiations. Don't pretty print
> > explicit
> > +     specializations like 'template <> void fun<int> (int)'. 
> 
> This seems like a significant change of behavior unrelated to printing
> default template arguments.  What's the rationale for handling
> specializations differently from instantiations?

Right, this is about "The general idea of this change is to print template 
parms wherever they would appear in the source code as well".

Initially, the change to print function template arguments/parameters only if 
the args were explicitly specified lead to printing 'void fun (T) [with T = 
...]' or 'template <> void fun (int)'. Both are not telling the full story, 
even if the former is how the function would be called. But if the reader 
should quickly recognize what code is getting called, it is helpful to see 
right away that a function template specialization is called. (It might also 
reveal an implementation detail of a library, so it's not 100% obvious how to 
choose here.) Also, saying 'T = int' is kind of wrong. Yes, 'int' was deduced. 
But there's no T in fun<int>:

template <class T> void fun (T);
template <> void fun<int> (int);

__FUNCTION__ was 'fun<int>' all the time, but __PRETTY_FUNCTION__ was 'void 
fun(T) [with T = int]'. It's more consistent that __PRETTY_FUNCTION__ contains 
__FUNCTION__, IMHO, so it would have to be at least 'void fun<int>(T) [with T 
= int]'. But that's strange: How it uses T and int for the same type. So I 
settled on 'void fun<int>(int)'.

> I also don't understand the purpose of TFF_AS_PRIMARY.

dump_function_decl generalizes the TEMPLATE_DECL (if flag_pretty_templates is 
true) and, before this change, passes the generalized TEMPLATE_DECL to 
dump_type (... DECL_CONTEXT (t) ...) and dump_function_name (... t ...). 
That's why the whole template is printed as primary template (i.e. with 
template parms instead of template args, as is needed for 
flag_pretty_templates). But this drops the count of non-default template args. 
To retain the count, dump_type and dump_function_name need to be called with 
the original TEMPLATE_DECL. But if I do this, pretty-templates is broken.
'template <class T> struct A { template <class U> void f(T, U); };' would 
print as 'A<int>::f<float>(T, U) [with U = float, T = int]'. To get back to 
'A<T>::f<U>(T, U) [with U = float, T = int]' I needed to tell 
dump_template_parms that even though the template args are there, it should 
print only the template parms. The most obvious way to do that was to carry it 
through via flags.

Note that this creates another problem. Given

template <class T0, class T1 = int> struct Outer {
  template <class T, class U> struct A;
  template <class X> struct A<X, int> {
    void f();
  };
};

we want to print e.g. 'void Outer<T0>::A<X, int>::f() [with X = int, T0 = 
int]', but certainly not 'void Outer<T0>::A<T, U>::f() [with X = int, T0 = 
int]'. However, specialized_t holds A<int, int> which is printed as A<T, U> 
with TFF_AS_PRIMARY. Only most_general_template of the function's 
TEMPLATE_DECL can give us A<X, int> as DECL_CONTEXT.

I have a solution in the diagnose_as patch, where I had to solve a similar 
problem because for the diagnose_as attribute (dump_template_scope).

> > +/* Print function template parameters if:
> > +   1. t is template, and
> > +   2. flags did not request "show only template-name", and
> > +   3. t is a specialization of a template (Why is this needed? This was
> > present +      since 1999 via !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION:
> > "Don't crash if +      given a friend pseudo-instantiation". The
> > DECL_USE_TEMPLATE should likely +      inform the PRIMARY parameter of
> > dump_template_parms.), and
> 
> Good question.  It seems that the existing
> !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION has mostly been excluding the
> most general template; removing that line changes things like
> 
> int bar(T) [with T = int]
> 
> to
> 
> int bar<T>(T) [with T = int]
> 
> 
> 
> > +   4. either
> > +      - flags requests to show no function arguments, in which case
> > deduced +        types could be hidden, or
> > +      - at least one function template argument was given explicitly, or
> > +      - we're printing a DWARF name,
> 
> ...but perhaps this can replace the above.

I'll rerun the tests with DECL_USE_TEMPLATE moved to the PRIMARY parameter of 
dump_template_parms.

> Though, why do we want
> different behavior here when printing a DWARF name?

Sorry, I should have asked ... I also added the same issue as an open question 
on the diagnose_as patch. When I ran the whole testsuite I had failures in the 
DWARF tests. This change resolved them. I don't know enough about how those 
strings are used and whether they may change between GCC versions. Anyway, the 
DWARF strings in that test had only the function name and template argument 
list (i.e. no function arguments). If the template argument list was removed 
(as was the case without the condition here), the DWARF strings lost important 
information, giving several different functions the same name. That seemed 
like a regression. So either the DWARF strings need to include the function 
arguments, or we need this condition to keep showing the template arguments 
independent of whether they were explicitly given or not.

> > +   5. either
> > +      - t is a member friend template of a template class (see
> > DECL_TI_TEMPLATE +        documentation), or
> > +      -
> 
> Missing the last item.  :)

Oh, I got distracted while trying to figure this out. :) I'll try to 
understand why PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t)) is needed and 
document it.

-- 
──────────────────────────────────────────────────────────────────────────
 Dr. Matthias Kretz                           https://mattkretz.github.io
 GSI Helmholtz Centre for Heavy Ion Research               https://gsi.de
 stdₓ::simd
──────────────────────────────────────────────────────────────────────────





More information about the Gcc-patches mailing list