Index: clang/include/clang/AST/DeclCXX.h =================================================================== --- clang/include/clang/AST/DeclCXX.h +++ clang/include/clang/AST/DeclCXX.h @@ -3154,7 +3154,7 @@ }; /// Represents a shadow declaration introduced into a scope by a -/// (resolved) using declaration. +/// (resolved) using or using-enum declaration. /// /// For example, /// \code @@ -3384,6 +3384,11 @@ /// embedded in the ValueDecl base class. DeclarationNameLoc DNLoc; + // FIXME: some kind of union with previous members? We only need exactly one + // sort. Reserve more int bits in the Shadow pointer to discriminate between + // using-enum and regular using? + EnumDecl *Enum; + /// The first shadow declaration of the shadow decl chain associated /// with this using declaration. /// @@ -3394,14 +3399,20 @@ UsingDecl(DeclContext *DC, SourceLocation UL, NestedNameSpecifierLoc QualifierLoc, const DeclarationNameInfo &NameInfo, bool HasTypenameKeyword) - : NamedDecl(Using, DC, NameInfo.getLoc(), NameInfo.getName()), - UsingLocation(UL), QualifierLoc(QualifierLoc), - DNLoc(NameInfo.getInfo()), FirstUsingShadow(nullptr, HasTypenameKeyword) { - } + : NamedDecl(Using, DC, NameInfo.getLoc(), NameInfo.getName()), + UsingLocation(UL), QualifierLoc(QualifierLoc), + DNLoc(NameInfo.getInfo()), Enum(nullptr), + FirstUsingShadow(nullptr, HasTypenameKeyword) {} + + // using-enum + UsingDecl(DeclContext *DC, SourceLocation UL, EnumDecl *ED) + : NamedDecl(Using, DC, UL, ED->getDeclName()), UsingLocation(UL), + QualifierLoc(), DNLoc(), Enum(ED) {} void anchor() override; public: + // FIXME: Teach these about using enum. (once we've sorted the data structure) friend class ASTDeclReader; friend class ASTDeclWriter; @@ -3427,6 +3438,13 @@ /// Return true if it is a C++03 access declaration (no 'using'). bool isAccessDeclaration() const { return UsingLocation.isInvalid(); } + /// Return true, if this is a C++20 using-enum declaration + bool isUsingEnum() const { return Enum != nullptr; } + + // FIXME: Add asserts to the following accessors? + /// Return the enum decl this is a using-enum for. + EnumDecl *getUsingEnum() const { return Enum; } + /// Return true if the using declaration has 'typename'. bool hasTypename() const { return FirstUsingShadow.getInt(); } @@ -3497,6 +3515,8 @@ NestedNameSpecifierLoc QualifierLoc, const DeclarationNameInfo &NameInfo, bool HasTypenameKeyword); + static UsingDecl *Create(ASTContext &C, DeclContext *DC, + SourceLocation UsingL, EnumDecl *ED); static UsingDecl *CreateDeserialized(ASTContext &C, unsigned ID); Index: clang/include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticParseKinds.td +++ clang/include/clang/Basic/DiagnosticParseKinds.td @@ -580,6 +580,9 @@ def err_expected_catch : Error<"expected catch">; def err_using_namespace_in_class : Error< "'using namespace' is not allowed in classes">; +def warn_cxx20_using_enum : Warning< + "using enum declaration is incompatible with C++ standards before C++20">, + InGroup; 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< Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -530,6 +530,10 @@ "%0 is not a direct base of %1, cannot inherit constructors">; def err_using_decl_can_not_refer_to_class_member : Error< "using declaration cannot refer to class member">; +def err_using_enum_lacks_definition : Error< + "enum named by using-enum declaration lacks a definition">; +def err_using_enum_is_dependent : Error< + "using-enum cannot name a dependent type">; def err_ambiguous_inherited_constructor : Error< "constructor of %0 inherited from multiple base class subobjects">; def note_ambiguous_inherited_constructor_using : Note< Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -5689,6 +5689,8 @@ bool HasTypenameKeyword, SourceLocation TypenameLoc, CXXScopeSpec &SS, DeclarationNameInfo NameInfo, SourceLocation EllipsisLoc, const ParsedAttributesView &AttrList, bool IsInstantiation); + NamedDecl *BuildUsingDeclaration(Scope *S, AccessSpecifier AS, + SourceLocation EnumLoc, EnumDecl *ED); NamedDecl *BuildUsingPackDecl(NamedDecl *InstantiatedFrom, ArrayRef Expansions); @@ -5706,6 +5708,8 @@ SourceLocation TypenameLoc, CXXScopeSpec &SS, UnqualifiedId &Name, SourceLocation EllipsisLoc, const ParsedAttributesView &AttrList); + Decl *ActOnUsingEnumDeclaration(Scope *CurScope, AccessSpecifier AS, + SourceLocation EnumLoc, const DeclSpec &); Decl *ActOnAliasDeclaration(Scope *CurScope, AccessSpecifier AS, MultiTemplateParamsArg TemplateParams, SourceLocation UsingLoc, UnqualifiedId &Name, Index: clang/lib/AST/DeclCXX.cpp =================================================================== --- clang/lib/AST/DeclCXX.cpp +++ clang/lib/AST/DeclCXX.cpp @@ -2983,7 +2983,8 @@ UsingShadowDecl::UsingShadowDecl(Kind K, ASTContext &C, DeclContext *DC, SourceLocation Loc, UsingDecl *Using, NamedDecl *Target) - : NamedDecl(K, DC, Loc, Using ? Using->getDeclName() : DeclarationName()), + : NamedDecl(K, DC, Loc, + (Using->isUsingEnum() ? Target : Using)->getDeclName()), redeclarable_base(C), UsingOrNextShadow(cast(Using)) { if (Target) setTargetDecl(Target); @@ -3066,6 +3067,11 @@ return new (C, DC) UsingDecl(DC, UL, QualifierLoc, NameInfo, HasTypename); } +UsingDecl *UsingDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation UL, + EnumDecl *ED) { + return new (C, DC) UsingDecl(DC, UL, ED); +} + UsingDecl *UsingDecl::CreateDeserialized(ASTContext &C, unsigned ID) { return new (C, ID) UsingDecl(nullptr, SourceLocation(), NestedNameSpecifierLoc(), DeclarationNameInfo(), Index: clang/lib/Parse/ParseDeclCXX.cpp =================================================================== --- clang/lib/Parse/ParseDeclCXX.cpp +++ clang/lib/Parse/ParseDeclCXX.cpp @@ -670,11 +670,49 @@ /// alias-declaration: C++11 [dcl.dcl]p1 /// 'using' identifier attribute-specifier-seq[opt] = type-id ; /// +/// using-enum-declaration: [C++20, dcl.enum] +/// 'using' elaborated-enum-specifier ; +/// +/// elaborated-enum-specifier: +/// 'enum' nested-name-specifier[opt] identifier + Parser::DeclGroupPtrTy Parser::ParseUsingDeclaration(DeclaratorContext Context, const ParsedTemplateInfo &TemplateInfo, SourceLocation UsingLoc, SourceLocation &DeclEnd, AccessSpecifier AS) { + SourceLocation UELoc; + if (TryConsumeToken(tok::kw_enum, UELoc)) { + // C++20 using-enum + if (!getLangOpts().CPlusPlus20) + Diag(UELoc, diag::warn_cxx20_using_enum); + + DeclSpec DS(AttrFactory); + ParseEnumSpecifier(UELoc, DS, TemplateInfo, AS, + // DSC_trailing has the semantics we desire + // FIXME: should we add an alias for it? + DeclSpecContext::DSC_trailing); + + if (TemplateInfo.Kind) { + SourceRange R = TemplateInfo.getSourceRange(); + Diag(UsingLoc, diag::err_templated_using_directive_declaration) + << 1 /* declaration */ << R << FixItHint::CreateRemoval(R); + + return nullptr; + } + + // FIXME: Should we parse (and ignore) attributes? The regular using-decl + // parsing does that. + + Decl *UED = Actions.ActOnUsingEnumDeclaration(getCurScope(), AS, UELoc, DS); + DeclEnd = Tok.getLocation(); + if (ExpectAndConsume(tok::semi, diag::err_expected_after, + "using-enum declaration")) + SkipUntil(tok::semi); + + return Actions.ConvertDeclToDeclGroup(UED); + } + // Check for misplaced attributes before the identifier in an // alias-declaration. ParsedAttributesWithRange MisplacedAttrs(AttrFactory); @@ -771,8 +809,9 @@ // Eat ';'. DeclEnd = Tok.getLocation(); if (ExpectAndConsume(tok::semi, diag::err_expected_after, - !Attrs.empty() ? "attributes list" - : "using declaration")) + !Attrs.empty() ? "attributes list" + : UELoc.isValid() ? "using-enum declaration" + : "using declaration")) SkipUntil(tok::semi); return Actions.BuildDeclaratorGroup(DeclsInGroup); Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -11611,7 +11611,45 @@ NamedDecl *UD = BuildUsingDeclaration(S, AS, UsingLoc, TypenameLoc.isValid(), TypenameLoc, SS, TargetNameInfo, EllipsisLoc, AttrList, - /*IsInstantiation*/false); + /*IsInstantiation*/ false); + if (UD) + PushOnScopeChains(UD, S, /*AddToContext*/ false); + + return UD; +} + +Decl *Sema::ActOnUsingEnumDeclaration(Scope *S, AccessSpecifier AS, + SourceLocation EnumLoc, + const DeclSpec &DS) { + switch (DS.getTypeSpecType()) { + case DeclSpec::TST_error: + // This will already have been diagnosed + return nullptr; + + case DeclSpec::TST_enum: + break; + + case DeclSpec::TST_typename: + Diag(DS.getTypeSpecTypeLoc(), diag::err_using_enum_is_dependent); + return nullptr; + + default: + llvm_unreachable("unexpected DeclSpec type"); + } + + auto *Enum = cast(DS.getRepAsDecl()); + auto *Def = Enum->getDefinition(); + + // FIXME: See the comment in TemplateInstantiator::VisitUsingDecl about + // completing the enum. Figure out best way of achieving that + // The enum's definition must be reachable + if (!Def) { + Diag(DS.getTypeSpecTypeLoc(), diag::err_using_enum_lacks_definition); + Diag(Enum->getLocation(), diag::note_entity_declared_at) << Enum; + return nullptr; + } + + auto *UD = BuildUsingDeclaration(S, AS, EnumLoc, Def); if (UD) PushOnScopeChains(UD, S, /*AddToContext*/ false); @@ -12264,6 +12302,30 @@ return UD; } +NamedDecl *Sema::BuildUsingDeclaration(Scope *S, AccessSpecifier AS, + SourceLocation EnumLoc, EnumDecl *ED) { + UsingDecl *UD = UsingDecl::Create(Context, CurContext, EnumLoc, ED); + UD->setAccess(AS); + CurContext->addDecl(UD); + + // Create the shadow decls for each enumerator + for (EnumConstantDecl *EC : ED->enumerators()) { + UsingShadowDecl *PrevDecl = nullptr; + // FIXME: Best location info for the Lookup? + DeclarationNameInfo DNI(EC->getDeclName(), EC->getLocation()); + LookupResult Previous(*this, DNI, LookupUsingDeclName, + ForVisibleRedeclaration); + LookupName(Previous, S); + // FIXME: See regular using-decl lookup nadgering here 'it is really dumb + // that we have to do this' do we need to do that here too? + if (!CheckUsingShadowDecl(UD, EC, Previous, PrevDecl)) { + BuildUsingShadowDecl(S, UD, EC, PrevDecl); + } + } + + return UD; +} + NamedDecl *Sema::BuildUsingPackDecl(NamedDecl *InstantiatedFrom, ArrayRef Expansions) { assert(isa(InstantiatedFrom) || Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3011,6 +3011,83 @@ Decl *TemplateDeclInstantiator::VisitUsingDecl(UsingDecl *D) { + bool CheckRedeclaration = Owner->isRecord(); + + if (D->isUsingEnum()) { + // Cannot be a dependent type, but still could be an instantiation + EnumDecl *EnumD = cast_or_null(SemaRef.FindInstantiatedDecl( + D->getLocation(), D->getUsingEnum(), TemplateArgs)); + + UsingDecl *NewUD = + UsingDecl::Create(SemaRef.Context, Owner, D->getLocation(), EnumD); + + // FIXME: Complete Enum -- we'll need this in the non-template case too + // Sema::RequireCompleteDeclContext wants a CXXScopeSpec, Where do I get one + // of those? + if (!EnumD->isCompleteDefinition()) { + if (EnumDecl *Pattern = EnumD->getInstantiatedFromMemberEnum()) { + MemberSpecializationInfo *MSI = EnumD->getMemberSpecializationInfo(); + if (MSI->getTemplateSpecializationKind() != + TSK_ExplicitSpecialization) { + if (SemaRef.InstantiateEnum( + D->getLocation(), EnumD, Pattern, + SemaRef.getTemplateInstantiationArgs(EnumD), + TSK_ImplicitInstantiation)) + NewUD->setInvalidDecl(); + } + } + } + + SemaRef.Context.setInstantiatedFromUsingDecl(NewUD, D); + NewUD->setAccess(D->getAccess()); + Owner->addDecl(NewUD); + + // Don't process the shadow decls for an invalid decl. + if (NewUD->isInvalidDecl()) + return NewUD; + + bool isFunctionScope = Owner->isFunctionOrMethod(); + + // FIXME: Helper routine for this? It's nearly the same as the + // non-using-enum case + // Process the shadow decls. + for (auto *Shadow : D->shadows()) { + // FIXME: UsingShadowDecl doesn't preserve its immediate target, so + // reconstruct it in the case where it matters. Hm, can we extract it + // from the DeclSpec when parsing and save it in the UsingDecl itself? + NamedDecl *OldTarget = Shadow->getTargetDecl(); + + NamedDecl *InstTarget = + cast_or_null(SemaRef.FindInstantiatedDecl( + Shadow->getLocation(), OldTarget, TemplateArgs)); + if (!InstTarget) + return nullptr; + + UsingShadowDecl *PrevDecl = nullptr; + if (CheckRedeclaration) { + DeclarationNameInfo DNI(Shadow->getDeclName(), Shadow->getLocation()); + LookupResult Prev(SemaRef, DNI, Sema::LookupUsingDeclName, + Sema::ForVisibleRedeclaration); + SemaRef.LookupQualifiedName(Prev, Owner); + if (SemaRef.CheckUsingShadowDecl(NewUD, InstTarget, Prev, PrevDecl)) + continue; + } else if (UsingShadowDecl *OldPrev = + getPreviousDeclForInstantiation(Shadow)) { + PrevDecl = cast_or_null(SemaRef.FindInstantiatedDecl( + Shadow->getLocation(), OldPrev, TemplateArgs)); + } + + UsingShadowDecl *InstShadow = SemaRef.BuildUsingShadowDecl( + /*Scope*/ nullptr, NewUD, InstTarget, PrevDecl); + SemaRef.Context.setInstantiatedFromUsingShadowDecl(InstShadow, Shadow); + + if (isFunctionScope) + SemaRef.CurrentInstantiationScope->InstantiatedLocal(Shadow, + InstShadow); + } + return NewUD; + } + // The nested name specifier may be dependent, for example // template struct t { // struct s1 { T f1(); }; @@ -3033,14 +3110,8 @@ if (auto *RD = dyn_cast(SemaRef.CurContext)) NameInfo.setName(SemaRef.Context.DeclarationNames.getCXXConstructorName( SemaRef.Context.getCanonicalType(SemaRef.Context.getRecordType(RD)))); - - // We only need to do redeclaration lookups if we're in a class - // scope (in fact, it's not really even possible in non-class - // scopes). - bool CheckRedeclaration = Owner->isRecord(); - - LookupResult Prev(SemaRef, NameInfo, Sema::LookupUsingDeclName, - Sema::ForVisibleRedeclaration); + LookupResult Prev = LookupResult(SemaRef, NameInfo, Sema::LookupUsingDeclName, + Sema::ForVisibleRedeclaration); UsingDecl *NewUD = UsingDecl::Create(SemaRef.Context, Owner, D->getUsingLoc(), @@ -3050,6 +3121,9 @@ CXXScopeSpec SS; SS.Adopt(QualifierLoc); + // We only need to do redeclaration lookups if we're in a class + // scope (in fact, it's not really even possible in non-class + // scopes). if (CheckRedeclaration) { Prev.setHideTags(false); SemaRef.LookupQualifiedName(Prev, Owner); @@ -3059,13 +3133,13 @@ D->hasTypename(), SS, D->getLocation(), Prev)) NewUD->setInvalidDecl(); - } if (!NewUD->isInvalidDecl() && SemaRef.CheckUsingDeclQualifier(D->getUsingLoc(), D->hasTypename(), SS, NameInfo, D->getLocation(), nullptr, D)) NewUD->setInvalidDecl(); + SemaRef.Context.setInstantiatedFromUsingDecl(NewUD, D); NewUD->setAccess(D->getAccess()); Owner->addDecl(NewUD); Index: clang/test/SemaCXX/cxx20-using-enum.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/cxx20-using-enum.cpp @@ -0,0 +1,175 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++17 -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s + +// p1099 'using enum ELABORATED-ENUM-SPECIFIER ;' + +namespace One { +namespace Bob { +enum A { a, // expected-note{{declared here}} + b, + c }; +class C; // expected-note{{previous use}} +enum class D : int; +enum class D { d, + e, + f }; +enum class D : int; +} // namespace Bob + +using enum Bob::A; +#if __cplusplus < 202002 +// expected-warning@-2{{standards before C++20}} +#endif +using enum Bob::B; // expected-error{{no enum named 'B'}} +#if __cplusplus < 202002 +// expected-warning@-2{{standards before C++20}} +#endif +using enum Bob::C; // expected-error{{tag type that does not match}} +#if __cplusplus < 202002 +// expected-warning@-2{{standards before C++20}} +#endif +auto v = a; + +A g; // expected-error{{unknown type name 'A'}} + +int A; + +using enum Bob::D; +#if __cplusplus < 202002 +// expected-warning@-2{{standards before C++20}} +#endif +} // namespace One + +namespace Two { +namespace Kevin { +enum class B { d, + e, + f }; +} + +using enum Kevin::B; +#if __cplusplus < 202002 +// expected-warning@-2{{standards before C++20}} +#endif +auto w = e; + +} // namespace Two + +namespace Three { +namespace Stuart { +enum class C : int; // expected-note{{declared here}} +} + +using enum Stuart::C; // expected-error{{lacks a definition}} +#if __cplusplus < 202002 +// expected-warning@-2{{standards before C++20}} +#endif +} // namespace Three + +namespace Four { +class Dave { +public: + enum D { a, + b, + c }; + +private: + enum class E { d, // expected-note{{declared private here}} + e, + f }; +}; + +using enum Dave::D; +#if __cplusplus < 202002 +// expected-warning@-2{{standards before C++20}} +#endif +using enum Dave::E; // expected-error{{is a private member}} +#if __cplusplus < 202002 +// expected-warning@-2{{standards before C++20}} +#endif + +} // namespace Four + +namespace Five { +enum class A { b, + c }; +class Dave { +public: + using enum A; +#if __cplusplus < 202002 +// expected-warning@-2{{standards before C++20}} +#endif + A f = b; +}; + +} // namespace Five + +namespace Six { +template class TPL; +template <> class TPL { +public: + enum A { a }; +}; + +template class USR { + using enum TPL::B; // expected-error{{cannot name a dependent type}} +#if __cplusplus < 202002 +// expected-warning@-2{{standards before C++20}} +#endif + using enum TPL::A; +#if __cplusplus < 202002 +// expected-warning@-2{{standards before C++20}} +#endif +}; +} // namespace Six + +// Now instantiate things +namespace Seven { +namespace Stuart { +enum class A { a, + b, + c }; +} + +static_assert(!int(Stuart::A::a)); +constexpr int Bar() { + using enum Stuart::A; +#if __cplusplus < 202002 +// expected-warning@-2{{standards before C++20}} +#endif + return int(b); +} +static_assert(Bar() == 1); + +template constexpr int Foo() { + using enum Stuart::A; +#if __cplusplus < 202002 +// expected-warning@-2{{standards before C++20}} +#endif + return int(b) + I; +} + +static_assert(Foo<10>() == 11); + +template struct C { + using enum Stuart::A; +#if __cplusplus < 202002 +// expected-warning@-2{{standards before C++20}} +#endif + static constexpr int V = int(c) + I; + + enum class D { d, + e, + f }; + using enum D; +#if __cplusplus < 202002 +// expected-warning@-2{{standards before C++20}} +#endif + + static constexpr int W = int(f) + I; +}; + +static_assert(C<2>::V == 4); +static_assert(C<20>::W == 22); + +} // namespace Seven