https://github.com/sdkrystian updated https://github.com/llvm/llvm-project/pull/80171
>From a39aab07696acfea3e3b78d6ad92c8b771eaf0d2 Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Wed, 31 Jan 2024 11:09:11 -0500 Subject: [PATCH 1/3] [Clang][Sema] Diagnose friend declarations with enum elaborated-type-specifier in all language modes --- clang/docs/ReleaseNotes.rst | 1 + .../clang/Basic/DiagnosticSemaKinds.td | 6 +- clang/include/clang/Parse/Parser.h | 8 +- clang/include/clang/Sema/DeclSpec.h | 20 +-- clang/include/clang/Sema/Sema.h | 3 - clang/lib/Parse/ParseDecl.cpp | 2 +- clang/lib/Parse/ParseTentative.cpp | 2 +- clang/lib/Sema/DeclSpec.cpp | 10 +- clang/lib/Sema/SemaDecl.cpp | 16 ++ clang/lib/Sema/SemaDeclCXX.cpp | 137 +++++------------- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 7 +- .../dcl.spec/dcl.type/dcl.type.elab/p3.cpp | 6 +- .../dcl.spec/dcl.type/dcl.type.elab/p4.cpp | 33 +++++ clang/test/CXX/drs/dr16xx.cpp | 1 + .../temp.class/temp.mem.enum/p1.cpp | 5 +- clang/test/FixIt/fixit-c++11.cpp | 1 + clang/test/Parser/cxx-decl.cpp | 3 - clang/test/Parser/cxx0x-decl.cpp | 2 +- clang/test/SemaCXX/cxx98-compat.cpp | 2 +- clang/test/SemaCXX/enum-scoped.cpp | 5 + 20 files changed, 123 insertions(+), 147 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 efd925e990f43..1624ba30dd326 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -145,6 +145,7 @@ Improvements to Clang's diagnostics prints. - Clang now diagnoses member template declarations with multiple declarators. +- Clang now diagnoses friend declarations with an ``enum`` elaborated-type-specifier outside of 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 7638a7e84c3c0..ca0c4509a9eb5 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1637,10 +1637,8 @@ 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 enumeration type cannot be a friend">, + InGroup<DiagGroup<"friend-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/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index da18cf88edcc9..ab92d4e7e9a2f 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2552,10 +2552,10 @@ class Parser : public CodeCompletionHandler { /// Starting with a scope specifier, identifier, or /// template-id that refers to the current class, determine whether /// this is a constructor declarator. - bool isConstructorDeclarator( - bool Unqualified, bool DeductionGuide = false, - DeclSpec::FriendSpecified IsFriend = DeclSpec::FriendSpecified::No, - const ParsedTemplateInfo *TemplateInfo = nullptr); + bool + isConstructorDeclarator(bool Unqualified, bool DeductionGuide = false, + bool IsFriend = false, + const ParsedTemplateInfo *TemplateInfo = nullptr); /// Specifies the context in which type-id/expression /// disambiguation will occur. diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index 77638def60063..c7266b66c014d 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -346,11 +346,6 @@ class DeclSpec { // FIXME: Attributes should be included here. }; - enum FriendSpecified : bool { - No, - Yes, - }; - private: // storage-class-specifier /*SCS*/unsigned StorageClassSpec : 3; @@ -380,7 +375,8 @@ class DeclSpec { unsigned FS_noreturn_specified : 1; // friend-specifier - unsigned Friend_specified : 1; + unsigned FriendSpecified : 1; + unsigned FriendSpecifiedFirst : 1; // constexpr-specifier unsigned ConstexprSpecifier : 2; @@ -470,9 +466,9 @@ 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), - ConstexprSpecifier( - static_cast<unsigned>(ConstexprSpecKind::Unspecified)), + FS_noreturn_specified(false), FriendSpecified(false), + FriendSpecifiedFirst(false), ConstexprSpecifier(static_cast<unsigned>( + ConstexprSpecKind::Unspecified)), Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {} // storage-class-specifier @@ -796,9 +792,9 @@ class DeclSpec { bool SetConstexprSpec(ConstexprSpecKind ConstexprKind, SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID); - FriendSpecified isFriendSpecified() const { - return static_cast<FriendSpecified>(Friend_specified); - } + bool isFriendSpecified() const { return FriendSpecified; } + + bool isFriendSpecifiedFirst() const { return FriendSpecifiedFirst; } SourceLocation getFriendSpecLoc() const { return FriendLoc; } diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index b780cee86c3c3..a5e519750a8f4 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -7973,9 +7973,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/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index a186253954f68..703603ea11db7 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -5936,7 +5936,7 @@ bool Parser::isDeclarationSpecifier( } bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide, - DeclSpec::FriendSpecified IsFriend, + bool IsFriend, const ParsedTemplateInfo *TemplateInfo) { RevertingTentativeParsingAction TPA(*this); // Parse the C++ scope specifier. diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index f1737cb844767..64aaa4ea0b206 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -81,7 +81,7 @@ bool Parser::isCXXDeclarationStatement( isDeductionGuide) { if (isConstructorDeclarator(/*Unqualified=*/SS.isEmpty(), isDeductionGuide, - DeclSpec::FriendSpecified::No)) + /*IsFriend*/ false)) 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 313f073445e8f..1c99508802ee7 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -1102,18 +1102,14 @@ bool DeclSpec::setFunctionSpecNoreturn(SourceLocation Loc, bool DeclSpec::SetFriendSpec(SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID) { - if (Friend_specified) { + if (FriendSpecified) { 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; + FriendSpecified = true; + FriendSpecifiedFirst = isEmpty(); FriendLoc = Loc; return false; } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index fd1c47008d685..99326c5c925c0 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -17208,6 +17208,22 @@ 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); + } + // 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 5adc262cd6bc9..c0a6c8ca81a49 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17534,79 +17534,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( @@ -17744,6 +17671,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); @@ -17755,9 +17683,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"; @@ -17784,24 +17713,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 @@ -17817,12 +17757,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 fcb27a880290b..36d2c441391d8 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 19406518402ff..75a33c0390897 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,8 @@ 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 a friend}} + friend enum E; // expected-warning {{cannot be a friend}} }; 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 0000000000000..c7792f3dd29ac --- /dev/null +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p4.cpp @@ -0,0 +1,33 @@ +// 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 enumeration type cannot be a friend}} + + // FIXME: Per [temp.expl.spec]p19, a friend declaration cannot be an explicit specialization + template<> + friend enum A<int>::F; // expected-error {{elaborated enumeration type cannot be a friend}} + + enum class G; + + friend enum E; // expected-error {{elaborated enumeration type cannot be a friend}} +}; + +template<typename T> +struct C { + friend enum T::G; // expected-error {{elaborated enumeration type cannot be a friend}} + friend enum A<T>::G; // expected-error {{elaborated enumeration type cannot be a friend}} +}; + +struct D { + friend enum B::G; // expected-error {{elaborated enumeration type cannot be a friend}} + friend enum class B::G; // expected-error {{elaborated enumeration type cannot be a friend}} + // expected-error@-1 {{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 6ce77fbba7cee..a6de1887baa87 100644 --- a/clang/test/CXX/drs/dr16xx.cpp +++ b/clang/test/CXX/drs/dr16xx.cpp @@ -107,6 +107,7 @@ 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 {{cannot be a friend}} }; #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 2884be146c7c3..012248de06057 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,11 @@ 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 {{cannot be a friend}} + template<> friend enum D<short>::E; // expected-warning {{cannot be a friend}} template<> friend enum D<double>::E { e3 }; // expected-error {{cannot define a type in a friend declaration}} + // expected-warning@-1 {{cannot be a friend}} 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 a5a47b7c937ba..627ef73d44d1e 100644 --- a/clang/test/FixIt/fixit-c++11.cpp +++ b/clang/test/FixIt/fixit-c++11.cpp @@ -44,6 +44,7 @@ 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 {{cannot be a friend}} }; } diff --git a/clang/test/Parser/cxx-decl.cpp b/clang/test/Parser/cxx-decl.cpp index 8a6e6546cd3ed..4c4bb87b1b953 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 18095a4d989dd..a0b3266c738ff 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 d26e3a1e684d5..0a4eea14ce3d9 100644 --- a/clang/test/SemaCXX/cxx98-compat.cpp +++ b/clang/test/SemaCXX/cxx98-compat.cpp @@ -220,7 +220,7 @@ 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 enumeration type cannot be a friend}} 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 a4da0607d74ae..fb7b41a30f3b7 100644 --- a/clang/test/SemaCXX/enum-scoped.cpp +++ b/clang/test/SemaCXX/enum-scoped.cpp @@ -174,11 +174,16 @@ namespace N2764 { struct S { friend enum class E; // expected-error {{reference to enumeration must use 'enum' not 'enum class'}} + // expected-warning@-1 {{cannot be a friend}} friend enum class F; // expected-error {{reference to enumeration must use 'enum' not 'enum class'}} + // expected-warning@-1 {{cannot be a friend}} friend enum G {}; // expected-error {{forward reference}} expected-error {{cannot define a type in a friend declaration}} + // expected-warning@-1 {{cannot be a friend}} friend enum class H {}; // expected-error {{forward reference}} expected-error {{cannot define a type in a friend declaration}} + // expected-warning@-1 {{cannot be a friend}} friend enum I : int {}; // expected-error {{forward reference}} expected-error {{cannot define a type in a friend declaration}} + // expected-warning@-1 {{cannot be a friend}} enum A : int; A a; >From 31315707c47e28c9222838cdb7c531acbfa40cef Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Thu, 1 Feb 2024 11:47:57 -0500 Subject: [PATCH 2/3] [FOLD] --- clang/test/CXX/drs/dr23xx.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/clang/test/CXX/drs/dr23xx.cpp b/clang/test/CXX/drs/dr23xx.cpp index 265b51aff8ae6..c1121e01a4090 100644 --- a/clang/test/CXX/drs/dr23xx.cpp +++ b/clang/test/CXX/drs/dr23xx.cpp @@ -252,4 +252,14 @@ 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 +struct A { + friend enum class E; // since-cxx11-error {{reference to enumeration must use 'enum' not 'enum class'}} + // expected-error@-1 {{elaborated enumeration type cannot be a friend}} +}; +} // namespace dr2363 + #endif >From 1f7961a1e108ba3b2eb70cef55b05cd369922cfd Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Tue, 6 Feb 2024 15:07:18 -0500 Subject: [PATCH 3/3] [FOLD] --- clang/docs/ReleaseNotes.rst | 2 +- clang/include/clang/Sema/DeclSpec.h | 2 ++ clang/test/CXX/drs/dr16xx.cpp | 2 +- clang/test/CXX/drs/dr23xx.cpp | 23 +++++++++++++++++++++-- 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 1624ba30dd326..b01da046b2cf1 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -145,7 +145,7 @@ Improvements to Clang's diagnostics prints. - Clang now diagnoses member template declarations with multiple declarators. -- Clang now diagnoses friend declarations with an ``enum`` elaborated-type-specifier outside of C++98. +- 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/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index c7266b66c014d..f3499edb7c681 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -375,7 +375,9 @@ class DeclSpec { unsigned FS_noreturn_specified : 1; // friend-specifier + LLVM_PREFERRED_TYPE(bool) unsigned FriendSpecified : 1; + LLVM_PREFERRED_TYPE(bool) unsigned FriendSpecifiedFirst : 1; // constexpr-specifier diff --git a/clang/test/CXX/drs/dr16xx.cpp b/clang/test/CXX/drs/dr16xx.cpp index a6de1887baa87..60fc628c69de6 100644 --- a/clang/test/CXX/drs/dr16xx.cpp +++ b/clang/test/CXX/drs/dr16xx.cpp @@ -107,7 +107,7 @@ 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 {{cannot be a friend}} + // since-cxx11-error@-2 {{elaborated enumeration type cannot be a friend}} }; #endif } diff --git a/clang/test/CXX/drs/dr23xx.cpp b/clang/test/CXX/drs/dr23xx.cpp index c1121e01a4090..af457784a17da 100644 --- a/clang/test/CXX/drs/dr23xx.cpp +++ b/clang/test/CXX/drs/dr23xx.cpp @@ -256,9 +256,28 @@ namespace dr2397 { // dr2397: 17 // 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 E; // since-cxx11-error {{reference to enumeration must use 'enum' not 'enum class'}} - // expected-error@-1 {{elaborated enumeration type cannot be a friend}} + friend enum class E0; + // since-cxx11-error@-1 {{reference to enumeration must use 'enum' not 'enum class'}} + // expected-error@-2 {{elaborated enumeration type cannot be a friend}} + + friend enum E0; + // expected-error@-1 {{elaborated enumeration type cannot be a friend}} + + friend enum class E1; + // since-cxx11-error@-1 {{reference to enumeration must use 'enum' not 'enum class'}} + // expected-error@-2 {{elaborated enumeration type cannot be a friend}} + + friend enum E1; + // expected-error@-1 {{elaborated enumeration type cannot be a friend}} + + friend enum class E2; + // since-cxx11-error@-1 {{reference to enumeration must use 'enum' not 'enum class'}} + // expected-error@-2 {{elaborated enumeration type cannot be a friend}} }; } // namespace dr2363 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits