Implement dependent friend function template specializations.

llvm-svn: 100753
This commit is contained in:
John McCall 2010-04-08 09:05:18 +00:00
parent ebe47c872f
commit b9c7848ba7
10 changed files with 262 additions and 43 deletions

View File

@ -31,7 +31,9 @@ class StringLiteral;
class TemplateArgumentList;
class MemberSpecializationInfo;
class FunctionTemplateSpecializationInfo;
class DependentFunctionTemplateSpecializationInfo;
class TypeLoc;
class UnresolvedSetImpl;
/// \brief A container of type source information.
///
@ -1037,9 +1039,10 @@ private:
/// FunctionTemplateSpecializationInfo, which contains information about
/// the template being specialized and the template arguments involved in
/// that specialization.
llvm::PointerUnion3<FunctionTemplateDecl *,
llvm::PointerUnion4<FunctionTemplateDecl *,
MemberSpecializationInfo *,
FunctionTemplateSpecializationInfo *>
FunctionTemplateSpecializationInfo *,
DependentFunctionTemplateSpecializationInfo *>
TemplateOrSpecialization;
protected:
@ -1365,6 +1368,18 @@ public:
void *InsertPos,
TemplateSpecializationKind TSK = TSK_ImplicitInstantiation);
/// \brief Specifies that this function declaration is actually a
/// dependent function template specialization.
void setDependentTemplateSpecialization(ASTContext &Context,
const UnresolvedSetImpl &Templates,
const TemplateArgumentListInfo &TemplateArgs);
DependentFunctionTemplateSpecializationInfo *
getDependentSpecializationInfo() const {
return TemplateOrSpecialization.
dyn_cast<DependentFunctionTemplateSpecializationInfo*>();
}
/// \brief Determine what kind of template instantiation this function
/// represents.
TemplateSpecializationKind getTemplateSpecializationKind() const;

View File

@ -48,10 +48,6 @@ private:
// Location of the 'friend' specifier.
SourceLocation FriendLoc;
// FIXME: Hack to keep track of whether this was a friend function
// template specialization.
bool WasSpecialization;
friend class CXXRecordDecl::friend_iterator;
friend class CXXRecordDecl;
@ -60,8 +56,7 @@ private:
: Decl(Decl::Friend, DC, L),
Friend(Friend),
NextFriend(0),
FriendLoc(FriendL),
WasSpecialization(false) {
FriendLoc(FriendL) {
}
public:
@ -88,9 +83,6 @@ public:
return FriendLoc;
}
bool wasSpecialization() const { return WasSpecialization; }
void setSpecialization(bool WS) { WasSpecialization = WS; }
// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classof(const FriendDecl *D) { return true; }

View File

@ -376,6 +376,81 @@ public:
PointOfInstantiation = POI;
}
};
/// \brief Provides information about a dependent function-template
/// specialization declaration. Since explicit function template
/// specialization and instantiation declarations can only appear in
/// namespace scope, and you can only specialize a member of a
/// fully-specialized class, the only way to get one of these is in
/// a friend declaration like the following:
///
/// template <class T> void foo(T);
/// template <class T> class A {
/// friend void foo<>(T);
/// };
class DependentFunctionTemplateSpecializationInfo {
union {
// Force sizeof to be a multiple of sizeof(void*) so that the
// trailing data is aligned.
void *Aligner;
struct {
/// The number of potential template candidates.
unsigned NumTemplates;
/// The number of template arguments.
unsigned NumArgs;
} d;
};
/// The locations of the left and right angle brackets.
SourceRange AngleLocs;
FunctionTemplateDecl * const *getTemplates() const {
return reinterpret_cast<FunctionTemplateDecl*const*>(this+1);
}
const TemplateArgumentLoc *getTemplateArgs() const {
return reinterpret_cast<const TemplateArgumentLoc*>(
getTemplates()[getNumTemplates()]);
}
public:
DependentFunctionTemplateSpecializationInfo(
const UnresolvedSetImpl &Templates,
const TemplateArgumentListInfo &TemplateArgs);
/// \brief Returns the number of function templates that this might
/// be a specialization of.
unsigned getNumTemplates() const {
return d.NumTemplates;
}
/// \brief Returns the i'th template candidate.
FunctionTemplateDecl *getTemplate(unsigned I) const {
assert(I < getNumTemplates() && "template index out of range");
return getTemplates()[I];
}
/// \brief Returns the number of explicit template arguments that were given.
unsigned getNumTemplateArgs() const {
return d.NumArgs;
}
/// \brief Returns the nth template argument.
const TemplateArgumentLoc &getTemplateArg(unsigned I) const {
assert(I < getNumTemplateArgs() && "template arg index out of range");
return getTemplateArgs()[I];
}
SourceLocation getLAngleLoc() const {
return AngleLocs.getBegin();
}
SourceLocation getRAngleLoc() const {
return AngleLocs.getEnd();
}
};
/// Declaration of a template function.
class FunctionTemplateDecl : public TemplateDecl {

View File

@ -1309,6 +1309,40 @@ FunctionDecl::setFunctionTemplateSpecialization(FunctionTemplateDecl *Template,
}
}
void
FunctionDecl::setDependentTemplateSpecialization(ASTContext &Context,
const UnresolvedSetImpl &Templates,
const TemplateArgumentListInfo &TemplateArgs) {
assert(TemplateOrSpecialization.isNull());
size_t Size = sizeof(DependentFunctionTemplateSpecializationInfo);
Size += Templates.size() * sizeof(FunctionTemplateDecl*);
Size += TemplateArgs.size();
void *Buffer = Context.Allocate(Size);
DependentFunctionTemplateSpecializationInfo *Info =
new (Buffer) DependentFunctionTemplateSpecializationInfo(Templates,
TemplateArgs);
TemplateOrSpecialization = Info;
}
DependentFunctionTemplateSpecializationInfo::
DependentFunctionTemplateSpecializationInfo(const UnresolvedSetImpl &Ts,
const TemplateArgumentListInfo &TArgs)
: AngleLocs(TArgs.getLAngleLoc(), TArgs.getRAngleLoc()) {
d.NumTemplates = Ts.size();
d.NumArgs = TArgs.size();
FunctionTemplateDecl **TsArray =
const_cast<FunctionTemplateDecl**>(getTemplates());
for (unsigned I = 0, E = Ts.size(); I != E; ++I)
TsArray[I] = cast<FunctionTemplateDecl>(Ts[I]->getUnderlyingDecl());
TemplateArgumentLoc *ArgsArray =
const_cast<TemplateArgumentLoc*>(getTemplateArgs());
for (unsigned I = 0, E = TArgs.size(); I != E; ++I)
new (&ArgsArray[I]) TemplateArgumentLoc(TArgs[I]);
}
TemplateSpecializationKind FunctionDecl::getTemplateSpecializationKind() const {
// For a function template specialization, query the specialization
// information object.

View File

@ -2839,6 +2839,10 @@ public:
SourceLocation PrevPointOfInstantiation,
bool &SuppressNew);
bool CheckDependentFunctionTemplateSpecialization(FunctionDecl *FD,
const TemplateArgumentListInfo &ExplicitTemplateArgs,
LookupResult &Previous);
bool CheckFunctionTemplateSpecialization(FunctionDecl *FD,
const TemplateArgumentListInfo *ExplicitTemplateArgs,
LookupResult &Previous);

View File

@ -2935,7 +2935,7 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
// This is a function template specialization.
isFunctionTemplateSpecialization = true;
// C++0x [temp.expl.spec]p20 forbids "template<> void foo(int);".
// C++0x [temp.expl.spec]p20 forbids "template<> friend void foo(int);".
if (isFriend && isFunctionTemplateSpecialization) {
// We want to remove the "template<>", found here.
SourceRange RemoveRange = TemplateParams->getSourceRange();
@ -3139,23 +3139,38 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
// "friend void foo<>(int);" is an implicit specialization decl.
isFunctionTemplateSpecialization = true;
}
} else if (isFriend && isFunctionTemplateSpecialization) {
// This combination is only possible in a recovery case; the user
// wrote something like:
// template <> friend void foo(int);
// which we're recovering from as if the user had written:
// friend void foo<>(int);
// Go ahead and fake up a template id.
HasExplicitTemplateArgs = true;
TemplateArgs.setLAngleLoc(D.getIdentifierLoc());
TemplateArgs.setRAngleLoc(D.getIdentifierLoc());
}
if (isFunctionTemplateSpecialization) {
if (isFriend && NewFD->getType()->isDependentType()) {
// FIXME: When we see a friend of a function template
// specialization with a dependent type, we can't match it now;
// for now, we just drop it, until we have a reasonable way to
// represent the parsed-but-not-matched friend function template
// specialization in the AST.
return 0;
} else if (CheckFunctionTemplateSpecialization(NewFD,
(HasExplicitTemplateArgs ? &TemplateArgs : 0),
Previous))
// If it's a friend (and only if it's a friend), it's possible
// that either the specialized function type or the specialized
// template is dependent, and therefore matching will fail. In
// this case, don't check the specialization yet.
if (isFunctionTemplateSpecialization && isFriend &&
(NewFD->getType()->isDependentType() || DC->isDependentContext())) {
assert(HasExplicitTemplateArgs &&
"friend function specialization without template args");
if (CheckDependentFunctionTemplateSpecialization(NewFD, TemplateArgs,
Previous))
NewFD->setInvalidDecl();
} else if (isExplicitSpecialization && isa<CXXMethodDecl>(NewFD) &&
CheckMemberSpecialization(NewFD, Previous))
NewFD->setInvalidDecl();
} else if (isFunctionTemplateSpecialization) {
if (CheckFunctionTemplateSpecialization(NewFD,
(HasExplicitTemplateArgs ? &TemplateArgs : 0),
Previous))
NewFD->setInvalidDecl();
} else if (isExplicitSpecialization && isa<CXXMethodDecl>(NewFD)) {
if (CheckMemberSpecialization(NewFD, Previous))
NewFD->setInvalidDecl();
}
// Perform semantic checking on the function declaration.
bool OverloadableAttrRequired = false; // FIXME: HACK!

View File

@ -5638,9 +5638,6 @@ Sema::ActOnFriendFunctionDecl(Scope *S,
FrD->setAccess(AS_public);
CurContext->addDecl(FrD);
if (D.getName().getKind() == UnqualifiedId::IK_TemplateId)
FrD->setSpecialization(true);
return DeclPtrTy::make(ND);
}

View File

@ -4083,6 +4083,42 @@ Sema::CheckSpecializationInstantiationRedecl(SourceLocation NewLoc,
return false;
}
/// \brief Perform semantic analysis for the given dependent function
/// template specialization. The only possible way to get a dependent
/// function template specialization is with a friend declaration,
/// like so:
///
/// template <class T> void foo(T);
/// template <class T> class A {
/// friend void foo<>(T);
/// };
///
/// There really isn't any useful analysis we can do here, so we
/// just store the information.
bool
Sema::CheckDependentFunctionTemplateSpecialization(FunctionDecl *FD,
const TemplateArgumentListInfo &ExplicitTemplateArgs,
LookupResult &Previous) {
// Remove anything from Previous that isn't a function template in
// the correct context.
DeclContext *FDLookupContext = FD->getDeclContext()->getLookupContext();
LookupResult::Filter F = Previous.makeFilter();
while (F.hasNext()) {
NamedDecl *D = F.next()->getUnderlyingDecl();
if (!isa<FunctionTemplateDecl>(D) ||
!FDLookupContext->Equals(D->getDeclContext()->getLookupContext()))
F.erase();
}
F.done();
// Should this be diagnosed here?
if (Previous.empty()) return true;
FD->setDependentTemplateSpecialization(Context, Previous.asUnresolvedSet(),
ExplicitTemplateArgs);
return false;
}
/// \brief Perform semantic analysis for the given function template
/// specialization.
///

View File

@ -497,18 +497,11 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
NamedDecl *ND = D->getFriendDecl();
assert(ND && "friend decl must be a decl or a type!");
// FIXME: We have a problem here, because the nested call to Visit(ND)
// will inject the thing that the friend references into the current
// owner, which is wrong.
Decl *NewND;
// Hack to make this work almost well pending a rewrite.
if (D->wasSpecialization()) {
// Totally egregious hack to work around PR5866
return 0;
} else {
NewND = Visit(ND);
}
// All of the Visit implementations for the various potential friend
// declarations have to be carefully written to work for friend
// objects, with the most important detail being that the target
// decl should almost certainly not be placed in Owner.
Decl *NewND = Visit(ND);
if (!NewND) return 0;
FriendDecl *FD =
@ -1024,11 +1017,47 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D,
bool Redeclaration = false;
bool OverloadableAttrRequired = false;
bool isExplicitSpecialization = false;
LookupResult Previous(SemaRef, Function->getDeclName(), SourceLocation(),
Sema::LookupOrdinaryName, Sema::ForRedeclaration);
if (TemplateParams || !FunctionTemplate) {
if (DependentFunctionTemplateSpecializationInfo *Info
= D->getDependentSpecializationInfo()) {
assert(isFriend && "non-friend has dependent specialization info?");
// This needs to be set now for future sanity.
Function->setObjectOfFriendDecl(/*HasPrevious*/ true);
// Instantiate the explicit template arguments.
TemplateArgumentListInfo ExplicitArgs(Info->getLAngleLoc(),
Info->getRAngleLoc());
for (unsigned I = 0, E = Info->getNumTemplateArgs(); I != E; ++I) {
TemplateArgumentLoc Loc;
if (SemaRef.Subst(Info->getTemplateArg(I), Loc, TemplateArgs))
return 0;
ExplicitArgs.addArgument(Loc);
}
// Map the candidate templates to their instantiations.
for (unsigned I = 0, E = Info->getNumTemplates(); I != E; ++I) {
Decl *Temp = SemaRef.FindInstantiatedDecl(D->getLocation(),
Info->getTemplate(I),
TemplateArgs);
if (!Temp) return 0;
Previous.addDecl(cast<FunctionTemplateDecl>(Temp));
}
if (SemaRef.CheckFunctionTemplateSpecialization(Function,
&ExplicitArgs,
Previous))
Function->setInvalidDecl();
isExplicitSpecialization = true;
} else if (TemplateParams || !FunctionTemplate) {
// Look only into the namespace where the friend would be declared to
// find a previous declaration. This is the innermost enclosing namespace,
// as described in ActOnFriendFunctionDecl.
@ -1043,7 +1072,7 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D,
}
SemaRef.CheckFunctionDeclaration(/*Scope*/ 0, Function, Previous,
false, Redeclaration,
isExplicitSpecialization, Redeclaration,
/*FIXME:*/OverloadableAttrRequired);
// If the original function was part of a friend declaration,

View File

@ -216,3 +216,25 @@ namespace test9 {
template class A<int>; // expected-note {{in instantiation}}
}
namespace test10 {
template <class T> class A;
template <class T> A<T> bar(const T*, const A<T>&);
template <class T> class A {
private:
void foo(); // expected-note {{declared private here}}
friend A bar<>(const T*, const A<T>&);
};
template <class T> A<T> bar(const T *l, const A<T> &r) {
A<T> l1;
l1.foo();
A<char> l2;
l2.foo(); // expected-error {{'foo' is a private member of 'test10::A<char>'}}
return l1;
}
template A<int> bar<int>(const int *, const A<int> &); // expected-note {{in instantiation}}
}