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

llvm-svn: 116786
This commit is contained in:
John McCall 2010-10-19 01:40:49 +00:00
parent 898fbd82a5
commit ace48cd872
6 changed files with 165 additions and 11 deletions

View File

@ -799,6 +799,13 @@ public:
bool &OwnedDecl, bool &IsDependent, bool ScopedEnum,
TypeResult UnderlyingType);
Decl *ActOnTemplatedFriendTag(Scope *S, SourceLocation FriendLoc,
unsigned TagSpec, SourceLocation TagLoc,
CXXScopeSpec &SS,
IdentifierInfo *Name, SourceLocation NameLoc,
AttributeList *Attr,
MultiTemplateParamsArg TempParamLists);
TypeResult ActOnDependentTag(Scope *S,
unsigned TagSpec,
TagUseKind TUK,

View File

@ -947,6 +947,15 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
TemplateInfo.TemplateLoc,
TagType, StartLoc, SS, Name,
NameLoc, AttrList);
} else if (TUK == Sema::TUK_Friend &&
TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate) {
TagOrTempResult =
Actions.ActOnTemplatedFriendTag(getCurScope(), DS.getFriendSpecLoc(),
TagType, StartLoc, SS,
Name, NameLoc, AttrList,
MultiTemplateParamsArg(Actions,
TemplateParams? &(*TemplateParams)[0] : 0,
TemplateParams? TemplateParams->size() : 0));
} else {
if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation &&
TUK == Sema::TUK_Definition) {
@ -956,8 +965,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
bool IsDependent = false;
// Declaration or definition of a class type
TagOrTempResult = Actions.ActOnTag(getCurScope(), TagType, TUK, StartLoc, SS,
Name, NameLoc, AttrList, AS,
TagOrTempResult = Actions.ActOnTag(getCurScope(), TagType, TUK, StartLoc,
SS, Name, NameLoc, AttrList, AS,
MultiTemplateParamsArg(Actions,
TemplateParams? &(*TemplateParams)[0] : 0,
TemplateParams? TemplateParams->size() : 0),
@ -966,9 +975,11 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
// If ActOnTag said the type was dependent, try again with the
// less common call.
if (IsDependent)
if (IsDependent) {
assert(TUK == Sema::TUK_Reference || TUK == Sema::TUK_Friend);
TypeResult = Actions.ActOnDependentTag(getCurScope(), TagType, TUK,
SS, Name, StartLoc, NameLoc);
}
}
// If there is a body, parse it and inform the actions module.

View File

@ -1623,9 +1623,9 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS,
}
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 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC,
= 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 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC,
if (NumMatchedTemplateParamLists > 0 && D.getCXXScopeSpec().isSet()) {
NewVD->setTemplateParameterListsInfo(Context,
NumMatchedTemplateParamLists,
(TemplateParameterList**)TemplateParamLists.release());
TemplateParamLists.release());
}
if (D.getDeclSpec().isThreadSpecified()) {
@ -5483,6 +5483,7 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
// 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 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
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 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
// 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;
}

View File

@ -6244,6 +6244,110 @@ FriendDecl *Sema::CheckFriendTypeDecl(SourceLocation FriendLoc,
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.
///

View File

@ -550,6 +550,7 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
return 0;
FD->setAccess(AS_public);
FD->setUnsupportedFriend(D->isUnsupportedFriend());
Owner->addDecl(FD);
return FD;
}
@ -568,6 +569,7 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
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;
}

View File

@ -56,3 +56,25 @@ namespace test2 {
void f() { C::foo(); }
};
}
// rdar://problem/8540527
namespace test3 {
template <class T> struct A {
struct Inner {
static int foo();
};
};
template <class U> class C {
int i;
template <class T> friend struct A<T>::Inner;
};
template <class T> int A<T>::Inner::foo() {
C<int> c;
c.i = 0;
return 0;
}
int test = A<int>::Inner::foo();
}