From: John McCall Date: Sat, 27 Mar 2010 05:57:59 +0000 (+0000) Subject: Implement method friends in class templates and fix a few related problems. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b0cb022daec8671406ab25f4b5d5a6d48d823bc4;p=clang Implement method friends in class templates and fix a few related problems. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@99708 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h index e9f004de7b..d5913e236c 100644 --- a/include/clang/AST/DeclBase.h +++ b/include/clang/AST/DeclBase.h @@ -452,15 +452,23 @@ public: /// same entity may not (and probably don't) share this property. void setObjectOfFriendDecl(bool PreviouslyDeclared) { unsigned OldNS = IdentifierNamespace; - assert((OldNS == IDNS_Tag || OldNS == IDNS_Ordinary || - OldNS == (IDNS_Tag | IDNS_Ordinary)) - && "unsupported namespace for undeclared friend"); - if (!PreviouslyDeclared) IdentifierNamespace = 0; - - if (OldNS == IDNS_Tag) + assert((OldNS & (IDNS_Tag | IDNS_Ordinary | + IDNS_TagFriend | IDNS_OrdinaryFriend)) && + "namespace includes neither ordinary nor tag"); + assert(!(OldNS & ~(IDNS_Tag | IDNS_Ordinary | + IDNS_TagFriend | IDNS_OrdinaryFriend)) && + "namespace includes other than ordinary or tag"); + + IdentifierNamespace = 0; + if (OldNS & (IDNS_Tag | IDNS_TagFriend)) { IdentifierNamespace |= IDNS_TagFriend; - else + if (PreviouslyDeclared) IdentifierNamespace |= IDNS_Tag; + } + + if (OldNS & (IDNS_Ordinary | IDNS_OrdinaryFriend)) { IdentifierNamespace |= IDNS_OrdinaryFriend; + if (PreviouslyDeclared) IdentifierNamespace |= IDNS_Ordinary; + } } enum FriendObjectKind { diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 9bdcf09b29..9dbe821ee8 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -2998,13 +2998,13 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, "previously-undeclared friend function being created " "in a non-namespace context"); + // For now, claim that the objects have no previous declaration. if (FunctionTemplate) { - FunctionTemplate->setObjectOfFriendDecl( - /* PreviouslyDeclared= */ !Previous.empty()); + FunctionTemplate->setObjectOfFriendDecl(false); FunctionTemplate->setAccess(AS_public); + } else { + NewFD->setObjectOfFriendDecl(false); } - else - NewFD->setObjectOfFriendDecl(/* PreviouslyDeclared= */ !Previous.empty()); NewFD->setAccess(AS_public); } @@ -3154,6 +3154,17 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, Previous.getResultKind() != LookupResult::FoundOverloaded) && "previous declaration set still overloaded"); + if (isFriend && Redeclaration) { + AccessSpecifier Access = NewFD->getPreviousDeclaration()->getAccess(); + if (FunctionTemplate) { + FunctionTemplate->setObjectOfFriendDecl(true); + FunctionTemplate->setAccess(Access); + } else { + NewFD->setObjectOfFriendDecl(true); + } + NewFD->setAccess(Access); + } + // If we have a function template, check the template parameter // list. This will check and merge default template arguments. if (FunctionTemplate) { diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 59c9819751..81c036be04 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -503,13 +503,7 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) { Decl *NewND; // Hack to make this work almost well pending a rewrite. - if (ND->getDeclContext()->isRecord()) { - // FIXME: Hack to avoid crashing when incorrectly trying to instantiate - // templated friend declarations. This doesn't produce a correct AST; - // however this is sufficient for some AST analysis. The real solution - // must be put in place during the pending rewrite. See PR5848. - return 0; - } else if (D->wasSpecialization()) { + if (D->wasSpecialization()) { // Totally egregious hack to work around PR5866 return 0; } else { @@ -906,15 +900,8 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D, // Check whether there is already a function template specialization for // this declaration. FunctionTemplateDecl *FunctionTemplate = D->getDescribedFunctionTemplate(); - - bool isFriend; - if (FunctionTemplate) - isFriend = (FunctionTemplate->getFriendObjectKind() != Decl::FOK_None); - else - isFriend = (D->getFriendObjectKind() != Decl::FOK_None); - void *InsertPos = 0; - if (!isFriend && FunctionTemplate && !TemplateParams) { + if (FunctionTemplate && !TemplateParams) { llvm::FoldingSetNodeID ID; FunctionTemplateSpecializationInfo::Profile(ID, TemplateArgs.getInnermost().getFlatArgumentList(), @@ -930,6 +917,12 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D, return Info->Function; } + bool isFriend; + if (FunctionTemplate) + isFriend = (FunctionTemplate->getFriendObjectKind() != Decl::FOK_None); + else + isFriend = (D->getFriendObjectKind() != Decl::FOK_None); + bool MergeWithParentScope = (TemplateParams != 0) || !(isa(Owner) && cast(Owner)->isDefinedOutsideFunctionOrMethod()); @@ -1098,6 +1091,12 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, return Info->Function; } + bool isFriend; + if (FunctionTemplate) + isFriend = (FunctionTemplate->getFriendObjectKind() != Decl::FOK_None); + else + isFriend = (D->getFriendObjectKind() != Decl::FOK_None); + bool MergeWithParentScope = (TemplateParams != 0) || !(isa(Owner) && cast(Owner)->isDefinedOutsideFunctionOrMethod()); @@ -1110,8 +1109,31 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, return 0; QualType T = TInfo->getType(); + NestedNameSpecifier *Qualifier = D->getQualifier(); + if (Qualifier) { + Qualifier = SemaRef.SubstNestedNameSpecifier(Qualifier, + D->getQualifierRange(), + TemplateArgs); + if (!Qualifier) return 0; + } + + DeclContext *DC = Owner; + if (isFriend) { + if (Qualifier) { + CXXScopeSpec SS; + SS.setScopeRep(Qualifier); + SS.setRange(D->getQualifierRange()); + DC = SemaRef.computeDeclContext(SS); + } else { + DC = SemaRef.FindInstantiatedContext(D->getLocation(), + D->getDeclContext(), + TemplateArgs); + } + if (!DC) return 0; + } + // Build the instantiated method declaration. - CXXRecordDecl *Record = cast(Owner); + CXXRecordDecl *Record = cast(DC); CXXMethodDecl *Method = 0; DeclarationName Name = D->getDeclName(); @@ -1148,9 +1170,8 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, D->isStatic(), D->isInlineSpecified()); } - // Substitute the nested name specifier, if any. - if (SubstQualifier(D, Method)) - return 0; + if (Qualifier) + Method->setQualifierInfo(Qualifier, D->getQualifierRange()); if (TemplateParams) { // Our resulting instantiation is actually a function template, since we @@ -1170,7 +1191,10 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, Method->getLocation(), Method->getDeclName(), TemplateParams, Method); - if (D->isOutOfLine()) + if (isFriend) { + FunctionTemplate->setLexicalDeclContext(Owner); + FunctionTemplate->setObjectOfFriendDecl(true); + } else if (D->isOutOfLine()) FunctionTemplate->setLexicalDeclContext(D->getLexicalDeclContext()); Method->setDescribedFunctionTemplate(FunctionTemplate); } else if (FunctionTemplate) { @@ -1178,7 +1202,7 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, Method->setFunctionTemplateSpecialization(FunctionTemplate, &TemplateArgs.getInnermost(), InsertPos); - } else { + } else if (!isFriend) { // Record that this is an instantiation of a member function. Method->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation); } @@ -1186,7 +1210,10 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, // If we are instantiating a member function defined // out-of-line, the instantiation will have the same lexical // context (which will be a namespace scope) as the template. - if (D->isOutOfLine()) + if (isFriend) { + Method->setLexicalDeclContext(Owner); + Method->setObjectOfFriendDecl(true); + } else if (D->isOutOfLine()) Method->setLexicalDeclContext(D->getLexicalDeclContext()); // Attach the parameters @@ -1200,8 +1227,8 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, LookupResult Previous(SemaRef, Name, SourceLocation(), Sema::LookupOrdinaryName, Sema::ForRedeclaration); - if (!FunctionTemplate || TemplateParams) { - SemaRef.LookupQualifiedName(Previous, Owner); + if (!FunctionTemplate || TemplateParams || isFriend) { + SemaRef.LookupQualifiedName(Previous, Record); // In C++, the previous declaration we find might be a tag type // (class or enum). In this case, the new declaration will hide the @@ -1221,9 +1248,19 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, Method->setAccess(D->getAccess()); - if (!FunctionTemplate && (!Method->isInvalidDecl() || Previous.empty()) && - !Method->getFriendObjectKind()) - Owner->addDecl(Method); + if (FunctionTemplate) { + // If there's a function template, let our caller handle it. + } else if (Method->isInvalidDecl() && !Previous.empty()) { + // Don't hide a (potentially) valid declaration with an invalid one. + } else { + NamedDecl *DeclToAdd = (TemplateParams + ? cast(FunctionTemplate) + : Method); + if (isFriend) + Record->makeDeclVisibleInContext(DeclToAdd); + else + Owner->addDecl(DeclToAdd); + } return Method; } diff --git a/test/CXX/temp/temp.decls/temp.friend/p1.cpp b/test/CXX/temp/temp.decls/temp.friend/p1.cpp index 0e41e5f832..c9dc5460a0 100644 --- a/test/CXX/temp/temp.decls/temp.friend/p1.cpp +++ b/test/CXX/temp/temp.decls/temp.friend/p1.cpp @@ -191,3 +191,28 @@ namespace test8 { } template void foo(); } + +namespace test9 { + template class A { + class B; class C; + + int foo(B *b) { + return b->x; + } + + int foo(C *c) { + return c->x; // expected-error {{'x' is a private member}} + } + + class B { + int x; + friend int A::foo(B*); + }; + + class C { + int x; // expected-note {{declared private here}} + }; + }; + + template class A; // expected-note {{in instantiation}} +}