[clang] [Clang][Sema] Diagnose friend declarations with enum elaborated-type-specifier in all language modes (PR #80171)
Krystian Stasiowski via cfe-commits
cfe-commits at lists.llvm.org
Thu Feb 8 10:25:11 PST 2024
https://github.com/sdkrystian updated https://github.com/llvm/llvm-project/pull/80171
>From 6f8851bf21ff0a6bc9b4f2ff39df6bf00f30ee34 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Wed, 31 Jan 2024 11:09:11 -0500
Subject: [PATCH 1/2] [Clang][Sema] Diagnose friend declarations with enum
elaborated-type-specifier in all language modes
---
clang/docs/ReleaseNotes.rst | 2 +
.../clang/Basic/DiagnosticSemaKinds.td | 6 +-
clang/include/clang/Sema/DeclSpec.h | 14 +-
clang/include/clang/Sema/Sema.h | 3 -
clang/lib/Parse/ParseTentative.cpp | 6 +-
clang/lib/Sema/DeclSpec.cpp | 9 +-
clang/lib/Sema/SemaDecl.cpp | 17 +++
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 | 7 +-
clang/test/CXX/drs/dr23xx.cpp | 33 ++++-
.../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 +
19 files changed, 154 insertions(+), 144 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 e158284aabeab..d06f4ea973a0a 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -156,6 +156,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 b4dc4feee8e63..02cc061cbb319 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 enum specifier cannot be declared as 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/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 77638def60063..3ea08ccf9809f 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
@@ -380,7 +377,8 @@ class DeclSpec {
unsigned FS_noreturn_specified : 1;
// friend-specifier
- unsigned Friend_specified : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned FriendSpecifiedFirst : 1;
// constexpr-specifier
unsigned ConstexprSpecifier : 2;
@@ -470,7 +468,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) {}
@@ -797,9 +795,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 3c26003b5bda7..34fd1eef03779 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -7974,9 +7974,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 f1737cb844767..47c85030f4f6c 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 313f073445e8f..aede602f1de84 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 18a5d93ab8e8c..7c2bc025bf4e0 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -17242,6 +17242,23 @@ 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);
+ }
+
// 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 ab8a967b06a45..d9fd7af5ce0cc 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -17538,79 +17538,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(
@@ -17748,6 +17675,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);
@@ -17759,9 +17687,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";
@@ -17788,24 +17717,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
@@ -17821,12 +17761,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 d67b21b4449e0..9c696e072ba4a 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..b3e7af7429b78 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 at -2 {{befriending enumeration type 'enum E' is a C++11 extension}}
-#endif
+ // expected-warning at -1 {{cannot be declared as a friend}}
+ friend enum E; // expected-warning {{cannot be declared as 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..06c5743f48c4a
--- /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 enum specifier cannot be declared as 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 enum specifier cannot be declared as a friend}}
+
+ enum class G;
+
+ friend enum E; // expected-error {{elaborated enum specifier cannot be declared as a friend}}
+};
+
+template<typename T>
+struct C {
+ friend enum T::G; // expected-error {{elaborated enum specifier cannot be declared as a friend}}
+ friend enum A<T>::G; // expected-error {{elaborated enum specifier cannot be declared as a friend}}
+};
+
+struct D {
+ friend enum B::G; // expected-error {{elaborated enum specifier cannot be declared as a friend}}
+ friend enum class B::G; // expected-error {{elaborated enum specifier cannot be declared as a friend}}
+ // expected-error at -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..3f753b9ac13f9 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 at -1 {{call to 'f' is ambiguous}}
@@ -107,6 +107,7 @@ namespace dr1638 { // dr1638: 3.1
struct B {
friend enum class A<unsigned>::E;
// since-cxx11-error at -1 {{reference to enumeration must use 'enum' not 'enum class'}}
+ // since-cxx11-error at -2 {{elaborated enum specifier cannot be declared as a friend}}
};
#endif
}
@@ -179,7 +180,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 at -1 {{defaulted function definitions are a C++11 extension}}
+ // cxx98-error at -1 {{defaulted function definitions are a C++11 extension}}
// cxx98-error at -2 {{base class 'A' has private default constructor}}
// cxx98-note at -3 {{in defaulted default constructor for 'dr1658::DefCtor::E' first required here}}
// cxx98-note@#dr1658-A1 {{implicitly declared private here}}
@@ -188,7 +189,7 @@ namespace dr1658 { // dr1658: 5
struct F : virtual A { F(); }; // #dr1658-F1
F::F() = default; // #dr1658-F1-ctor
// cxx98-error at -1 {{defaulted function definitions are a C++11 extension}}
- // cxx98-error at -2 {{inherited virtual base class 'A' has private default constructor}}
+ // cxx98-error at -2 {{inherited virtual base class 'A' has private default constructor}}
// cxx98-note at -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 3f8c476427eba..7a7f15cf852c8 100644
--- a/clang/test/CXX/drs/dr23xx.cpp
+++ b/clang/test/CXX/drs/dr23xx.cpp
@@ -236,9 +236,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())::*(); }
}
@@ -252,4 +252,33 @@ 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 at -1 {{reference to enumeration must use 'enum' not 'enum class'}}
+ // expected-error at -2 {{elaborated enum specifier cannot be declared as a friend}}
+
+ friend enum E0;
+ // expected-error at -1 {{elaborated enum specifier cannot be declared as a friend}}
+
+ friend enum class E1;
+ // since-cxx11-error at -1 {{reference to enumeration must use 'enum' not 'enum class'}}
+ // expected-error at -2 {{elaborated enum specifier cannot be declared as a friend}}
+
+ friend enum E1;
+ // expected-error at -1 {{elaborated enum specifier cannot be declared as a friend}}
+
+ friend enum class E2;
+ // since-cxx11-error at -1 {{reference to enumeration must use 'enum' not 'enum class'}}
+ // expected-error at -2 {{elaborated enum specifier cannot be declared as a friend}}
+};
+} // 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 2884be146c7c3..9f5dddf737978 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 {{elaborated enum specifier cannot be declared as a friend}}
+ template<> friend enum D<short>::E; // expected-warning {{elaborated enum specifier cannot be declared as a friend}}
template<> friend enum D<double>::E { e3 }; // expected-error {{cannot define a type in a friend declaration}}
+ // expected-warning at -1 {{elaborated enum specifier cannot be declared as 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..f4c73950579ac 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 at -1 {{elaborated enum specifier cannot be declared as 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 at -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..255e11fbe0cce 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 enum specifier cannot be declared as 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..ac66f43089549 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 at -1 {{elaborated enum specifier cannot be declared as a friend}}
friend enum class F; // expected-error {{reference to enumeration must use 'enum' not 'enum class'}}
+ // expected-warning at -1 {{elaborated enum specifier cannot be declared as a friend}}
friend enum G {}; // expected-error {{forward reference}} expected-error {{cannot define a type in a friend declaration}}
+ // expected-warning at -1 {{elaborated enum specifier cannot be declared as a friend}}
friend enum class H {}; // expected-error {{forward reference}} expected-error {{cannot define a type in a friend declaration}}
+ // expected-warning at -1 {{elaborated enum specifier cannot be declared as a friend}}
friend enum I : int {}; // expected-error {{forward reference}} expected-error {{cannot define a type in a friend declaration}}
+ // expected-warning at -1 {{elaborated enum specifier cannot be declared as a friend}}
enum A : int;
A a;
>From 9577266ee2d3fe6681bdfe24d52fe74727cc0e75 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Thu, 8 Feb 2024 13:24:57 -0500
Subject: [PATCH 2/2] [FOLD]
---
clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 ++
clang/lib/Sema/SemaDecl.cpp | 3 +++
.../CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp | 2 ++
.../CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p4.cpp | 9 ++++++++-
clang/test/CXX/drs/dr16xx.cpp | 1 +
clang/test/CXX/drs/dr23xx.cpp | 5 +++++
.../CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp | 3 +++
clang/test/FixIt/fixit-c++11.cpp | 5 +++--
clang/test/SemaCXX/cxx98-compat.cpp | 1 +
clang/test/SemaCXX/enum-scoped.cpp | 5 +++++
10 files changed, 33 insertions(+), 3 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 02cc061cbb319..adcdf2425c929 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1639,6 +1639,8 @@ def err_unexpected_friend : Error<
def ext_enum_friend : ExtWarn<
"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/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 7c2bc025bf4e0..add7a56554596 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -17257,6 +17257,9 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
// 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
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 b3e7af7429b78..8bdd490531119 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
@@ -17,7 +17,9 @@ class A1 {
friend enum A; // expected-error {{use of 'A' with tag type that does not match previous declaration}}
// expected-warning at -1 {{cannot be declared as a friend}}
+ // expected-note at -2 {{remove 'enum' to befriend an enum}}
friend enum E; // expected-warning {{cannot be declared as a friend}}
+ // expected-note at -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
index 06c5743f48c4a..b516b1fe15dac 100644
--- 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
@@ -10,24 +10,31 @@ struct A {
struct B {
template<typename T>
friend enum A<T>::F; // expected-error {{elaborated enum specifier cannot be declared as a friend}}
+ // expected-note at -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 at -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 at -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 at -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 at -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 at -1 {{remove 'enum' to befriend an enum}}
friend enum class B::G; // expected-error {{elaborated enum specifier cannot be declared as a friend}}
- // expected-error at -1 {{reference to enumeration must use 'enum' not 'enum class'}}
+ // expected-note at -1 {{remove 'enum class' to befriend an enum}}
+ // expected-error at -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 3f753b9ac13f9..2dd7d1502e59f 100644
--- a/clang/test/CXX/drs/dr16xx.cpp
+++ b/clang/test/CXX/drs/dr16xx.cpp
@@ -108,6 +108,7 @@ namespace dr1638 { // dr1638: 3.1
friend enum class A<unsigned>::E;
// since-cxx11-error at -1 {{reference to enumeration must use 'enum' not 'enum class'}}
// since-cxx11-error at -2 {{elaborated enum specifier cannot be declared as a friend}}
+ // since-cxx11-note at -3 {{remove 'enum class' to befriend an enum}}
};
#endif
}
diff --git a/clang/test/CXX/drs/dr23xx.cpp b/clang/test/CXX/drs/dr23xx.cpp
index 7a7f15cf852c8..96bcee8d08898 100644
--- a/clang/test/CXX/drs/dr23xx.cpp
+++ b/clang/test/CXX/drs/dr23xx.cpp
@@ -264,20 +264,25 @@ struct A {
friend enum class E0;
// since-cxx11-error at -1 {{reference to enumeration must use 'enum' not 'enum class'}}
// expected-error at -2 {{elaborated enum specifier cannot be declared as a friend}}
+ // expected-note at -3 {{remove 'enum class' to befriend an enum}}
friend enum E0;
// expected-error at -1 {{elaborated enum specifier cannot be declared as a friend}}
+ // expected-note at -2 {{remove 'enum' to befriend an enum}}
friend enum class E1;
// since-cxx11-error at -1 {{reference to enumeration must use 'enum' not 'enum class'}}
// expected-error at -2 {{elaborated enum specifier cannot be declared as a friend}}
+ // expected-note at -3 {{remove 'enum class' to befriend an enum}}
friend enum E1;
// expected-error at -1 {{elaborated enum specifier cannot be declared as a friend}}
+ // expected-note at -2 {{remove 'enum' to befriend an enum}}
friend enum class E2;
// since-cxx11-error at -1 {{reference to enumeration must use 'enum' not 'enum class'}}
// expected-error at -2 {{elaborated enum specifier cannot be declared as a friend}}
+ // expected-note at -3 {{remove 'enum class' to befriend an enum}}
};
} // namespace dr2363
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 9f5dddf737978..e5807993a7a18 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
@@ -102,10 +102,13 @@ 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; // expected-warning {{elaborated enum specifier cannot be declared as a friend}}
+ // expected-note at -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 at -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 at -1 {{elaborated enum specifier cannot be declared as a friend}}
+ // expected-note at -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 f4c73950579ac..10f4a9d0554cc 100644
--- a/clang/test/FixIt/fixit-c++11.cpp
+++ b/clang/test/FixIt/fixit-c++11.cpp
@@ -45,11 +45,12 @@ namespace ScopedEnum {
struct S {
friend enum class E; // expected-error {{must use 'enum' not 'enum class'}}
// expected-warning at -1 {{elaborated enum specifier cannot be declared as a friend}}
+ // expected-note at -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/SemaCXX/cxx98-compat.cpp b/clang/test/SemaCXX/cxx98-compat.cpp
index 255e11fbe0cce..b31bee672bbe3 100644
--- a/clang/test/SemaCXX/cxx98-compat.cpp
+++ b/clang/test/SemaCXX/cxx98-compat.cpp
@@ -221,6 +221,7 @@ struct Struct {};
enum Enum { enum_val = 0 };
struct BadFriends {
friend enum ::Enum; // expected-warning {{elaborated enum specifier cannot be declared as a friend}}
+ // expected-note at -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 ac66f43089549..b1d9a215c437c 100644
--- a/clang/test/SemaCXX/enum-scoped.cpp
+++ b/clang/test/SemaCXX/enum-scoped.cpp
@@ -175,15 +175,20 @@ namespace N2764 {
struct S {
friend enum class E; // expected-error {{reference to enumeration must use 'enum' not 'enum class'}}
// expected-warning at -1 {{elaborated enum specifier cannot be declared as a friend}}
+ // expected-note at -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 at -1 {{elaborated enum specifier cannot be declared as a friend}}
+ // expected-note at -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 at -1 {{elaborated enum specifier cannot be declared as a friend}}
+ // expected-note at -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 at -1 {{elaborated enum specifier cannot be declared as a friend}}
+ // expected-note at -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 at -1 {{elaborated enum specifier cannot be declared as a friend}}
+ // expected-note at -2 {{remove 'enum' to befriend an enum}}
enum A : int;
A a;
More information about the cfe-commits
mailing list