From: Douglas Gregor Date: Tue, 13 Oct 2009 14:39:41 +0000 (+0000) Subject: Improve the internal representation and semantic analysis of friend X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a735b206fdb5c15767a45289e1ffb3b568f70f2b;p=clang Improve the internal representation and semantic analysis of friend function templates. This commit ensures that friend function templates are constructed as FunctionTemplateDecls rather than partial FunctionDecls (as they previously were). It then implements template instantiation for friend function templates, injecting the friend function template only when no previous declaration exists at the time of instantiation. Oh, and make sure that explicit specialization declarations are not friends. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@83970 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index f15c028a53..051dd436bd 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -971,6 +971,8 @@ def err_specialization_after_instantiation : Error< "explicit specialization of %0 after instantiation">; def note_instantiation_required_here : Note< "%select{implicit|explicit}0 instantiation first required here">; +def err_template_spec_friend : Error< + "template specialization declaration cannot be a friend">; // C++ class template specializations and out-of-line definitions def err_template_spec_needs_header : Error< diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 998abb3403..69426ee755 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -2575,9 +2575,6 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, // lexical context will be different from the semantic context. NewFD->setLexicalDeclContext(CurContext); - if (isFriend) - NewFD->setObjectOfFriendDecl(/* PreviouslyDeclared= */ PrevDecl != NULL); - // Match up the template parameter lists with the scope specifier, then // determine whether we have a template or a template specialization. FunctionTemplateDecl *FunctionTemplate = 0; @@ -2640,6 +2637,19 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, } } + if (isFriend) { + if (FunctionTemplate) { + FunctionTemplate->setObjectOfFriendDecl( + /* PreviouslyDeclared= */ PrevDecl != NULL); + FunctionTemplate->setAccess(AS_public); + } + else + NewFD->setObjectOfFriendDecl(/* PreviouslyDeclared= */ PrevDecl != NULL); + + NewFD->setAccess(AS_public); + } + + if (CXXMethodDecl *NewMD = dyn_cast(NewFD)) { // Look for virtual methods in base classes that this method might override. CXXBasePaths Paths; diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index eb1abafb7d..519694aea8 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -4347,8 +4347,6 @@ Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, bool IsDefinition, MultiTemplateParamsArg TemplateParams) { - // FIXME: do something with template parameters - const DeclSpec &DS = D.getDeclSpec(); assert(DS.isFriendSpecified()); @@ -4404,6 +4402,7 @@ Sema::ActOnFriendFunctionDecl(Scope *S, // Recover from invalid scope qualifiers as if they just weren't there. NamedDecl *PrevDecl = 0; if (!ScopeQual.isInvalid() && ScopeQual.isSet()) { + // FIXME: RequireCompleteDeclContext DC = computeDeclContext(ScopeQual); // FIXME: handle dependent contexts @@ -4479,7 +4478,7 @@ Sema::ActOnFriendFunctionDecl(Scope *S, bool Redeclaration = false; NamedDecl *ND = ActOnFunctionDeclarator(S, D, DC, T, DInfo, PrevDecl, - MultiTemplateParamsArg(*this), + move(TemplateParams), IsDefinition, Redeclaration); if (!ND) return DeclPtrTy(); diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 9d39ddbe68..7412049857 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -2720,7 +2720,16 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, } } } - } else if (!TemplateParams && TUK != TUK_Friend) { + } else if (TemplateParams) { + if (TUK == TUK_Friend) + Diag(KWLoc, diag::err_template_spec_friend) + << CodeModificationHint::CreateRemoval( + SourceRange(TemplateParams->getTemplateLoc(), + TemplateParams->getRAngleLoc())) + << SourceRange(LAngleLoc, RAngleLoc); + else + isExplicitSpecialization = true; + } else if (TUK != TUK_Friend) { Diag(KWLoc, diag::err_template_spec_needs_header) << CodeModificationHint::CreateInsertion(KWLoc, "template<> "); isExplicitSpecialization = true; diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 4fd2a734b1..b03734346c 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -47,7 +47,8 @@ namespace { Decl *VisitEnumDecl(EnumDecl *D); Decl *VisitEnumConstantDecl(EnumConstantDecl *D); Decl *VisitFriendDecl(FriendDecl *D); - Decl *VisitFunctionDecl(FunctionDecl *D); + Decl *VisitFunctionDecl(FunctionDecl *D, + TemplateParameterList *TemplateParams = 0); Decl *VisitCXXRecordDecl(CXXRecordDecl *D); Decl *VisitCXXMethodDecl(CXXMethodDecl *D, TemplateParameterList *TemplateParams = 0); @@ -280,6 +281,9 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) { NamedDecl *ND = D->getFriendDecl(); assert(ND && "friend decl must be a decl or a type!"); + // FIXME: We have a problem here, because the nested call to Visit(ND) + // will inject the thing that the friend references into the current + // owner, which is wrong. Decl *NewND = Visit(ND); if (!NewND) return 0; @@ -423,24 +427,31 @@ TemplateDeclInstantiator::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) { if (!InstParams) return NULL; - // FIXME: Handle instantiation of nested function templates that aren't - // member function templates. This could happen inside a FriendDecl. - assert(isa(D->getTemplatedDecl())); - CXXMethodDecl *InstMethod - = cast_or_null( - VisitCXXMethodDecl(cast(D->getTemplatedDecl()), - InstParams)); - if (!InstMethod) + FunctionDecl *Instantiated = 0; + if (CXXMethodDecl *DMethod = dyn_cast(D->getTemplatedDecl())) + Instantiated = cast_or_null(VisitCXXMethodDecl(DMethod, + InstParams)); + else + Instantiated = cast_or_null(VisitFunctionDecl( + D->getTemplatedDecl(), + InstParams)); + + if (!Instantiated) return 0; // Link the instantiated function template declaration to the function // template from which it was instantiated. FunctionTemplateDecl *InstTemplate - = InstMethod->getDescribedFunctionTemplate(); + = Instantiated->getDescribedFunctionTemplate(); InstTemplate->setAccess(D->getAccess()); - assert(InstTemplate && "VisitCXXMethodDecl didn't create a template!"); - InstTemplate->setInstantiatedFromMemberTemplate(D); - Owner->addDecl(InstTemplate); + assert(InstTemplate && + "VisitFunctionDecl/CXXMethodDecl didn't create a template!"); + if (!InstTemplate->getInstantiatedFromMemberTemplate()) + InstTemplate->setInstantiatedFromMemberTemplate(D); + + // Add non-friends into the owner. + if (!InstTemplate->getFriendObjectKind()) + Owner->addDecl(InstTemplate); return InstTemplate; } @@ -478,12 +489,13 @@ Decl *TemplateDeclInstantiator::VisitCXXRecordDecl(CXXRecordDecl *D) { /// 1) instantiating function templates /// 2) substituting friend declarations /// FIXME: preserve function definitions in case #2 -Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D) { + Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D, + TemplateParameterList *TemplateParams) { // Check whether there is already a function template specialization for // this declaration. FunctionTemplateDecl *FunctionTemplate = D->getDescribedFunctionTemplate(); void *InsertPos = 0; - if (FunctionTemplate) { + if (FunctionTemplate && !TemplateParams) { llvm::FoldingSetNodeID ID; FunctionTemplateSpecializationInfo::Profile(ID, TemplateArgs.getInnermost().getFlatArgumentList(), @@ -521,27 +533,79 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D) { Params[P]->setOwningFunction(Function); Function->setParams(SemaRef.Context, Params.data(), Params.size()); - // If the original function was part of a friend declaration, - // inherit its namespace state and add it to the owner. - if (Decl::FriendObjectKind FOK = D->getFriendObjectKind()) { - bool WasDeclared = (FOK == Decl::FOK_Declared); - Function->setObjectOfFriendDecl(WasDeclared); - if (!Owner->isDependentContext()) - DC->makeDeclVisibleInContext(Function, /* Recoverable = */ false); - - Function->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation); + if (TemplateParams) { + // Our resulting instantiation is actually a function template, since we + // are substituting only the outer template parameters. For example, given + // + // template + // struct X { + // template friend void f(T, U); + // }; + // + // X x; + // + // We are instantiating the friend function template "f" within X, + // which means substituting int for T, but leaving "f" as a friend function + // template. + // Build the function template itself. + FunctionTemplate = FunctionTemplateDecl::Create(SemaRef.Context, Owner, + Function->getLocation(), + Function->getDeclName(), + TemplateParams, Function); + Function->setDescribedFunctionTemplate(FunctionTemplate); + FunctionTemplate->setLexicalDeclContext(D->getLexicalDeclContext()); } - + if (InitFunctionInstantiation(Function, D)) Function->setInvalidDecl(); bool Redeclaration = false; bool OverloadableAttrRequired = false; + NamedDecl *PrevDecl = 0; + if (TemplateParams || !FunctionTemplate) { + // Look only into the namespace where the friend would be declared to + // find a previous declaration. This is the innermost enclosing namespace, + // as described in ActOnFriendFunctionDecl. + Sema::LookupResult R; + SemaRef.LookupQualifiedName(R, DC, Function->getDeclName(), + Sema::LookupOrdinaryName, true); + + PrevDecl = R.getAsSingleDecl(SemaRef.Context); + + // In C++, the previous declaration we find might be a tag type + // (class or enum). In this case, the new declaration will hide the + // tag type. Note that this does does not apply if we're declaring a + // typedef (C++ [dcl.typedef]p4). + if (PrevDecl && PrevDecl->getIdentifierNamespace() == Decl::IDNS_Tag) + PrevDecl = 0; + } + SemaRef.CheckFunctionDeclaration(Function, PrevDecl, Redeclaration, /*FIXME:*/OverloadableAttrRequired); - if (FunctionTemplate) { + // If the original function was part of a friend declaration, + // inherit its namespace state and add it to the owner. + NamedDecl *FromFriendD + = TemplateParams? cast(D->getDescribedFunctionTemplate()) : D; + if (FromFriendD->getFriendObjectKind()) { + NamedDecl *ToFriendD = 0; + if (TemplateParams) { + ToFriendD = cast(FunctionTemplate); + PrevDecl = FunctionTemplate->getPreviousDeclaration(); + } else { + ToFriendD = Function; + PrevDecl = Function->getPreviousDeclaration(); + } + ToFriendD->setObjectOfFriendDecl(PrevDecl != NULL); + if (!Owner->isDependentContext() && !PrevDecl) + DC->makeDeclVisibleInContext(ToFriendD, /* Recoverable = */ false); + + if (!TemplateParams) + Function->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation); + } + + if (FunctionTemplate && !TemplateParams) { // Record this function template specialization. Function->setFunctionTemplateSpecialization(SemaRef.Context, FunctionTemplate, @@ -687,7 +751,8 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, SemaRef.CheckFunctionDeclaration(Method, PrevDecl, Redeclaration, /*FIXME:*/OverloadableAttrRequired); - if (!FunctionTemplate && (!Method->isInvalidDecl() || !PrevDecl)) + if (!FunctionTemplate && (!Method->isInvalidDecl() || !PrevDecl) && + !Method->getFriendObjectKind()) Owner->addDecl(Method); return Method; diff --git a/test/CXX/temp/temp.spec/temp.expl.spec/p20.cpp b/test/CXX/temp/temp.spec/temp.expl.spec/p20.cpp new file mode 100644 index 0000000000..d270b8167a --- /dev/null +++ b/test/CXX/temp/temp.spec/temp.expl.spec/p20.cpp @@ -0,0 +1,14 @@ +// RUN: clang-cc -fsyntax-only -verify %s +template +void f(T); + +template +struct A { }; + +struct X { + template<> friend void f(int); // expected-error{{in class scope}} + template<> friend class A; // expected-error{{cannot be a friend}} + + friend void f(float); // okay + friend class A; // okay +}; diff --git a/test/SemaTemplate/friend-template.cpp b/test/SemaTemplate/friend-template.cpp index 9a483aeb5b..761c13076d 100644 --- a/test/SemaTemplate/friend-template.cpp +++ b/test/SemaTemplate/friend-template.cpp @@ -44,3 +44,21 @@ template<> struct X0 { template friend struct X0; }; + +template +struct X1 { + template friend void f2(U); + template friend void f3(U); +}; + +template void f2(U); + +X1 x1i; + +template<> void f2(int); + +// FIXME: Should this declaration of f3 be required for the specialization of +// f3 (further below) to work? GCC and EDG don't require it, we do... +template void f3(U); + +template<> void f3(int);