aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn McCall <rjmccall@apple.com>2010-03-25 02:56:08 +0000
committerJohn McCall <rjmccall@apple.com>2010-03-25 02:56:08 +0000
commita56623b01ed9f28f10416432d147c7a1729d5f1d (patch)
tree31d4a5f62a0694321a9837ed0af6cdcdd522fb06
parentf7b714df46cdbdf9e2ebf26eb2fd7881790d83e6 (diff)
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
-rw-r--r--lib/Sema/SemaAccess.cpp10
-rw-r--r--lib/Sema/SemaTemplateInstantiateDecl.cpp107
-rw-r--r--test/CXX/temp/temp.decls/temp.friend/p1.cpp14
-rw-r--r--test/SemaTemplate/friend-template.cpp8
4 files changed, 111 insertions, 28 deletions
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<CXXRecordDecl*>::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<NamedDecl>(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<ClassTemplateDecl>();
+ 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<bool>;
template class User<int>; // expected-note {{requested here}}
+}
+
+namespace test4 {
+ template <class T> class A {
+ template <class T0> friend class B;
+ bool foo(const A<T> *) const;
+ };
+
+ template <class T> class B {
+ bool bar(const A<T> *a, const A<T> *b) {
+ return a->foo(b);
+ }
+ };
+ template class B<int>;
}
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<typename T>
class X3 {
template<typename U, U Value> friend struct X2a;
- template<typename U, T Value> friend struct X2b;
+
+ // FIXME: the redeclaration note ends up here because redeclaration
+ // lookup ends up finding the friend target from X3<int>.
+ template<typename U, T Value> 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<int> x3i; // okay
- X3<long> x3l; // FIXME: should cause an instantiation-time failure
+ X3<long> x3l; // expected-note {{in instantiation}}
}
// PR5716