diff options
author | John McCall <rjmccall@apple.com> | 2009-09-08 17:47:29 +0000 |
---|---|---|
committer | John McCall <rjmccall@apple.com> | 2009-09-08 17:47:29 +0000 |
commit | 6b2becfc434b0bdced8560802c4d0e03148c61b8 (patch) | |
tree | da73b449e948d8acea2174ffe593b0aa22810b79 | |
parent | 724827ff93b2c563c4a90ea43aed959f4af35e54 (diff) |
Support templateids in friend declarations. Fixes bug 4859.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@81233 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/Parse/Action.h | 27 | ||||
-rw-r--r-- | lib/Parse/ParseDeclCXX.cpp | 23 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 11 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 96 | ||||
-rw-r--r-- | lib/Sema/SemaTemplate.cpp | 44 | ||||
-rw-r--r-- | test/CXX/temp/temp.decls/temp.friend/p1.cpp | 5 |
6 files changed, 118 insertions, 88 deletions
diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 2a47e7b8aa..feecfdfbc7 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -1591,21 +1591,34 @@ public: /// /// \param Template A template whose specialization results in a /// type, e.g., a class template or template template parameter. - /// - /// \param TagSpec The tag spec that was provided as part of the - /// elaborated-type-specifier, or TST_unspecified if this was not - /// an elaborated type. virtual TypeResult ActOnTemplateIdType(TemplateTy Template, SourceLocation TemplateLoc, SourceLocation LAngleLoc, ASTTemplateArgsPtr TemplateArgs, SourceLocation *TemplateArgLocs, - SourceLocation RAngleLoc, - DeclSpec::TST TagSpec = DeclSpec::TST_unspecified, - SourceLocation TagLoc = SourceLocation()) { + SourceLocation RAngleLoc) { return TypeResult(); }; + /// \brief Note that a template ID was used with a tag. + /// + /// \param Type The result of ActOnTemplateIdType. + /// + /// \param TUK Either TUK_Reference or TUK_Friend. Declarations and + /// definitions are interpreted as explicit instantiations or + /// specializations. + /// + /// \param TagSpec The tag keyword that was provided as part of the + /// elaborated-type-specifier; either class, struct, union, or enum. + /// + /// \param TagLoc The location of the tag keyword. + virtual TypeResult ActOnTagTemplateIdType(TypeResult Type, + TagUseKind TUK, + DeclSpec::TST TagSpec, + SourceLocation TagLoc) { + return TypeResult(); + } + /// \brief Form a reference to a template-id (that will refer to a function) /// from a template and a list of template arguments. /// diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index c82f6a4114..55c7c85fa6 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -651,26 +651,27 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, TemplateId->RAngleLoc, Attr); } else if (TUK == Action::TUK_Reference || TUK == Action::TUK_Friend) { - Action::TypeResult TypeResult = - Actions.ActOnTemplateIdType(TemplateTy::make(TemplateId->Template), - TemplateId->TemplateNameLoc, - TemplateId->LAngleLoc, - TemplateArgsPtr, - TemplateId->getTemplateArgLocations(), - TemplateId->RAngleLoc, - TagType, StartLoc); + Action::TypeResult Type + = Actions.ActOnTemplateIdType(TemplateTy::make(TemplateId->Template), + TemplateId->TemplateNameLoc, + TemplateId->LAngleLoc, + TemplateArgsPtr, + TemplateId->getTemplateArgLocations(), + TemplateId->RAngleLoc); + + Type = Actions.ActOnTagTemplateIdType(Type, TUK, TagType, StartLoc); TemplateId->Destroy(); - if (TypeResult.isInvalid()) { + if (Type.isInvalid()) { DS.SetTypeSpecError(); return; } - + const char *PrevSpec = 0; unsigned DiagID; if (DS.SetTypeSpecType(DeclSpec::TST_typename, StartLoc, PrevSpec, - DiagID, TypeResult.get())) + DiagID, Type.get())) Diag(StartLoc, DiagID) << PrevSpec; return; diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index a040a0f169..7c4bcab2de 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -2401,10 +2401,13 @@ public: SourceLocation LAngleLoc, ASTTemplateArgsPtr TemplateArgs, SourceLocation *TemplateArgLocs, - SourceLocation RAngleLoc, - DeclSpec::TST TagSpec, - SourceLocation TagLoc); - + SourceLocation RAngleLoc); + + virtual TypeResult ActOnTagTemplateIdType(TypeResult Type, + TagUseKind TUK, + DeclSpec::TST TagSpec, + SourceLocation TagLoc); + OwningExprResult BuildTemplateIdExpr(TemplateName Template, SourceLocation TemplateNameLoc, SourceLocation LAngleLoc, diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index c37ff10941..1c7157c2c0 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -3917,61 +3917,60 @@ Sema::DeclPtrTy Sema::ActOnFriendTypeDecl(Scope *S, assert(DS.isFriendSpecified()); assert(DS.getStorageClassSpec() == DeclSpec::SCS_unspecified); - // Check to see if the decl spec was syntactically like "struct foo". - RecordDecl *RD = NULL; - - switch (DS.getTypeSpecType()) { - case DeclSpec::TST_class: - case DeclSpec::TST_struct: - case DeclSpec::TST_union: - RD = dyn_cast_or_null<CXXRecordDecl>((Decl*) DS.getTypeRep()); - if (!RD) return DeclPtrTy(); - - // The parser doesn't quite handle - // friend class A {} - // as we'd like, because it might have been the (valid) prefix of - // friend class A {} foo(); - // So even in C++0x mode we don't want to - IsDefinition |= RD->isDefinition(); - break; - - default: break; - } - - FriendDecl::FriendUnion FU = RD; + // Try to convert the decl specifier to a type. + bool invalid = false; + QualType T = ConvertDeclSpecToType(DS, Loc, invalid); + if (invalid) return DeclPtrTy(); // C++ [class.friend]p2: // An elaborated-type-specifier shall be used in a friend declaration // for a class.* // * The class-key of the elaborated-type-specifier is required. - // So if we didn't get a record decl above, we're invalid in C++98 mode. - if (!RD) { - bool invalid = false; - QualType T = ConvertDeclSpecToType(DS, Loc, invalid); - if (invalid) return DeclPtrTy(); - + // This is one of the rare places in Clang where it's legitimate to + // ask about the "spelling" of the type. + if (!getLangOptions().CPlusPlus0x && !isa<ElaboratedType>(T)) { + // If we evaluated the type to a record type, suggest putting + // a tag in front. if (const RecordType *RT = T->getAs<RecordType>()) { - FU = RD = cast<CXXRecordDecl>(RT->getDecl()); - - // Untagged typenames are invalid prior to C++0x, but we can - // suggest an easy fix which should work. - if (!getLangOptions().CPlusPlus0x) { - Diag(DS.getFriendSpecLoc(), diag::err_unelaborated_friend_type) - << (RD->isUnion()) - << CodeModificationHint::CreateInsertion(DS.getTypeSpecTypeLoc(), - RD->isUnion() ? " union" : " class"); - return DeclPtrTy(); - } - }else if (!getLangOptions().CPlusPlus0x) { - Diag(DS.getFriendSpecLoc(), diag::err_unexpected_friend) - << DS.getSourceRange(); + RecordDecl *RD = RT->getDecl(); + + std::string InsertionText = std::string(" ") + RD->getKindName(); + + Diag(DS.getFriendSpecLoc(), diag::err_unelaborated_friend_type) + << (RD->isUnion()) + << CodeModificationHint::CreateInsertion(DS.getTypeSpecTypeLoc(), + InsertionText); return DeclPtrTy(); }else { - FU = T.getTypePtr(); + Diag(DS.getFriendSpecLoc(), diag::err_unexpected_friend) + << DS.getSourceRange(); + return DeclPtrTy(); } } - assert(FU && "should have a friend decl/type by here!"); + FriendDecl::FriendUnion FU = T.getTypePtr(); + + // The parser doesn't quite handle + // friend class A { ... } + // optimally, because it might have been the (valid) prefix of + // friend class A { ... } foo(); + // So in a very particular set of circumstances, we need to adjust + // IsDefinition. + // + // Also, if we made a RecordDecl in ActOnTag, we want that to be the + // object of our friend declaration. + switch (DS.getTypeSpecType()) { + default: break; + case DeclSpec::TST_class: + case DeclSpec::TST_struct: + case DeclSpec::TST_union: + CXXRecordDecl *RD = cast_or_null<CXXRecordDecl>((Decl*) DS.getTypeRep()); + if (RD) { + IsDefinition |= RD->isDefinition(); + FU = RD; + } + break; + } // C++ [class.friend]p2: A class shall not be defined inside // a friend declaration. @@ -3986,11 +3985,10 @@ Sema::DeclPtrTy Sema::ActOnFriendTypeDecl(Scope *S, // But that's a silly restriction which nobody implements for // inner classes, and C++0x removes it anyway, so we only report // this (as a warning) if we're being pedantic. - if (!getLangOptions().CPlusPlus0x) { - assert(RD && "must have a record decl in C++98 mode"); - if (RD->getDeclContext() == CurContext) - Diag(DS.getFriendSpecLoc(), diag::ext_friend_inner_class); - } + if (!getLangOptions().CPlusPlus0x) + if (const RecordType *RT = T->getAs<RecordType>()) + if (RT->getDecl()->getDeclContext() == CurContext) + Diag(DS.getFriendSpecLoc(), diag::ext_friend_inner_class); FriendDecl *FD = FriendDecl::Create(Context, CurContext, Loc, FU, DS.getFriendSpecLoc()); diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index c243765bec..bc2c304ecc 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -1121,9 +1121,7 @@ Sema::ActOnTemplateIdType(TemplateTy TemplateD, SourceLocation TemplateLoc, SourceLocation LAngleLoc, ASTTemplateArgsPtr TemplateArgsIn, SourceLocation *TemplateArgLocs, - SourceLocation RAngleLoc, - DeclSpec::TST TagSpec, - SourceLocation TagLoc) { + SourceLocation RAngleLoc) { TemplateName Template = TemplateD.getAsVal<TemplateName>(); // Translate the parser's template argument list in our AST format. @@ -1139,26 +1137,38 @@ Sema::ActOnTemplateIdType(TemplateTy TemplateD, SourceLocation TemplateLoc, if (Result.isNull()) return true; - // If we were given a tag specifier, verify it. - if (TagSpec != DeclSpec::TST_unspecified) { - TagDecl::TagKind TagKind = TagDecl::getTagKindForTypeSpec(TagSpec); + return Result.getAsOpaquePtr(); +} - if (const RecordType *T = Result->getAs<RecordType>()) { - RecordDecl *D = T->getDecl(); +Sema::TypeResult Sema::ActOnTagTemplateIdType(TypeResult TypeResult, + TagUseKind TUK, + DeclSpec::TST TagSpec, + SourceLocation TagLoc) { + if (TypeResult.isInvalid()) + return Sema::TypeResult(); - IdentifierInfo *Id = D->getIdentifier(); - assert(Id && "templated class must have an identifier"); + QualType Type = QualType::getFromOpaquePtr(TypeResult.get()); - if (!isAcceptableTagRedeclaration(D, TagKind, TagLoc, *Id)) { - Diag(TagLoc, diag::err_use_with_wrong_tag) - << Id - << CodeModificationHint::CreateReplacement(SourceRange(TagLoc), - D->getKindName()); - } + // Verify the tag specifier. + TagDecl::TagKind TagKind = TagDecl::getTagKindForTypeSpec(TagSpec); + + if (const RecordType *RT = Type->getAs<RecordType>()) { + RecordDecl *D = RT->getDecl(); + + IdentifierInfo *Id = D->getIdentifier(); + assert(Id && "templated class must have an identifier"); + + if (!isAcceptableTagRedeclaration(D, TagKind, TagLoc, *Id)) { + Diag(TagLoc, diag::err_use_with_wrong_tag) + << Id + << CodeModificationHint::CreateReplacement(SourceRange(TagLoc), + D->getKindName()); } } - return Result.getAsOpaquePtr(); + QualType ElabType = Context.getElaboratedType(Type, TagKind); + + return ElabType.getAsOpaquePtr(); } Sema::OwningExprResult Sema::BuildTemplateIdExpr(TemplateName Template, diff --git a/test/CXX/temp/temp.decls/temp.friend/p1.cpp b/test/CXX/temp/temp.decls/temp.friend/p1.cpp index 3777485e1a..fc392da00a 100644 --- a/test/CXX/temp/temp.decls/temp.friend/p1.cpp +++ b/test/CXX/temp/temp.decls/temp.friend/p1.cpp @@ -36,6 +36,11 @@ class A { template <typename T> friend bool iszero(const A &a) throw(); }; +template <class T> class B_iterator; +template <class T> class B { + friend class B_iterator<T>; +}; + int calc1() { Num<int> left = -1; Num<int> right = 1; |