Source location for the template keyword.

Hello.

It seems that the AST representation is missing information regarding the presence and the source location of the 'template' keyword in a few expression nodes.

For instance, consider the following example:

template <typename T>
struct S {
   template <class U> static int foo(U*);

   static const unsigned s = sizeof(S<T>::template foo<T>(0));
};

The argument of sizeof is an UnresolvedLookupExpr.
This gets pretty printed as follows:

template <typename T> struct S {
     template <class U> static int foo(U *);
     static const unsigned int s = sizeof (S<T>::foo<T>(0));
};

We were considering the addition of a SourceLocation for the template keyword to UnresolvedLookupExpr and similar AST nodes. Such a source location will be invalid if the keyword is not written in the sources. Afawct, there are examples when such a template keyword is needed (as the one above) and other examples where it is just optional; a possibly invalid source location should be enough to distinguish among all possibilities.

Afawct, the template keyword can appear (it is meant, after the nested name specifier and before the declaration name) in the following expressions:

   DeclRefExpr
   MemberExpr
   DependentScopeDeclRefExpr
   CXXDependentScopeMemberExpr
   UnresolvedLookupExpr
   UnresolvedMemberExpr

What about CXXUnresolvedConstructExpr?
Are there other cases?

For the DeclRefExpr and MemberExpr nodes, we could add it to the ExplicitTemplateArgs structure (even though, strictly speaking, the template keyword is not part of the template args ... this way it will go after the end of the node, taking no space if there are no explicit template arguments).
For the Unresolved* nodes, we guess that it should be stored in the common base class OverloadedExpr.

Does this proposal sound OK?

Regards,
Enea.

template <typename T>
struct S {
  template <class U> static int foo(U*);

  static const unsigned s = sizeof(S<T>::template foo<T>(0));
};

The argument of sizeof is an UnresolvedLookupExpr.

Assuming you mean the operand of the CallExpr inside the sizeof, yes.
Note that the 'template' is actually unnecessary here because the NNS
refers to the current instantiation.

We were considering the addition of a SourceLocation for the template
keyword to UnresolvedLookupExpr and similar AST nodes. Such a source
location will be invalid if the keyword is not written in the sources.
Afawct, there are examples when such a template keyword is needed (as
the one above) and other examples where it is just optional; a possibly
invalid source location should be enough to distinguish among all
possibilities.

Afawct, the template keyword can appear (it is meant, after the nested
name specifier and before the declaration name) in the following
expressions:

  DeclRefExpr
  MemberExpr
  DependentScopeDeclRefExpr
  CXXDependentScopeMemberExpr
  UnresolvedLookupExpr
  UnresolvedMemberExpr

What about CXXUnresolvedConstructExpr?

Yes, but only in the NNS case; see below.

Are there other cases?

It can also happen within an arbitrary nested name specifier:
  A<T>::template B<T>::foo

If we were able to say "this NNS ends with a 'template' keyword", that would
be sufficient for all these cases except the member expressions
(where it's possible to have 'a.template foo<>()').

John.

John McCall wrote:

template <typename T>
struct S {
  template <class U> static int foo(U*);

  static const unsigned s = sizeof(S<T>::template foo<T>(0));
};

The argument of sizeof is an UnresolvedLookupExpr.

Assuming you mean the operand of the CallExpr inside the sizeof, yes.
Note that the 'template' is actually unnecessary here because the NNS
refers to the current instantiation.

Right. I was (lazily) happy with the gcc error message, but clang gives no error.

We were considering the addition of a SourceLocation for the template keyword to UnresolvedLookupExpr and similar AST nodes. Such a source location will be invalid if the keyword is not written in the sources. Afawct, there are examples when such a template keyword is needed (as the one above) and other examples where it is just optional; a possibly invalid source location should be enough to distinguish among all possibilities.

Afawct, the template keyword can appear (it is meant, after the nested name specifier and before the declaration name) in the following expressions:

  DeclRefExpr
  MemberExpr
  DependentScopeDeclRefExpr
  CXXDependentScopeMemberExpr
  UnresolvedLookupExpr
  UnresolvedMemberExpr

What about CXXUnresolvedConstructExpr?

Yes, but only in the NNS case; see below.

Are there other cases?

It can also happen within an arbitrary nested name specifier:
  A<T>::template B<T>::foo

Yes, we were excluding these from our analysis as the NNS already says if the keyword is present and, as far as its source location is concerned, the impression from previous messages on the list is that sooner or later a "source info" version of the NNS concept (with canonical types replaced by TypeSourceInfo and namespace alias names as provided in the source code) will appear.

If we were able to say "this NNS ends with a 'template' keyword", that would
be sufficient for all these cases except the member expressions
(where it's possible to have 'a.template foo<>()').

John.

We did not considered this option. It has some pro indeed.
It still leave us with three different kinds of member expr nodes (plain, dependent scope and unresolved) to be dealt with.

On the other hand, the approach we were suggesting (putting the source location of the template keyword inside ExplicitTemplateArgs) is not really going to work, as the template keyword could occur even when there is no explicit template argument list.

So here is another proposal: put the source location for the template keyword inside the DeclarationNameLoc structure that is part of DeclarationNameInfo. Wouldn't that be a cleaner approach?

Enea.

We were considering the addition of a SourceLocation for the template keyword to UnresolvedLookupExpr and similar AST nodes. Such a source location will be invalid if the keyword is not written in the sources. Afawct, there are examples when such a template keyword is needed (as the one above) and other examples where it is just optional; a possibly invalid source location should be enough to distinguish among all possibilities.

Afawct, the template keyword can appear (it is meant, after the nested name specifier and before the declaration name) in the following expressions:

DeclRefExpr
MemberExpr
DependentScopeDeclRefExpr
CXXDependentScopeMemberExpr
UnresolvedLookupExpr
UnresolvedMemberExpr

What about CXXUnresolvedConstructExpr?

Yes, but only in the NNS case; see below.

Are there other cases?

It can also happen within an arbitrary nested name specifier:
A<T>::template B<T>::foo

Yes, we were excluding these from our analysis as the NNS already says if the keyword is present and, as far as its source location is concerned, the impression from previous messages on the list is that sooner or later a "source info" version of the NNS concept (with canonical types replaced by TypeSourceInfo and namespace alias names as provided in the source code) will appear.

Right, I'm just saying that this is a possible reason to generally build this into the NNS, if you can bear to wait until NNSLoc appears.

If we were able to say "this NNS ends with a 'template' keyword", that would
be sufficient for all these cases except the member expressions
(where it's possible to have 'a.template foo<>()').
John.

We did not considered this option. It has some pro indeed.
It still leave us with three different kinds of member expr nodes (plain, dependent scope and unresolved) to be dealt with.

Yeah, which I agree is not appealing.

On the other hand, the approach we were suggesting (putting the source location of the template keyword inside ExplicitTemplateArgs) is not really going to work, as the template keyword could occur even when there is no explicit template argument list.

Heh, yes, I guess this is true.

So here is another proposal: put the source location for the template keyword inside the DeclarationNameLoc structure that is part of DeclarationNameInfo. Wouldn't that be a cleaner approach?

That's potentially expensive in terms of space because the common cases in DeclarationName can all be template-ids.

John.