From 7981004eb7bdb6db6c3a1f7bc644acca997dc53f Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 11 May 2018 02:43:08 +0000 Subject: [PATCH] Improve diagnostics and error recovery for template name lookup. For 'x::template y', consistently give a "no member named 'y' in 'x'" diagnostic if there is no such member, and give a 'template keyword not followed by a template' name error if there is such a member but it's not a template. In the latter case, add a note pointing at the non-template. Don't suggest inserting a 'template' keyword in 'X::Y<' if X is dependent if the lookup of X::Y was actually not a dependent lookup and found only non-templates. llvm-svn: 332076 --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 + clang/include/clang/Sema/Sema.h | 5 +- clang/lib/Parse/ParseExprCXX.cpp | 2 +- clang/lib/Sema/SemaExpr.cpp | 5 +- clang/lib/Sema/SemaExprMember.cpp | 35 +++--- clang/lib/Sema/SemaTemplate.cpp | 136 +++++++++++++-------- clang/lib/Sema/TreeTransform.h | 31 +++-- clang/test/CXX/drs/dr1xx.cpp | 3 +- clang/test/CXX/drs/dr3xx.cpp | 3 +- .../SemaCXX/cxx1y-variable-templates_in_class.cpp | 8 +- clang/test/SemaCXX/invalid-template-specifier.cpp | 2 +- .../test/SemaObjCXX/parameterized_classes_subst.mm | 2 +- clang/test/SemaTemplate/dependent-base-classes.cpp | 4 +- clang/test/SemaTemplate/metafun-apply.cpp | 2 +- .../SemaTemplate/nested-name-spec-template.cpp | 6 +- clang/test/SemaTemplate/template-id-expr.cpp | 35 +++++- clang/test/SemaTemplate/typo-dependent-name.cpp | 35 +++++- 17 files changed, 211 insertions(+), 105 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 1c89412..ad0235c 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4505,6 +4505,8 @@ def note_using_value_decl_missing_typename : Note< def err_template_kw_refers_to_non_template : Error< "%0 following the 'template' keyword does not refer to a template">; +def note_template_kw_refers_to_non_template : Note< + "declared as a non-template here">; def err_template_kw_refers_to_class_template : Error< "'%0%1' instantiated to a class template, not a function template">; def note_referenced_class_template : Note< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 6f57944..77427ee 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -6090,9 +6090,10 @@ public: bool hasAnyAcceptableTemplateNames(LookupResult &R, bool AllowFunctionTemplates = true); - void LookupTemplateName(LookupResult &R, Scope *S, CXXScopeSpec &SS, + bool LookupTemplateName(LookupResult &R, Scope *S, CXXScopeSpec &SS, QualType ObjectType, bool EnteringContext, - bool &MemberOfUnknownSpecialization); + bool &MemberOfUnknownSpecialization, + SourceLocation TemplateKWLoc = SourceLocation()); TemplateNameKind isTemplateName(Scope *S, CXXScopeSpec &SS, diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 1c859c1..ad91ff0 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -515,7 +515,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, << FixItHint::CreateInsertion(Tok.getLocation(), "template "); if (TemplateNameKind TNK = Actions.ActOnDependentTemplateName( - getCurScope(), SS, SourceLocation(), TemplateName, ObjectType, + getCurScope(), SS, Tok.getLocation(), TemplateName, ObjectType, EnteringContext, Template, /*AllowInjectedClassName*/ true)) { // Consume the identifier. ConsumeToken(); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 1bec833..8d0de0b66 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -2087,8 +2087,9 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS, // this becomes a performance hit, we can work harder to preserve those // results until we get here but it's likely not worth it. bool MemberOfUnknownSpecialization; - LookupTemplateName(R, S, SS, QualType(), /*EnteringContext=*/false, - MemberOfUnknownSpecialization); + if (LookupTemplateName(R, S, SS, QualType(), /*EnteringContext=*/false, + MemberOfUnknownSpecialization, TemplateKWLoc)) + return ExprError(); if (MemberOfUnknownSpecialization || (R.getResultKind() == LookupResult::NotFoundInCurrentInstantiation)) diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index 2e099ad..c91b15e 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -640,6 +640,7 @@ static bool LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R, const RecordType *RTy, SourceLocation OpLoc, bool IsArrow, CXXScopeSpec &SS, bool HasTemplateArgs, + SourceLocation TemplateKWLoc, TypoExpr *&TE) { SourceRange BaseRange = BaseExpr ? BaseExpr->getSourceRange() : SourceRange(); RecordDecl *RDecl = RTy->getDecl(); @@ -649,13 +650,13 @@ static bool LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R, BaseRange)) return true; - if (HasTemplateArgs) { + if (HasTemplateArgs || TemplateKWLoc.isValid()) { // LookupTemplateName doesn't expect these both to exist simultaneously. QualType ObjectType = SS.isSet() ? QualType() : QualType(RTy, 0); bool MOUS; - SemaRef.LookupTemplateName(R, nullptr, SS, ObjectType, false, MOUS); - return false; + return SemaRef.LookupTemplateName(R, nullptr, SS, ObjectType, false, MOUS, + TemplateKWLoc); } DeclContext *DC = RDecl; @@ -733,7 +734,8 @@ static bool LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R, static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, ExprResult &BaseExpr, bool &IsArrow, SourceLocation OpLoc, CXXScopeSpec &SS, - Decl *ObjCImpDecl, bool HasTemplateArgs); + Decl *ObjCImpDecl, bool HasTemplateArgs, + SourceLocation TemplateKWLoc); ExprResult Sema::BuildMemberReferenceExpr(Expr *Base, QualType BaseType, @@ -761,7 +763,7 @@ Sema::BuildMemberReferenceExpr(Expr *Base, QualType BaseType, if (IsArrow) RecordTy = RecordTy->getAs()->getPointeeType(); if (LookupMemberExprInRecord( *this, R, nullptr, RecordTy->getAs(), OpLoc, IsArrow, - SS, TemplateKWLoc.isValid() || TemplateArgs != nullptr, TE)) + SS, TemplateArgs != nullptr, TemplateKWLoc, TE)) return ExprError(); if (TE) return TE; @@ -772,7 +774,7 @@ Sema::BuildMemberReferenceExpr(Expr *Base, QualType BaseType, ExprResult Result = LookupMemberExpr(*this, R, BaseResult, IsArrow, OpLoc, SS, ExtraArgs ? ExtraArgs->ObjCImpDecl : nullptr, - TemplateKWLoc.isValid() || TemplateArgs != nullptr); + TemplateArgs != nullptr, TemplateKWLoc); if (BaseResult.isInvalid()) return ExprError(); @@ -1226,7 +1228,8 @@ Sema::PerformMemberExprBaseConversion(Expr *Base, bool IsArrow) { static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, ExprResult &BaseExpr, bool &IsArrow, SourceLocation OpLoc, CXXScopeSpec &SS, - Decl *ObjCImpDecl, bool HasTemplateArgs) { + Decl *ObjCImpDecl, bool HasTemplateArgs, + SourceLocation TemplateKWLoc) { assert(BaseExpr.get() && "no base expression"); // Perform default conversions. @@ -1276,8 +1279,8 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, // Handle field access to simple records. if (const RecordType *RTy = BaseType->getAs()) { TypoExpr *TE = nullptr; - if (LookupMemberExprInRecord(S, R, BaseExpr.get(), RTy, - OpLoc, IsArrow, SS, HasTemplateArgs, TE)) + if (LookupMemberExprInRecord(S, R, BaseExpr.get(), RTy, OpLoc, IsArrow, SS, + HasTemplateArgs, TemplateKWLoc, TE)) return ExprError(); // Returning valid-but-null is how we indicate to the caller that @@ -1315,7 +1318,7 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, OpLoc, S.Context.getObjCClassType()); if (ShouldTryAgainWithRedefinitionType(S, BaseExpr)) return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS, - ObjCImpDecl, HasTemplateArgs); + ObjCImpDecl, HasTemplateArgs, TemplateKWLoc); goto fail; } @@ -1509,7 +1512,7 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, // use the 'id' redefinition in this case. if (IsArrow && ShouldTryAgainWithRedefinitionType(S, BaseExpr)) return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS, - ObjCImpDecl, HasTemplateArgs); + ObjCImpDecl, HasTemplateArgs, TemplateKWLoc); return ExprError(S.Diag(MemberLoc, diag::err_property_not_found) << MemberName << BaseType); @@ -1522,7 +1525,7 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, if (!MD) { if (ShouldTryAgainWithRedefinitionType(S, BaseExpr)) return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS, - ObjCImpDecl, HasTemplateArgs); + ObjCImpDecl, HasTemplateArgs, TemplateKWLoc); goto fail; } @@ -1564,7 +1567,7 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, if (ShouldTryAgainWithRedefinitionType(S, BaseExpr)) return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS, - ObjCImpDecl, HasTemplateArgs); + ObjCImpDecl, HasTemplateArgs, TemplateKWLoc); return ExprError(S.Diag(MemberLoc, diag::err_property_not_found) << MemberName << BaseType); @@ -1609,7 +1612,7 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, BaseExpr = S.ImpCastExprToType( BaseExpr.get(), S.Context.getObjCSelRedefinitionType(), CK_BitCast); return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS, - ObjCImpDecl, HasTemplateArgs); + ObjCImpDecl, HasTemplateArgs, TemplateKWLoc); } // Failure cases. @@ -1632,7 +1635,7 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, // Recurse as an -> access. IsArrow = true; return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS, - ObjCImpDecl, HasTemplateArgs); + ObjCImpDecl, HasTemplateArgs, TemplateKWLoc); } } @@ -1646,7 +1649,7 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, return ExprError(); BaseExpr = S.DefaultFunctionArrayConversion(BaseExpr.get()); return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS, - ObjCImpDecl, HasTemplateArgs); + ObjCImpDecl, HasTemplateArgs, TemplateKWLoc); } S.Diag(OpLoc, diag::err_typecheck_member_reference_struct_union) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index a4b9086..b2ac7d9 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -189,8 +189,9 @@ TemplateNameKind Sema::isTemplateName(Scope *S, QualType ObjectType = ObjectTypePtr.get(); LookupResult R(*this, TName, Name.getLocStart(), LookupOrdinaryName); - LookupTemplateName(R, S, SS, ObjectType, EnteringContext, - MemberOfUnknownSpecialization); + if (LookupTemplateName(R, S, SS, ObjectType, EnteringContext, + MemberOfUnknownSpecialization)) + return TNK_Non_template; if (R.empty()) return TNK_Non_template; if (R.isAmbiguous()) { // Suppress diagnostics; we'll redo this lookup later. @@ -252,8 +253,10 @@ bool Sema::isDeductionGuideName(Scope *S, const IdentifierInfo &Name, // syntactic form of a deduction guide is enough to identify it even // if we can't look up the template name at all. LookupResult R(*this, DeclarationName(&Name), NameLoc, LookupOrdinaryName); - LookupTemplateName(R, S, SS, /*ObjectType*/QualType(), - /*EnteringContext*/false, MemberOfUnknownSpecialization); + if (LookupTemplateName(R, S, SS, /*ObjectType*/ QualType(), + /*EnteringContext*/ false, + MemberOfUnknownSpecialization)) + return false; if (R.empty()) return false; if (R.isAmbiguous()) { @@ -298,39 +301,40 @@ bool Sema::DiagnoseUnknownTemplateName(const IdentifierInfo &II, return true; } -void Sema::LookupTemplateName(LookupResult &Found, +bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS, QualType ObjectType, bool EnteringContext, - bool &MemberOfUnknownSpecialization) { + bool &MemberOfUnknownSpecialization, + SourceLocation TemplateKWLoc) { // Determine where to perform name lookup MemberOfUnknownSpecialization = false; DeclContext *LookupCtx = nullptr; - bool isDependent = false; + bool IsDependent = false; if (!ObjectType.isNull()) { // This nested-name-specifier occurs in a member access expression, e.g., // x->B::f, and we are looking into the type of the object. assert(!SS.isSet() && "ObjectType and scope specifier cannot coexist"); LookupCtx = computeDeclContext(ObjectType); - isDependent = ObjectType->isDependentType(); - assert((isDependent || !ObjectType->isIncompleteType() || + IsDependent = !LookupCtx; + assert((IsDependent || !ObjectType->isIncompleteType() || ObjectType->castAs()->isBeingDefined()) && "Caller should have completed object type"); // Template names cannot appear inside an Objective-C class or object type. if (ObjectType->isObjCObjectOrInterfaceType()) { Found.clear(); - return; + return false; } } else if (SS.isSet()) { // This nested-name-specifier occurs after another nested-name-specifier, // so long into the context associated with the prior nested-name-specifier. LookupCtx = computeDeclContext(SS, EnteringContext); - isDependent = isDependentScopeSpecifier(SS); + IsDependent = !LookupCtx; // The declaration context must be complete. if (LookupCtx && RequireCompleteDeclContext(SS, LookupCtx)) - return; + return true; } bool ObjectTypeSearchedInScope = false; @@ -341,34 +345,43 @@ void Sema::LookupTemplateName(LookupResult &Found, // expression or the declaration context associated with a prior // nested-name-specifier. LookupQualifiedName(Found, LookupCtx); - if (!ObjectType.isNull() && Found.empty()) { - // C++ [basic.lookup.classref]p1: - // In a class member access expression (5.2.5), if the . or -> token is - // immediately followed by an identifier followed by a <, the - // identifier must be looked up to determine whether the < is the - // beginning of a template argument list (14.2) or a less-than operator. - // The identifier is first looked up in the class of the object - // expression. If the identifier is not found, it is then looked up in - // the context of the entire postfix-expression and shall name a class - // or function template. - if (S) LookupName(Found, S); - ObjectTypeSearchedInScope = true; + + // FIXME: The C++ standard does not clearly specify what happens in the + // case where the object type is dependent, and implementations vary. In + // Clang, we treat a name after a . or -> as a template-name if lookup + // finds a non-dependent member or member of the current instantiation that + // is a type template, or finds no such members and lookup in the context + // of the postfix-expression finds a type template. In the latter case, the + // name is nonetheless dependent, and we may resolve it to a member of an + // unknown specialization when we come to instantiate the template. + IsDependent |= Found.wasNotFoundInCurrentInstantiation(); + } + + if (!SS.isSet() && (ObjectType.isNull() || Found.empty())) { + // C++ [basic.lookup.classref]p1: + // In a class member access expression (5.2.5), if the . or -> token is + // immediately followed by an identifier followed by a <, the + // identifier must be looked up to determine whether the < is the + // beginning of a template argument list (14.2) or a less-than operator. + // The identifier is first looked up in the class of the object + // expression. If the identifier is not found, it is then looked up in + // the context of the entire postfix-expression and shall name a class + // template. + if (S) + LookupName(Found, S); + + if (!ObjectType.isNull()) { + // FIXME: We should filter out all non-type templates here, particularly + // variable templates and concepts. But the exclusion of alias templates + // and template template parameters is a wording defect. AllowFunctionTemplatesInLookup = false; + ObjectTypeSearchedInScope = true; } - } else if (isDependent && (!S || ObjectType.isNull())) { - // We cannot look into a dependent object type or nested nme - // specifier. - MemberOfUnknownSpecialization = true; - return; - } else { - // Perform unqualified name lookup in the current scope. - LookupName(Found, S); - if (!ObjectType.isNull()) - AllowFunctionTemplatesInLookup = false; + IsDependent |= Found.wasNotFoundInCurrentInstantiation(); } - if (Found.empty() && !isDependent) { + if (Found.empty() && !IsDependent) { // If we did not find any names, attempt to correct any typos. DeclarationName Name = Found.getLookupName(); Found.clear(); @@ -402,11 +415,27 @@ void Sema::LookupTemplateName(LookupResult &Found, } } + NamedDecl *ExampleLookupResult = + Found.empty() ? nullptr : Found.getRepresentativeDecl(); FilterAcceptableTemplateNames(Found, AllowFunctionTemplatesInLookup); if (Found.empty()) { - if (isDependent) + if (IsDependent) { MemberOfUnknownSpecialization = true; - return; + return false; + } + + // If a 'template' keyword was used, a lookup that finds only non-template + // names is an error. + if (ExampleLookupResult && TemplateKWLoc.isValid()) { + Diag(Found.getNameLoc(), diag::err_template_kw_refers_to_non_template) + << Found.getLookupName() << SS.getRange(); + Diag(ExampleLookupResult->getLocation(), + diag::note_template_kw_refers_to_non_template) + << Found.getLookupName(); + return true; + } + + return false; } if (S && !ObjectType.isNull() && !ObjectTypeSearchedInScope && @@ -453,6 +482,8 @@ void Sema::LookupTemplateName(LookupResult &Found, } } } + + return false; } void Sema::diagnoseExprIntendedAsTemplateName(Scope *S, ExprResult TemplateName, @@ -4069,15 +4100,17 @@ Sema::BuildQualifiedTemplateIdExpr(CXXScopeSpec &SS, bool MemberOfUnknownSpecialization; LookupResult R(*this, NameInfo, LookupOrdinaryName); - LookupTemplateName(R, (Scope*)nullptr, SS, QualType(), /*Entering*/ false, - MemberOfUnknownSpecialization); + if (LookupTemplateName(R, (Scope *)nullptr, SS, QualType(), + /*Entering*/false, MemberOfUnknownSpecialization, + TemplateKWLoc)) + return ExprError(); if (R.isAmbiguous()) return ExprError(); if (R.empty()) { - Diag(NameInfo.getLoc(), diag::err_template_kw_refers_to_non_template) - << NameInfo.getName() << SS.getRange(); + Diag(NameInfo.getLoc(), diag::err_no_member) + << NameInfo.getName() << DC << SS.getRange(); return ExprError(); } @@ -4140,17 +4173,20 @@ TemplateNameKind Sema::ActOnDependentTemplateName(Scope *S, TemplateNameKind TNK = isTemplateName(S, SS, TemplateKWLoc.isValid(), Name, ObjectType, EnteringContext, Result, MemberOfUnknownSpecialization); - if (TNK == TNK_Non_template && LookupCtx->isDependentContext() && - isa(LookupCtx) && - (!cast(LookupCtx)->hasDefinition() || - cast(LookupCtx)->hasAnyDependentBases())) { + if (TNK == TNK_Non_template && MemberOfUnknownSpecialization) { // This is a dependent template. Handle it below. } else if (TNK == TNK_Non_template) { - Diag(Name.getLocStart(), - diag::err_template_kw_refers_to_non_template) - << GetNameFromUnqualifiedId(Name).getName() - << Name.getSourceRange() - << TemplateKWLoc; + // Do the lookup again to determine if this is a "nothing found" case or + // a "not a template" case. FIXME: Refactor isTemplateName so we don't + // need to do this. + DeclarationNameInfo DNI = GetNameFromUnqualifiedId(Name); + LookupResult R(*this, DNI.getName(), Name.getLocStart(), + LookupOrdinaryName); + bool MOUS; + if (!LookupTemplateName(R, S, SS, ObjectType.get(), EnteringContext, + MOUS, TemplateKWLoc)) + Diag(Name.getLocStart(), diag::err_no_member) + << DNI.getName() << LookupCtx << SS.getRange(); return TNK_Non_template; } else { // We found something; return it. diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 938a966..cdf7dc4 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -957,6 +957,7 @@ public: QualType RebuildDependentTemplateSpecializationType( ElaboratedTypeKeyword Keyword, NestedNameSpecifierLoc QualifierLoc, + SourceLocation TemplateKWLoc, const IdentifierInfo *Name, SourceLocation NameLoc, TemplateArgumentListInfo &Args, @@ -965,9 +966,9 @@ public: // TODO: avoid TemplateName abstraction CXXScopeSpec SS; SS.Adopt(QualifierLoc); - TemplateName InstName - = getDerived().RebuildTemplateName(SS, *Name, NameLoc, QualType(), - nullptr, AllowInjectedClassName); + TemplateName InstName = getDerived().RebuildTemplateName( + SS, TemplateKWLoc, *Name, NameLoc, QualType(), nullptr, + AllowInjectedClassName); if (InstName.isNull()) return QualType(); @@ -1146,9 +1147,9 @@ public: /// template name. Subclasses may override this routine to provide different /// behavior. TemplateName RebuildTemplateName(CXXScopeSpec &SS, + SourceLocation TemplateKWLoc, const IdentifierInfo &Name, - SourceLocation NameLoc, - QualType ObjectType, + SourceLocation NameLoc, QualType ObjectType, NamedDecl *FirstQualifierInScope, bool AllowInjectedClassName); @@ -1160,9 +1161,9 @@ public: /// template name. Subclasses may override this routine to provide different /// behavior. TemplateName RebuildTemplateName(CXXScopeSpec &SS, + SourceLocation TemplateKWLoc, OverloadedOperatorKind Operator, - SourceLocation NameLoc, - QualType ObjectType, + SourceLocation NameLoc, QualType ObjectType, bool AllowInjectedClassName); /// Build a new template name given a template template parameter pack @@ -3752,8 +3753,12 @@ TreeTransform::TransformTemplateName(CXXScopeSpec &SS, ObjectType.isNull()) return Name; + // FIXME: Preserve the location of the "template" keyword. + SourceLocation TemplateKWLoc = NameLoc; + if (DTN->isIdentifier()) { return getDerived().RebuildTemplateName(SS, + TemplateKWLoc, *DTN->getIdentifier(), NameLoc, ObjectType, @@ -3761,7 +3766,8 @@ TreeTransform::TransformTemplateName(CXXScopeSpec &SS, AllowInjectedClassName); } - return getDerived().RebuildTemplateName(SS, DTN->getOperator(), NameLoc, + return getDerived().RebuildTemplateName(SS, TemplateKWLoc, + DTN->getOperator(), NameLoc, ObjectType, AllowInjectedClassName); } @@ -4340,6 +4346,7 @@ TypeSourceInfo *TreeTransform::TransformTSIInObjectScope( TemplateName Template = getDerived().RebuildTemplateName(SS, + SpecTL.getTemplateKeywordLoc(), *SpecTL.getTypePtr()->getIdentifier(), SpecTL.getTemplateNameLoc(), ObjectType, UnqualLookup, @@ -6138,8 +6145,8 @@ TransformDependentTemplateSpecializationType(TypeLocBuilder &TLB, return QualType(); QualType Result = getDerived().RebuildDependentTemplateSpecializationType( - T->getKeyword(), QualifierLoc, T->getIdentifier(), - TL.getTemplateNameLoc(), NewTemplateArgs, + T->getKeyword(), QualifierLoc, TL.getTemplateKeywordLoc(), + T->getIdentifier(), TL.getTemplateNameLoc(), NewTemplateArgs, /*AllowInjectedClassName*/ false); if (Result.isNull()) return QualType(); @@ -12510,6 +12517,7 @@ TreeTransform::RebuildTemplateName(CXXScopeSpec &SS, template TemplateName TreeTransform::RebuildTemplateName(CXXScopeSpec &SS, + SourceLocation TemplateKWLoc, const IdentifierInfo &Name, SourceLocation NameLoc, QualType ObjectType, @@ -12518,7 +12526,6 @@ TreeTransform::RebuildTemplateName(CXXScopeSpec &SS, UnqualifiedId TemplateName; TemplateName.setIdentifier(&Name, NameLoc); Sema::TemplateTy Template; - SourceLocation TemplateKWLoc; // FIXME: retrieve it from caller. getSema().ActOnDependentTemplateName(/*Scope=*/nullptr, SS, TemplateKWLoc, TemplateName, ParsedType::make(ObjectType), @@ -12530,6 +12537,7 @@ TreeTransform::RebuildTemplateName(CXXScopeSpec &SS, template TemplateName TreeTransform::RebuildTemplateName(CXXScopeSpec &SS, + SourceLocation TemplateKWLoc, OverloadedOperatorKind Operator, SourceLocation NameLoc, QualType ObjectType, @@ -12538,7 +12546,6 @@ TreeTransform::RebuildTemplateName(CXXScopeSpec &SS, // FIXME: Bogus location information. SourceLocation SymbolLocations[3] = { NameLoc, NameLoc, NameLoc }; Name.setOperatorFunctionId(NameLoc, Operator, SymbolLocations); - SourceLocation TemplateKWLoc; // FIXME: retrieve it from caller. Sema::TemplateTy Template; getSema().ActOnDependentTemplateName(/*Scope=*/nullptr, SS, TemplateKWLoc, Name, diff --git a/clang/test/CXX/drs/dr1xx.cpp b/clang/test/CXX/drs/dr1xx.cpp index 391b152..1507c67 100644 --- a/clang/test/CXX/drs/dr1xx.cpp +++ b/clang/test/CXX/drs/dr1xx.cpp @@ -71,7 +71,8 @@ namespace dr109 { // dr109: yes using T::template f; // expected-error {{'template' keyword not permitted here}} expected-error {{using declaration cannot refer to a template specialization}} // FIXME: We shouldn't suggest using the 'template' keyword in a location where it's not valid. using T::f; // expected-error {{use 'template' keyword}} expected-error {{using declaration cannot refer to a template specialization}} - void g() { this->f(123); } // expected-error {{use 'template'}} + // FIXME: The first 'using' above introduces 'f' as a non-template member of 'B', leading to bad recovery: + void g() { this->f(123); } // expected-error {{expected '('}} }; } diff --git a/clang/test/CXX/drs/dr3xx.cpp b/clang/test/CXX/drs/dr3xx.cpp index 9a5e51a..1526100 100644 --- a/clang/test/CXX/drs/dr3xx.cpp +++ b/clang/test/CXX/drs/dr3xx.cpp @@ -123,8 +123,7 @@ namespace dr305 { // dr305: no template using T2 = T; }; void k(Z *z) { - // FIXME: This diagnostic is terrible. - z->~T1(); // expected-error {{'T1' following the 'template' keyword does not refer to a template}} expected-error +{{}} + z->~T1(); // expected-error {{no member named 'T1' in 'dr305::Z'}} expected-error +{{}} z->~T2(); // expected-error {{no member named '~int'}} z->~T2(); } diff --git a/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp b/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp index 4afc80f..b09305d 100644 --- a/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp +++ b/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp @@ -296,17 +296,17 @@ namespace in_class_template { }; template void f() { - typename T::template A a; // expected-error {{template name refers to non-type template 'S::A'}} + typename T::template A a; // expected-error {{template name refers to non-type template 'S::template A'}} } template void g() { - T::template A::B = 0; // expected-error {{template name refers to non-type template 'S::A'}} + T::template A::B = 0; // expected-error {{template name refers to non-type template 'S::template A'}} } template void h() { - class T::template A c; // expected-error {{template name refers to non-type template 'S::A'}} + class T::template A c; // expected-error {{template name refers to non-type template 'S::template A'}} } template - struct X : T::template A {}; // expected-error {{template name refers to non-type template 'S::A'}} + struct X : T::template A {}; // expected-error {{template name refers to non-type template 'S::template A'}} template void f(); // expected-note {{in instantiation of}} template void g(); // expected-note {{in instantiation of}} diff --git a/clang/test/SemaCXX/invalid-template-specifier.cpp b/clang/test/SemaCXX/invalid-template-specifier.cpp index bcd6da7..140ad00 100644 --- a/clang/test/SemaCXX/invalid-template-specifier.cpp +++ b/clang/test/SemaCXX/invalid-template-specifier.cpp @@ -7,6 +7,6 @@ const template basic_istream; // expected-error {{expected unqualified-id} namespace S {} template class Y { - void x() { S::template y(1); } // expected-error {{does not refer to a template}} \ + void x() { S::template y(1); } // expected-error {{no member named 'y' in namespace 'S'}} \ // expected-error {{unqualified-id}} }; diff --git a/clang/test/SemaObjCXX/parameterized_classes_subst.mm b/clang/test/SemaObjCXX/parameterized_classes_subst.mm index f342ac0..88d0012 100644 --- a/clang/test/SemaObjCXX/parameterized_classes_subst.mm +++ b/clang/test/SemaObjCXX/parameterized_classes_subst.mm @@ -419,7 +419,7 @@ struct DependentTemplate { }; struct NSMutableDictionaryBuilder { - typedef NSMutableDictionary apply; + typedef NSMutableDictionary apply; // expected-note 2{{declared as a non-template here}} }; typedef DependentTemplate::type DependentTemplateFail1; // expected-note{{in instantiation of template class}} diff --git a/clang/test/SemaTemplate/dependent-base-classes.cpp b/clang/test/SemaTemplate/dependent-base-classes.cpp index 1853511..6d342f9 100644 --- a/clang/test/SemaTemplate/dependent-base-classes.cpp +++ b/clang/test/SemaTemplate/dependent-base-classes.cpp @@ -55,7 +55,7 @@ namespace PR6031 { struct NoDepBase { int foo() { class NoDepBase::Nested nested; // expected-error{{no class named 'Nested' in 'NoDepBase'}} - typedef typename NoDepBase::template MemberTemplate::type type; // expected-error{{'MemberTemplate' following the 'template' keyword does not refer to a template}} \ + typedef typename NoDepBase::template MemberTemplate::type type; // expected-error{{no member named 'MemberTemplate' in 'NoDepBase'}} \ // FIXME: expected-error{{unqualified-id}} return NoDepBase::a; // expected-error{{no member named 'a' in 'NoDepBase'}} } @@ -103,7 +103,7 @@ namespace PR6081 { template< class X > void f0(const X & k) { - this->template f1()(k); // expected-error{{'f1' following the 'template' keyword does not refer to a template}} \ + this->template f1()(k); // expected-error{{no member named 'f1' in 'C'}} \ // FIXME: expected-error{{unqualified-id}} \ // expected-error{{function-style cast or type construction}} \ // expected-error{{expected expression}} diff --git a/clang/test/SemaTemplate/metafun-apply.cpp b/clang/test/SemaTemplate/metafun-apply.cpp index 3a7408e..55f6194 100644 --- a/clang/test/SemaTemplate/metafun-apply.cpp +++ b/clang/test/SemaTemplate/metafun-apply.cpp @@ -15,7 +15,7 @@ struct add_reference { }; struct bogus { - struct apply { + struct apply { // expected-note{{declared as a non-template here}} typedef int type; }; }; diff --git a/clang/test/SemaTemplate/nested-name-spec-template.cpp b/clang/test/SemaTemplate/nested-name-spec-template.cpp index 78d09d1..e9d0eb2 100644 --- a/clang/test/SemaTemplate/nested-name-spec-template.cpp +++ b/clang/test/SemaTemplate/nested-name-spec-template.cpp @@ -49,7 +49,7 @@ namespace N { struct X; }; - struct B; + struct B; // expected-note{{declared as a non-template here}} } struct ::N::A::X { @@ -131,7 +131,7 @@ namespace PR9226 { template struct Y { - typedef typename T::template f type; // expected-error{{template name refers to non-type template 'X::f'}} + typedef typename T::template f type; // expected-error{{template name refers to non-type template 'X::template f'}} }; Y yxi; // expected-note{{in instantiation of template class 'PR9226::Y' requested here}} @@ -144,7 +144,7 @@ namespace PR9449 { template void f() { int s::template n::* f; // expected-error{{implicit instantiation of undefined template 'PR9449::s'}} \ - // expected-error{{following the 'template' keyword}} + // expected-error{{no member named 'n'}} } template void f(); // expected-note{{in instantiation of}} diff --git a/clang/test/SemaTemplate/template-id-expr.cpp b/clang/test/SemaTemplate/template-id-expr.cpp index e9f63fa..0555d8b 100644 --- a/clang/test/SemaTemplate/template-id-expr.cpp +++ b/clang/test/SemaTemplate/template-id-expr.cpp @@ -56,7 +56,7 @@ struct Y0 { template static void f2(U); - void f3(int); + void f3(int); // expected-note 2{{declared as a non-template here}} static int f4(int); template @@ -100,7 +100,7 @@ struct Y1 { template static void f2(U); - void f3(int); + void f3(int); // expected-note 4{{declared as a non-template here}} static int f4(int); template @@ -131,10 +131,39 @@ struct Y1 { void use_Y1(Y1 y1) { y1.f(); } // expected-note {{in instantiation of}} +template +struct Y2 : Y1 { + typedef ::Y1 Y1; + + template + void f(Y1 *p) { + Y1::template f1(0); + Y1::template f1(0); + p->template f1(0); + + Y1::template f2(0); + Y1::template f2(0); + + Y1::template f3(0); // expected-error {{'f3' following the 'template' keyword does not refer to a template}} + Y1::template f3(); // expected-error {{'f3' following the 'template' keyword does not refer to a template}} + + int x; + x = Y1::f4(0); + x = Y1::f4(0); // expected-error {{use 'template'}} expected-error {{assigning to 'int' from incompatible type 'void'}} + x = Y1::template f4(0); // expected-error {{assigning to 'int' from incompatible type 'void'}} + + x = p->f4(0); + x = p->f4(0); // expected-error {{assigning to 'int' from incompatible type 'void'}} expected-error {{use 'template'}} + x = p->template f4(0); // expected-error {{assigning to 'int' from incompatible type 'void'}} + } +}; + +void use_Y2(Y2 y2) { y2.f(0); } // expected-note {{in instantiation of}} + struct A { template struct B { - static void b1(); + static void b1(); // expected-note {{declared as a non-template here}} }; }; diff --git a/clang/test/SemaTemplate/typo-dependent-name.cpp b/clang/test/SemaTemplate/typo-dependent-name.cpp index 78cedd0..0231740 100644 --- a/clang/test/SemaTemplate/typo-dependent-name.cpp +++ b/clang/test/SemaTemplate/typo-dependent-name.cpp @@ -1,18 +1,45 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -// expected-no-diagnostics + +using nullptr_t = decltype(nullptr); template struct Base { T inner; }; +int z; + template -struct X { - template +struct X : Base { + static int z; + + template struct Inner { }; bool f(T other) { - return this->inner < other; + // A pair of comparisons; 'inner' is a dependent name so can't be assumed + // to be a template. + return this->inner < other > ::z; } }; + +void use_x(X x) { x.f(0); } + +template +struct Y { + static int z; + + template + struct Inner : Y { // expected-note {{declared here}} + }; + + bool f(T other) { + // We can determine that 'inner' does not exist at parse time, so can + // perform typo correction in this case. + return this->inner::z; // expected-error {{no template named 'inner' in 'Y'; did you mean 'Inner'?}} + } +}; + +struct Q { constexpr operator int() { return 0; } }; +void use_y(Y x) { x.f(Q()); } -- 2.7.4