Implement method friends in class templates and fix a few related problems.
llvm-svn: 99708
This commit is contained in:
parent
b832e3276a
commit
2f88d7d72c
|
@ -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 {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue