diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -356,6 +356,8 @@ the time of checking, which should now allow the libstdc++ ranges implementation to work for at least trivial examples. This fixes `Issue 44178 `_. +- Clang implements DR2621, correcting a defect in ``using enum`` handling. The + name is found via ordinary lookup so typedefs are found. C++2b Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -607,6 +607,9 @@ def ext_using_enum_declaration : ExtWarn< "using enum declaration is a C++20 extension">, InGroup; +def err_using_enum_expect_identifier : Error< + "using enum %select{requires an enum or typedef name|" + "does not permit an elaborated enum specifier}0">; def err_constructor_bad_name : Error< "missing return type for function %0; did you mean the constructor name %1?">; def err_destructor_tilde_identifier : Error< diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -562,6 +562,8 @@ "with C++ standards before C++20">, InGroup, DefaultIgnore; def err_using_enum_is_dependent : Error< "using-enum cannot name a dependent type">; +def err_using_enum_not_enum : Error< + "%0 is not an enumerated type">; def err_ambiguous_inherited_constructor : Error< "constructor of %0 inherited from multiple base class subobjects">; def note_ambiguous_inherited_constructor_using : Note< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -6125,7 +6125,9 @@ const ParsedAttributesView &AttrList); Decl *ActOnUsingEnumDeclaration(Scope *CurScope, AccessSpecifier AS, SourceLocation UsingLoc, - SourceLocation EnumLoc, const DeclSpec &); + SourceLocation EnumLoc, + SourceLocation IdentLoc, IdentifierInfo &II, + CXXScopeSpec *SS = nullptr); Decl *ActOnAliasDeclaration(Scope *CurScope, AccessSpecifier AS, MultiTemplateParamsArg TemplateParams, SourceLocation UsingLoc, UnqualifiedId &Name, diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -678,6 +678,8 @@ /// /// using-enum-declaration: [C++20, dcl.enum] /// 'using' elaborated-enum-specifier ; +/// The terminal name of the elaborated-enum-specifier undergoes +/// ordinary lookup /// /// elaborated-enum-specifier: /// 'enum' nested-name-specifier[opt] identifier @@ -697,21 +699,47 @@ DiagnoseCXX11AttributeExtension(PrefixAttrs); - DeclSpec DS(AttrFactory); - ParseEnumSpecifier(UELoc, DS, TemplateInfo, AS, - // DSC_trailing has the semantics we desire - DeclSpecContext::DSC_trailing); - if (TemplateInfo.Kind) { SourceRange R = TemplateInfo.getSourceRange(); Diag(UsingLoc, diag::err_templated_using_directive_declaration) << 1 /* declaration */ << R << FixItHint::CreateRemoval(R); + SkipUntil(tok::semi); + return nullptr; + } + CXXScopeSpec SS; + if (ParseOptionalCXXScopeSpecifier(SS, /*ParsedType=*/nullptr, + /*ObectHasErrors=*/false, + /*EnteringConttext=*/false, + /*MayBePseudoDestructor=*/nullptr, + /*IsTypename=*/false, + /*IdentifierInfo=*/nullptr, + /*OnlyNamespace=*/false, + /*InUsingDeclaration=*/true)) { + SkipUntil(tok::semi); + return nullptr; + } + if (Tok.is(tok::code_completion)) { + cutOffParsing(); + Actions.CodeCompleteUsing(getCurScope()); + return nullptr; + } + + if (!Tok.is(tok::identifier)) { + Diag(Tok.getLocation(), diag::err_using_enum_expect_identifier) + << Tok.is(tok::kw_enum); + SkipUntil(tok::semi); + return nullptr; + } + IdentifierInfo *IdentInfo = Tok.getIdentifierInfo(); + SourceLocation IdentLoc = ConsumeToken(); + Decl *UED = Actions.ActOnUsingEnumDeclaration( + getCurScope(), AS, UsingLoc, UELoc, IdentLoc, *IdentInfo, &SS); + if (!UED) { + SkipUntil(tok::semi); return nullptr; } - Decl *UED = Actions.ActOnUsingEnumDeclaration(getCurScope(), AS, UsingLoc, - UELoc, DS); DeclEnd = Tok.getLocation(); if (ExpectAndConsume(tok::semi, diag::err_expected_after, "using-enum declaration")) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -11851,30 +11851,30 @@ Decl *Sema::ActOnUsingEnumDeclaration(Scope *S, AccessSpecifier AS, SourceLocation UsingLoc, SourceLocation EnumLoc, - const DeclSpec &DS) { - switch (DS.getTypeSpecType()) { - case DeclSpec::TST_error: - // This will already have been diagnosed + SourceLocation IdentLoc, + IdentifierInfo &II, CXXScopeSpec *SS) { + assert(!SS->isInvalid() && "ScopeSpec is invalid"); + ParsedType TypeRep = getTypeName(II, IdentLoc, S, SS); + if (!TypeRep) { + Diag(IdentLoc, SS && isDependentScopeSpecifier(*SS) + ? diag::err_using_enum_is_dependent + : diag::err_unknown_typename) + << II.getName() + << SourceRange(SS ? SS->getBeginLoc() : IdentLoc, IdentLoc); return nullptr; + } - case DeclSpec::TST_enum: - break; - - case DeclSpec::TST_typename: - Diag(DS.getTypeSpecTypeLoc(), diag::err_using_enum_is_dependent); + auto *Enum = dyn_cast_if_present(TypeRep.get()->getAsTagDecl()); + if (!Enum) { + Diag(IdentLoc, diag::err_using_enum_not_enum) << TypeRep.get(); return nullptr; - - default: - llvm_unreachable("unexpected DeclSpec type"); } - // As with enum-decls, we ignore attributes for now. - auto *Enum = cast(DS.getRepAsDecl()); if (auto *Def = Enum->getDefinition()) Enum = Def; - auto *UD = BuildUsingEnumDeclaration(S, AS, UsingLoc, EnumLoc, - DS.getTypeSpecTypeNameLoc(), Enum); + auto *UD = + BuildUsingEnumDeclaration(S, AS, UsingLoc, EnumLoc, IdentLoc, Enum); if (UD) PushOnScopeChains(UD, S, /*AddToContext*/ false); diff --git a/clang/test/CXX/drs/dr26xx.cpp b/clang/test/CXX/drs/dr26xx.cpp --- a/clang/test/CXX/drs/dr26xx.cpp +++ b/clang/test/CXX/drs/dr26xx.cpp @@ -1,5 +1,19 @@ // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify +namespace dr2621 { // dr2621: yes +enum class E { a }; +namespace One { +using E_t = E; +using enum E_t; // typedef ok +auto v = a; +} +namespace Two { +using dr2621::E; +int E; // we see this +using enum E; // expected-error {{unknown type name E}} +} +} + namespace dr2628 { // dr2628: yes template diff --git a/clang/test/CodeCompletion/using-enum.cpp b/clang/test/CodeCompletion/using-enum.cpp --- a/clang/test/CodeCompletion/using-enum.cpp +++ b/clang/test/CodeCompletion/using-enum.cpp @@ -2,6 +2,6 @@ namespace N2 { using enum AAA; - // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:4:14 %s | FileCheck -check-prefix=CHECK-CC1 %s + // RUN: %clang_cc1 -std=c++20 -fsyntax-only -code-completion-at=%s:4:14 %s | FileCheck -check-prefix=CHECK-CC1 %s // CHECK-CC1: COMPLETION: AAA }; diff --git a/clang/test/Parser/cxx20-using-enum.cpp b/clang/test/Parser/cxx20-using-enum.cpp --- a/clang/test/Parser/cxx20-using-enum.cpp +++ b/clang/test/Parser/cxx20-using-enum.cpp @@ -4,9 +4,9 @@ namespace A {} void f() { - using enum A::+; // expected-error {{expected identifier}} - using enum; // expected-error {{expected identifier or '{'}} - using enum class; // expected-error {{expected identifier or '{'}} - using enum : blah; // expected-error {{unknown type name 'blah'}} expected-error {{unnamed enumeration must be a definition}} + using enum A::+; // expected-error {{using enum requires an enum or typedef name}} + using enum; // expected-error {{using enum requires an enum or typedef name}} + using enum class; // expected-error {{using enum requires an enum or typedef name}} + using enum enum q; // expected-error {{using enum does not permit an elaborated enum specifier}} } } diff --git a/clang/test/SemaCXX/cxx20-using-enum.cpp b/clang/test/SemaCXX/cxx20-using-enum.cpp --- a/clang/test/SemaCXX/cxx20-using-enum.cpp +++ b/clang/test/SemaCXX/cxx20-using-enum.cpp @@ -8,7 +8,7 @@ enum A { a, // expected-note{{declared here}} b, c }; -class C; // expected-note{{previous use}} +class C; enum class D : int; enum class D { d, e, @@ -20,11 +20,11 @@ #if __cplusplus < 202002 // expected-warning@-2{{is a C++20 extension}} #endif -using enum Bob::B; // expected-error{{no enum named 'B'}} +using enum Bob::B; // expected-error{{unknown type name B}} #if __cplusplus < 202002 // expected-warning@-2{{is a C++20 extension}} #endif -using enum Bob::C; // expected-error{{tag type that does not match}} +using enum Bob::C; // expected-error{{'Bob::C' is not an enumerated type}} #if __cplusplus < 202002 // expected-warning@-2{{is a C++20 extension}} #endif @@ -38,6 +38,16 @@ #if __cplusplus < 202002 // expected-warning@-2{{is a C++20 extension}} #endif + +void DR2621() { + using A_t = Bob::A; + using enum A_t; +#if __cplusplus < 202002 +// expected-warning@-2{{is a C++20 extension}} +#endif + A_t x = a; +} + } // namespace One namespace Two {