[PATCH] D53847: [C++2a] P0634r3: Down with typename!

Richard Smith - zygoloid via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Wed May 22 14:52:28 PDT 2019


rsmith marked 2 inline comments as done.
rsmith added inline comments.


================
Comment at: clang/lib/Parse/ParseDecl.cpp:4321
+             isCXXDeclarationSpecifier(ITC_Never, TPResult::True) !=
+                 TPResult::True) ||
+            (!getLangOpts().CPlusPlus && !isDeclarationSpecifier(ITC_Yes))) {
----------------
Rakete1111 wrote:
> rsmith wrote:
> > It seems like a wording oversight that we don't assume `typename` in an //enum-base//. Probably would be good to raise this on the core reflector.
> Do you know why this isn't allowed in `operator` ids?
There's already been discussion of that on the core reflector, and people seem to agree it's an oversight in the wording. If you want to accept that here, I think that's fine, under the assumption that this will be fixed by DR. (If you want to follow the wording-as-moved, that's fine too.)


================
Comment at: clang/lib/Parse/ParseDecl.cpp:5100-5101
   bool IsConstructor = false;
-  if (isDeclarationSpecifier())
+  if (isDeclarationSpecifier(ITC_Never))
     IsConstructor = true;
   else if (Tok.is(tok::identifier) ||
----------------
Rakete1111 wrote:
> rsmith wrote:
> > Oh, this could be a problem.
> > 
> > If this *is* a constructor declaration, then this is implicit typename context: this is either a "//parameter-declaration// in a //member-declaration//" ([temp.res]/5.2.3) or a "//parameter-declaration// in a //declarator// of a function or function template declaration whose //declarator-id// is qualified". But if it's *not* a constructor declaration, then this is either the //declarator-id// of a declaration or the //nested-name-specifier// of a pointer-to-member declarator:
> > 
> > ```
> > template<typename T>
> > struct C {
> >   C(T::type); // implicit typename context
> >   friend C (T::fn)(); // not implicit typename context, declarator-id of friend declaration
> >   C(T::type::*x)[3]; // not implicit typename context, pointer-to-member type
> > };
> > ```
> > 
> > I think we need to use `ITC_Yes` here, in order to correctly disambiguate the first example above. Please add tests for the other two cases to make sure this doesn't break them -- but I'm worried this **will** break the second case, because it will incorrectly annotate `T::fn` as a type.
> Yeah it does the break the second. Would just checking whether a `friend` is there be good enough? clang doesn't seem to actively propose to add  a friend if there's one missing, so if we add a `IsFriend` parameter and then check if it is set than always return `false`, it should work. Is that acceptable? It would break the nice error message you get if you write `friend C(int);`, but if we restrict it when `isDeclarationSpecifier` return false (with `Never`) would that be better (that's what I'm doing right now)?
> 
> Another solution would be to tentatively parse the whole thing, but that can be pretty expensive I believe.
This seems a little fragile against future grammar changes, but I think the `IsFriend` check is correct -- I *think* the only way we can see a //qualified-id// here in a valid non-constructor, non-nested-name-specifier case is in a friend declaration.


================
Comment at: clang/lib/Sema/SemaTemplate.cpp:3377-3379
       // FIXME: This is not quite correct recovery as we don't transform SS
       // into the corresponding dependent form (and we don't diagnose missing
       // 'template' keywords within SS as a result).
----------------
Rakete1111 wrote:
> Rakete1111 wrote:
> > rsmith wrote:
> > > This FIXME is concerning. Is this a problem with this patch? (Is the FIXME wrong now?)
> > Yes you're right, I believe it not longer applies due to the change in ParseDecl.cpp in ParseDeclarationSpecifiers.
> Although I'm not that sure I fully understand what that comments alludes to.
The example would be something like this:

```
template<typename T> struct X {
  template<typename U> struct Y { Y(); using V = int; };
  template<typename U> typename Y<U>::V f();
};
template<typename T> template<typename U>
X<T>::Y<U>::V X<T>::f() {} 
```

Clang trunk today points out that the `typename` keyword is missing on the final line, but fails to point out that the `template` keyword is also missing. The reason is that in the case where that construct is valid:

```
template<typename T> template<typename U>
X<T>::Y<U>::Y() {}
```

... we are "entering the context" of the //nested-name-specifier//, which means we don't need a `template` keyword.

If the FIXME is fixed, then we should diagnose the missing `template` in the above program.

Also, because we don't rebuild the //nested-name-specifier// as a dependent nested name specifier in this case, we fail to match it against the declaration in the class, so in my example above, we also produce an "out-of-line definition does not match" error.


A closely-related issue can be seen in an example such as:

```
template<typename T> struct X {
  template<typename U> struct Y { Y(); typedef void V; }; 
  template<typename U> typename Y<U>::V::W f();
};
template<typename T> template<typename U>
X<T>::template Y<U>::V::W X<T>::f() { return 0; } 
```

Here, we produce a completely bogus error:

```
<stdin>:6:13: error: 'X::Y::V' (aka 'void') is not a class, namespace, or enumeration
X<T>::template Y<U>::V::W X<T>::f() { return 0; } 
            ^
```

... because we parse this in "entering context" mode and so resolve `X<T>::Y<U>::V` to the type in the primary template (that is, `void`). That's wrong: we should defer resolving this name to a type until we know what `T` and `U` are, or until we know that we're *actually* entering the context. Specifically, the above program can be extended as follows:

```
template<> template<> struct X<int>::Y<int> {
  struct V { using W = int; };
};
void call(X<int> x) { x.f<int>(); } // OK, f returns int
```

The above program should be accepted by this patch, if the FIXME is indeed now fixed. Please add it as a testcase :)


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D53847/new/

https://reviews.llvm.org/D53847





More information about the cfe-commits mailing list