mirror of https://github.com/microsoft/clang.git
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:
parent
57890d00f8
commit
d00b55fa4e
|
@ -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<
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 '('}}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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>();
|
||||
}
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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}}
|
||||
};
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -15,7 +15,7 @@ struct add_reference {
|
|||
};
|
||||
|
||||
struct bogus {
|
||||
struct apply {
|
||||
struct apply { // expected-note{{declared as a non-template here}}
|
||||
typedef int type;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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}}
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -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()); }
|
||||
|
|
Loading…
Reference in New Issue