diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 6021cf76a3aa..c0def1215638 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -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, diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 8fed447bc262..ce227c6ff238 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -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. diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 315bc4d804c7..2e50227bf4a5 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -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(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; } diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index e37326ac6412..cc13e8f5a360 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -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(T)) { + DependentNameTypeLoc TL = cast(TSI->getTypeLoc()); + TL.setKeywordLoc(TagLoc); + TL.setQualifierRange(SS.getRange()); + TL.setNameLoc(NameLoc); + } else { + ElaboratedTypeLoc TL = cast(TSI->getTypeLoc()); + TL.setKeywordLoc(TagLoc); + TL.setQualifierRange(SS.getRange()); + cast(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 A::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(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/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 0b4b5101b65c..57ea18809d99 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -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(NewND), D->getFriendLoc()); FD->setAccess(AS_public); + FD->setUnsupportedFriend(D->isUnsupportedFriend()); Owner->addDecl(FD); return FD; } diff --git a/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp b/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp index 82c2b3169d4e..ceefc055838b 100644 --- a/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp +++ b/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp @@ -56,3 +56,25 @@ namespace test2 { void f() { C::foo(); } }; } + +// rdar://problem/8540527 +namespace test3 { + template struct A { + struct Inner { + static int foo(); + }; + }; + + template class C { + int i; + template friend struct A::Inner; + }; + + template int A::Inner::foo() { + C c; + c.i = 0; + return 0; + } + + int test = A::Inner::foo(); +}