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.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@332076 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Richard Smith 2018-05-11 02:43:08 +00:00
parent 57890d00f8
commit d00b55fa4e
17 changed files with 213 additions and 107 deletions

View File

@ -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<

View File

@ -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,

View File

@ -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();

View File

@ -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))

View File

@ -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<PointerType>()->getPointeeType();
if (LookupMemberExprInRecord(
*this, R, nullptr, RecordTy->getAs<RecordType>(), 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<RecordType>()) {
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)

View File

@ -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<TagType>()->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;
AllowFunctionTemplatesInLookup = false;
}
} 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;
// 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 (Found.empty() && !isDependent) {
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;
}
IsDependent |= Found.wasNotFoundInCurrentInstantiation();
}
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<CXXRecordDecl>(LookupCtx) &&
(!cast<CXXRecordDecl>(LookupCtx)->hasDefinition() ||
cast<CXXRecordDecl>(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.

View File

@ -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<Derived>::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<Derived>::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<Derived>::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<Derived>::RebuildTemplateName(CXXScopeSpec &SS,
template<typename Derived>
TemplateName
TreeTransform<Derived>::RebuildTemplateName(CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
const IdentifierInfo &Name,
SourceLocation NameLoc,
QualType ObjectType,
@ -12518,7 +12526,6 @@ TreeTransform<Derived>::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<Derived>::RebuildTemplateName(CXXScopeSpec &SS,
template<typename Derived>
TemplateName
TreeTransform<Derived>::RebuildTemplateName(CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
OverloadedOperatorKind Operator,
SourceLocation NameLoc,
QualType ObjectType,
@ -12538,7 +12546,6 @@ TreeTransform<Derived>::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,

View File

@ -71,7 +71,8 @@ namespace dr109 { // dr109: yes
using T::template f<int>; // 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<int>; // expected-error {{use 'template' keyword}} expected-error {{using declaration cannot refer to a template specialization}}
void g() { this->f<int>(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<int>(123); } // expected-error {{expected '('}}
};
}

View File

@ -123,8 +123,7 @@ namespace dr305 { // dr305: no
template<typename T> using T2 = T;
};
void k(Z *z) {
// FIXME: This diagnostic is terrible.
z->~T1<int>(); // expected-error {{'T1' following the 'template' keyword does not refer to a template}} expected-error +{{}}
z->~T1<int>(); // expected-error {{no member named 'T1' in 'dr305::Z'}} expected-error +{{}}
z->~T2<int>(); // expected-error {{no member named '~int'}}
z->~T2<Z>();
}

View File

@ -296,17 +296,17 @@ namespace in_class_template {
};
template<typename T> void f() {
typename T::template A<int> a; // expected-error {{template name refers to non-type template 'S::A'}}
typename T::template A<int> a; // expected-error {{template name refers to non-type template 'S::template A'}}
}
template<typename T> void g() {
T::template A<int>::B = 0; // expected-error {{template name refers to non-type template 'S::A'}}
T::template A<int>::B = 0; // expected-error {{template name refers to non-type template 'S::template A'}}
}
template<typename T> void h() {
class T::template A<int> c; // expected-error {{template name refers to non-type template 'S::A'}}
class T::template A<int> c; // expected-error {{template name refers to non-type template 'S::template A'}}
}
template<typename T>
struct X : T::template A<int> {}; // expected-error {{template name refers to non-type template 'S::A'}}
struct X : T::template A<int> {}; // expected-error {{template name refers to non-type template 'S::template A'}}
template void f<S>(); // expected-note {{in instantiation of}}
template void g<S>(); // expected-note {{in instantiation of}}

View File

@ -7,6 +7,6 @@ const template basic_istream<char>; // expected-error {{expected unqualified-id}
namespace S {}
template <class X> class Y {
void x() { S::template y<char>(1); } // expected-error {{does not refer to a template}} \
void x() { S::template y<char>(1); } // expected-error {{no member named 'y' in namespace 'S'}} \
// expected-error {{unqualified-id}}
};

View File

@ -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<NSMutableDictionaryBuilder>::type DependentTemplateFail1; // expected-note{{in instantiation of template class}}

View File

@ -55,7 +55,7 @@ namespace PR6031 {
struct NoDepBase {
int foo() {
class NoDepBase::Nested nested; // expected-error{{no class named 'Nested' in 'NoDepBase<T>'}}
typedef typename NoDepBase::template MemberTemplate<T>::type type; // expected-error{{'MemberTemplate' following the 'template' keyword does not refer to a template}} \
typedef typename NoDepBase::template MemberTemplate<T>::type type; // expected-error{{no member named 'MemberTemplate' in 'NoDepBase<T>'}} \
// FIXME: expected-error{{unqualified-id}}
return NoDepBase::a; // expected-error{{no member named 'a' in 'NoDepBase<T>'}}
}
@ -103,7 +103,7 @@ namespace PR6081 {
template< class X >
void f0(const X & k)
{
this->template f1<int>()(k); // expected-error{{'f1' following the 'template' keyword does not refer to a template}} \
this->template f1<int>()(k); // expected-error{{no member named 'f1' in 'C<T>'}} \
// FIXME: expected-error{{unqualified-id}} \
// expected-error{{function-style cast or type construction}} \
// expected-error{{expected expression}}

View File

@ -15,7 +15,7 @@ struct add_reference {
};
struct bogus {
struct apply {
struct apply { // expected-note{{declared as a non-template here}}
typedef int type;
};
};

View File

@ -49,7 +49,7 @@ namespace N {
struct X;
};
struct B;
struct B; // expected-note{{declared as a non-template here}}
}
struct ::N::A<int>::X {
@ -131,7 +131,7 @@ namespace PR9226 {
template<typename T, typename U>
struct Y {
typedef typename T::template f<U> type; // expected-error{{template name refers to non-type template 'X::f'}}
typedef typename T::template f<U> type; // expected-error{{template name refers to non-type template 'X::template f'}}
};
Y<X, int> yxi; // expected-note{{in instantiation of template class 'PR9226::Y<PR9226::X, int>' requested here}}
@ -144,7 +144,7 @@ namespace PR9449 {
template <typename T>
void f() {
int s<T>::template n<T>::* f; // expected-error{{implicit instantiation of undefined template 'PR9449::s<int>'}} \
// expected-error{{following the 'template' keyword}}
// expected-error{{no member named 'n'}}
}
template void f<int>(); // expected-note{{in instantiation of}}

View File

@ -56,7 +56,7 @@ struct Y0 {
template<typename U>
static void f2(U);
void f3(int);
void f3(int); // expected-note 2{{declared as a non-template here}}
static int f4(int);
template<typename U>
@ -100,7 +100,7 @@ struct Y1 {
template<typename U>
static void f2(U);
void f3(int);
void f3(int); // expected-note 4{{declared as a non-template here}}
static int f4(int);
template<typename U>
@ -131,10 +131,39 @@ struct Y1 {
void use_Y1(Y1<int> y1) { y1.f<int>(); } // expected-note {{in instantiation of}}
template<typename T>
struct Y2 : Y1<T> {
typedef ::Y1<T> Y1;
template<typename U>
void f(Y1 *p) {
Y1::template f1<U>(0);
Y1::template f1(0);
p->template f1(0);
Y1::template f2<U>(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<int>(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<int>(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<int> y2) { y2.f<int>(0); } // expected-note {{in instantiation of}}
struct A {
template<int I>
struct B {
static void b1();
static void b1(); // expected-note {{declared as a non-template here}}
};
};

View File

@ -1,18 +1,45 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
// expected-no-diagnostics
using nullptr_t = decltype(nullptr);
template<typename T>
struct Base {
T inner;
};
int z;
template<typename T>
struct X {
template<typename U>
struct X : Base<T> {
static int z;
template<int U>
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<int> x) { x.f(0); }
template<typename T>
struct Y {
static int z;
template<int U>
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<other>::z; // expected-error {{no template named 'inner' in 'Y<T>'; did you mean 'Inner'?}}
}
};
struct Q { constexpr operator int() { return 0; } };
void use_y(Y<Q> x) { x.f(Q()); }