Improved representation and support for friend class templates. Angst about same.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@82088 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
John McCall 2009-09-16 22:47:08 +00:00
Родитель 95f29c2703
Коммит dd4a3b0065
12 изменённых файлов: 185 добавлений и 44 удалений

Просмотреть файл

@ -127,6 +127,7 @@ DECL(ObjCForwardProtocol, Decl)
DECL(ObjCClass, Decl) DECL(ObjCClass, Decl)
DECL(FileScopeAsm, Decl) DECL(FileScopeAsm, Decl)
DECL(Friend, Decl) DECL(Friend, Decl)
DECL(FriendTemplate, Decl)
DECL(StaticAssert, Decl) DECL(StaticAssert, Decl)
LAST_DECL(Block, Decl) LAST_DECL(Block, Decl)

Просмотреть файл

@ -1227,6 +1227,85 @@ public:
virtual void Destroy(ASTContext& C); virtual void Destroy(ASTContext& C);
}; };
/// Declaration of a friend template. For example:
///
/// template <typename T> class A {
/// friend class MyVector<T>; // not a friend template
/// template <typename U> friend class B; // friend template
/// template <typename U> friend class Foo<T>::Nested; // friend template
class FriendTemplateDecl : public Decl {
public:
typedef llvm::PointerUnion<NamedDecl*,Type*> FriendUnion;
private:
// The number of template parameters; always non-zero.
unsigned NumParams;
// The parameter list.
TemplateParameterList **Params;
// The declaration that's a friend of this class.
FriendUnion Friend;
// Location of the 'friend' specifier.
SourceLocation FriendLoc;
FriendTemplateDecl(DeclContext *DC, SourceLocation Loc,
unsigned NParams,
TemplateParameterList **Params,
FriendUnion Friend,
SourceLocation FriendLoc)
: Decl(Decl::FriendTemplate, DC, Loc),
NumParams(NParams),
Params(Params),
Friend(Friend),
FriendLoc(FriendLoc)
{}
public:
static FriendTemplateDecl *Create(ASTContext &Context,
DeclContext *DC, SourceLocation Loc,
unsigned NParams,
TemplateParameterList **Params,
FriendUnion Friend,
SourceLocation FriendLoc);
/// If this friend declaration names a templated type (or
/// a dependent member type of a templated type), return that
/// type; otherwise return null.
Type *getFriendType() const {
return Friend.dyn_cast<Type*>();
}
/// If this friend declaration names a templated function (or
/// a member function of a templated type), return that type;
/// otherwise return null.
NamedDecl *getFriendDecl() const {
return Friend.dyn_cast<NamedDecl*>();
}
/// Retrieves the location of the 'friend' keyword.
SourceLocation getFriendLoc() const {
return FriendLoc;
}
TemplateParameterList *getTemplateParameterList(unsigned i) const {
assert(i <= NumParams);
return Params[i];
}
unsigned getNumTemplateParameters() const {
return NumParams;
}
// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) {
return D->getKind() == Decl::FriendTemplate;
}
static bool classof(const FriendTemplateDecl *D) { return true; }
};
/// Implementation of inline functions that require the template declarations /// Implementation of inline functions that require the template declarations
inline AnyFunctionDecl::AnyFunctionDecl(FunctionTemplateDecl *FTD) inline AnyFunctionDecl::AnyFunctionDecl(FunctionTemplateDecl *FTD)
: Function(FTD) { } : Function(FTD) { }

Просмотреть файл

@ -324,6 +324,8 @@ def err_qualified_friend_not_found : Error<
def err_introducing_special_friend : Error< def err_introducing_special_friend : Error<
"must use a qualified name when declaring a %select{constructor|" "must use a qualified name when declaring a %select{constructor|"
"destructor|conversion operator}0 as a friend">; "destructor|conversion operator}0 as a friend">;
def err_tagless_friend_type_template : Error<
"friend type templates must use an elaborated type">;
def err_abstract_type_in_decl : Error< def err_abstract_type_in_decl : Error<
"%select{return|parameter|variable|field}0 type %1 is an abstract class">; "%select{return|parameter|variable|field}0 type %1 is an abstract class">;

Просмотреть файл

@ -1215,7 +1215,7 @@ public:
/// ActOnFriendTypeDecl - Parsed a friend type declaration. /// ActOnFriendTypeDecl - Parsed a friend type declaration.
virtual DeclPtrTy ActOnFriendTypeDecl(Scope *S, virtual DeclPtrTy ActOnFriendTypeDecl(Scope *S,
const DeclSpec &DS, const DeclSpec &DS,
bool IsTemplate) { MultiTemplateParamsArg TParams) {
return DeclPtrTy(); return DeclPtrTy();
} }

Просмотреть файл

@ -230,6 +230,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
// Never have names. // Never have names.
case Friend: case Friend:
case FriendTemplate:
case LinkageSpec: case LinkageSpec:
case FileScopeAsm: case FileScopeAsm:
case StaticAssert: case StaticAssert:

Просмотреть файл

@ -463,3 +463,19 @@ Create(ASTContext &Context, DeclContext *DC, SourceLocation L,
Context.getTypeDeclType(Result, PrevDecl); Context.getTypeDeclType(Result, PrevDecl);
return Result; return Result;
} }
//===----------------------------------------------------------------------===//
// FriendTemplateDecl Implementation
//===----------------------------------------------------------------------===//
FriendTemplateDecl *FriendTemplateDecl::Create(ASTContext &Context,
DeclContext *DC,
SourceLocation L,
unsigned NParams,
TemplateParameterList **Params,
FriendUnion Friend,
SourceLocation FLoc) {
FriendTemplateDecl *Result
= new (Context) FriendTemplateDecl(DC, L, NParams, Params, Friend, FLoc);
return Result;
}

Просмотреть файл

@ -1011,12 +1011,15 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
DeclSpec DS; DeclSpec DS;
ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC_class); ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC_class);
Action::MultiTemplateParamsArg TemplateParams(Actions,
TemplateInfo.TemplateParams? TemplateInfo.TemplateParams->data() : 0,
TemplateInfo.TemplateParams? TemplateInfo.TemplateParams->size() : 0);
if (Tok.is(tok::semi)) { if (Tok.is(tok::semi)) {
ConsumeToken(); ConsumeToken();
if (DS.isFriendSpecified()) { if (DS.isFriendSpecified()) {
bool IsTemplate = TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate; Actions.ActOnFriendTypeDecl(CurScope, DS, move(TemplateParams));
Actions.ActOnFriendTypeDecl(CurScope, DS, IsTemplate);
} else } else
Actions.ParsedFreeStandingDeclSpec(CurScope, DS); Actions.ParsedFreeStandingDeclSpec(CurScope, DS);
@ -1119,10 +1122,6 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
// this call will *not* return the created decl; It will return null. // this call will *not* return the created decl; It will return null.
// See Sema::ActOnCXXMemberDeclarator for details. // See Sema::ActOnCXXMemberDeclarator for details.
Action::MultiTemplateParamsArg TemplateParams(Actions,
TemplateInfo.TemplateParams? TemplateInfo.TemplateParams->data() : 0,
TemplateInfo.TemplateParams? TemplateInfo.TemplateParams->size() : 0);
DeclPtrTy ThisDecl; DeclPtrTy ThisDecl;
if (DS.isFriendSpecified()) { if (DS.isFriendSpecified()) {
// TODO: handle initializers, bitfields, 'delete' // TODO: handle initializers, bitfields, 'delete'

Просмотреть файл

@ -2248,7 +2248,8 @@ public:
ExprArg AssertExpr, ExprArg AssertExpr,
ExprArg AssertMessageExpr); ExprArg AssertMessageExpr);
DeclPtrTy ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS, bool IsTemplate); DeclPtrTy ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
MultiTemplateParamsArg TemplateParams);
DeclPtrTy ActOnFriendFunctionDecl(Scope *S, Declarator &D, bool IsDefinition, DeclPtrTy ActOnFriendFunctionDecl(Scope *S, Declarator &D, bool IsDefinition,
MultiTemplateParamsArg TemplateParams); MultiTemplateParamsArg TemplateParams);

Просмотреть файл

@ -3957,7 +3957,11 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
= MatchTemplateParametersToScopeSpecifier(KWLoc, SS, = MatchTemplateParametersToScopeSpecifier(KWLoc, SS,
(TemplateParameterList**)TemplateParameterLists.get(), (TemplateParameterList**)TemplateParameterLists.get(),
TemplateParameterLists.size())) { TemplateParameterLists.size())) {
if (TemplateParams->size() > 0) { if (TUK == TUK_Friend) {
// When declaring a friend template, we do want to match the
// template parameters to the scope specifier, but don't go so far
// as to try to declare a new template.
} else if (TemplateParams->size() > 0) {
// This is a declaration or definition of a class template (which may // This is a declaration or definition of a class template (which may
// be a member of another template). // be a member of another template).
OwnedDecl = false; OwnedDecl = false;

Просмотреть файл

@ -4023,41 +4023,58 @@ Sema::DeclPtrTy Sema::ActOnStaticAssertDeclaration(SourceLocation AssertLoc,
return DeclPtrTy::make(Decl); return DeclPtrTy::make(Decl);
} }
/// Handle a friend type declaration. This works in tandem with
/// ActOnTag.
///
/// Notes on friend class templates:
///
/// We generally treat friend class declarations as if they were
/// declaring a class. So, for example, the elaborated type specifier
/// in a friend declaration is required to obey the restrictions of a
/// class-head (i.e. no typedefs in the scope chain), template
/// parameters are required to match up with simple template-ids, &c.
/// However, unlike when declaring a template specialization, it's
/// okay to refer to a template specialization without an empty
/// template parameter declaration, e.g.
/// friend class A<T>::B<unsigned>;
/// We permit this as a special case; if there are any template
/// parameters present at all, require proper matching, i.e.
/// template <> template <class T> friend class A<int>::B;
Sema::DeclPtrTy Sema::ActOnFriendTypeDecl(Scope *S, Sema::DeclPtrTy Sema::ActOnFriendTypeDecl(Scope *S,
const DeclSpec &DS, const DeclSpec &DS,
bool IsTemplate) { MultiTemplateParamsArg TempParams) {
SourceLocation Loc = DS.getSourceRange().getBegin(); SourceLocation Loc = DS.getSourceRange().getBegin();
assert(DS.isFriendSpecified()); assert(DS.isFriendSpecified());
assert(DS.getStorageClassSpec() == DeclSpec::SCS_unspecified); assert(DS.getStorageClassSpec() == DeclSpec::SCS_unspecified);
// Handle friend templates specially. // Try to convert the decl specifier to a type. This works for
if (IsTemplate) { // friend templates because ActOnTag never produces a ClassTemplateDecl
Decl *D; // for a TUK_Friend.
switch (DS.getTypeSpecType()) {
default:
// FIXME: implement this
assert(false && "unelaborated type templates are currently unimplemented!");
case DeclSpec::TST_class:
case DeclSpec::TST_union:
case DeclSpec::TST_struct:
D = (Decl*) DS.getTypeRep();
}
ClassTemplateDecl *Temp = cast<ClassTemplateDecl>(D);
FriendDecl *FD = FriendDecl::Create(Context, CurContext, Loc, Temp,
DS.getFriendSpecLoc());
FD->setAccess(AS_public);
CurContext->addDecl(FD);
return DeclPtrTy::make(FD);
}
// Try to convert the decl specifier to a type.
bool invalid = false; bool invalid = false;
QualType T = ConvertDeclSpecToType(DS, Loc, invalid); QualType T = ConvertDeclSpecToType(DS, Loc, invalid);
if (invalid) return DeclPtrTy(); if (invalid) return DeclPtrTy();
// This is definitely an error in C++98. It's probably meant to
// be forbidden in C++0x, too, but the specification is just
// poorly written.
//
// The problem is with declarations like the following:
// template <T> friend A<T>::foo;
// where deciding whether a class C is a friend or not now hinges
// on whether there exists an instantiation of A that causes
// 'foo' to equal C. There are restrictions on class-heads
// (which we declare (by fiat) elaborated friend declarations to
// be) that makes this tractable.
//
// FIXME: handle "template <> friend class A<T>;", which
// is possibly well-formed? Who even knows?
if (TempParams.size() && !isa<ElaboratedType>(T)) {
Diag(Loc, diag::err_tagless_friend_type_template)
<< DS.getSourceRange();
return DeclPtrTy();
}
// C++ [class.friend]p2: // C++ [class.friend]p2:
// An elaborated-type-specifier shall be used in a friend declaration // An elaborated-type-specifier shall be used in a friend declaration
// for a class.* // for a class.*
@ -4085,7 +4102,6 @@ Sema::DeclPtrTy Sema::ActOnFriendTypeDecl(Scope *S,
} }
bool IsDefinition = false; bool IsDefinition = false;
FriendDecl::FriendUnion FU = T.getTypePtr();
// We want to do a few things differently if the type was declared with // We want to do a few things differently if the type was declared with
// a tag: specifically, we want to use the associated RecordDecl as // a tag: specifically, we want to use the associated RecordDecl as
@ -4097,10 +4113,8 @@ Sema::DeclPtrTy Sema::ActOnFriendTypeDecl(Scope *S,
case DeclSpec::TST_struct: case DeclSpec::TST_struct:
case DeclSpec::TST_union: case DeclSpec::TST_union:
CXXRecordDecl *RD = cast_or_null<CXXRecordDecl>((Decl*) DS.getTypeRep()); CXXRecordDecl *RD = cast_or_null<CXXRecordDecl>((Decl*) DS.getTypeRep());
if (RD) { if (RD)
IsDefinition |= RD->isDefinition(); IsDefinition |= RD->isDefinition();
FU = RD;
}
break; break;
} }
@ -4122,12 +4136,20 @@ Sema::DeclPtrTy Sema::ActOnFriendTypeDecl(Scope *S,
if (RT->getDecl()->getDeclContext() == CurContext) if (RT->getDecl()->getDeclContext() == CurContext)
Diag(DS.getFriendSpecLoc(), diag::ext_friend_inner_class); Diag(DS.getFriendSpecLoc(), diag::ext_friend_inner_class);
FriendDecl *FD = FriendDecl::Create(Context, CurContext, Loc, FU, Decl *D;
if (TempParams.size())
D = FriendTemplateDecl::Create(Context, CurContext, Loc,
TempParams.size(),
(TemplateParameterList**) TempParams.release(),
T.getTypePtr(),
DS.getFriendSpecLoc()); DS.getFriendSpecLoc());
FD->setAccess(AS_public); else
CurContext->addDecl(FD); D = FriendDecl::Create(Context, CurContext, Loc, T.getTypePtr(),
DS.getFriendSpecLoc());
D->setAccess(AS_public);
CurContext->addDecl(D);
return DeclPtrTy::make(FD); return DeclPtrTy::make(D);
} }
Sema::DeclPtrTy Sema::DeclPtrTy

Просмотреть файл

@ -0,0 +1,11 @@
// RUN: clang-cc -fsyntax-only -verify %s
template <class T> class A {
typedef int Member;
};
class B {
template <class T> friend class A;
template <class T> friend class Undeclared;
template <class T> friend typename A<T>::Member; // expected-error {{friend type templates must use an elaborated type}}
};

Просмотреть файл

@ -1,6 +1,11 @@
// RUN: clang-cc -fsyntax-only -verify %s // RUN: clang-cc -fsyntax-only -verify %s
class A { template <class T> class A {
template <class T> friend class B; class Member {
};
};
class B {
template <class T> friend class A<T>::Member;
}; };