Skip to content

Commit 3d20806

Browse files
committed
[clang][DR2621] using enum NAME lookup fix
Although using-enum's grammar is 'using elaborated-enum-specifier', the lookup for the enum is ordinary lookup (and not the tagged-type lookup that normally occurs wth an tagged-type specifier). Thus (a) we can find typedefs and (b) do not find enum tags hidden by a non-tag name (the struct stat thing). This reimplements that part of using-enum handling, to address DR2621, where clang's behaviour does not match std intent (and other compilers). Reviewed By: aaron.ballman Differential Revision: https://reviews.llvm.org/D134283
1 parent 868a8fd commit 3d20806

File tree

10 files changed

+93
-32
lines changed

10 files changed

+93
-32
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,8 @@ C++20 Feature Support
356356
the time of checking, which should now allow the libstdc++ ranges implementation
357357
to work for at least trivial examples. This fixes
358358
`Issue 44178 <https://github.com/llvm/llvm-project/issues/44178>`_.
359+
- Clang implements DR2621, correcting a defect in ``using enum`` handling. The
360+
name is found via ordinary lookup so typedefs are found.
359361

360362
C++2b Feature Support
361363
^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,9 @@ def warn_cxx17_compat_using_enum_declaration : Warning<
607607
def ext_using_enum_declaration : ExtWarn<
608608
"using enum declaration is a C++20 extension">,
609609
InGroup<CXX20>;
610+
def err_using_enum_expect_identifier : Error<
611+
"using enum %select{requires an enum or typedef name|"
612+
"does not permit an elaborated enum specifier}0">;
610613
def err_constructor_bad_name : Error<
611614
"missing return type for function %0; did you mean the constructor name %1?">;
612615
def err_destructor_tilde_identifier : Error<

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,8 @@ def warn_cxx17_compat_using_decl_class_member_enumerator : Warning<
562562
"with C++ standards before C++20">, InGroup<CXXPre20Compat>, DefaultIgnore;
563563
def err_using_enum_is_dependent : Error<
564564
"using-enum cannot name a dependent type">;
565+
def err_using_enum_not_enum : Error<
566+
"%0 is not an enumerated type">;
565567
def err_ambiguous_inherited_constructor : Error<
566568
"constructor of %0 inherited from multiple base class subobjects">;
567569
def note_ambiguous_inherited_constructor_using : Note<

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6125,7 +6125,9 @@ class Sema final {
61256125
const ParsedAttributesView &AttrList);
61266126
Decl *ActOnUsingEnumDeclaration(Scope *CurScope, AccessSpecifier AS,
61276127
SourceLocation UsingLoc,
6128-
SourceLocation EnumLoc, const DeclSpec &);
6128+
SourceLocation EnumLoc,
6129+
SourceLocation IdentLoc, IdentifierInfo &II,
6130+
CXXScopeSpec *SS = nullptr);
61296131
Decl *ActOnAliasDeclaration(Scope *CurScope, AccessSpecifier AS,
61306132
MultiTemplateParamsArg TemplateParams,
61316133
SourceLocation UsingLoc, UnqualifiedId &Name,

clang/lib/Parse/ParseDeclCXX.cpp

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,8 @@ bool Parser::ParseUsingDeclarator(DeclaratorContext Context,
678678
///
679679
/// using-enum-declaration: [C++20, dcl.enum]
680680
/// 'using' elaborated-enum-specifier ;
681+
/// The terminal name of the elaborated-enum-specifier undergoes
682+
/// ordinary lookup
681683
///
682684
/// elaborated-enum-specifier:
683685
/// 'enum' nested-name-specifier[opt] identifier
@@ -697,21 +699,47 @@ Parser::DeclGroupPtrTy Parser::ParseUsingDeclaration(
697699

698700
DiagnoseCXX11AttributeExtension(PrefixAttrs);
699701

700-
DeclSpec DS(AttrFactory);
701-
ParseEnumSpecifier(UELoc, DS, TemplateInfo, AS,
702-
// DSC_trailing has the semantics we desire
703-
DeclSpecContext::DSC_trailing);
704-
705702
if (TemplateInfo.Kind) {
706703
SourceRange R = TemplateInfo.getSourceRange();
707704
Diag(UsingLoc, diag::err_templated_using_directive_declaration)
708705
<< 1 /* declaration */ << R << FixItHint::CreateRemoval(R);
706+
SkipUntil(tok::semi);
707+
return nullptr;
708+
}
709+
CXXScopeSpec SS;
710+
if (ParseOptionalCXXScopeSpecifier(SS, /*ParsedType=*/nullptr,
711+
/*ObectHasErrors=*/false,
712+
/*EnteringConttext=*/false,
713+
/*MayBePseudoDestructor=*/nullptr,
714+
/*IsTypename=*/false,
715+
/*IdentifierInfo=*/nullptr,
716+
/*OnlyNamespace=*/false,
717+
/*InUsingDeclaration=*/true)) {
718+
SkipUntil(tok::semi);
719+
return nullptr;
720+
}
709721

722+
if (Tok.is(tok::code_completion)) {
723+
cutOffParsing();
724+
Actions.CodeCompleteUsing(getCurScope());
725+
return nullptr;
726+
}
727+
728+
if (!Tok.is(tok::identifier)) {
729+
Diag(Tok.getLocation(), diag::err_using_enum_expect_identifier)
730+
<< Tok.is(tok::kw_enum);
731+
SkipUntil(tok::semi);
732+
return nullptr;
733+
}
734+
IdentifierInfo *IdentInfo = Tok.getIdentifierInfo();
735+
SourceLocation IdentLoc = ConsumeToken();
736+
Decl *UED = Actions.ActOnUsingEnumDeclaration(
737+
getCurScope(), AS, UsingLoc, UELoc, IdentLoc, *IdentInfo, &SS);
738+
if (!UED) {
739+
SkipUntil(tok::semi);
710740
return nullptr;
711741
}
712742

713-
Decl *UED = Actions.ActOnUsingEnumDeclaration(getCurScope(), AS, UsingLoc,
714-
UELoc, DS);
715743
DeclEnd = Tok.getLocation();
716744
if (ExpectAndConsume(tok::semi, diag::err_expected_after,
717745
"using-enum declaration"))

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11851,30 +11851,30 @@ Decl *Sema::ActOnUsingDeclaration(Scope *S, AccessSpecifier AS,
1185111851
Decl *Sema::ActOnUsingEnumDeclaration(Scope *S, AccessSpecifier AS,
1185211852
SourceLocation UsingLoc,
1185311853
SourceLocation EnumLoc,
11854-
const DeclSpec &DS) {
11855-
switch (DS.getTypeSpecType()) {
11856-
case DeclSpec::TST_error:
11857-
// This will already have been diagnosed
11854+
SourceLocation IdentLoc,
11855+
IdentifierInfo &II, CXXScopeSpec *SS) {
11856+
assert(!SS->isInvalid() && "ScopeSpec is invalid");
11857+
ParsedType TypeRep = getTypeName(II, IdentLoc, S, SS);
11858+
if (!TypeRep) {
11859+
Diag(IdentLoc, SS && isDependentScopeSpecifier(*SS)
11860+
? diag::err_using_enum_is_dependent
11861+
: diag::err_unknown_typename)
11862+
<< II.getName()
11863+
<< SourceRange(SS ? SS->getBeginLoc() : IdentLoc, IdentLoc);
1185811864
return nullptr;
11865+
}
1185911866

11860-
case DeclSpec::TST_enum:
11861-
break;
11862-
11863-
case DeclSpec::TST_typename:
11864-
Diag(DS.getTypeSpecTypeLoc(), diag::err_using_enum_is_dependent);
11867+
auto *Enum = dyn_cast_if_present<EnumDecl>(TypeRep.get()->getAsTagDecl());
11868+
if (!Enum) {
11869+
Diag(IdentLoc, diag::err_using_enum_not_enum) << TypeRep.get();
1186511870
return nullptr;
11866-
11867-
default:
11868-
llvm_unreachable("unexpected DeclSpec type");
1186911871
}
1187011872

11871-
// As with enum-decls, we ignore attributes for now.
11872-
auto *Enum = cast<EnumDecl>(DS.getRepAsDecl());
1187311873
if (auto *Def = Enum->getDefinition())
1187411874
Enum = Def;
1187511875

11876-
auto *UD = BuildUsingEnumDeclaration(S, AS, UsingLoc, EnumLoc,
11877-
DS.getTypeSpecTypeNameLoc(), Enum);
11876+
auto *UD =
11877+
BuildUsingEnumDeclaration(S, AS, UsingLoc, EnumLoc, IdentLoc, Enum);
1187811878
if (UD)
1187911879
PushOnScopeChains(UD, S, /*AddToContext*/ false);
1188011880

clang/test/CXX/drs/dr26xx.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify
22

3+
namespace dr2621 { // dr2621: yes
4+
enum class E { a };
5+
namespace One {
6+
using E_t = E;
7+
using enum E_t; // typedef ok
8+
auto v = a;
9+
}
10+
namespace Two {
11+
using dr2621::E;
12+
int E; // we see this
13+
using enum E; // expected-error {{unknown type name E}}
14+
}
15+
}
16+
317
namespace dr2628 { // dr2628: yes
418

519
template <bool A = false, bool B = false>

clang/test/CodeCompletion/using-enum.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ enum class AAA { X, Y, Z };
22

33
namespace N2 {
44
using enum AAA;
5-
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:4:14 %s | FileCheck -check-prefix=CHECK-CC1 %s
5+
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -code-completion-at=%s:4:14 %s | FileCheck -check-prefix=CHECK-CC1 %s
66
// CHECK-CC1: COMPLETION: AAA
77
};

clang/test/Parser/cxx20-using-enum.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ namespace GH57347 {
44
namespace A {}
55

66
void f() {
7-
using enum A::+; // expected-error {{expected identifier}}
8-
using enum; // expected-error {{expected identifier or '{'}}
9-
using enum class; // expected-error {{expected identifier or '{'}}
10-
using enum : blah; // expected-error {{unknown type name 'blah'}} expected-error {{unnamed enumeration must be a definition}}
7+
using enum A::+; // expected-error {{using enum requires an enum or typedef name}}
8+
using enum; // expected-error {{using enum requires an enum or typedef name}}
9+
using enum class; // expected-error {{using enum requires an enum or typedef name}}
10+
using enum enum q; // expected-error {{using enum does not permit an elaborated enum specifier}}
1111
}
1212
}

clang/test/SemaCXX/cxx20-using-enum.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Bob {
88
enum A { a, // expected-note{{declared here}}
99
b,
1010
c };
11-
class C; // expected-note{{previous use}}
11+
class C;
1212
enum class D : int;
1313
enum class D { d,
1414
e,
@@ -20,11 +20,11 @@ using enum Bob::A;
2020
#if __cplusplus < 202002
2121
// expected-warning@-2{{is a C++20 extension}}
2222
#endif
23-
using enum Bob::B; // expected-error{{no enum named 'B'}}
23+
using enum Bob::B; // expected-error{{unknown type name B}}
2424
#if __cplusplus < 202002
2525
// expected-warning@-2{{is a C++20 extension}}
2626
#endif
27-
using enum Bob::C; // expected-error{{tag type that does not match}}
27+
using enum Bob::C; // expected-error{{'Bob::C' is not an enumerated type}}
2828
#if __cplusplus < 202002
2929
// expected-warning@-2{{is a C++20 extension}}
3030
#endif
@@ -38,6 +38,16 @@ using enum Bob::D;
3838
#if __cplusplus < 202002
3939
// expected-warning@-2{{is a C++20 extension}}
4040
#endif
41+
42+
void DR2621() {
43+
using A_t = Bob::A;
44+
using enum A_t;
45+
#if __cplusplus < 202002
46+
// expected-warning@-2{{is a C++20 extension}}
47+
#endif
48+
A_t x = a;
49+
}
50+
4151
} // namespace One
4252

4353
namespace Two {

0 commit comments

Comments
 (0)