Implement method friends in class templates and fix a few related problems.

llvm-svn: 99708
This commit is contained in:
John McCall 2010-03-27 05:57:59 +00:00
parent b832e3276a
commit 2f88d7d72c
4 changed files with 118 additions and 37 deletions

View File

@ -452,15 +452,23 @@ public:
/// same entity may not (and probably don't) share this property. /// same entity may not (and probably don't) share this property.
void setObjectOfFriendDecl(bool PreviouslyDeclared) { void setObjectOfFriendDecl(bool PreviouslyDeclared) {
unsigned OldNS = IdentifierNamespace; unsigned OldNS = IdentifierNamespace;
assert((OldNS == IDNS_Tag || OldNS == IDNS_Ordinary || assert((OldNS & (IDNS_Tag | IDNS_Ordinary |
OldNS == (IDNS_Tag | IDNS_Ordinary)) IDNS_TagFriend | IDNS_OrdinaryFriend)) &&
&& "unsupported namespace for undeclared friend"); "namespace includes neither ordinary nor tag");
if (!PreviouslyDeclared) IdentifierNamespace = 0; assert(!(OldNS & ~(IDNS_Tag | IDNS_Ordinary |
IDNS_TagFriend | IDNS_OrdinaryFriend)) &&
"namespace includes other than ordinary or tag");
if (OldNS == IDNS_Tag) IdentifierNamespace = 0;
if (OldNS & (IDNS_Tag | IDNS_TagFriend)) {
IdentifierNamespace |= IDNS_TagFriend; IdentifierNamespace |= IDNS_TagFriend;
else if (PreviouslyDeclared) IdentifierNamespace |= IDNS_Tag;
}
if (OldNS & (IDNS_Ordinary | IDNS_OrdinaryFriend)) {
IdentifierNamespace |= IDNS_OrdinaryFriend; IdentifierNamespace |= IDNS_OrdinaryFriend;
if (PreviouslyDeclared) IdentifierNamespace |= IDNS_Ordinary;
}
} }
enum FriendObjectKind { enum FriendObjectKind {

View File

@ -2998,13 +2998,13 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
"previously-undeclared friend function being created " "previously-undeclared friend function being created "
"in a non-namespace context"); "in a non-namespace context");
// For now, claim that the objects have no previous declaration.
if (FunctionTemplate) { if (FunctionTemplate) {
FunctionTemplate->setObjectOfFriendDecl( FunctionTemplate->setObjectOfFriendDecl(false);
/* PreviouslyDeclared= */ !Previous.empty());
FunctionTemplate->setAccess(AS_public); FunctionTemplate->setAccess(AS_public);
} else {
NewFD->setObjectOfFriendDecl(false);
} }
else
NewFD->setObjectOfFriendDecl(/* PreviouslyDeclared= */ !Previous.empty());
NewFD->setAccess(AS_public); NewFD->setAccess(AS_public);
} }
@ -3154,6 +3154,17 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
Previous.getResultKind() != LookupResult::FoundOverloaded) && Previous.getResultKind() != LookupResult::FoundOverloaded) &&
"previous declaration set still overloaded"); "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 // If we have a function template, check the template parameter
// list. This will check and merge default template arguments. // list. This will check and merge default template arguments.
if (FunctionTemplate) { if (FunctionTemplate) {

View File

@ -503,13 +503,7 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
Decl *NewND; Decl *NewND;
// Hack to make this work almost well pending a rewrite. // Hack to make this work almost well pending a rewrite.
if (ND->getDeclContext()->isRecord()) { if (D->wasSpecialization()) {
// 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 // Totally egregious hack to work around PR5866
return 0; return 0;
} else { } else {
@ -906,15 +900,8 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D,
// Check whether there is already a function template specialization for // Check whether there is already a function template specialization for
// this declaration. // this declaration.
FunctionTemplateDecl *FunctionTemplate = D->getDescribedFunctionTemplate(); FunctionTemplateDecl *FunctionTemplate = D->getDescribedFunctionTemplate();
bool isFriend;
if (FunctionTemplate)
isFriend = (FunctionTemplate->getFriendObjectKind() != Decl::FOK_None);
else
isFriend = (D->getFriendObjectKind() != Decl::FOK_None);
void *InsertPos = 0; void *InsertPos = 0;
if (!isFriend && FunctionTemplate && !TemplateParams) { if (FunctionTemplate && !TemplateParams) {
llvm::FoldingSetNodeID ID; llvm::FoldingSetNodeID ID;
FunctionTemplateSpecializationInfo::Profile(ID, FunctionTemplateSpecializationInfo::Profile(ID,
TemplateArgs.getInnermost().getFlatArgumentList(), TemplateArgs.getInnermost().getFlatArgumentList(),
@ -930,6 +917,12 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D,
return Info->Function; return Info->Function;
} }
bool isFriend;
if (FunctionTemplate)
isFriend = (FunctionTemplate->getFriendObjectKind() != Decl::FOK_None);
else
isFriend = (D->getFriendObjectKind() != Decl::FOK_None);
bool MergeWithParentScope = (TemplateParams != 0) || bool MergeWithParentScope = (TemplateParams != 0) ||
!(isa<Decl>(Owner) && !(isa<Decl>(Owner) &&
cast<Decl>(Owner)->isDefinedOutsideFunctionOrMethod()); cast<Decl>(Owner)->isDefinedOutsideFunctionOrMethod());
@ -1098,6 +1091,12 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D,
return Info->Function; return Info->Function;
} }
bool isFriend;
if (FunctionTemplate)
isFriend = (FunctionTemplate->getFriendObjectKind() != Decl::FOK_None);
else
isFriend = (D->getFriendObjectKind() != Decl::FOK_None);
bool MergeWithParentScope = (TemplateParams != 0) || bool MergeWithParentScope = (TemplateParams != 0) ||
!(isa<Decl>(Owner) && !(isa<Decl>(Owner) &&
cast<Decl>(Owner)->isDefinedOutsideFunctionOrMethod()); cast<Decl>(Owner)->isDefinedOutsideFunctionOrMethod());
@ -1110,8 +1109,31 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D,
return 0; return 0;
QualType T = TInfo->getType(); 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. // Build the instantiated method declaration.
CXXRecordDecl *Record = cast<CXXRecordDecl>(Owner); CXXRecordDecl *Record = cast<CXXRecordDecl>(DC);
CXXMethodDecl *Method = 0; CXXMethodDecl *Method = 0;
DeclarationName Name = D->getDeclName(); DeclarationName Name = D->getDeclName();
@ -1148,9 +1170,8 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D,
D->isStatic(), D->isInlineSpecified()); D->isStatic(), D->isInlineSpecified());
} }
// Substitute the nested name specifier, if any. if (Qualifier)
if (SubstQualifier(D, Method)) Method->setQualifierInfo(Qualifier, D->getQualifierRange());
return 0;
if (TemplateParams) { if (TemplateParams) {
// Our resulting instantiation is actually a function template, since we // Our resulting instantiation is actually a function template, since we
@ -1170,7 +1191,10 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D,
Method->getLocation(), Method->getLocation(),
Method->getDeclName(), Method->getDeclName(),
TemplateParams, Method); TemplateParams, Method);
if (D->isOutOfLine()) if (isFriend) {
FunctionTemplate->setLexicalDeclContext(Owner);
FunctionTemplate->setObjectOfFriendDecl(true);
} else if (D->isOutOfLine())
FunctionTemplate->setLexicalDeclContext(D->getLexicalDeclContext()); FunctionTemplate->setLexicalDeclContext(D->getLexicalDeclContext());
Method->setDescribedFunctionTemplate(FunctionTemplate); Method->setDescribedFunctionTemplate(FunctionTemplate);
} else if (FunctionTemplate) { } else if (FunctionTemplate) {
@ -1178,7 +1202,7 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D,
Method->setFunctionTemplateSpecialization(FunctionTemplate, Method->setFunctionTemplateSpecialization(FunctionTemplate,
&TemplateArgs.getInnermost(), &TemplateArgs.getInnermost(),
InsertPos); InsertPos);
} else { } else if (!isFriend) {
// Record that this is an instantiation of a member function. // Record that this is an instantiation of a member function.
Method->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation); Method->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation);
} }
@ -1186,7 +1210,10 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D,
// If we are instantiating a member function defined // If we are instantiating a member function defined
// out-of-line, the instantiation will have the same lexical // out-of-line, the instantiation will have the same lexical
// context (which will be a namespace scope) as the template. // 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()); Method->setLexicalDeclContext(D->getLexicalDeclContext());
// Attach the parameters // Attach the parameters
@ -1200,8 +1227,8 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D,
LookupResult Previous(SemaRef, Name, SourceLocation(), LookupResult Previous(SemaRef, Name, SourceLocation(),
Sema::LookupOrdinaryName, Sema::ForRedeclaration); Sema::LookupOrdinaryName, Sema::ForRedeclaration);
if (!FunctionTemplate || TemplateParams) { if (!FunctionTemplate || TemplateParams || isFriend) {
SemaRef.LookupQualifiedName(Previous, Owner); SemaRef.LookupQualifiedName(Previous, Record);
// In C++, the previous declaration we find might be a tag type // In C++, the previous declaration we find might be a tag type
// (class or enum). In this case, the new declaration will hide the // (class or enum). In this case, the new declaration will hide the
@ -1221,9 +1248,19 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D,
Method->setAccess(D->getAccess()); Method->setAccess(D->getAccess());
if (!FunctionTemplate && (!Method->isInvalidDecl() || Previous.empty()) && if (FunctionTemplate) {
!Method->getFriendObjectKind()) // If there's a function template, let our caller handle it.
Owner->addDecl(Method); } else if (Method->isInvalidDecl() && !Previous.empty()) {
// Don't hide a (potentially) valid declaration with an invalid one.
} else {
NamedDecl *DeclToAdd = (TemplateParams
? cast<NamedDecl>(FunctionTemplate)
: Method);
if (isFriend)
Record->makeDeclVisibleInContext(DeclToAdd);
else
Owner->addDecl(DeclToAdd);
}
return Method; return Method;
} }

View File

@ -191,3 +191,28 @@ namespace test8 {
} }
template void foo<int>(); template void foo<int>();
} }
namespace test9 {
template <class T> 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<int>; // expected-note {{in instantiation}}
}