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<
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,
<< 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();
// 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))
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();
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;
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,
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;
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();
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.
// 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
OpLoc, S.Context.getObjCClassType());
if (ShouldTryAgainWithRedefinitionType(S, BaseExpr))
return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS,
- ObjCImpDecl, HasTemplateArgs);
+ ObjCImpDecl, HasTemplateArgs, TemplateKWLoc);
goto fail;
}
// 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);
if (!MD) {
if (ShouldTryAgainWithRedefinitionType(S, BaseExpr))
return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS,
- ObjCImpDecl, HasTemplateArgs);
+ ObjCImpDecl, HasTemplateArgs, TemplateKWLoc);
goto fail;
}
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);
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.
// Recurse as an -> access.
IsArrow = true;
return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS,
- ObjCImpDecl, HasTemplateArgs);
+ ObjCImpDecl, HasTemplateArgs, TemplateKWLoc);
}
}
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)
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.
// 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()) {
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;
// 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();
}
}
+ 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 &&
}
}
}
+
+ return false;
}
void Sema::diagnoseExprIntendedAsTemplateName(Scope *S, ExprResult TemplateName,
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();
}
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.
QualType RebuildDependentTemplateSpecializationType(
ElaboratedTypeKeyword Keyword,
NestedNameSpecifierLoc QualifierLoc,
+ SourceLocation TemplateKWLoc,
const IdentifierInfo *Name,
SourceLocation NameLoc,
TemplateArgumentListInfo &Args,
// 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();
/// 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);
/// 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
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,
AllowInjectedClassName);
}
- return getDerived().RebuildTemplateName(SS, DTN->getOperator(), NameLoc,
+ return getDerived().RebuildTemplateName(SS, TemplateKWLoc,
+ DTN->getOperator(), NameLoc,
ObjectType, AllowInjectedClassName);
}
TemplateName Template
= getDerived().RebuildTemplateName(SS,
+ SpecTL.getTemplateKeywordLoc(),
*SpecTL.getTypePtr()->getIdentifier(),
SpecTL.getTemplateNameLoc(),
ObjectType, UnqualLookup,
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();
template<typename Derived>
TemplateName
TreeTransform<Derived>::RebuildTemplateName(CXXScopeSpec &SS,
+ SourceLocation TemplateKWLoc,
const IdentifierInfo &Name,
SourceLocation NameLoc,
QualType ObjectType,
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),
template<typename Derived>
TemplateName
TreeTransform<Derived>::RebuildTemplateName(CXXScopeSpec &SS,
+ SourceLocation TemplateKWLoc,
OverloadedOperatorKind Operator,
SourceLocation NameLoc,
QualType ObjectType,
// 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,
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 '('}}
};
}
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>();
}
};
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}}
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}}
};
};
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}}
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>'}}
}
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}}
};
struct bogus {
- struct apply {
+ struct apply { // expected-note{{declared as a non-template here}}
typedef int type;
};
};
struct X;
};
- struct B;
+ struct B; // expected-note{{declared as a non-template here}}
}
struct ::N::A<int>::X {
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}}
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}}
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>
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>
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}}
};
};
// 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()); }