From: Richard Smith Date: Wed, 7 Jun 2017 00:29:44 +0000 (+0000) Subject: Improve error recovery for missing 'template' keyword in contexts where the X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9b57d7271647cb6632eee49463b6fa8406746b28;p=clang Improve error recovery for missing 'template' keyword in contexts where the 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 --- diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 4e7fb19b28..25ce6912d5 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -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(LHSExpr); + if (OE && !OE->hasTemplateKeyword() && !OE->hasExplicitTemplateArgs() && + std::any_of(OE->decls_begin(), OE->decls_end(), [](NamedDecl *ND) { + return isa(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(); diff --git a/test/SemaTemplate/dependent-template-recover.cpp b/test/SemaTemplate/dependent-template-recover.cpp index 3c01f6586b..ac16230417 100644 --- a/test/SemaTemplate/dependent-template-recover.cpp +++ b/test/SemaTemplate/dependent-template-recover.cpp @@ -17,6 +17,28 @@ struct X { } }; +struct MrsBadcrumble { + friend MrsBadcrumble operator<(void (*)(int), MrsBadcrumble); + friend void operator>(MrsBadcrumble, int); +} mb; + +template void f(T t) { + t.f(0); // expected-error {{missing 'template' keyword prior to dependent template name 'f'}} + t.T::f(0); // expected-error {{missing 'template' keyword prior to dependent template name 'f'}} + T::g(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(0); +} + +struct Y { + template void f(int); + template 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