diff --git a/include/clang/AST/DeclNodes.def b/include/clang/AST/DeclNodes.def index 61df9d052f..c3a97f9a11 100644 --- a/include/clang/AST/DeclNodes.def +++ b/include/clang/AST/DeclNodes.def @@ -127,6 +127,7 @@ DECL(ObjCForwardProtocol, Decl) DECL(ObjCClass, Decl) DECL(FileScopeAsm, Decl) DECL(Friend, Decl) +DECL(FriendTemplate, Decl) DECL(StaticAssert, Decl) LAST_DECL(Block, Decl) diff --git a/include/clang/AST/DeclTemplate.h b/include/clang/AST/DeclTemplate.h index 4ff9eb1db4..fb33758a2e 100644 --- a/include/clang/AST/DeclTemplate.h +++ b/include/clang/AST/DeclTemplate.h @@ -1227,6 +1227,85 @@ public: virtual void Destroy(ASTContext& C); }; +/// Declaration of a friend template. For example: +/// +/// template class A { +/// friend class MyVector; // not a friend template +/// template friend class B; // friend template +/// template friend class Foo::Nested; // friend template +class FriendTemplateDecl : public Decl { +public: + typedef llvm::PointerUnion FriendUnion; + +private: + // The number of template parameters; always non-zero. + unsigned NumParams; + + // The parameter list. + TemplateParameterList **Params; + + // The declaration that's a friend of this class. + FriendUnion Friend; + + // Location of the 'friend' specifier. + SourceLocation FriendLoc; + + + FriendTemplateDecl(DeclContext *DC, SourceLocation Loc, + unsigned NParams, + TemplateParameterList **Params, + FriendUnion Friend, + SourceLocation FriendLoc) + : Decl(Decl::FriendTemplate, DC, Loc), + NumParams(NParams), + Params(Params), + Friend(Friend), + FriendLoc(FriendLoc) + {} + +public: + static FriendTemplateDecl *Create(ASTContext &Context, + DeclContext *DC, SourceLocation Loc, + unsigned NParams, + TemplateParameterList **Params, + FriendUnion Friend, + SourceLocation FriendLoc); + + /// If this friend declaration names a templated type (or + /// a dependent member type of a templated type), return that + /// type; otherwise return null. + Type *getFriendType() const { + return Friend.dyn_cast(); + } + + /// If this friend declaration names a templated function (or + /// a member function of a templated type), return that type; + /// otherwise return null. + NamedDecl *getFriendDecl() const { + return Friend.dyn_cast(); + } + + /// Retrieves the location of the 'friend' keyword. + SourceLocation getFriendLoc() const { + return FriendLoc; + } + + TemplateParameterList *getTemplateParameterList(unsigned i) const { + assert(i <= NumParams); + return Params[i]; + } + + unsigned getNumTemplateParameters() const { + return NumParams; + } + + // Implement isa/cast/dyncast/etc. + static bool classof(const Decl *D) { + return D->getKind() == Decl::FriendTemplate; + } + static bool classof(const FriendTemplateDecl *D) { return true; } +}; + /// Implementation of inline functions that require the template declarations inline AnyFunctionDecl::AnyFunctionDecl(FunctionTemplateDecl *FTD) : Function(FTD) { } diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index b14317eb08..1ca9a1fb1b 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -324,6 +324,8 @@ def err_qualified_friend_not_found : Error< def err_introducing_special_friend : Error< "must use a qualified name when declaring a %select{constructor|" "destructor|conversion operator}0 as a friend">; +def err_tagless_friend_type_template : Error< + "friend type templates must use an elaborated type">; def err_abstract_type_in_decl : Error< "%select{return|parameter|variable|field}0 type %1 is an abstract class">; diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index f15398465b..627ed20055 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -1215,7 +1215,7 @@ public: /// ActOnFriendTypeDecl - Parsed a friend type declaration. virtual DeclPtrTy ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS, - bool IsTemplate) { + MultiTemplateParamsArg TParams) { return DeclPtrTy(); } diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index 8d16139fcd..5f639d807d 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -230,6 +230,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { // Never have names. case Friend: + case FriendTemplate: case LinkageSpec: case FileScopeAsm: case StaticAssert: diff --git a/lib/AST/DeclTemplate.cpp b/lib/AST/DeclTemplate.cpp index e01cbed20f..bd1aa7ca9e 100644 --- a/lib/AST/DeclTemplate.cpp +++ b/lib/AST/DeclTemplate.cpp @@ -463,3 +463,19 @@ Create(ASTContext &Context, DeclContext *DC, SourceLocation L, Context.getTypeDeclType(Result, PrevDecl); return Result; } + +//===----------------------------------------------------------------------===// +// FriendTemplateDecl Implementation +//===----------------------------------------------------------------------===// + +FriendTemplateDecl *FriendTemplateDecl::Create(ASTContext &Context, + DeclContext *DC, + SourceLocation L, + unsigned NParams, + TemplateParameterList **Params, + FriendUnion Friend, + SourceLocation FLoc) { + FriendTemplateDecl *Result + = new (Context) FriendTemplateDecl(DC, L, NParams, Params, Friend, FLoc); + return Result; +} diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index e1228cf1fd..671a5421cc 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -1011,12 +1011,15 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, DeclSpec DS; ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC_class); + Action::MultiTemplateParamsArg TemplateParams(Actions, + TemplateInfo.TemplateParams? TemplateInfo.TemplateParams->data() : 0, + TemplateInfo.TemplateParams? TemplateInfo.TemplateParams->size() : 0); + if (Tok.is(tok::semi)) { ConsumeToken(); if (DS.isFriendSpecified()) { - bool IsTemplate = TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate; - Actions.ActOnFriendTypeDecl(CurScope, DS, IsTemplate); + Actions.ActOnFriendTypeDecl(CurScope, DS, move(TemplateParams)); } else Actions.ParsedFreeStandingDeclSpec(CurScope, DS); @@ -1119,10 +1122,6 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, // this call will *not* return the created decl; It will return null. // See Sema::ActOnCXXMemberDeclarator for details. - Action::MultiTemplateParamsArg TemplateParams(Actions, - TemplateInfo.TemplateParams? TemplateInfo.TemplateParams->data() : 0, - TemplateInfo.TemplateParams? TemplateInfo.TemplateParams->size() : 0); - DeclPtrTy ThisDecl; if (DS.isFriendSpecified()) { // TODO: handle initializers, bitfields, 'delete' diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index c078d43152..802dab29d0 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -2248,7 +2248,8 @@ public: ExprArg AssertExpr, ExprArg AssertMessageExpr); - DeclPtrTy ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS, bool IsTemplate); + DeclPtrTy ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS, + MultiTemplateParamsArg TemplateParams); DeclPtrTy ActOnFriendFunctionDecl(Scope *S, Declarator &D, bool IsDefinition, MultiTemplateParamsArg TemplateParams); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 696c10a941..9f86f1233f 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -3957,7 +3957,11 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, = MatchTemplateParametersToScopeSpecifier(KWLoc, SS, (TemplateParameterList**)TemplateParameterLists.get(), TemplateParameterLists.size())) { - if (TemplateParams->size() > 0) { + if (TUK == TUK_Friend) { + // When declaring a friend template, we do want to match the + // template parameters to the scope specifier, but don't go so far + // as to try to declare a new template. + } else if (TemplateParams->size() > 0) { // This is a declaration or definition of a class template (which may // be a member of another template). OwnedDecl = false; diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index a971b8a281..6135368039 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -4023,41 +4023,58 @@ Sema::DeclPtrTy Sema::ActOnStaticAssertDeclaration(SourceLocation AssertLoc, return DeclPtrTy::make(Decl); } +/// Handle a friend type declaration. This works in tandem with +/// ActOnTag. +/// +/// Notes on friend class templates: +/// +/// We generally treat friend class declarations as if they were +/// declaring a class. So, for example, the elaborated type specifier +/// in a friend declaration is required to obey the restrictions of a +/// class-head (i.e. no typedefs in the scope chain), template +/// parameters are required to match up with simple template-ids, &c. +/// However, unlike when declaring a template specialization, it's +/// okay to refer to a template specialization without an empty +/// template parameter declaration, e.g. +/// friend class A::B; +/// We permit this as a special case; if there are any template +/// parameters present at all, require proper matching, i.e. +/// template <> template friend class A::B; Sema::DeclPtrTy Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS, - bool IsTemplate) { + MultiTemplateParamsArg TempParams) { SourceLocation Loc = DS.getSourceRange().getBegin(); assert(DS.isFriendSpecified()); assert(DS.getStorageClassSpec() == DeclSpec::SCS_unspecified); - // Handle friend templates specially. - if (IsTemplate) { - Decl *D; - switch (DS.getTypeSpecType()) { - default: - // FIXME: implement this - assert(false && "unelaborated type templates are currently unimplemented!"); - case DeclSpec::TST_class: - case DeclSpec::TST_union: - case DeclSpec::TST_struct: - D = (Decl*) DS.getTypeRep(); - } - - ClassTemplateDecl *Temp = cast(D); - FriendDecl *FD = FriendDecl::Create(Context, CurContext, Loc, Temp, - DS.getFriendSpecLoc()); - FD->setAccess(AS_public); - CurContext->addDecl(FD); - - return DeclPtrTy::make(FD); - } - - // Try to convert the decl specifier to a type. + // Try to convert the decl specifier to a type. This works for + // friend templates because ActOnTag never produces a ClassTemplateDecl + // for a TUK_Friend. bool invalid = false; QualType T = ConvertDeclSpecToType(DS, Loc, invalid); if (invalid) return DeclPtrTy(); + // This is definitely an error in C++98. It's probably meant to + // be forbidden in C++0x, too, but the specification is just + // poorly written. + // + // The problem is with declarations like the following: + // template friend A::foo; + // where deciding whether a class C is a friend or not now hinges + // on whether there exists an instantiation of A that causes + // 'foo' to equal C. There are restrictions on class-heads + // (which we declare (by fiat) elaborated friend declarations to + // be) that makes this tractable. + // + // FIXME: handle "template <> friend class A;", which + // is possibly well-formed? Who even knows? + if (TempParams.size() && !isa(T)) { + Diag(Loc, diag::err_tagless_friend_type_template) + << DS.getSourceRange(); + return DeclPtrTy(); + } + // C++ [class.friend]p2: // An elaborated-type-specifier shall be used in a friend declaration // for a class.* @@ -4085,7 +4102,6 @@ Sema::DeclPtrTy Sema::ActOnFriendTypeDecl(Scope *S, } bool IsDefinition = false; - FriendDecl::FriendUnion FU = T.getTypePtr(); // We want to do a few things differently if the type was declared with // a tag: specifically, we want to use the associated RecordDecl as @@ -4097,10 +4113,8 @@ Sema::DeclPtrTy Sema::ActOnFriendTypeDecl(Scope *S, case DeclSpec::TST_struct: case DeclSpec::TST_union: CXXRecordDecl *RD = cast_or_null((Decl*) DS.getTypeRep()); - if (RD) { + if (RD) IsDefinition |= RD->isDefinition(); - FU = RD; - } break; } @@ -4122,12 +4136,20 @@ Sema::DeclPtrTy Sema::ActOnFriendTypeDecl(Scope *S, if (RT->getDecl()->getDeclContext() == CurContext) Diag(DS.getFriendSpecLoc(), diag::ext_friend_inner_class); - FriendDecl *FD = FriendDecl::Create(Context, CurContext, Loc, FU, - DS.getFriendSpecLoc()); - FD->setAccess(AS_public); - CurContext->addDecl(FD); + Decl *D; + if (TempParams.size()) + D = FriendTemplateDecl::Create(Context, CurContext, Loc, + TempParams.size(), + (TemplateParameterList**) TempParams.release(), + T.getTypePtr(), + DS.getFriendSpecLoc()); + else + D = FriendDecl::Create(Context, CurContext, Loc, T.getTypePtr(), + DS.getFriendSpecLoc()); + D->setAccess(AS_public); + CurContext->addDecl(D); - return DeclPtrTy::make(FD); + return DeclPtrTy::make(D); } Sema::DeclPtrTy diff --git a/test/CXX/temp/temp.decls/temp.friend/p3.cpp b/test/CXX/temp/temp.decls/temp.friend/p3.cpp new file mode 100644 index 0000000000..dd856e9cc3 --- /dev/null +++ b/test/CXX/temp/temp.decls/temp.friend/p3.cpp @@ -0,0 +1,11 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +template class A { + typedef int Member; +}; + +class B { + template friend class A; + template friend class Undeclared; + template friend typename A::Member; // expected-error {{friend type templates must use an elaborated type}} +}; diff --git a/test/CXX/temp/temp.decls/temp.friend/p5.cpp b/test/CXX/temp/temp.decls/temp.friend/p5.cpp index 2c4a7c9371..f1142a4129 100644 --- a/test/CXX/temp/temp.decls/temp.friend/p5.cpp +++ b/test/CXX/temp/temp.decls/temp.friend/p5.cpp @@ -1,6 +1,11 @@ // RUN: clang-cc -fsyntax-only -verify %s -class A { - template friend class B; +template class A { + class Member { + }; +}; + +class B { + template friend class A::Member; };