https://github.com/sdkrystian updated https://github.com/llvm/llvm-project/pull/80171
>From 5de10ae0864dacf5f48daf476a4fa20177545d9b Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Wed, 31 Jan 2024 11:09:11 -0500 Subject: [PATCH] [Clang][Sema] Diagnose friend declarations with enum elaborated-type-specifier in all language modes --- clang/docs/ReleaseNotes.rst | 2 + .../clang/Basic/DiagnosticSemaKinds.td | 8 +- clang/include/clang/Sema/DeclSpec.h | 13 +- clang/include/clang/Sema/Sema.h | 3 - clang/lib/Parse/ParseTentative.cpp | 6 +- clang/lib/Sema/DeclSpec.cpp | 9 +- clang/lib/Sema/SemaDecl.cpp | 20 +++ clang/lib/Sema/SemaDeclCXX.cpp | 137 +++++------------- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 7 +- .../dcl.spec/dcl.type/dcl.type.elab/p3.cpp | 8 +- .../dcl.spec/dcl.type/dcl.type.elab/p4.cpp | 40 +++++ clang/test/CXX/drs/dr16xx.cpp | 8 +- clang/test/CXX/drs/dr23xx.cpp | 38 ++++- .../temp.class/temp.mem.enum/p1.cpp | 8 +- clang/test/FixIt/fixit-c++11.cpp | 6 +- clang/test/Parser/cxx-decl.cpp | 3 - clang/test/Parser/cxx0x-decl.cpp | 2 +- clang/test/SemaCXX/cxx98-compat.cpp | 3 +- clang/test/SemaCXX/enum-scoped.cpp | 10 ++ 19 files changed, 185 insertions(+), 146 deletions(-) create mode 100644 clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p4.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index dc2fb3b25e3a54..e12a802e2e9ede 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -159,6 +159,8 @@ Improvements to Clang's diagnostics - The ``-Wshorten-64-to-32`` diagnostic is now grouped under ``-Wimplicit-int-conversion`` instead of ``-Wconversion``. Fixes `#69444 <https://github.com/llvm/llvm-project/issues/69444>`_. +- Clang now diagnoses friend declarations with an ``enum`` elaborated-type-specifier in language modes after C++98. + Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 754733a6c5fffd..40b47c3ca92e7d 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1637,10 +1637,10 @@ def err_inline_namespace_std : Error< def err_unexpected_friend : Error< "friends can only be classes or functions">; def ext_enum_friend : ExtWarn< - "befriending enumeration type %0 is a C++11 extension">, InGroup<CXX11>; -def warn_cxx98_compat_enum_friend : Warning< - "befriending enumeration type %0 is incompatible with C++98">, - InGroup<CXX98Compat>, DefaultIgnore; + "elaborated enum specifier cannot be declared as a friend">, + InGroup<DiagGroup<"friend-enum">>; +def note_enum_friend : Note< + "remove 'enum%select{| struct| class}0' to befriend an enum">; def ext_nonclass_type_friend : ExtWarn< "non-class friend type %0 is a C++11 extension">, InGroup<CXX11>; def warn_cxx98_compat_nonclass_type_friend : Warning< diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index d161147527dc34..316e8071169a3a 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -346,10 +346,7 @@ class DeclSpec { // FIXME: Attributes should be included here. }; - enum FriendSpecified : bool { - No, - Yes, - }; + enum FriendSpecified : bool { No, Yes }; private: // storage-class-specifier @@ -400,7 +397,7 @@ class DeclSpec { // friend-specifier LLVM_PREFERRED_TYPE(bool) - unsigned Friend_specified : 1; + unsigned FriendSpecifiedFirst : 1; // constexpr-specifier LLVM_PREFERRED_TYPE(ConstexprSpecKind) @@ -491,7 +488,7 @@ class DeclSpec { TypeSpecPipe(false), TypeSpecSat(false), ConstrainedAuto(false), TypeQualifiers(TQ_unspecified), FS_inline_specified(false), FS_forceinline_specified(false), FS_virtual_specified(false), - FS_noreturn_specified(false), Friend_specified(false), + FS_noreturn_specified(false), FriendSpecifiedFirst(false), ConstexprSpecifier( static_cast<unsigned>(ConstexprSpecKind::Unspecified)), Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {} @@ -818,9 +815,11 @@ class DeclSpec { const char *&PrevSpec, unsigned &DiagID); FriendSpecified isFriendSpecified() const { - return static_cast<FriendSpecified>(Friend_specified); + return static_cast<FriendSpecified>(FriendLoc.isValid()); } + bool isFriendSpecifiedFirst() const { return FriendSpecifiedFirst; } + SourceLocation getFriendSpecLoc() const { return FriendLoc; } bool isModulePrivateSpecified() const { return ModulePrivateLoc.isValid(); } diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index ed933f27f8df6b..978949a9803ac8 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -8039,9 +8039,6 @@ class Sema final { SourceLocation RParenLoc, bool Failed); void DiagnoseStaticAssertDetails(const Expr *E); - FriendDecl *CheckFriendTypeDecl(SourceLocation LocStart, - SourceLocation FriendLoc, - TypeSourceInfo *TSInfo); Decl *ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS, MultiTemplateParamsArg TemplateParams); NamedDecl *ActOnFriendFunctionDecl(Scope *S, Declarator &D, diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index f1737cb8447677..47c85030f4f6c5 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -79,9 +79,9 @@ bool Parser::isCXXDeclarationStatement( getCurScope(), *II, Tok.getLocation(), SS, /*Template=*/nullptr); if (Actions.isCurrentClassName(*II, getCurScope(), &SS) || isDeductionGuide) { - if (isConstructorDeclarator(/*Unqualified=*/SS.isEmpty(), - isDeductionGuide, - DeclSpec::FriendSpecified::No)) + if (isConstructorDeclarator( + /*Unqualified=*/SS.isEmpty(), isDeductionGuide, + /*IsFriend=*/DeclSpec::FriendSpecified::No)) return true; } else if (SS.isNotEmpty()) { // If the scope is not empty, it could alternatively be something like diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp index 313f073445e8f2..aede602f1de84c 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -1102,18 +1102,13 @@ bool DeclSpec::setFunctionSpecNoreturn(SourceLocation Loc, bool DeclSpec::SetFriendSpec(SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID) { - if (Friend_specified) { + if (isFriendSpecified()) { PrevSpec = "friend"; - // Keep the later location, so that we can later diagnose ill-formed - // declarations like 'friend class X friend;'. Per [class.friend]p3, - // 'friend' must be the first token in a friend declaration that is - // not a function declaration. - FriendLoc = Loc; DiagID = diag::warn_duplicate_declspec; return true; } - Friend_specified = true; + FriendSpecifiedFirst = isEmpty(); FriendLoc = Loc; return false; } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index be23c0fffe0576..375f92e4ac573c 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -17264,6 +17264,26 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc, return true; } + if (TUK == TUK_Friend && Kind == TagTypeKind::Enum) { + // C++23 [dcl.type.elab]p4: + // If an elaborated-type-specifier appears with the friend specifier as + // an entire member-declaration, the member-declaration shall have one + // of the following forms: + // friend class-key nested-name-specifier(opt) identifier ; + // friend class-key simple-template-id ; + // friend class-key nested-name-specifier template(opt) + // simple-template-id ; + // + // Since enum is not a class-key, so declarations like "friend enum E;" + // are ill-formed. Although CWG2363 reaffirms that such declarations are + // invalid, most implementations accept so we issue a pedantic warning. + Diag(KWLoc, diag::ext_enum_friend) << FixItHint::CreateRemoval( + ScopedEnum ? SourceRange(KWLoc, ScopedEnumKWLoc) : KWLoc); + assert(ScopedEnum || !ScopedEnumUsesClassTag); + Diag(KWLoc, diag::note_enum_friend) + << (ScopedEnum + ScopedEnumUsesClassTag); + } + // Figure out the underlying type if this a enum declaration. We need to do // this early, because it's needed to detect if this is an incompatible // redeclaration. diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index ba233c9e2f35d6..79263bc3ff671d 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17545,79 +17545,6 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, return Decl; } -/// Perform semantic analysis of the given friend type declaration. -/// -/// \returns A friend declaration that. -FriendDecl *Sema::CheckFriendTypeDecl(SourceLocation LocStart, - SourceLocation FriendLoc, - TypeSourceInfo *TSInfo) { - assert(TSInfo && "NULL TypeSourceInfo for friend type declaration"); - - 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 (!CodeSynthesisContexts.empty()) { - // Do not complain about the form of friend template types during any kind - // of code synthesis. For template instantiation, we will have complained - // when the template was defined. - } 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<RecordType>()) { - RecordDecl *RD = RT->getDecl(); - - SmallString<16> InsertionText(" "); - InsertionText += RD->getKindName(); - - Diag(TypeRange.getBegin(), - getLangOpts().CPlusPlus11 ? - diag::warn_cxx98_compat_unelaborated_friend_type : - diag::ext_unelaborated_friend_type) - << (unsigned) RD->getTagKind() - << T - << FixItHint::CreateInsertion(getLocForEndOfToken(FriendLoc), - InsertionText); - } else { - Diag(FriendLoc, - getLangOpts().CPlusPlus11 ? - diag::warn_cxx98_compat_nonclass_type_friend : - diag::ext_nonclass_type_friend) - << T - << TypeRange; - } - } else if (T->getAs<EnumType>()) { - Diag(FriendLoc, - getLangOpts().CPlusPlus11 ? - diag::warn_cxx98_compat_enum_friend : - diag::ext_enum_friend) - << T - << TypeRange; - } - - // C++11 [class.friend]p3: - // A friend declaration that does not declare a function shall have one - // of the following forms: - // friend elaborated-type-specifier ; - // friend simple-type-specifier ; - // friend typename-specifier ; - if (getLangOpts().CPlusPlus11 && LocStart != FriendLoc) - Diag(FriendLoc, diag::err_friend_not_first_in_declaration) << T; - } - - // 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. - return FriendDecl::Create(Context, CurContext, - TSInfo->getTypeLoc().getBeginLoc(), TSInfo, - FriendLoc); -} - /// Handle a friend tag declaration where the scope specifier was /// templated. DeclResult Sema::ActOnTemplatedFriendTag( @@ -17755,6 +17682,7 @@ DeclResult Sema::ActOnTemplatedFriendTag( Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS, MultiTemplateParamsArg TempParams) { SourceLocation Loc = DS.getBeginLoc(); + SourceLocation FriendLoc = DS.getFriendSpecLoc(); assert(DS.isFriendSpecified()); assert(DS.getStorageClassSpec() == DeclSpec::SCS_unspecified); @@ -17766,9 +17694,10 @@ Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS, // friend simple-type-specifier ; // friend typename-specifier ; // - // Any declaration with a type qualifier does not have that form. (It's - // legal to specify a qualified type as a friend, you just can't write the - // keywords.) + // If the friend keyword isn't first, or if the declarations has any type + // qualifiers, then the declaration doesn't have that form. + if (getLangOpts().CPlusPlus11 && !DS.isFriendSpecifiedFirst()) + Diag(FriendLoc, diag::err_friend_not_first_in_declaration); if (DS.getTypeQualifiers()) { if (DS.getTypeQualifiers() & DeclSpec::TQ_const) Diag(DS.getConstSpecLoc(), diag::err_friend_decl_spec) << "const"; @@ -17795,24 +17724,35 @@ Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS, if (DiagnoseUnexpandedParameterPack(Loc, TSI, UPPC_FriendDeclaration)) return nullptr; - // 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() && !T->isElaboratedTypeSpecifier()) { - Diag(Loc, diag::err_tagless_friend_type_template) - << DS.getSourceRange(); - return nullptr; + if (!T->isElaboratedTypeSpecifier()) { + if (TempParams.size()) { + // C++23 [dcl.pre]p5: + // In a simple-declaration, the optional init-declarator-list can be + // omitted only when declaring a class or enumeration, that is, when + // the decl-specifier-seq contains either a class-specifier, an + // elaborated-type-specifier with a class-key, or an enum-specifier. + // + // The declaration of a template-declaration or explicit-specialization + // is never a member-declaration, so this must be a simple-declaration + // with no init-declarator-list. Therefore, this is ill-formed. + Diag(Loc, diag::err_tagless_friend_type_template) << DS.getSourceRange(); + return nullptr; + } else if (const RecordDecl *RD = T->getAsRecordDecl()) { + SmallString<16> InsertionText(" "); + InsertionText += RD->getKindName(); + + Diag(Loc, getLangOpts().CPlusPlus11 + ? diag::warn_cxx98_compat_unelaborated_friend_type + : diag::ext_unelaborated_friend_type) + << (unsigned)RD->getTagKind() << T + << FixItHint::CreateInsertion(getLocForEndOfToken(FriendLoc), + InsertionText); + } else { + Diag(FriendLoc, getLangOpts().CPlusPlus11 + ? diag::warn_cxx98_compat_nonclass_type_friend + : diag::ext_nonclass_type_friend) + << T << DS.getSourceRange(); + } } // C++98 [class.friend]p1: A friend of a class is a function @@ -17828,12 +17768,11 @@ Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS, Decl *D; if (!TempParams.empty()) - D = FriendTemplateDecl::Create(Context, CurContext, Loc, - TempParams, - TSI, - DS.getFriendSpecLoc()); + D = FriendTemplateDecl::Create(Context, CurContext, Loc, TempParams, TSI, + FriendLoc); else - D = CheckFriendTypeDecl(Loc, DS.getFriendSpecLoc(), TSI); + D = FriendDecl::Create(Context, CurContext, TSI->getTypeLoc().getBeginLoc(), + TSI, FriendLoc); if (!D) return nullptr; diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index d67b21b4449e01..9c696e072ba4a7 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1407,11 +1407,8 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) { if (!InstTy) return nullptr; - FriendDecl *FD = SemaRef.CheckFriendTypeDecl(D->getBeginLoc(), - D->getFriendLoc(), InstTy); - if (!FD) - return nullptr; - + FriendDecl *FD = FriendDecl::Create( + SemaRef.Context, Owner, D->getLocation(), InstTy, D->getFriendLoc()); FD->setAccess(AS_public); FD->setUnsupportedFriend(D->isUnsupportedFriend()); Owner->addDecl(FD); diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp index 19406518402ff9..8bdd490531119b 100644 --- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp @@ -16,10 +16,10 @@ class A1 { friend union A; // expected-error {{use of 'A' with tag type that does not match previous declaration}} friend enum A; // expected-error {{use of 'A' with tag type that does not match previous declaration}} - friend enum E; -#if __cplusplus <= 199711L // C++03 or earlier modes - // expected-warning@-2 {{befriending enumeration type 'enum E' is a C++11 extension}} -#endif + // expected-warning@-1 {{cannot be declared as a friend}} + // expected-note@-2 {{remove 'enum' to befriend an enum}} + friend enum E; // expected-warning {{cannot be declared as a friend}} + // expected-note@-1 {{remove 'enum' to befriend an enum}} }; template <class T> struct B { // expected-note {{previous use is here}} diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p4.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p4.cpp new file mode 100644 index 00000000000000..b516b1fe15dacc --- /dev/null +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p4.cpp @@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -verify %s -std=c++11 -pedantic-errors + +enum class E; + +template<typename T> +struct A { + enum class F; +}; + +struct B { + template<typename T> + friend enum A<T>::F; // expected-error {{elaborated enum specifier cannot be declared as a friend}} + // expected-note@-1 {{remove 'enum' to befriend an enum}} + + // FIXME: Per [temp.expl.spec]p19, a friend declaration cannot be an explicit specialization + template<> + friend enum A<int>::F; // expected-error {{elaborated enum specifier cannot be declared as a friend}} + // expected-note@-1 {{remove 'enum' to befriend an enum}} + + enum class G; + + friend enum E; // expected-error {{elaborated enum specifier cannot be declared as a friend}} + // expected-note@-1 {{remove 'enum' to befriend an enum}} +}; + +template<typename T> +struct C { + friend enum T::G; // expected-error {{elaborated enum specifier cannot be declared as a friend}} + // expected-note@-1 {{remove 'enum' to befriend an enum}} + friend enum A<T>::G; // expected-error {{elaborated enum specifier cannot be declared as a friend}} + // expected-note@-1 {{remove 'enum' to befriend an enum}} +}; + +struct D { + friend enum B::G; // expected-error {{elaborated enum specifier cannot be declared as a friend}} + // expected-note@-1 {{remove 'enum' to befriend an enum}} + friend enum class B::G; // expected-error {{elaborated enum specifier cannot be declared as a friend}} + // expected-note@-1 {{remove 'enum class' to befriend an enum}} + // expected-error@-2 {{reference to enumeration must use 'enum' not 'enum class'}} +}; diff --git a/clang/test/CXX/drs/dr16xx.cpp b/clang/test/CXX/drs/dr16xx.cpp index 6ce77fbba7ceec..2dd7d1502e59fb 100644 --- a/clang/test/CXX/drs/dr16xx.cpp +++ b/clang/test/CXX/drs/dr16xx.cpp @@ -61,7 +61,7 @@ namespace dr1631 { // dr1631: 3.7 void f(B, int); // TODO: expected- note {{candidate function}} void f(int, A); // #dr1631-f void f(int, A, int = 0); // #dr1631-f-int - + void test() { f({0}, {{1}}); // since-cxx11-error@-1 {{call to 'f' is ambiguous}} @@ -107,6 +107,8 @@ namespace dr1638 { // dr1638: 3.1 struct B { friend enum class A<unsigned>::E; // since-cxx11-error@-1 {{reference to enumeration must use 'enum' not 'enum class'}} + // since-cxx11-error@-2 {{elaborated enum specifier cannot be declared as a friend}} + // since-cxx11-note@-3 {{remove 'enum class' to befriend an enum}} }; #endif } @@ -179,7 +181,7 @@ namespace dr1658 { // dr1658: 5 // In all other cases, we are not so lucky. struct E : A { E(); virtual void foo() = 0; }; // #dr1658-E1 E::E() = default; // #dr1658-E1-ctor - // cxx98-error@-1 {{defaulted function definitions are a C++11 extension}} + // cxx98-error@-1 {{defaulted function definitions are a C++11 extension}} // cxx98-error@-2 {{base class 'A' has private default constructor}} // cxx98-note@-3 {{in defaulted default constructor for 'dr1658::DefCtor::E' first required here}} // cxx98-note@#dr1658-A1 {{implicitly declared private here}} @@ -188,7 +190,7 @@ namespace dr1658 { // dr1658: 5 struct F : virtual A { F(); }; // #dr1658-F1 F::F() = default; // #dr1658-F1-ctor // cxx98-error@-1 {{defaulted function definitions are a C++11 extension}} - // cxx98-error@-2 {{inherited virtual base class 'A' has private default constructor}} + // cxx98-error@-2 {{inherited virtual base class 'A' has private default constructor}} // cxx98-note@-3 {{in defaulted default constructor for 'dr1658::DefCtor::F' first required here}} // cxx98-note@#dr1658-A1 {{implicitly declared private here}} // since-cxx11-error@#dr1658-F1-ctor {{defaulting this default constructor would delete it after its first declaration}} diff --git a/clang/test/CXX/drs/dr23xx.cpp b/clang/test/CXX/drs/dr23xx.cpp index c0463730b6a23a..38c6f8a915600d 100644 --- a/clang/test/CXX/drs/dr23xx.cpp +++ b/clang/test/CXX/drs/dr23xx.cpp @@ -261,9 +261,9 @@ namespace dr2396 { // dr2396: no // FIXME: per P1787 "Calling a conversion function" example, all of the // examples below are well-formed, with B resolving to A::B, but currently - // it's been resolved to dr2396::B. + // it's been resolved to dr2396::B. - // void f(A a) { a.operator B B::*(); } + // void f(A a) { a.operator B B::*(); } // void g(A a) { a.operator decltype(B()) B::*(); } // void g2(A a) { a.operator B decltype(B())::*(); } } @@ -277,4 +277,38 @@ namespace dr2397 { // dr2397: 17 auto (*c)[5] = &a; } } // namespace dr2397 + +// CWG2363 was closed as NAD, but its resolution does affirm that +// a friend declaration cannot have an opaque-enumm-specifier. +namespace dr2363 { // dr2363: yes + +enum class E0; +enum E1 : int; + +struct A { + friend enum class E0; + // since-cxx11-error@-1 {{reference to enumeration must use 'enum' not 'enum class'}} + // expected-error@-2 {{elaborated enum specifier cannot be declared as a friend}} + // expected-note@-3 {{remove 'enum class' to befriend an enum}} + + friend enum E0; + // expected-error@-1 {{elaborated enum specifier cannot be declared as a friend}} + // expected-note@-2 {{remove 'enum' to befriend an enum}} + + friend enum class E1; + // since-cxx11-error@-1 {{reference to enumeration must use 'enum' not 'enum class'}} + // expected-error@-2 {{elaborated enum specifier cannot be declared as a friend}} + // expected-note@-3 {{remove 'enum class' to befriend an enum}} + + friend enum E1; + // expected-error@-1 {{elaborated enum specifier cannot be declared as a friend}} + // expected-note@-2 {{remove 'enum' to befriend an enum}} + + friend enum class E2; + // since-cxx11-error@-1 {{reference to enumeration must use 'enum' not 'enum class'}} + // expected-error@-2 {{elaborated enum specifier cannot be declared as a friend}} + // expected-note@-3 {{remove 'enum class' to befriend an enum}} +}; +} // namespace dr2363 + #endif diff --git a/clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp b/clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp index 2884be146c7c34..e5807993a7a18f 100644 --- a/clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp +++ b/clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp @@ -101,10 +101,14 @@ template<> enum class D<short>::E; struct F { // Per C++11 [class.friend]p3, these friend declarations have no effect. // Only classes and functions can be friends. - template<typename T> friend enum D<T>::E; - template<> friend enum D<short>::E; + template<typename T> friend enum D<T>::E; // expected-warning {{elaborated enum specifier cannot be declared as a friend}} + // expected-note@-1 {{remove 'enum' to befriend an enum}} + template<> friend enum D<short>::E; // expected-warning {{elaborated enum specifier cannot be declared as a friend}} + // expected-note@-1 {{remove 'enum' to befriend an enum}} template<> friend enum D<double>::E { e3 }; // expected-error {{cannot define a type in a friend declaration}} + // expected-warning@-1 {{elaborated enum specifier cannot be declared as a friend}} + // expected-note@-2 {{remove 'enum' to befriend an enum}} private: static const int n = 1; // expected-note {{private here}} diff --git a/clang/test/FixIt/fixit-c++11.cpp b/clang/test/FixIt/fixit-c++11.cpp index a5a47b7c937bab..10f4a9d0554ccc 100644 --- a/clang/test/FixIt/fixit-c++11.cpp +++ b/clang/test/FixIt/fixit-c++11.cpp @@ -44,11 +44,13 @@ namespace ScopedEnum { enum class E b = E::a; // expected-error {{must use 'enum' not 'enum class'}} struct S { friend enum class E; // expected-error {{must use 'enum' not 'enum class'}} + // expected-warning@-1 {{elaborated enum specifier cannot be declared as a friend}} + // expected-note@-2 {{remove 'enum class' to befriend an enum}} }; } -struct S2 { - void f(int i); +struct S2 { + void f(int i); void g(int i); }; diff --git a/clang/test/Parser/cxx-decl.cpp b/clang/test/Parser/cxx-decl.cpp index 8a6e6546cd3ed9..4c4bb87b1b9531 100644 --- a/clang/test/Parser/cxx-decl.cpp +++ b/clang/test/Parser/cxx-decl.cpp @@ -252,9 +252,6 @@ namespace DuplicateFriend { struct A { friend void friend f(); // expected-warning {{duplicate 'friend' declaration specifier}} friend struct B friend; // expected-warning {{duplicate 'friend' declaration specifier}} -#if __cplusplus >= 201103L - // expected-error@-2 {{'friend' must appear first in a non-function declaration}} -#endif }; } diff --git a/clang/test/Parser/cxx0x-decl.cpp b/clang/test/Parser/cxx0x-decl.cpp index 18095a4d989dda..a0b3266c738ff5 100644 --- a/clang/test/Parser/cxx0x-decl.cpp +++ b/clang/test/Parser/cxx0x-decl.cpp @@ -157,7 +157,7 @@ namespace DuplicateSpecifier { struct A { friend constexpr int constexpr friend f(); // expected-warning {{duplicate 'friend' declaration specifier}} \ // expected-error {{duplicate 'constexpr' declaration specifier}} - friend struct A friend; // expected-warning {{duplicate 'friend'}} expected-error {{'friend' must appear first}} + friend struct A friend; // expected-warning {{duplicate 'friend'}} }; constinit constexpr int n1 = 0; // expected-error {{cannot combine with previous 'constinit'}} diff --git a/clang/test/SemaCXX/cxx98-compat.cpp b/clang/test/SemaCXX/cxx98-compat.cpp index d26e3a1e684d54..b31bee672bbe32 100644 --- a/clang/test/SemaCXX/cxx98-compat.cpp +++ b/clang/test/SemaCXX/cxx98-compat.cpp @@ -220,7 +220,8 @@ struct HasExplicitConversion { struct Struct {}; enum Enum { enum_val = 0 }; struct BadFriends { - friend enum ::Enum; // expected-warning {{befriending enumeration type 'enum ::Enum' is incompatible with C++98}} + friend enum ::Enum; // expected-warning {{elaborated enum specifier cannot be declared as a friend}} + // expected-note@-1 {{remove 'enum' to befriend an enum}} friend int; // expected-warning {{non-class friend type 'int' is incompatible with C++98}} friend Struct; // expected-warning {{befriending 'Struct' without 'struct' keyword is incompatible with C++98}} }; diff --git a/clang/test/SemaCXX/enum-scoped.cpp b/clang/test/SemaCXX/enum-scoped.cpp index a4da0607d74ae5..b1d9a215c437c7 100644 --- a/clang/test/SemaCXX/enum-scoped.cpp +++ b/clang/test/SemaCXX/enum-scoped.cpp @@ -174,11 +174,21 @@ namespace N2764 { struct S { friend enum class E; // expected-error {{reference to enumeration must use 'enum' not 'enum class'}} + // expected-warning@-1 {{elaborated enum specifier cannot be declared as a friend}} + // expected-note@-2 {{remove 'enum class' to befriend an enum}} friend enum class F; // expected-error {{reference to enumeration must use 'enum' not 'enum class'}} + // expected-warning@-1 {{elaborated enum specifier cannot be declared as a friend}} + // expected-note@-2 {{remove 'enum class' to befriend an enum}} friend enum G {}; // expected-error {{forward reference}} expected-error {{cannot define a type in a friend declaration}} + // expected-warning@-1 {{elaborated enum specifier cannot be declared as a friend}} + // expected-note@-2 {{remove 'enum' to befriend an enum}} friend enum class H {}; // expected-error {{forward reference}} expected-error {{cannot define a type in a friend declaration}} + // expected-warning@-1 {{elaborated enum specifier cannot be declared as a friend}} + // expected-note@-2 {{remove 'enum' to befriend an enum}} friend enum I : int {}; // expected-error {{forward reference}} expected-error {{cannot define a type in a friend declaration}} + // expected-warning@-1 {{elaborated enum specifier cannot be declared as a friend}} + // expected-note@-2 {{remove 'enum' to befriend an enum}} enum A : int; A a; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits