Redirect templated friend class decls to a new Sema callback and
construct an unsupported friend when there's a friend with a templated
scope specifier. Fixes a consistency crash, rdar://problem/8540527
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@116786 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 315bc4d..2e50227 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -1623,9 +1623,9 @@
}
if (DS.isFriendSpecified()) {
- // If we're dealing with a class template decl, assume that the
- // template routines are handling it.
- if (TagD && isa<ClassTemplateDecl>(TagD))
+ // If we're dealing with a decl but not a TagDecl, assume that
+ // whatever routines created it handled the friendship aspect.
+ if (TagD && !Tag)
return 0;
return ActOnFriendTypeDecl(S, DS, MultiTemplateParamsArg(*this, 0, 0));
}
@@ -2797,8 +2797,8 @@
= MatchTemplateParametersToScopeSpecifier(
D.getDeclSpec().getSourceRange().getBegin(),
D.getCXXScopeSpec(),
- (TemplateParameterList**)TemplateParamLists.get(),
- TemplateParamLists.size(),
+ TemplateParamLists.get(),
+ TemplateParamLists.size(),
/*never a friend*/ false,
isExplicitSpecialization,
Invalid)) {
@@ -2836,7 +2836,7 @@
if (NumMatchedTemplateParamLists > 0 && D.getCXXScopeSpec().isSet()) {
NewVD->setTemplateParameterListsInfo(Context,
NumMatchedTemplateParamLists,
- (TemplateParameterList**)TemplateParamLists.release());
+ TemplateParamLists.release());
}
if (D.getDeclSpec().isThreadSpecified()) {
@@ -5483,6 +5483,7 @@
// If this is not a definition, it must have a name.
assert((Name != 0 || TUK == TUK_Definition) &&
"Nameless record must be a definition!");
+ assert(TemplateParameterLists.size() == 0 || TUK != TUK_Reference);
OwnedDecl = false;
TagTypeKind Kind = TypeWithKeyword::getTagTypeKindForTypeSpec(TagSpec);
@@ -5491,7 +5492,12 @@
bool isExplicitSpecialization = false;
unsigned NumMatchedTemplateParamLists = TemplateParameterLists.size();
bool Invalid = false;
- if (TUK != TUK_Reference) {
+
+ // We only need to do this matching if we have template parameters
+ // or a scope specifier, which also conveniently avoids this work
+ // for non-C++ cases.
+ if (NumMatchedTemplateParamLists ||
+ (SS.isNotEmpty() && TUK != TUK_Reference)) {
if (TemplateParameterList *TemplateParams
= MatchTemplateParametersToScopeSpecifier(KWLoc, SS,
TemplateParameterLists.get(),
@@ -5606,7 +5612,9 @@
// and that current instantiation has any dependent base
// classes, we might find something at instantiation time: treat
// this as a dependent elaborated-type-specifier.
- if (Previous.wasNotFoundInCurrentInstantiation()) {
+ // But this only makes any sense for reference-like lookups.
+ if (Previous.wasNotFoundInCurrentInstantiation() &&
+ (TUK == TUK_Reference || TUK == TUK_Friend)) {
IsDependent = true;
return 0;
}
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index e37326a..cc13e8f 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -6244,6 +6244,110 @@
return FriendDecl::Create(Context, CurContext, FriendLoc, TSInfo, FriendLoc);
}
+/// Handle a friend tag declaration where the scope specifier was
+/// templated.
+Decl *Sema::ActOnTemplatedFriendTag(Scope *S, SourceLocation FriendLoc,
+ unsigned TagSpec, SourceLocation TagLoc,
+ CXXScopeSpec &SS,
+ IdentifierInfo *Name, SourceLocation NameLoc,
+ AttributeList *Attr,
+ MultiTemplateParamsArg TempParamLists) {
+ TagTypeKind Kind = TypeWithKeyword::getTagTypeKindForTypeSpec(TagSpec);
+
+ bool isExplicitSpecialization = false;
+ unsigned NumMatchedTemplateParamLists = TempParamLists.size();
+ bool Invalid = false;
+
+ if (TemplateParameterList *TemplateParams
+ = MatchTemplateParametersToScopeSpecifier(TagLoc, SS,
+ TempParamLists.get(),
+ TempParamLists.size(),
+ /*friend*/ true,
+ isExplicitSpecialization,
+ Invalid)) {
+ --NumMatchedTemplateParamLists;
+
+ if (TemplateParams->size() > 0) {
+ // This is a declaration of a class template.
+ if (Invalid)
+ return 0;
+
+ return CheckClassTemplate(S, TagSpec, TUK_Friend, TagLoc,
+ SS, Name, NameLoc, Attr,
+ TemplateParams, AS_public).take();
+ } else {
+ // The "template<>" header is extraneous.
+ Diag(TemplateParams->getTemplateLoc(), diag::err_template_tag_noparams)
+ << TypeWithKeyword::getTagTypeKindName(Kind) << Name;
+ isExplicitSpecialization = true;
+ }
+ }
+
+ if (Invalid) return 0;
+
+ assert(SS.isNotEmpty() && "valid templated tag with no SS and no direct?");
+
+ bool isAllExplicitSpecializations = true;
+ for (unsigned I = 0; I != NumMatchedTemplateParamLists; ++I) {
+ if (TempParamLists.get()[I]->size()) {
+ isAllExplicitSpecializations = false;
+ break;
+ }
+ }
+
+ // FIXME: don't ignore attributes.
+
+ // If it's explicit specializations all the way down, just forget
+ // about the template header and build an appropriate non-templated
+ // friend. TODO: for source fidelity, remember the headers.
+ if (isAllExplicitSpecializations) {
+ ElaboratedTypeKeyword Keyword
+ = TypeWithKeyword::getKeywordForTagTypeKind(Kind);
+ QualType T = CheckTypenameType(Keyword, SS.getScopeRep(), *Name,
+ TagLoc, SS.getRange(), NameLoc);
+ if (T.isNull())
+ return 0;
+
+ TypeSourceInfo *TSI = Context.CreateTypeSourceInfo(T);
+ if (isa<DependentNameType>(T)) {
+ DependentNameTypeLoc TL = cast<DependentNameTypeLoc>(TSI->getTypeLoc());
+ TL.setKeywordLoc(TagLoc);
+ TL.setQualifierRange(SS.getRange());
+ TL.setNameLoc(NameLoc);
+ } else {
+ ElaboratedTypeLoc TL = cast<ElaboratedTypeLoc>(TSI->getTypeLoc());
+ TL.setKeywordLoc(TagLoc);
+ TL.setQualifierRange(SS.getRange());
+ cast<TypeSpecTypeLoc>(TL.getNamedTypeLoc()).setNameLoc(NameLoc);
+ }
+
+ FriendDecl *Friend = FriendDecl::Create(Context, CurContext, NameLoc,
+ TSI, FriendLoc);
+ Friend->setAccess(AS_public);
+ CurContext->addDecl(Friend);
+ return Friend;
+ }
+
+ // Handle the case of a templated-scope friend class. e.g.
+ // template <class T> class A<T>::B;
+ // FIXME: we don't support these right now.
+ ElaboratedTypeKeyword ETK = TypeWithKeyword::getKeywordForTagTypeKind(Kind);
+ QualType T = Context.getDependentNameType(ETK, SS.getScopeRep(), Name);
+ TypeSourceInfo *TSI = Context.CreateTypeSourceInfo(T);
+ DependentNameTypeLoc TL = cast<DependentNameTypeLoc>(TSI->getTypeLoc());
+ TL.setKeywordLoc(TagLoc);
+ TL.setQualifierRange(SS.getRange());
+ TL.setNameLoc(NameLoc);
+
+ FriendDecl *Friend = FriendDecl::Create(Context, CurContext, NameLoc,
+ TSI, FriendLoc);
+ Friend->setAccess(AS_public);
+ Friend->setUnsupportedFriend(true);
+ CurContext->addDecl(Friend);
+ return Friend;
+}
+
+
/// Handle a friend type declaration. This works in tandem with
/// ActOnTag.
///
diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 0b4b510..57ea188 100644
--- a/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -550,6 +550,7 @@
return 0;
FD->setAccess(AS_public);
+ FD->setUnsupportedFriend(D->isUnsupportedFriend());
Owner->addDecl(FD);
return FD;
}
@@ -568,6 +569,7 @@
FriendDecl::Create(SemaRef.Context, Owner, D->getLocation(),
cast<NamedDecl>(NewND), D->getFriendLoc());
FD->setAccess(AS_public);
+ FD->setUnsupportedFriend(D->isUnsupportedFriend());
Owner->addDecl(FD);
return FD;
}