diff --git a/include/clang/AST/DeclFriend.h b/include/clang/AST/DeclFriend.h index 4b5e6fd48b..10befe0831 100644 --- a/include/clang/AST/DeclFriend.h +++ b/include/clang/AST/DeclFriend.h @@ -48,6 +48,11 @@ private: // Location of the 'friend' specifier. SourceLocation FriendLoc; + /// True if this 'friend' declaration is unsupported. Eventually we + /// will support every possible friend declaration, but for now we + /// silently ignore some and set this flag to authorize all access. + bool UnsupportedFriend; + friend class CXXRecordDecl::friend_iterator; friend class CXXRecordDecl; @@ -56,7 +61,8 @@ private: : Decl(Decl::Friend, DC, L), Friend(Friend), NextFriend(0), - FriendLoc(FriendL) { + FriendLoc(FriendL), + UnsupportedFriend(false) { } explicit FriendDecl(EmptyShell Empty) @@ -87,6 +93,14 @@ public: return FriendLoc; } + /// Determines if this friend kind is unsupported. + bool isUnsupportedFriend() const { + return UnsupportedFriend; + } + void setUnsupportedFriend(bool Unsupported) { + UnsupportedFriend = Unsupported; + } + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classof(const FriendDecl *D) { return true; } diff --git a/lib/Sema/SemaAccess.cpp b/lib/Sema/SemaAccess.cpp index ea6481bd8a..c3a1e75210 100644 --- a/lib/Sema/SemaAccess.cpp +++ b/lib/Sema/SemaAccess.cpp @@ -516,8 +516,9 @@ static AccessResult MatchesFriend(Sema &S, static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC, FriendDecl *FriendD) { - // Whitelist accesses if there's an invalid friend declaration. - if (FriendD->isInvalidDecl()) + // Whitelist accesses if there's an invalid or unsupported friend + // declaration. + if (FriendD->isInvalidDecl() || FriendD->isUnsupportedFriend()) return AR_accessible; if (TypeSourceInfo *T = FriendD->getFriendType()) diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index e7714521c8..d11819461e 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -3410,11 +3410,11 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, = MatchTemplateParametersToScopeSpecifier( D.getDeclSpec().getSourceRange().getBegin(), D.getCXXScopeSpec(), - (TemplateParameterList**)TemplateParamLists.get(), - TemplateParamLists.size(), - isFriend, - isExplicitSpecialization, - Invalid)) { + TemplateParamLists.get(), + TemplateParamLists.size(), + isFriend, + isExplicitSpecialization, + Invalid)) { // All but one template parameter lists have been matching. --NumMatchedTemplateParamLists; @@ -3462,7 +3462,7 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, if (NumMatchedTemplateParamLists > 0 && D.getCXXScopeSpec().isSet()) { NewFD->setTemplateParameterListsInfo(Context, NumMatchedTemplateParamLists, - (TemplateParameterList**)TemplateParamLists.release()); + TemplateParamLists.release()); } if (Invalid) { @@ -3732,14 +3732,20 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, // Qualified decls generally require a previous declaration. if (D.getCXXScopeSpec().isSet()) { - // ...with the major exception of dependent friend declarations. - // In theory, this condition could be whether the qualifier - // is dependent; in practice, the way we nest template parameters - // prevents this sort of matching from working, so we have to base it - // on the general dependence of the context. - if (isFriend && CurContext->isDependentContext()) { - // ignore these + // ...with the major exception of templated-scope or + // dependent-scope friend declarations. + // TODO: we currently also suppress this check in dependent + // contexts because (1) the parameter depth will be off when + // matching friend templates and (2) we might actually be + // selecting a friend based on a dependent factor. But there + // are situations where these conditions don't apply and we + // can actually do this check immediately. + if (isFriend && + (NumMatchedTemplateParamLists || + D.getCXXScopeSpec().getScopeRep()->isDependent() || + CurContext->isDependentContext())) { + // ignore these } else { // The user tried to provide an out-of-line definition for a // function that is a member of a class or namespace, but there diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index be1ee358e2..583f7c59b4 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -6532,6 +6532,17 @@ Decl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, bool IsDefinition, if (ND->isInvalidDecl()) FrD->setInvalidDecl(); + else { + FunctionDecl *FD; + if (FunctionTemplateDecl *FTD = dyn_cast(ND)) + FD = FTD->getTemplatedDecl(); + else + FD = cast(ND); + + // Mark templated-scope function declarations as unsupported. + if (FD->getNumTemplateParameterLists()) + FrD->setUnsupportedFriend(true); + } return ND; } diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index f85c3f0070..66ded48c24 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -1375,13 +1375,8 @@ Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc, // If there were at least as many template-ids as there were template // parameter lists, then there are no template parameter lists remaining for // the declaration itself. - if (Idx >= NumParamLists) { - // Silently drop template member friend declarations. - // TODO: implement these - if (IsFriend && NumParamLists) Invalid = true; - + if (Idx >= NumParamLists) return 0; - } // If there were too many template parameter lists, complain about that now. if (Idx != NumParamLists - 1) { @@ -1410,11 +1405,6 @@ Sema::MatchTemplateParametersToScopeSpecifier(SourceLocation DeclStartLoc, } } - // Silently drop template member template friend declarations. - // TODO: implement these - if (IsFriend && NumParamLists > 1) - Invalid = true; - // Return the last template parameter list, which corresponds to the // entity being declared. return ParamLists[NumParamLists - 1]; diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index c89273567e..05ade63719 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -908,6 +908,7 @@ void ASTDeclReader::VisitFriendDecl(FriendDecl *D) { else D->Friend = cast(Reader.GetDecl(Record[Idx++])); D->NextFriend = cast_or_null(Reader.GetDecl(Record[Idx++])); + D->UnsupportedFriend = (Record[Idx++] != 0); D->FriendLoc = ReadSourceLocation(Record, Idx); } diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 0dcc822fbc..bf7c259635 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -835,6 +835,7 @@ void ASTDeclWriter::VisitFriendDecl(FriendDecl *D) { else Writer.AddDeclRef(D->Friend.get(), Record); Writer.AddDeclRef(D->NextFriend, Record); + Record.push_back(D->UnsupportedFriend); Writer.AddSourceLocation(D->FriendLoc, Record); Code = serialization::DECL_FRIEND; } diff --git a/test/CodeGenCXX/template-instantiation.cpp b/test/CodeGenCXX/template-instantiation.cpp index 47ff16b8a6..a8729035e2 100644 --- a/test/CodeGenCXX/template-instantiation.cpp +++ b/test/CodeGenCXX/template-instantiation.cpp @@ -92,3 +92,20 @@ namespace test3 { // don't have key functions. template void S::m(); } + +namespace test4 { + template struct A { static void foo(); }; + + class B { + template friend void A::foo(); + B(); + }; + + template void A::foo() { + B b; + } + + unsigned test() { + A::foo(); + } +}