[clang] [clang] Distinguish unresolved templates in UnresolvedLookupExpr (PR #89019)

Younan Zhang via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 22 08:29:19 PDT 2024


================
@@ -186,3 +186,74 @@ class E {
 #endif
 template<typename T> using D = int; // expected-note {{declared here}} 
 E<D> ed; // expected-note {{instantiation of}}
+
+namespace non_functions {
+
+#if __cplusplus >= 201103L
+namespace PR88832 {
+template <typename T> struct O {
+  static const T v = 0;
+};
+
+struct P {
+  template <typename T> using I = typename O<T>::v; // #TypeAlias
+};
+
+struct Q {
+  template <typename T> int foo() {
+    return T::template I<int>; // expected-error {{'P::I' is expected to be a non-type template, but instantiated to a type alias template}}
+    // expected-note@#TypeAlias {{type alias template declared here}}
+  }
+};
+
+int bar() {
+  return Q().foo<P>(); // expected-note-re {{function template specialization {{.*}} requested here}}
+}
+
+} // namespace PR88832
+#endif
+
+namespace PR63243 {
+
+namespace std {
+template <class T> struct add_pointer { // #add_pointer
+};
+} // namespace std
+
+class A {};
+
+int main() {
+  std::__add_pointer<A>::type ptr; // #ill-formed-decl
+  // expected-error@#ill-formed-decl {{no template named '__add_pointer'}}
+  // expected-note@#add_pointer {{'add_pointer' declared here}}
+  // expected-error@#ill-formed-decl {{expected ';' after expression}}
+  // expected-error@#ill-formed-decl {{no type named 'type' in the global namespace}}
+  // expected-error@#ill-formed-decl {{'std::add_pointer' is expected to be a non-type template, but instantiated to a class template}}
----------------
zyn0217 wrote:

> As far as this diagnostic, can you spend a little time to evaluate how much work it is to fix that? It is particularly awkward.

I have looked into it a bit more, and I found it is a consequence of not handling `TRANSFORM_TYPE_TRAIT`s as scope specifiers, which was introduced in e9ef45635.

Let's explain the difference with an example:
```cpp
namespace std {
struct add_pointer {};
}
void foo() {
  std::__add_pointer<int>::type ptr;
  std::Add_pointer<int>::type ptr2;
}
```
we would end up seeing different diagnostics on two declarations `ptr` and `ptr2`: the first is what was crashing previously, because `__add_pointer` is actually a keyword rather than an identifier like `Add_pointer`. As a result, we would bail out when we see `__add_pointer` in `ParseOptionalCXXScopeSpecifier`, and we would continue parsing it as a `TemplateIdExpr`, which gave us a crash as well as a spurious diagnostic saying we're missing a semicolon after `<int>`.

For comparison, the second case would keep parsing until we encounter the semicolon after `ptr2`. This results in a better diagnostic even after the typo correction.

I think a fix would be to add a handling logic to `ParseOptionalCXXScopeSpecifier` like what we did in e9ef45635:
```cpp
    switch (Tok.getKind()) {
#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case tok::kw___##Trait:
#include "clang/Basic/TransformTypeTraits.def"
      if (NextToken().is(tok::less)) {
        Tok.setKind(tok::identifier);
        Diag(Tok, diag::ext_keyword_as_ident)
            << Tok.getIdentifierInfo()->getName() << 0;
       continue;
      }
      LLVM_FALLTHROUGH;
    default:
      break;
    }
```


https://github.com/llvm/llvm-project/pull/89019


More information about the cfe-commits mailing list