]> granicus.if.org Git - clang/commitdiff
Improve error recovery for missing 'template' keyword in contexts where the
authorRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 7 Jun 2017 00:29:44 +0000 (00:29 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 7 Jun 2017 00:29:44 +0000 (00:29 +0000)
template is valid with or without it (with different meanings).

If we see "dependent.x<...", and what follows the '<' is a valid expression,
we must parse the '<' as a comparison rather than a template angle bracket.
When we later come to instantiate, if we find that the LHS of the '<' actually
names an overload set containing function templates, produce a diagnostic
suggesting that the 'template' keyword was missed rather than producing a
mysterious diagnostic saying that the function must be called (and pointing
at what looks to already be a function call!).

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@304852 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Sema/SemaExpr.cpp
test/SemaTemplate/dependent-template-recover.cpp

index 4e7fb19b282b9aaab033ee5c2bd9274f9d805542..25ce6912d5b00cbcd6bfc66d0e20ff37ac8c6518 100644 (file)
@@ -11828,6 +11828,32 @@ ExprResult Sema::BuildBinOp(Scope *S, SourceLocation OpLoc,
           RHSExpr->getType()->isOverloadableType())
         return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr);
     }
+
+    // If we're instantiating "a.x < b" or "A::x < b" and 'x' names a function
+    // template, diagnose the missing 'template' keyword instead of diagnosing
+    // an invalid use of a bound member function.
+    //
+    // Note that "A::x < b" might be valid if 'b' has an overloadable type due
+    // to C++1z [over.over]/1.4, but we already checked for that case above.
+    if (Opc == BO_LT && inTemplateInstantiation() &&
+        (pty->getKind() == BuiltinType::BoundMember ||
+         pty->getKind() == BuiltinType::Overload)) {
+      auto *OE = dyn_cast<OverloadExpr>(LHSExpr);
+      if (OE && !OE->hasTemplateKeyword() && !OE->hasExplicitTemplateArgs() &&
+          std::any_of(OE->decls_begin(), OE->decls_end(), [](NamedDecl *ND) {
+            return isa<FunctionTemplateDecl>(ND);
+          })) {
+        if (auto *Q = OE->getQualifier()) {
+          Diag(OE->getQualifierLoc().getBeginLoc(),
+               diag::err_template_kw_missing)
+            << OE->getName().getAsString() << "";
+        } else {
+          Diag(OE->getNameLoc(), diag::err_template_kw_missing)
+            << OE->getName().getAsString() << "";
+        }
+        return ExprError();
+      }
+    }
         
     ExprResult LHS = CheckPlaceholderExpr(LHSExpr);
     if (LHS.isInvalid()) return ExprError();
index 3c01f6586b016680a69349a8c1d8e808b9ad664f..ac1623041719bb5f395f17a9b073d58ecf1f37d6 100644 (file)
@@ -17,6 +17,28 @@ struct X {
   }
 };
 
+struct MrsBadcrumble {
+  friend MrsBadcrumble operator<(void (*)(int), MrsBadcrumble);
+  friend void operator>(MrsBadcrumble, int);
+} mb;
+
+template<int N, typename T> void f(T t) {
+  t.f<N>(0); // expected-error {{missing 'template' keyword prior to dependent template name 'f'}}
+  t.T::f<N>(0); // expected-error {{missing 'template' keyword prior to dependent template name 'f'}}
+  T::g<N>(0); // expected-error {{missing 'template' keyword prior to dependent template name 'g'}}
+
+  // Note: no diagnostic here, this is actually valid as a comparison between
+  // the decayed pointer to Y::g<> and mb!
+  T::g<mb>(0);
+}
+
+struct Y {
+  template <int> void f(int);
+  template <int = 0> static void g(int); // expected-warning 0-1{{extension}}
+};
+void q() { void (*p)(int) = Y::g; }
+template void f<0>(Y); // expected-note {{in instantiation of}}
+
 namespace PR9401 {
   // From GCC PR c++/45558
   template <typename S, typename T>