From 06245bfb3ae40bb24a8bfb17eafeb266a4daf5ca Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 7 Apr 2010 17:57:12 +0000 Subject: [PATCH] Improve handling of friend types in several ways: - When instantiating a friend type template, perform semantic analysis on the resulting type. - Downgrade the errors concerning friend type declarations that do not refer to classes to ExtWarns in C++98/03. C++0x allows practically any type to be befriended, and ignores the friend declaration if the type is not a class. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@100635 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 8 ++- lib/Sema/SemaDeclCXX.cpp | 72 ++++++++++--------- lib/Sema/SemaTemplateInstantiateDecl.cpp | 55 +++++++------- .../class.access/class.friend/p2-cxx03.cpp | 13 ++++ .../class.access/class.friend/p3-cxx0x.cpp | 29 ++++++++ test/CXX/class/class.friend/p2.cpp | 4 +- .../dcl.spec/dcl.type/dcl.type.elab/p3.cpp | 2 +- test/CXX/temp/temp.decls/temp.friend/p3.cpp | 3 +- test/Parser/cxx-friend.cpp | 6 +- 9 files changed, 117 insertions(+), 75 deletions(-) create mode 100644 test/CXX/class.access/class.friend/p2-cxx03.cpp create mode 100644 test/CXX/class.access/class.friend/p3-cxx0x.cpp diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index dfbb597954..2c5a7d4e2c 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -382,11 +382,13 @@ def err_static_assert_failed : Error<"static_assert failed \"%0\"">; def err_unexpected_friend : Error< "friends can only be classes or functions">; -def err_enum_friend : Error< - "enum types cannot be friends">; +def ext_enum_friend : ExtWarn< + "enumeration type %0 cannot be a friend">; +def ext_nonclass_type_friend : ExtWarn< + "non-class type %0 cannot be a friend">; def err_friend_is_member : Error< "friends cannot be members of the declaring class">; -def err_unelaborated_friend_type : Error< +def ext_unelaborated_friend_type : ExtWarn< "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">; diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 75dbb46d42..60b6220227 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -5328,39 +5328,48 @@ FriendDecl *Sema::CheckFriendTypeDecl(SourceLocation FriendLoc, QualType T = TSInfo->getType(); SourceRange TypeRange = TSInfo->getTypeLoc().getSourceRange(); - // C++03 [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. - if (!getLangOptions().CPlusPlus0x && !T->isElaboratedTypeSpecifier()) { - // If we evaluated the type to a record type, suggest putting - // a tag in front. - if (const RecordType *RT = T->getAs()) { - RecordDecl *RD = RT->getDecl(); - - std::string InsertionText = std::string(" ") + RD->getKindName(); - - Diag(FriendLoc, diag::err_unelaborated_friend_type) - << (unsigned) RD->getTagKind() + if (!getLangOptions().CPlusPlus0x) { + // C++03 [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. + if (!ActiveTemplateInstantiations.empty()) { + // Do not complain about the form of friend template types during + // template instantiation; we will already have complained when the + // template was declared. + } else if (!T->isElaboratedTypeSpecifier()) { + // If we evaluated the type to a record type, suggest putting + // a tag in front. + if (const RecordType *RT = T->getAs()) { + RecordDecl *RD = RT->getDecl(); + + std::string InsertionText = std::string(" ") + RD->getKindName(); + + Diag(TypeRange.getBegin(), diag::ext_unelaborated_friend_type) + << (unsigned) RD->getTagKind() + << T + << FixItHint::CreateInsertion(PP.getLocForEndOfToken(FriendLoc), + InsertionText); + } else { + Diag(FriendLoc, diag::ext_nonclass_type_friend) + << T + << SourceRange(FriendLoc, TypeRange.getEnd()); + } + } else if (T->getAs()) { + Diag(FriendLoc, diag::ext_enum_friend) << T - << SourceRange(FriendLoc) - << FixItHint::CreateInsertion(TypeRange.getBegin(), - InsertionText); - return 0; - } else { - Diag(FriendLoc, diag::err_unexpected_friend) << SourceRange(FriendLoc, TypeRange.getEnd()); - return 0; } } - // Enum types cannot be friends. - if (T->getAs()) { - Diag(FriendLoc, diag::err_enum_friend) - << SourceRange(FriendLoc, TypeRange.getEnd()); - return 0; - } + // C++0x [class.friend]p3: + // If the type specifier in a friend declaration designates a (possibly + // cv-qualified) class type, that class is declared as a friend; otherwise, + // the friend declaration is ignored. + + // FIXME: C++0x has some syntactic restrictions on friend type declarations + // in [class.friend]p3 that we do not implement. return FriendDecl::Create(Context, CurContext, FriendLoc, TSInfo, FriendLoc); } @@ -5421,13 +5430,6 @@ Sema::DeclPtrTy Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS, return DeclPtrTy(); } - // Enum templates cannot be friends. - if (TempParams.size() && T->getAs()) { - Diag(DS.getTypeSpecTypeLoc(), diag::err_enum_friend) - << SourceRange(DS.getFriendSpecLoc()); - 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 . . . // This is fixed in DR77, which just barely didn't make the C++03 diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 3375ccc0eb..69f183c706 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -476,47 +476,44 @@ Decl *TemplateDeclInstantiator::VisitFieldDecl(FieldDecl *D) { } Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) { - FriendDecl::FriendUnion FU; - // Handle friend type expressions by simply substituting template - // parameters into the pattern type. + // parameters into the pattern type and checking the result. if (TypeSourceInfo *Ty = D->getFriendType()) { TypeSourceInfo *InstTy = SemaRef.SubstType(Ty, TemplateArgs, D->getLocation(), DeclarationName()); - if (!InstTy) return 0; + if (!InstTy) + return 0; - // This assertion is valid because the source type was necessarily - // an elaborated-type-specifier with a record tag. - assert(getLangOptions().CPlusPlus0x || InstTy->getType()->isRecordType()); + FriendDecl *FD = SemaRef.CheckFriendTypeDecl(D->getFriendLoc(), InstTy); + if (!FD) + return 0; + + FD->setAccess(AS_public); + Owner->addDecl(FD); + return FD; + } + + NamedDecl *ND = D->getFriendDecl(); + assert(ND && "friend decl must be a decl or a type!"); - FU = InstTy; + // 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; - // Handle everything else by appropriate substitution. + // Hack to make this work almost well pending a rewrite. + if (D->wasSpecialization()) { + // Totally egregious hack to work around PR5866 + return 0; } else { - 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); - } - if (!NewND) return 0; - - FU = cast(NewND); + NewND = Visit(ND); } + if (!NewND) return 0; FriendDecl *FD = - FriendDecl::Create(SemaRef.Context, Owner, D->getLocation(), FU, - D->getFriendLoc()); + FriendDecl::Create(SemaRef.Context, Owner, D->getLocation(), + cast(NewND), D->getFriendLoc()); FD->setAccess(AS_public); Owner->addDecl(FD); return FD; diff --git a/test/CXX/class.access/class.friend/p2-cxx03.cpp b/test/CXX/class.access/class.friend/p2-cxx03.cpp new file mode 100644 index 0000000000..0391c4b989 --- /dev/null +++ b/test/CXX/class.access/class.friend/p2-cxx03.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +template +class X0 { + friend T; // expected-warning{{non-class type 'T' cannot be a friend}} +}; + +class X1 { }; +enum E1 { }; +X0 x0a; +X0 x0b; +X0 x0c; +X0 x0d; + diff --git a/test/CXX/class.access/class.friend/p3-cxx0x.cpp b/test/CXX/class.access/class.friend/p3-cxx0x.cpp new file mode 100644 index 0000000000..4f55e5339a --- /dev/null +++ b/test/CXX/class.access/class.friend/p3-cxx0x.cpp @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++0x -verify %s +template +class X0 { + friend T; +}; + +class Y1 { }; +enum E1 { }; +X0 x0a; +X0 x0b; +X0 x0c; +X0 x0d; + +template +class X1 { + friend typename T::type; // expected-error{{no type named 'type' in 'Y1'}} +}; + +struct Y2 { + struct type { }; +}; + +struct Y3 { + typedef int type; +}; + +X1 x1a; +X1 x1b; +X1 x1c; // expected-note{{in instantiation of template class 'X1' requested here}} diff --git a/test/CXX/class/class.friend/p2.cpp b/test/CXX/class/class.friend/p2.cpp index 9fe2b17e50..eb5036f812 100644 --- a/test/CXX/class/class.friend/p2.cpp +++ b/test/CXX/class/class.friend/p2.cpp @@ -4,7 +4,7 @@ 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 B0; // expected-error {{must specify 'struct' to befriend}} + friend int; // expected-warning {{non-class type 'int' cannot be a friend}} + friend B0; // expected-warning {{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 c530311773..b04e869a48 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,7 @@ class A1 { friend union A; // expected-error {{use of 'A' with tag type that does not match previous declaration}} friend enum A; // expected-error {{ISO C++ forbids forward references to 'enum' types}} \ - // expected-error {{enum types cannot be friends}} + // expected-warning {{cannot be a friend}} }; template 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 17d8c85df0..d116e016f1 100644 --- a/test/CXX/temp/temp.decls/temp.friend/p3.cpp +++ b/test/CXX/temp/temp.decls/temp.friend/p3.cpp @@ -8,6 +8,5 @@ class B { template friend class A; template friend class Undeclared; - // FIXME: Diagnostic below could be (and was) better. - template friend typename A::Member; // expected-error {{classes or functions}} + template friend typename A::Member; // expected-warning {{non-class type 'typename A::Member' cannot be a friend}} }; diff --git a/test/Parser/cxx-friend.cpp b/test/Parser/cxx-friend.cpp index 2fe30cd3e4..59350b5663 100644 --- a/test/Parser/cxx-friend.cpp +++ b/test/Parser/cxx-friend.cpp @@ -21,9 +21,9 @@ class B { // 'A' here should refer to the declaration above. friend class A; - 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 C; // expected-warning {{must specify 'class' to befriend}} + friend U; // expected-warning {{must specify 'union' to befriend}} + friend int; // expected-warning {{non-class type 'int' cannot be a friend}} friend void myfunc(); -- 2.40.0