diff options
-rw-r--r-- | include/clang/Basic/DiagnosticParseKinds.td | 4 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 5 | ||||
-rw-r--r-- | lib/Parse/ParseDeclCXX.cpp | 26 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 61 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 8 | ||||
-rw-r--r-- | lib/Sema/SemaTemplate.cpp | 40 | ||||
-rw-r--r-- | test/CXX/class/class.friend/p2.cpp | 4 | ||||
-rw-r--r-- | test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp | 3 | ||||
-rw-r--r-- | test/CXX/temp/temp.decls/temp.friend/p3.cpp | 4 | ||||
-rw-r--r-- | test/Parser/cxx-friend.cpp | 4 | ||||
-rw-r--r-- | test/SemaTemplate/friend-template.cpp | 17 |
11 files changed, 130 insertions, 46 deletions
diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index c018497907..6971df50cb 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -279,6 +279,10 @@ def err_expected_type_name_after_typename : Error< def err_variadic_templates : Error< "variadic templates are only allowed in C++0x">; + +// C++ declarations +def err_friend_decl_defines_class : Error< + "cannot define a type in a friend declaration">; // Language specific pragmas // - Generic warnings diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 095b7ab387..44b582d43c 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -308,8 +308,6 @@ def err_static_assert_expression_is_not_constant : Error< "static_assert expression is not an integral constant expression">; def err_static_assert_failed : Error<"static_assert failed \"%0\"">; -def err_friend_decl_defines_class : Error< - "cannot define a type in a friend declaration">; def err_unexpected_friend : Error< "friends can only be classes or functions">; def err_friend_is_member : Error< @@ -317,8 +315,7 @@ def err_friend_is_member : Error< def ext_friend_inner_class : Extension< "C++ 98 does not allow inner classes as friends">; def err_unelaborated_friend_type : Error< - "must specify '%select{class|union}0' in a friend " - "%select{class|union}0 declaration">; + "must specify '%select{struct|union|class|enum}0' to befriend %1">; def err_qualified_friend_not_found : Error< "no function named %0 with type %1 was found in the specified scope">; def err_introducing_special_friend : Error< diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 59a6e6281c..bcf332bf30 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -624,9 +624,22 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, // 'struct foo :...' then this is a definition. Otherwise we have // something like 'struct foo xyz', a reference. Action::TagUseKind TUK; - if (Tok.is(tok::l_brace) || (getLang().CPlusPlus && Tok.is(tok::colon))) - TUK = Action::TUK_Definition; - else if (Tok.is(tok::semi)) + if (Tok.is(tok::l_brace) || (getLang().CPlusPlus && Tok.is(tok::colon))) { + if (DS.isFriendSpecified()) { + // C++ [class.friend]p2: + // A class shall not be defined in a friend declaration. + Diag(Tok.getLocation(), diag::err_friend_decl_defines_class) + << SourceRange(DS.getFriendSpecLoc()); + + // Skip everything up to the semicolon, so that this looks like a proper + // friend class (or template thereof) declaration. + SkipUntil(tok::semi, true, true); + TUK = Action::TUK_Friend; + } else { + // Okay, this is a class definition. + TUK = Action::TUK_Definition; + } + } else if (Tok.is(tok::semi)) TUK = DS.isFriendSpecified() ? Action::TUK_Friend : Action::TUK_Declaration; else TUK = Action::TUK_Reference; @@ -1043,12 +1056,7 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, if (Tok.is(tok::semi)) { ConsumeToken(); - - if (DS.isFriendSpecified()) { - Actions.ActOnFriendTypeDecl(CurScope, DS, move(TemplateParams)); - } else - Actions.ParsedFreeStandingDeclSpec(CurScope, DS); - + Actions.ParsedFreeStandingDeclSpec(CurScope, DS); return; } diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 1f53cde64b..f8751fb9c9 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -1288,6 +1288,59 @@ Sema::DeclPtrTy Sema::ParsedFreeStandingDeclSpec(Scope *S, DeclSpec &DS) { Tag = dyn_cast<TagDecl>(static_cast<Decl *>(DS.getTypeRep())); } + if (DS.isFriendSpecified()) { + // We have a "friend" declaration that does not have a declarator. + // Look at the type to see if the friend declaration was handled + // elsewhere (e.g., for friend classes and friend class templates). + // If not, produce a suitable diagnostic or go try to befriend the + // type itself. + QualType T; + if (DS.getTypeSpecType() == DeclSpec::TST_typename || + DS.getTypeSpecType() == DeclSpec::TST_typeofType) + T = QualType::getFromOpaquePtr(DS.getTypeRep()); + else if (DS.getTypeSpecType() == DeclSpec::TST_typeofExpr || + DS.getTypeSpecType() == DeclSpec::TST_decltype) + T = ((Expr *)DS.getTypeRep())->getType(); + else if (DS.getTypeSpecType() == DeclSpec::TST_class || + DS.getTypeSpecType() == DeclSpec::TST_struct || + DS.getTypeSpecType() == DeclSpec::TST_union) + return DeclPtrTy::make(Tag); + + if (T.isNull()) { + // Fall through to diagnose this error, below. + } else if (const RecordType *RecordT = T->getAs<RecordType>()) { + // C++ [class.friend]p2: + // An elaborated-type-specifier shall be used in a friend declaration + // for a class. + + // We have something like "friend C;", where C is the name of a + // class type but is missing an elaborated type specifier. Complain, + // but tell the user exactly how to fix the problem. + RecordDecl *RecordD = RecordT->getDecl(); + Diag(DS.getTypeSpecTypeLoc(), diag::err_unelaborated_friend_type) + << (unsigned)RecordD->getTagKind() + << QualType(RecordT, 0) + << SourceRange(DS.getFriendSpecLoc()) + << CodeModificationHint::CreateInsertion(DS.getTypeSpecTypeLoc(), + RecordD->getKindName() + std::string(" ")); + + // FIXME: We could go into ActOnTag to actually make the friend + // declaration happen at this point. + return DeclPtrTy(); + } + + if (!T.isNull() && T->isDependentType()) { + // Since T is a dependent type, handle it as a friend type + // declaration. + return ActOnFriendTypeDecl(S, DS, MultiTemplateParamsArg(*this, 0, 0)); + } + + // Complain about any non-dependent friend type here. + Diag(DS.getFriendSpecLoc(), diag::err_unexpected_friend) + << DS.getSourceRange(); + return DeclPtrTy(); + } + if (RecordDecl *Record = dyn_cast_or_null<RecordDecl>(Tag)) { if (!Record->getDeclName() && Record->isDefinition() && DS.getStorageClassSpec() != DeclSpec::SCS_typedef) { @@ -1305,7 +1358,7 @@ Sema::DeclPtrTy Sema::ParsedFreeStandingDeclSpec(Scope *S, DeclSpec &DS) { if (Record->getDeclName() && getLangOptions().Microsoft) return DeclPtrTy::make(Tag); } - + if (!DS.isMissingDeclaratorOk() && DS.getTypeSpecType() != DeclSpec::TST_error) { // Warn about typedefs of enums without names, since this is an @@ -4022,11 +4075,7 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, = MatchTemplateParametersToScopeSpecifier(KWLoc, SS, (TemplateParameterList**)TemplateParameterLists.get(), TemplateParameterLists.size())) { - 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) { + if (TemplateParams->size() > 0) { // This is a declaration or definition of a class template (which may // be a member of another template). OwnedDecl = false; diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 390a8048db..02c1a08c8f 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -4141,14 +4141,6 @@ Sema::DeclPtrTy Sema::ActOnFriendTypeDecl(Scope *S, break; } - // C++ [class.friend]p2: A class shall not be defined inside - // a friend declaration. - if (IsDefinition) { - Diag(DS.getFriendSpecLoc(), diag::err_friend_decl_defines_class) - << DS.getSourceRange(); - return DeclPtrTy(); - } - // C++98 [class.friend]p1: A friend of a class is a function // or class that is not a member of the class . . . // But that's a silly restriction which nobody implements for diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index d5fb7e82a8..72274525fd 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -579,6 +579,18 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, Previous = LookupQualifiedName(SemanticContext, Name, LookupOrdinaryName, true); + } else if (TUK == TUK_Friend) { + // C++ [namespace.memdef]p3: + // [...] When looking for a prior declaration of a class or a function + // declared as a friend, and when the name of the friend class or + // function is neither a qualified name nor a template-id, scopes outside + // the innermost enclosing namespace scope are not considered. + SemanticContext = CurContext; + while (!SemanticContext->isFileContext()) + SemanticContext = SemanticContext->getLookupParent(); + + Previous = LookupQualifiedName(SemanticContext, Name, LookupOrdinaryName, + true); } else { SemanticContext = CurContext; Previous = LookupName(S, Name, LookupOrdinaryName, true); @@ -654,13 +666,6 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, // FIXME: If we had a scope specifier, we better have a previous template // declaration! - // If this is a friend declaration of an undeclared template, - // create the template in the innermost namespace scope. - if (TUK == TUK_Friend && !PrevClassTemplate) { - while (!SemanticContext->isFileContext()) - SemanticContext = SemanticContext->getParent(); - } - CXXRecordDecl *NewClass = CXXRecordDecl::Create(Context, Kind, SemanticContext, NameLoc, Name, KWLoc, PrevClassTemplate? @@ -682,10 +687,7 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, (void)T; // Set the access specifier. - if (TUK == TUK_Friend) - NewTemplate->setObjectOfFriendDecl(/* PreviouslyDeclared = */ - PrevClassTemplate != NULL); - else + if (!Invalid && TUK != TUK_Friend) SetMemberAccessSpecifier(NewTemplate, PrevClassTemplate, AS); // Set the lexical context of these templates @@ -701,11 +703,14 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, if (TUK != TUK_Friend) PushOnScopeChains(NewTemplate, S); else { - // We might be replacing an existing declaration in the lookup tables; - // if so, borrow its access specifier. - if (PrevClassTemplate) + if (PrevClassTemplate && PrevClassTemplate->getAccess() != AS_none) { NewTemplate->setAccess(PrevClassTemplate->getAccess()); + NewClass->setAccess(PrevClassTemplate->getAccess()); + } + NewTemplate->setObjectOfFriendDecl(/* PreviouslyDeclared = */ + PrevClassTemplate != NULL); + // Friend templates are visible in fairly strange ways. if (!CurContext->isDependentContext()) { DeclContext *DC = SemanticContext->getLookupContext(); @@ -714,6 +719,13 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, PushOnScopeChains(NewTemplate, EnclosingScope, /* AddToContext = */ false); } + + FriendDecl *Friend = FriendDecl::Create(Context, CurContext, + NewClass->getLocation(), + NewTemplate, + /*FIXME:*/NewClass->getLocation()); + Friend->setAccess(AS_public); + CurContext->addDecl(Friend); } if (Invalid) { diff --git a/test/CXX/class/class.friend/p2.cpp b/test/CXX/class/class.friend/p2.cpp index f130beb7d2..98be2049e7 100644 --- a/test/CXX/class/class.friend/p2.cpp +++ b/test/CXX/class/class.friend/p2.cpp @@ -1,8 +1,10 @@ // RUN: clang-cc -fsyntax-only -verify %s +struct B0; + class A { friend class B {}; // expected-error {{cannot define a type in a friend declaration}} friend int; // expected-error {{friends can only be classes or functions}} - friend B; // expected-error {{must specify 'class' in a friend class declaration}} + friend B0; // expected-error {{must specify 'struct' to befriend}} friend class C; // okay }; diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp index 89f938d1f7..657cf20cf3 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp @@ -13,7 +13,8 @@ class A1 { friend union A; // expected-error {{use of 'A' with tag type that does not match previous declaration}} // FIXME: a better error would be something like 'enum types cannot be friends' - friend enum A; // expected-error {{ISO C++ forbids forward references to 'enum' types}} + friend enum A; // expected-error {{ISO C++ forbids forward references to 'enum' types}} \ + // expected-error{{classes or functions}} }; template <class T> struct B { // expected-note {{previous use is here}} diff --git a/test/CXX/temp/temp.decls/temp.friend/p3.cpp b/test/CXX/temp/temp.decls/temp.friend/p3.cpp index dd856e9cc3..4615bebe71 100644 --- a/test/CXX/temp/temp.decls/temp.friend/p3.cpp +++ b/test/CXX/temp/temp.decls/temp.friend/p3.cpp @@ -7,5 +7,7 @@ template <class T> class A { 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}} + + // FIXME: Diagnostic below could be (and was) better. + template <class T> friend typename A<T>::Member; // expected-error {{classes or functions}} }; diff --git a/test/Parser/cxx-friend.cpp b/test/Parser/cxx-friend.cpp index 397894727b..14b31af761 100644 --- a/test/Parser/cxx-friend.cpp +++ b/test/Parser/cxx-friend.cpp @@ -21,8 +21,8 @@ class B { // 'A' here should refer to the declaration above. friend class A; - friend C; // expected-error {{must specify 'class' in a friend class declaration}} - friend U; // expected-error {{must specify 'union' in a friend union declaration}} + friend C; // expected-error {{must specify 'class' to befriend}} + friend U; // expected-error {{must specify 'union' to befriend}} friend int; // expected-error {{friends can only be classes or functions}} friend void myfunc(); diff --git a/test/SemaTemplate/friend-template.cpp b/test/SemaTemplate/friend-template.cpp new file mode 100644 index 0000000000..6268cd8907 --- /dev/null +++ b/test/SemaTemplate/friend-template.cpp @@ -0,0 +1,17 @@ +// RUN: clang-cc -fsyntax-only %s + +// PR5057 +namespace std { + class X { + public: + template<typename T> + friend struct Y; + }; +} + +namespace std { + template<typename T> + struct Y + { + }; +} |