From: John McCall Date: Thu, 25 Mar 2010 02:56:08 +0000 (+0000) Subject: Properly instantiate and link in friend-class-template declarations. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a56623b01ed9f28f10416432d147c7a1729d5f1d;p=clang Properly instantiate and link in friend-class-template declarations. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@99477 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaAccess.cpp b/lib/Sema/SemaAccess.cpp index 7e2520c9ee..62b13d4e95 100644 --- a/lib/Sema/SemaAccess.cpp +++ b/lib/Sema/SemaAccess.cpp @@ -240,13 +240,13 @@ static Sema::AccessResult MatchesFriend(Sema &S, ClassTemplateDecl *Friend) { Sema::AccessResult OnFailure = Sema::AR_inaccessible; + // Check whether the friend is the template of a class in the + // context chain. for (llvm::SmallVectorImpl::const_iterator I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I) { CXXRecordDecl *Record = *I; - // Check whether the friend is the template of a class in the - // context chain. To do that, we need to figure out whether the - // current class has a template: + // Figure out whether the current class has a template: ClassTemplateDecl *CTD; // A specialization of the template... @@ -264,6 +264,10 @@ static Sema::AccessResult MatchesFriend(Sema &S, if (Friend == CTD->getCanonicalDecl()) return Sema::AR_accessible; + // If the context isn't dependent, it can't be a dependent match. + if (!EC.isDependent()) + continue; + // If the template names don't match, it can't be a dependent // match. This isn't true in C++0x because of template aliases. if (!S.LangOpts.CPlusPlus0x && CTD->getDeclName() != Friend->getDeclName()) diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 7b0e88d163..93eb1525ff 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -500,21 +500,17 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) { // Hack to make this work almost well pending a rewrite. if (ND->getDeclContext()->isRecord()) { - if (!ND->getDeclContext()->isDependentContext()) { - NewND = SemaRef.FindInstantiatedDecl(D->getLocation(), ND, - TemplateArgs); - } else { - // 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; - } + // 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()) { // Totally egregious hack to work around PR5866 return 0; - } else + } else { NewND = Visit(ND); + } if (!NewND) return 0; FU = cast(NewND); @@ -641,6 +637,8 @@ namespace { } Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) { + bool isFriend = (D->getFriendObjectKind() != Decl::FOK_None); + // Create a local instantiation scope for this class template, which // will contain the instantiations of the template parameters. Sema::LocalInstantiationScope Scope(SemaRef); @@ -650,32 +648,95 @@ Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) { return NULL; CXXRecordDecl *Pattern = D->getTemplatedDecl(); + + // Instantiate the qualifier. We have to do this first in case + // we're a friend declaration, because if we are then we need to put + // the new declaration in the appropriate context. + NestedNameSpecifier *Qualifier = Pattern->getQualifier(); + if (Qualifier) { + Qualifier = SemaRef.SubstNestedNameSpecifier(Qualifier, + Pattern->getQualifierRange(), + TemplateArgs); + if (!Qualifier) return 0; + } + + CXXRecordDecl *PrevDecl = 0; + ClassTemplateDecl *PrevClassTemplate = 0; + + // If this isn't a friend, then it's a member template, in which + // case we just want to build the instantiation in the + // specialization. If it is a friend, we want to build it in + // the appropriate context. + DeclContext *DC = Owner; + if (isFriend) { + if (Qualifier) { + CXXScopeSpec SS; + SS.setScopeRep(Qualifier); + SS.setRange(Pattern->getQualifierRange()); + DC = SemaRef.computeDeclContext(SS); + if (!DC) return 0; + } else { + DC = SemaRef.FindInstantiatedContext(Pattern->getLocation(), + Pattern->getDeclContext(), + TemplateArgs); + } + + // Look for a previous declaration of the template in the owning + // context. + LookupResult R(SemaRef, Pattern->getDeclName(), Pattern->getLocation(), + Sema::LookupOrdinaryName, Sema::ForRedeclaration); + SemaRef.LookupQualifiedName(R, DC); + + if (R.isSingleResult()) { + PrevClassTemplate = R.getAsSingle(); + if (PrevClassTemplate) + PrevDecl = PrevClassTemplate->getTemplatedDecl(); + } + + if (!PrevClassTemplate && Qualifier) { + SemaRef.Diag(Pattern->getLocation(), diag::err_not_tag_in_scope) + << Pattern->getDeclName() << Pattern->getQualifierRange(); + return 0; + } + + if (PrevClassTemplate && + !SemaRef.TemplateParameterListsAreEqual(InstParams, + PrevClassTemplate->getTemplateParameters(), + /*Complain=*/true, + Sema::TPL_TemplateMatch)) + return 0; + } + CXXRecordDecl *RecordInst - = CXXRecordDecl::Create(SemaRef.Context, Pattern->getTagKind(), Owner, + = CXXRecordDecl::Create(SemaRef.Context, Pattern->getTagKind(), DC, Pattern->getLocation(), Pattern->getIdentifier(), - Pattern->getTagKeywordLoc(), /*PrevDecl=*/ NULL, + Pattern->getTagKeywordLoc(), PrevDecl, /*DelayTypeCreation=*/true); - // Substitute the nested name specifier, if any. - if (SubstQualifier(Pattern, RecordInst)) - return 0; + if (Qualifier) + RecordInst->setQualifierInfo(Qualifier, Pattern->getQualifierRange()); ClassTemplateDecl *Inst - = ClassTemplateDecl::Create(SemaRef.Context, Owner, D->getLocation(), - D->getIdentifier(), InstParams, RecordInst, 0); + = ClassTemplateDecl::Create(SemaRef.Context, DC, D->getLocation(), + D->getIdentifier(), InstParams, RecordInst, + PrevClassTemplate); RecordInst->setDescribedClassTemplate(Inst); - if (D->getFriendObjectKind()) - Inst->setObjectOfFriendDecl(true); - else + if (isFriend) { + Inst->setObjectOfFriendDecl(PrevClassTemplate != 0); + // TODO: do we want to track the instantiation progeny of this + // friend target decl? + } else { Inst->setAccess(D->getAccess()); - Inst->setInstantiatedFromMemberTemplate(D); + Inst->setInstantiatedFromMemberTemplate(D); + } // Trigger creation of the type for the instantiation. SemaRef.Context.getInjectedClassNameType(RecordInst, Inst->getInjectedClassNameSpecialization(SemaRef.Context)); // Finish handling of friends. - if (Inst->getFriendObjectKind()) { + if (isFriend) { + DC->makeDeclVisibleInContext(Inst, /*Recoverable*/ false); return Inst; } diff --git a/test/CXX/temp/temp.decls/temp.friend/p1.cpp b/test/CXX/temp/temp.decls/temp.friend/p1.cpp index 277106c2bd..1bf16de8f8 100644 --- a/test/CXX/temp/temp.decls/temp.friend/p1.cpp +++ b/test/CXX/temp/temp.decls/temp.friend/p1.cpp @@ -114,7 +114,21 @@ namespace test3 { template class User; template class User; // expected-note {{requested here}} +} + +namespace test4 { + template class A { + template friend class B; + bool foo(const A *) const; + }; + + template class B { + bool bar(const A *a, const A *b) { + return a->foo(b); + } + }; + template class B; } namespace Dependent { diff --git a/test/SemaTemplate/friend-template.cpp b/test/SemaTemplate/friend-template.cpp index 8bc2631e11..6ee30aa777 100644 --- a/test/SemaTemplate/friend-template.cpp +++ b/test/SemaTemplate/friend-template.cpp @@ -74,12 +74,16 @@ namespace test3 { template class X3 { template friend struct X2a; - template friend struct X2b; + + // FIXME: the redeclaration note ends up here because redeclaration + // lookup ends up finding the friend target from X3. + template friend struct X2b; // expected-error {{template non-type parameter has a different type 'long' in template redeclaration}} \ + // expected-note {{previous non-type template parameter with type 'int' is here}} }; X3 x3i; // okay - X3 x3l; // FIXME: should cause an instantiation-time failure + X3 x3l; // expected-note {{in instantiation}} } // PR5716