[clang] b2d0c16 - [clang] p1099 using enum part 2

Nathan Sidwell via cfe-commits cfe-commits at lists.llvm.org
Tue Jun 8 11:12:29 PDT 2021


Author: Nathan Sidwell
Date: 2021-06-08T11:11:46-07:00
New Revision: b2d0c16e91f39def3646b71e5afebfaea262cca1

URL: https://github.com/llvm/llvm-project/commit/b2d0c16e91f39def3646b71e5afebfaea262cca1
DIFF: https://github.com/llvm/llvm-project/commit/b2d0c16e91f39def3646b71e5afebfaea262cca1.diff

LOG: [clang] p1099 using enum part 2

This implements the 'using enum maybe-qualified-enum-tag ;' part of
1099. It introduces a new 'UsingEnumDecl', subclassed from
'BaseUsingDecl'. Much of the diff is the boilerplate needed to get the
new class set up.

There is one case where we accept ill-formed, but I believe this is
merely an extended case of an existing bug, so consider it
orthogonal. AFAICT in class-scope the c++20 rule is that no 2 using
decls can bring in the same target decl ([namespace.udecl]/8). But we
already accept:

struct A { enum { a }; };
struct B : A { using A::a; };
struct C : B { using A::a;
using B::a; }; // same enumerator

this patch permits mixtures of 'using enum Bob;' and 'using Bob::member;' in the same way.

Differential Revision: https://reviews.llvm.org/D102241

Added: 
    clang/test/AST/ast-dump-using-enum.cpp
    clang/test/SemaCXX/cxx20-using-enum.cpp

Modified: 
    clang-tools-extra/clangd/FindTarget.cpp
    clang/include/clang/AST/ASTContext.h
    clang/include/clang/AST/DeclCXX.h
    clang/include/clang/AST/JSONNodeDumper.h
    clang/include/clang/AST/RecursiveASTVisitor.h
    clang/include/clang/AST/TextNodeDumper.h
    clang/include/clang/ASTMatchers/ASTMatchers.h
    clang/include/clang/Basic/DeclNodes.td
    clang/include/clang/Basic/DiagnosticParseKinds.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Index/IndexSymbol.h
    clang/include/clang/Sema/Sema.h
    clang/include/clang/Sema/Template.h
    clang/include/clang/Serialization/ASTBitCodes.h
    clang/lib/AST/ASTContext.cpp
    clang/lib/AST/ASTImporter.cpp
    clang/lib/AST/Decl.cpp
    clang/lib/AST/DeclBase.cpp
    clang/lib/AST/DeclCXX.cpp
    clang/lib/AST/DeclPrinter.cpp
    clang/lib/AST/JSONNodeDumper.cpp
    clang/lib/AST/TextNodeDumper.cpp
    clang/lib/ASTMatchers/ASTMatchersInternal.cpp
    clang/lib/ASTMatchers/Dynamic/Registry.cpp
    clang/lib/CodeGen/CGDebugInfo.cpp
    clang/lib/CodeGen/CGDebugInfo.h
    clang/lib/CodeGen/CGDecl.cpp
    clang/lib/CodeGen/CodeGenModule.cpp
    clang/lib/Index/IndexSymbol.cpp
    clang/lib/Parse/ParseDeclCXX.cpp
    clang/lib/Sema/SemaCXXScopeSpec.cpp
    clang/lib/Sema/SemaCodeComplete.cpp
    clang/lib/Sema/SemaDeclCXX.cpp
    clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
    clang/lib/Serialization/ASTCommon.cpp
    clang/lib/Serialization/ASTReaderDecl.cpp
    clang/lib/Serialization/ASTWriterDecl.cpp
    clang/tools/libclang/CIndex.cpp
    clang/unittests/AST/ASTImporterTest.cpp
    clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clangd/FindTarget.cpp b/clang-tools-extra/clangd/FindTarget.cpp
index 9068fffcbd40..b68e5c6c1e89 100644
--- a/clang-tools-extra/clangd/FindTarget.cpp
+++ b/clang-tools-extra/clangd/FindTarget.cpp
@@ -181,6 +181,9 @@ struct TargetFinder {
       for (const UsingShadowDecl *S : UD->shadows())
         add(S->getUnderlyingDecl(), Flags);
       Flags |= Rel::Alias; // continue with the alias.
+    } else if (const UsingEnumDecl *UED = dyn_cast<UsingEnumDecl>(D)) {
+      add(UED->getEnumDecl(), Flags);
+      Flags |= Rel::Alias; // continue with the alias.
     } else if (const auto *NAD = dyn_cast<NamespaceAliasDecl>(D)) {
       add(NAD->getUnderlyingDecl(), Flags | Rel::Underlying);
       Flags |= Rel::Alias; // continue with the alias

diff  --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 22588a61f6ff..f103ec6deb1f 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -518,6 +518,17 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// B<int> to the UnresolvedUsingDecl in B<T>.
   llvm::DenseMap<NamedDecl *, NamedDecl *> InstantiatedFromUsingDecl;
 
+  /// Like InstantiatedFromUsingDecl, but for using-enum-declarations. Maps
+  /// from the instantiated using-enum to the templated decl from whence it
+  /// came.
+  /// Note that using-enum-declarations cannot be dependent and
+  /// thus will never be instantiated from an "unresolved"
+  /// version thereof (as with using-declarations), so each mapping is from
+  /// a (resolved) UsingEnumDecl to a (resolved) UsingEnumDecl.
+  llvm::DenseMap<UsingEnumDecl *, UsingEnumDecl *>
+      InstantiatedFromUsingEnumDecl;
+
+  /// Simlarly maps instantiated UsingShadowDecls to their origin.
   llvm::DenseMap<UsingShadowDecl*, UsingShadowDecl*>
     InstantiatedFromUsingShadowDecl;
 
@@ -886,30 +897,38 @@ class ASTContext : public RefCountedBase<ASTContext> {
   MemberSpecializationInfo *getInstantiatedFromStaticDataMember(
                                                            const VarDecl *Var);
 
-  TemplateOrSpecializationInfo
-  getTemplateOrSpecializationInfo(const VarDecl *Var);
-
   /// Note that the static data member \p Inst is an instantiation of
   /// the static data member template \p Tmpl of a class template.
   void setInstantiatedFromStaticDataMember(VarDecl *Inst, VarDecl *Tmpl,
                                            TemplateSpecializationKind TSK,
                         SourceLocation PointOfInstantiation = SourceLocation());
 
+  TemplateOrSpecializationInfo
+  getTemplateOrSpecializationInfo(const VarDecl *Var);
+
   void setTemplateOrSpecializationInfo(VarDecl *Inst,
                                        TemplateOrSpecializationInfo TSI);
 
-  /// If the given using decl \p Inst is an instantiation of a
-  /// (possibly unresolved) using decl from a template instantiation,
-  /// return it.
+  /// If the given using decl \p Inst is an instantiation of
+  /// another (possibly unresolved) using decl, return it.
   NamedDecl *getInstantiatedFromUsingDecl(NamedDecl *Inst);
 
   /// Remember that the using decl \p Inst is an instantiation
   /// of the using decl \p Pattern of a class template.
   void setInstantiatedFromUsingDecl(NamedDecl *Inst, NamedDecl *Pattern);
 
+  /// If the given using-enum decl \p Inst is an instantiation of
+  /// another using-enum decl, return it.
+  UsingEnumDecl *getInstantiatedFromUsingEnumDecl(UsingEnumDecl *Inst);
+
+  /// Remember that the using enum decl \p Inst is an instantiation
+  /// of the using enum decl \p Pattern of a class template.
+  void setInstantiatedFromUsingEnumDecl(UsingEnumDecl *Inst,
+                                        UsingEnumDecl *Pattern);
+
+  UsingShadowDecl *getInstantiatedFromUsingShadowDecl(UsingShadowDecl *Inst);
   void setInstantiatedFromUsingShadowDecl(UsingShadowDecl *Inst,
                                           UsingShadowDecl *Pattern);
-  UsingShadowDecl *getInstantiatedFromUsingShadowDecl(UsingShadowDecl *Inst);
 
   FieldDecl *getInstantiatedFromUnnamedFieldDecl(FieldDecl *Field);
 

diff  --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index c8095bb2ca2b..e9f9da6bd4bc 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -3366,7 +3366,7 @@ class BaseUsingDecl : public NamedDecl {
   void removeShadowDecl(UsingShadowDecl *S);
 
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
-  static bool classofKind(Kind K) { return K == Using; }
+  static bool classofKind(Kind K) { return K == Using || K == UsingEnum; }
 };
 
 /// Represents a C++ using-declaration.
@@ -3568,6 +3568,65 @@ class ConstructorUsingShadowDecl final : public UsingShadowDecl {
   static bool classofKind(Kind K) { return K == ConstructorUsingShadow; }
 };
 
+/// Represents a C++ using-enum-declaration.
+///
+/// For example:
+/// \code
+///    using enum SomeEnumTag ;
+/// \endcode
+
+class UsingEnumDecl : public BaseUsingDecl, public Mergeable<UsingEnumDecl> {
+  /// The source location of the 'using' keyword itself.
+  SourceLocation UsingLocation;
+
+  /// Location of the 'enum' keyword.
+  SourceLocation EnumLocation;
+
+  /// The enum
+  EnumDecl *Enum;
+
+  UsingEnumDecl(DeclContext *DC, DeclarationName DN, SourceLocation UL,
+                SourceLocation EL, SourceLocation NL, EnumDecl *ED)
+      : BaseUsingDecl(UsingEnum, DC, NL, DN), UsingLocation(UL),
+        EnumLocation(EL), Enum(ED) {}
+
+  void anchor() override;
+
+public:
+  friend class ASTDeclReader;
+  friend class ASTDeclWriter;
+
+  /// The source location of the 'using' keyword.
+  SourceLocation getUsingLoc() const { return UsingLocation; }
+  void setUsingLoc(SourceLocation L) { UsingLocation = L; }
+
+  /// The source location of the 'enum' keyword.
+  SourceLocation getEnumLoc() const { return EnumLocation; }
+  void setEnumLoc(SourceLocation L) { EnumLocation = L; }
+
+public:
+  EnumDecl *getEnumDecl() const { return Enum; }
+
+  static UsingEnumDecl *Create(ASTContext &C, DeclContext *DC,
+                               SourceLocation UsingL, SourceLocation EnumL,
+                               SourceLocation NameL, EnumDecl *ED);
+
+  static UsingEnumDecl *CreateDeserialized(ASTContext &C, unsigned ID);
+
+  SourceRange getSourceRange() const override LLVM_READONLY;
+
+  /// Retrieves the canonical declaration of this declaration.
+  UsingEnumDecl *getCanonicalDecl() override {
+    return cast<UsingEnumDecl>(getFirstDecl());
+  }
+  const UsingEnumDecl *getCanonicalDecl() const {
+    return cast<UsingEnumDecl>(getFirstDecl());
+  }
+
+  static bool classof(const Decl *D) { return classofKind(D->getKind()); }
+  static bool classofKind(Kind K) { return K == UsingEnum; }
+};
+
 /// Represents a pack of using declarations that a single
 /// using-declarator pack-expanded into.
 ///

diff  --git a/clang/include/clang/AST/JSONNodeDumper.h b/clang/include/clang/AST/JSONNodeDumper.h
index a8b731a7cd31..1f3585910a40 100644
--- a/clang/include/clang/AST/JSONNodeDumper.h
+++ b/clang/include/clang/AST/JSONNodeDumper.h
@@ -234,6 +234,7 @@ class JSONNodeDumper
   void VisitUsingDirectiveDecl(const UsingDirectiveDecl *UDD);
   void VisitNamespaceAliasDecl(const NamespaceAliasDecl *NAD);
   void VisitUsingDecl(const UsingDecl *UD);
+  void VisitUsingEnumDecl(const UsingEnumDecl *UED);
   void VisitUsingShadowDecl(const UsingShadowDecl *USD);
   void VisitVarDecl(const VarDecl *VD);
   void VisitFieldDecl(const FieldDecl *FD);

diff  --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index ab49b39307be..73d757f0255a 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1593,6 +1593,8 @@ DEF_TRAVERSE_DECL(UsingDecl, {
   TRY_TO(TraverseDeclarationNameInfo(D->getNameInfo()));
 })
 
+DEF_TRAVERSE_DECL(UsingEnumDecl, {})
+
 DEF_TRAVERSE_DECL(UsingPackDecl, {})
 
 DEF_TRAVERSE_DECL(UsingDirectiveDecl, {

diff  --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h
index e3aa94d5a1e8..4dc68d5237b4 100644
--- a/clang/include/clang/AST/TextNodeDumper.h
+++ b/clang/include/clang/AST/TextNodeDumper.h
@@ -351,6 +351,7 @@ class TextNodeDumper
   void VisitUsingDecl(const UsingDecl *D);
   void VisitUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl *D);
   void VisitUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D);
+  void VisitUsingEnumDecl(const UsingEnumDecl *D);
   void VisitUsingShadowDecl(const UsingShadowDecl *D);
   void VisitConstructorUsingShadowDecl(const ConstructorUsingShadowDecl *D);
   void VisitLinkageSpecDecl(const LinkageSpecDecl *D);

diff  --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index b3c43458fce7..4db562910c53 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -1752,6 +1752,18 @@ extern const internal::VariadicDynCastAllOfMatcher<Stmt,
 ///   matches \code using X::x \endcode
 extern const internal::VariadicDynCastAllOfMatcher<Decl, UsingDecl> usingDecl;
 
+/// Matches using-enum declarations.
+///
+/// Given
+/// \code
+///   namespace X { enum x {...}; }
+///   using enum X::x;
+/// \endcode
+/// usingEnumDecl()
+///   matches \code using enum X::x \endcode
+extern const internal::VariadicDynCastAllOfMatcher<Decl, UsingEnumDecl>
+    usingEnumDecl;
+
 /// Matches using namespace declarations.
 ///
 /// Given
@@ -6197,7 +6209,7 @@ AST_POLYMORPHIC_MATCHER_P(
 /// \endcode
 /// usingDecl(hasAnyUsingShadowDecl(hasName("b"))))
 ///   matches \code using X::b \endcode
-AST_MATCHER_P(UsingDecl, hasAnyUsingShadowDecl,
+AST_MATCHER_P(BaseUsingDecl, hasAnyUsingShadowDecl,
               internal::Matcher<UsingShadowDecl>, InnerMatcher) {
   return matchesFirstInPointerRange(InnerMatcher, Node.shadow_begin(),
                                     Node.shadow_end(), Finder,

diff  --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td
index ceaea521f118..f8ad6cf5b262 100644
--- a/clang/include/clang/Basic/DeclNodes.td
+++ b/clang/include/clang/Basic/DeclNodes.td
@@ -73,6 +73,7 @@ def Named : DeclNode<Decl, "named declarations", 1>;
     def Concept : DeclNode<Template>;
   def BaseUsing : DeclNode<Named, "", 1>;
     def Using : DeclNode<BaseUsing>;
+    def UsingEnum : DeclNode<BaseUsing>;
   def UsingPack : DeclNode<Named>;
   def UsingShadow : DeclNode<Named>;
     def ConstructorUsingShadow : DeclNode<UsingShadow>;

diff  --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index c99ce6dfa246..aecc5859c718 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -580,6 +580,12 @@ def warn_cxx98_compat_noexcept_decl : Warning<
 def err_expected_catch : Error<"expected catch">;
 def err_using_namespace_in_class : Error<
   "'using namespace' is not allowed in classes">;
+def warn_cxx17_compat_using_enum_declaration : Warning<
+  "using enum declaration is incompatible with C++ standards before C++20">,
+  InGroup<CXXPre20Compat>, DefaultIgnore;
+def ext_using_enum_declaration : ExtWarn<
+  "using enum declaration is a C++20 extension">,
+  InGroup<CXX20>;
 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
index 21672ed09559..ba55095a8266 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -554,8 +554,6 @@ def warn_cxx17_compat_using_decl_class_member_enumerator : Warning<
 def ext_using_decl_class_member_enumerator : ExtWarn<
   "member using declaration naming a non-member enumerator is "
   "a C++20 extension">, InGroup<CXX20>;
-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<
@@ -598,6 +596,9 @@ def note_empty_using_if_exists_here : Note<
   "using declaration annotated with 'using_if_exists' here">;
 def err_using_if_exists_on_ctor : Error<
   "'using_if_exists' attribute cannot be applied to an inheriting constructor">;
+def err_using_enum_decl_redeclaration : Error<
+  "redeclaration of using-enum declaration">;
+def note_using_enum_decl : Note<"%select{|previous }0using-enum declaration">;
 
 def warn_access_decl_deprecated : Warning<
   "access declarations are deprecated; use using declarations instead">,
@@ -1760,6 +1761,8 @@ def warn_cxx98_compat_sfinae_access_control : Warning<
 // C++ name lookup
 def err_incomplete_nested_name_spec : Error<
   "incomplete type %0 named in nested name specifier">;
+def err_incomplete_enum : Error<
+  "enumeration %0 is incomplete">;
 def err_dependent_nested_name_spec : Error<
   "nested name specifier for a declaration cannot depend on a template "
   "parameter">;

diff  --git a/clang/include/clang/Index/IndexSymbol.h b/clang/include/clang/Index/IndexSymbol.h
index de98b8147e8a..2ba81986c2fe 100644
--- a/clang/include/clang/Index/IndexSymbol.h
+++ b/clang/include/clang/Index/IndexSymbol.h
@@ -75,6 +75,7 @@ enum class SymbolSubKind : uint8_t {
   AccessorSetter,
   UsingTypename,
   UsingValue,
+  UsingEnum,
 };
 
 typedef uint16_t SymbolPropertySet;

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index c122b991af9e..940630c6508d 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -5701,6 +5701,7 @@ class Sema final {
                                SourceLocation IdentLoc,
                                IdentifierInfo *Ident);
 
+  void FilterUsingLookup(Scope *S, LookupResult &lookup);
   void HideUsingShadowDecl(Scope *S, UsingShadowDecl *Shadow);
   bool CheckUsingShadowDecl(BaseUsingDecl *BUD, NamedDecl *Target,
                             const LookupResult &PreviousDecls,
@@ -5721,14 +5722,16 @@ class Sema final {
                                const LookupResult *R = nullptr,
                                const UsingDecl *UD = nullptr);
 
-  NamedDecl *BuildUsingDeclaration(Scope *S, AccessSpecifier AS,
-                                   SourceLocation UsingLoc,
-                                   bool HasTypenameKeyword,
-                                   SourceLocation TypenameLoc, CXXScopeSpec &SS,
-                                   DeclarationNameInfo NameInfo,
-                                   SourceLocation EllipsisLoc,
-                                   const ParsedAttributesView &AttrList,
-                                   bool IsInstantiation, bool IsUsingIfExists);
+  NamedDecl *BuildUsingDeclaration(
+      Scope *S, AccessSpecifier AS, SourceLocation UsingLoc,
+      bool HasTypenameKeyword, SourceLocation TypenameLoc, CXXScopeSpec &SS,
+      DeclarationNameInfo NameInfo, SourceLocation EllipsisLoc,
+      const ParsedAttributesView &AttrList, bool IsInstantiation,
+      bool IsUsingIfExists);
+  NamedDecl *BuildUsingEnumDeclaration(Scope *S, AccessSpecifier AS,
+                                       SourceLocation UsingLoc,
+                                       SourceLocation EnumLoc,
+                                       SourceLocation NameLoc, EnumDecl *ED);
   NamedDecl *BuildUsingPackDecl(NamedDecl *InstantiatedFrom,
                                 ArrayRef<NamedDecl *> Expansions);
 
@@ -5746,6 +5749,9 @@ class Sema final {
                               SourceLocation TypenameLoc, CXXScopeSpec &SS,
                               UnqualifiedId &Name, SourceLocation EllipsisLoc,
                               const ParsedAttributesView &AttrList);
+  Decl *ActOnUsingEnumDeclaration(Scope *CurScope, AccessSpecifier AS,
+                                  SourceLocation UsingLoc,
+                                  SourceLocation EnumLoc, const DeclSpec &);
   Decl *ActOnAliasDeclaration(Scope *CurScope, AccessSpecifier AS,
                               MultiTemplateParamsArg TemplateParams,
                               SourceLocation UsingLoc, UnqualifiedId &Name,

diff  --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index 0dcaf565591b..540d2c9aa87e 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -537,6 +537,8 @@ enum class TemplateSubstitutionKind : char {
     Decl *VisitDecl(Decl *D);
     Decl *VisitVarDecl(VarDecl *D, bool InstantiatingVarTemplate,
                        ArrayRef<BindingDecl *> *Bindings = nullptr);
+    Decl *VisitBaseUsingDecls(BaseUsingDecl *D, BaseUsingDecl *Inst,
+                              LookupResult *Lookup);
 
     // Enable late instantiation of attributes.  Late instantiated attributes
     // will be stored in LA.

diff  --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index ff316904c0a3..edf8987d61a6 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1339,6 +1339,9 @@ enum DeclCode {
   /// A UsingDecl record.
   DECL_USING,
 
+  /// A UsingEnumDecl record.
+  DECL_USING_ENUM,
+
   /// A UsingPackDecl record.
   DECL_USING_PACK,
 

diff  --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 76f84c728bc6..a1f3baf99704 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -1571,6 +1571,21 @@ ASTContext::setInstantiatedFromUsingDecl(NamedDecl *Inst, NamedDecl *Pattern) {
   InstantiatedFromUsingDecl[Inst] = Pattern;
 }
 
+UsingEnumDecl *
+ASTContext::getInstantiatedFromUsingEnumDecl(UsingEnumDecl *UUD) {
+  auto Pos = InstantiatedFromUsingEnumDecl.find(UUD);
+  if (Pos == InstantiatedFromUsingEnumDecl.end())
+    return nullptr;
+
+  return Pos->second;
+}
+
+void ASTContext::setInstantiatedFromUsingEnumDecl(UsingEnumDecl *Inst,
+                                                  UsingEnumDecl *Pattern) {
+  assert(!InstantiatedFromUsingEnumDecl[Inst] && "pattern already exists");
+  InstantiatedFromUsingEnumDecl[Inst] = Pattern;
+}
+
 UsingShadowDecl *
 ASTContext::getInstantiatedFromUsingShadowDecl(UsingShadowDecl *Inst) {
   llvm::DenseMap<UsingShadowDecl*, UsingShadowDecl*>::const_iterator Pos

diff  --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index c27a50eba1d8..0fd1401e9e9f 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -512,6 +512,8 @@ namespace clang {
     ExpectedDecl VisitUsingDecl(UsingDecl *D);
     ExpectedDecl VisitUsingShadowDecl(UsingShadowDecl *D);
     ExpectedDecl VisitUsingDirectiveDecl(UsingDirectiveDecl *D);
+    ExpectedDecl ImportUsingShadowDecls(BaseUsingDecl *D, BaseUsingDecl *ToSI);
+    ExpectedDecl VisitUsingEnumDecl(UsingEnumDecl *D);
     ExpectedDecl VisitUnresolvedUsingValueDecl(UnresolvedUsingValueDecl *D);
     ExpectedDecl VisitUnresolvedUsingTypenameDecl(UnresolvedUsingTypenameDecl *D);
     ExpectedDecl VisitBuiltinTemplateDecl(BuiltinTemplateDecl *D);
@@ -4564,6 +4566,19 @@ ExpectedDecl ASTNodeImporter::VisitLinkageSpecDecl(LinkageSpecDecl *D) {
   return ToLinkageSpec;
 }
 
+ExpectedDecl ASTNodeImporter::ImportUsingShadowDecls(BaseUsingDecl *D,
+                                                     BaseUsingDecl *ToSI) {
+  for (UsingShadowDecl *FromShadow : D->shadows()) {
+    if (Expected<UsingShadowDecl *> ToShadowOrErr = import(FromShadow))
+      ToSI->addShadowDecl(*ToShadowOrErr);
+    else
+      // FIXME: We return error here but the definition is already created
+      // and available with lookups. How to fix this?..
+      return ToShadowOrErr.takeError();
+  }
+  return ToSI;
+}
+
 ExpectedDecl ASTNodeImporter::VisitUsingDecl(UsingDecl *D) {
   DeclContext *DC, *LexicalDC;
   DeclarationName Name;
@@ -4603,15 +4618,44 @@ ExpectedDecl ASTNodeImporter::VisitUsingDecl(UsingDecl *D) {
       return ToPatternOrErr.takeError();
   }
 
-  for (UsingShadowDecl *FromShadow : D->shadows()) {
-    if (Expected<UsingShadowDecl *> ToShadowOrErr = import(FromShadow))
-      ToUsing->addShadowDecl(*ToShadowOrErr);
+  return ImportUsingShadowDecls(D, ToUsing);
+}
+
+ExpectedDecl ASTNodeImporter::VisitUsingEnumDecl(UsingEnumDecl *D) {
+  DeclContext *DC, *LexicalDC;
+  DeclarationName Name;
+  SourceLocation Loc;
+  NamedDecl *ToD = nullptr;
+  if (Error Err = ImportDeclParts(D, DC, LexicalDC, Name, ToD, Loc))
+    return std::move(Err);
+  if (ToD)
+    return ToD;
+
+  Error Err = Error::success();
+  auto ToUsingLoc = importChecked(Err, D->getUsingLoc());
+  auto ToEnumLoc = importChecked(Err, D->getEnumLoc());
+  auto ToEnumDecl = importChecked(Err, D->getEnumDecl());
+  if (Err)
+    return std::move(Err);
+
+  UsingEnumDecl *ToUsingEnum;
+  if (GetImportedOrCreateDecl(ToUsingEnum, D, Importer.getToContext(), DC,
+                              ToUsingLoc, ToEnumLoc, Loc, ToEnumDecl))
+    return ToUsingEnum;
+
+  ToUsingEnum->setLexicalDeclContext(LexicalDC);
+  LexicalDC->addDeclInternal(ToUsingEnum);
+
+  if (UsingEnumDecl *FromPattern =
+          Importer.getFromContext().getInstantiatedFromUsingEnumDecl(D)) {
+    if (Expected<UsingEnumDecl *> ToPatternOrErr = import(FromPattern))
+      Importer.getToContext().setInstantiatedFromUsingEnumDecl(ToUsingEnum,
+                                                               *ToPatternOrErr);
     else
-      // FIXME: We return error here but the definition is already created
-      // and available with lookups. How to fix this?..
-      return ToShadowOrErr.takeError();
+      return ToPatternOrErr.takeError();
   }
-  return ToUsing;
+
+  return ImportUsingShadowDecls(D, ToUsingEnum);
 }
 
 ExpectedDecl ASTNodeImporter::VisitUsingShadowDecl(UsingShadowDecl *D) {

diff  --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index c5c8f473dda9..27b34d3d1699 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -1370,6 +1370,7 @@ LinkageInfo LinkageComputer::computeLVForDecl(const NamedDecl *D,
     case Decl::NamespaceAlias:
     case Decl::ParmVar:
     case Decl::Using:
+    case Decl::UsingEnum:
     case Decl::UsingShadow:
     case Decl::UsingDirective:
       return LinkageInfo::none();

diff  --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index 4e6d2da1e0d5..80b591e2a81a 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -784,6 +784,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
 
     case Using:
     case UsingPack:
+    case UsingEnum:
       return IDNS_Using;
 
     case ObjCProtocol:

diff  --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 10d59bce3f5c..628dff786c6e 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -3082,6 +3082,23 @@ SourceRange UsingDecl::getSourceRange() const {
   return SourceRange(Begin, getNameInfo().getEndLoc());
 }
 
+void UsingEnumDecl::anchor() {}
+
+UsingEnumDecl *UsingEnumDecl::Create(ASTContext &C, DeclContext *DC,
+                                     SourceLocation UL, SourceLocation EL,
+                                     SourceLocation NL, EnumDecl *Enum) {
+  return new (C, DC) UsingEnumDecl(DC, Enum->getDeclName(), UL, EL, NL, Enum);
+}
+
+UsingEnumDecl *UsingEnumDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
+  return new (C, ID) UsingEnumDecl(nullptr, DeclarationName(), SourceLocation(),
+                                   SourceLocation(), SourceLocation(), nullptr);
+}
+
+SourceRange UsingEnumDecl::getSourceRange() const {
+  return SourceRange(EnumLocation, getLocation());
+}
+
 void UsingPackDecl::anchor() {}
 
 UsingPackDecl *UsingPackDecl::Create(ASTContext &C, DeclContext *DC,

diff  --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index 90391cab08d6..516be54b90f6 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -98,6 +98,7 @@ namespace {
     void VisitUnresolvedUsingTypenameDecl(UnresolvedUsingTypenameDecl *D);
     void VisitUnresolvedUsingValueDecl(UnresolvedUsingValueDecl *D);
     void VisitUsingDecl(UsingDecl *D);
+    void VisitUsingEnumDecl(UsingEnumDecl *D);
     void VisitUsingShadowDecl(UsingShadowDecl *D);
     void VisitOMPThreadPrivateDecl(OMPThreadPrivateDecl *D);
     void VisitOMPAllocateDecl(OMPAllocateDecl *D);
@@ -1609,6 +1610,10 @@ void DeclPrinter::VisitUsingDecl(UsingDecl *D) {
   Out << *D;
 }
 
+void DeclPrinter::VisitUsingEnumDecl(UsingEnumDecl *D) {
+  Out << "using enum " << D->getEnumDecl();
+}
+
 void
 DeclPrinter::VisitUnresolvedUsingTypenameDecl(UnresolvedUsingTypenameDecl *D) {
   Out << "using typename ";

diff  --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp
index 5d5388880e67..d2835583a0f2 100644
--- a/clang/lib/AST/JSONNodeDumper.cpp
+++ b/clang/lib/AST/JSONNodeDumper.cpp
@@ -756,6 +756,10 @@ void JSONNodeDumper::VisitUsingDecl(const UsingDecl *UD) {
   JOS.attribute("name", Name);
 }
 
+void JSONNodeDumper::VisitUsingEnumDecl(const UsingEnumDecl *UED) {
+  JOS.attribute("target", createBareDeclRef(UED->getEnumDecl()));
+}
+
 void JSONNodeDumper::VisitUsingShadowDecl(const UsingShadowDecl *USD) {
   JOS.attribute("target", createBareDeclRef(USD->getTargetDecl()));
 }

diff  --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index cb5b4230a5ab..d17db0a0c9fe 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -2058,6 +2058,11 @@ void TextNodeDumper::VisitUsingDecl(const UsingDecl *D) {
   OS << D->getDeclName();
 }
 
+void TextNodeDumper::VisitUsingEnumDecl(const UsingEnumDecl *D) {
+  OS << ' ';
+  dumpBareDeclRef(D->getEnumDecl());
+}
+
 void TextNodeDumper::VisitUnresolvedUsingTypenameDecl(
     const UnresolvedUsingTypenameDecl *D) {
   OS << ' ';

diff  --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
index 7c4eb6dd77ca..169ce3b83980 100644
--- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -841,6 +841,7 @@ const internal::VariadicDynCastAllOfMatcher<Stmt, ParenListExpr> parenListExpr;
 const internal::VariadicDynCastAllOfMatcher<Stmt, SubstNonTypeTemplateParmExpr>
     substNonTypeTemplateParmExpr;
 const internal::VariadicDynCastAllOfMatcher<Decl, UsingDecl> usingDecl;
+const internal::VariadicDynCastAllOfMatcher<Decl, UsingEnumDecl> usingEnumDecl;
 const internal::VariadicDynCastAllOfMatcher<Decl, UsingDirectiveDecl>
     usingDirectiveDecl;
 const internal::VariadicDynCastAllOfMatcher<Stmt, UnresolvedLookupExpr>

diff  --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
index 31b4f601faf4..0048f1133ca2 100644
--- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -561,6 +561,7 @@ RegistryMaps::RegistryMaps() {
   REGISTER_MATCHER(userDefinedLiteral);
   REGISTER_MATCHER(usesADL);
   REGISTER_MATCHER(usingDecl);
+  REGISTER_MATCHER(usingEnumDecl);
   REGISTER_MATCHER(usingDirectiveDecl);
   REGISTER_MATCHER(valueDecl);
   REGISTER_MATCHER(varDecl);

diff  --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index b1db8a889b3f..1367ef46d85d 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -5002,6 +5002,17 @@ void CGDebugInfo::EmitUsingDecl(const UsingDecl &UD) {
   }
 }
 
+void CGDebugInfo::EmitUsingEnumDecl(const UsingEnumDecl &UD) {
+  if (!CGM.getCodeGenOpts().hasReducedDebugInfo())
+    return;
+  assert(UD.shadow_size() &&
+         "We shouldn't be codegening an invalid UsingEnumDecl"
+         " containing no decls");
+
+  for (const auto *USD : UD.shadows())
+    EmitUsingShadowDecl(*USD);
+}
+
 void CGDebugInfo::EmitImportDecl(const ImportDecl &ID) {
   if (CGM.getCodeGenOpts().getDebuggerTuning() != llvm::DebuggerKind::LLDB)
     return;

diff  --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h
index e9ead90aafb5..9c52bc75475f 100644
--- a/clang/lib/CodeGen/CGDebugInfo.h
+++ b/clang/lib/CodeGen/CGDebugInfo.h
@@ -508,6 +508,9 @@ class CGDebugInfo {
   /// Emit C++ using declaration.
   void EmitUsingDecl(const UsingDecl &UD);
 
+  /// Emit C++ using-enum declaration.
+  void EmitUsingEnumDecl(const UsingEnumDecl &UD);
+
   /// Emit an @import declaration.
   void EmitImportDecl(const ImportDecl &ID);
 

diff  --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 7948f431299c..e9a7836bab47 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -138,6 +138,10 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
     if (CGDebugInfo *DI = getDebugInfo())
         DI->EmitUsingDecl(cast<UsingDecl>(D));
     return;
+  case Decl::UsingEnum: // using enum X; [C++]
+    if (CGDebugInfo *DI = getDebugInfo())
+      DI->EmitUsingEnumDecl(cast<UsingEnumDecl>(D));
+    return;
   case Decl::UsingPack:
     for (auto *Using : cast<UsingPackDecl>(D).expansions())
       EmitDecl(*Using);

diff  --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index c93339a211f3..7efc97c51eda 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -5740,6 +5740,10 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
     if (CGDebugInfo *DI = getModuleDebugInfo())
         DI->EmitUsingDecl(cast<UsingDecl>(*D));
     break;
+  case Decl::UsingEnum: // using enum X; [C++]
+    if (CGDebugInfo *DI = getModuleDebugInfo())
+      DI->EmitUsingEnumDecl(cast<UsingEnumDecl>(*D));
+    break;
   case Decl::NamespaceAlias:
     if (CGDebugInfo *DI = getModuleDebugInfo())
         DI->EmitNamespaceAlias(cast<NamespaceAliasDecl>(*D));

diff  --git a/clang/lib/Index/IndexSymbol.cpp b/clang/lib/Index/IndexSymbol.cpp
index 0d2e557cdd36..68e457de5265 100644
--- a/clang/lib/Index/IndexSymbol.cpp
+++ b/clang/lib/Index/IndexSymbol.cpp
@@ -329,6 +329,11 @@ SymbolInfo index::getSymbolInfo(const Decl *D) {
       Info.Kind = SymbolKind::Using;
       Info.Lang = SymbolLanguage::CXX;
       break;
+    case Decl::UsingEnum:
+      Info.Kind = SymbolKind::Using;
+      Info.Lang = SymbolLanguage::CXX;
+      Info.SubKind = SymbolSubKind::UsingEnum;
+      break;
     case Decl::Binding:
       Info.Kind = SymbolKind::Variable;
       Info.Lang = SymbolLanguage::CXX;
@@ -542,6 +547,8 @@ StringRef index::getSymbolSubKindString(SymbolSubKind K) {
   case SymbolSubKind::AccessorSetter: return "acc-set";
   case SymbolSubKind::UsingTypename: return "using-typename";
   case SymbolSubKind::UsingValue: return "using-value";
+  case SymbolSubKind::UsingEnum:
+    return "using-enum";
   }
   llvm_unreachable("invalid symbol subkind");
 }

diff  --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index eebbb545f060..37a5ae1f1377 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -667,10 +667,48 @@ bool Parser::ParseUsingDeclarator(DeclaratorContext Context,
 ///     alias-declaration: C++11 [dcl.dcl]p1
 ///       'using' identifier attribute-specifier-seq[opt] = type-id ;
 ///
-Parser::DeclGroupPtrTy Parser::ParseUsingDeclaration(
+///     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,
     ParsedAttributesWithRange &PrefixAttrs, AccessSpecifier AS) {
+  SourceLocation UELoc;
+  if (TryConsumeToken(tok::kw_enum, UELoc)) {
+    // C++20 using-enum
+    Diag(UELoc, getLangOpts().CPlusPlus20
+                    ? diag::warn_cxx17_compat_using_enum_declaration
+                    : diag::ext_using_enum_declaration);
+
+    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);
+
+      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"))
+      SkipUntil(tok::semi);
+
+    return Actions.ConvertDeclToDeclGroup(UED);
+  }
+
   // Check for misplaced attributes before the identifier in an
   // alias-declaration.
   ParsedAttributesWithRange MisplacedAttrs(AttrFactory);
@@ -768,8 +806,9 @@ Parser::DeclGroupPtrTy Parser::ParseUsingDeclaration(
   // 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);

diff  --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp
index 87059eb45085..1c8f6329bd67 100644
--- a/clang/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp
@@ -240,7 +240,6 @@ bool Sema::RequireCompleteDeclContext(CXXScopeSpec &SS,
 ///
 bool Sema::RequireCompleteEnumDecl(EnumDecl *EnumD, SourceLocation L,
                                    CXXScopeSpec *SS) {
-  assert (SS && "missing scope");
   if (EnumD->isCompleteDefinition()) {
     // If we know about the definition but it is not visible, complain.
     NamedDecl *SuggestedDef = nullptr;
@@ -264,16 +263,22 @@ bool Sema::RequireCompleteEnumDecl(EnumDecl *EnumD, SourceLocation L,
       if (InstantiateEnum(L, EnumD, Pattern,
                           getTemplateInstantiationArgs(EnumD),
                           TSK_ImplicitInstantiation)) {
-        SS->SetInvalid(SS->getRange());
+        if (SS)
+          SS->SetInvalid(SS->getRange());
         return true;
       }
       return false;
     }
   }
 
-  Diag(L, diag::err_incomplete_nested_name_spec)
-      << QualType(EnumD->getTypeForDecl(), 0) << SS->getRange();
-  SS->SetInvalid(SS->getRange());
+  if (SS) {
+    Diag(L, diag::err_incomplete_nested_name_spec)
+        << QualType(EnumD->getTypeForDecl(), 0) << SS->getRange();
+    SS->SetInvalid(SS->getRange());
+  } else {
+    Diag(L, diag::err_incomplete_enum) << QualType(EnumD->getTypeForDecl(), 0);
+    Diag(EnumD->getLocation(), diag::note_declared_at);
+  }
 
   return true;
 }

diff  --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index 0bde8eaf7550..1ab9c50709a9 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -3915,6 +3915,9 @@ CXCursorKind clang::getCursorKindForDecl(const Decl *D) {
   case Decl::UnresolvedUsingTypename:
     return CXCursor_UsingDeclaration;
 
+  case Decl::UsingEnum:
+    return CXCursor_EnumDecl;
+
   case Decl::ObjCPropertyImpl:
     switch (cast<ObjCPropertyImplDecl>(D)->getPropertyImplementation()) {
     case ObjCPropertyImplDecl::Dynamic:

diff  --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 1a8745ea9a27..c1d7217e4548 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -1835,9 +1835,11 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl,
     case Decl::UsingDirective:
     case Decl::UnresolvedUsingTypename:
     case Decl::UnresolvedUsingValue:
+    case Decl::UsingEnum:
       //   - static_assert-declarations
       //   - using-declarations,
       //   - using-directives,
+      //   - using-enum-declaration
       continue;
 
     case Decl::Typedef:
@@ -11604,11 +11606,44 @@ Decl *Sema::ActOnUsingDeclaration(Scope *S, AccessSpecifier AS,
     }
   }
 
-  NamedDecl *UD = BuildUsingDeclaration(
-      S, AS, UsingLoc, TypenameLoc.isValid(), TypenameLoc, SS, TargetNameInfo,
-      EllipsisLoc, AttrList,
-      /*IsInstantiation=*/false,
-      AttrList.hasAttribute(ParsedAttr::AT_UsingIfExists));
+  NamedDecl *UD =
+      BuildUsingDeclaration(S, AS, UsingLoc, TypenameLoc.isValid(), TypenameLoc,
+                            SS, TargetNameInfo, EllipsisLoc, AttrList,
+                            /*IsInstantiation*/ false,
+                            AttrList.hasAttribute(ParsedAttr::AT_UsingIfExists));
+  if (UD)
+    PushOnScopeChains(UD, S, /*AddToContext*/ false);
+
+  return UD;
+}
+
+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
+    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");
+  }
+
+  // As with enum-decls, we ignore attributes for now.
+  auto *Enum = cast<EnumDecl>(DS.getRepAsDecl());
+  if (auto *Def = Enum->getDefinition())
+    Enum = Def;
+
+  auto *UD = BuildUsingEnumDeclaration(S, AS, UsingLoc, EnumLoc,
+                                       DS.getTypeSpecTypeNameLoc(), Enum);
   if (UD)
     PushOnScopeChains(UD, S, /*AddToContext*/ false);
 
@@ -11712,7 +11747,7 @@ bool Sema::CheckUsingShadowDecl(BaseUsingDecl *BUD, NamedDecl *Orig,
     // We can have UsingDecls in our Previous results because we use the same
     // LookupResult for checking whether the UsingDecl itself is a valid
     // redeclaration.
-    if (isa<UsingDecl>(D) || isa<UsingPackDecl>(D))
+    if (isa<UsingDecl>(D) || isa<UsingPackDecl>(D) || isa<UsingEnumDecl>(D))
       continue;
 
     if (auto *RD = dyn_cast<CXXRecordDecl>(D)) {
@@ -12014,6 +12049,29 @@ class UsingValidatorCCC final : public CorrectionCandidateCallback {
 };
 } // end anonymous namespace
 
+/// Remove decls we can't actually see from a lookup being used to declare
+/// shadow using decls.
+///
+/// \param S - The scope of the potential shadow decl
+/// \param Previous - The lookup of a potential shadow decl's name.
+void Sema::FilterUsingLookup(Scope *S, LookupResult &Previous) {
+  // It is really dumb that we have to do this.
+  LookupResult::Filter F = Previous.makeFilter();
+  while (F.hasNext()) {
+    NamedDecl *D = F.next();
+    if (!isDeclInScope(D, CurContext, S))
+      F.erase();
+    // If we found a local extern declaration that's not ordinarily visible,
+    // and this declaration is being added to a non-block scope, ignore it.
+    // We're only checking for scope conflicts here, not also for violations
+    // of the linkage rules.
+    else if (!CurContext->isFunctionOrMethod() && D->isLocalExternDecl() &&
+             !(D->getIdentifierNamespace() & Decl::IDNS_Ordinary))
+      F.erase();
+  }
+  F.done();
+}
+
 /// Builds a using declaration.
 ///
 /// \param IsInstantiation - Whether this call arises from an
@@ -12047,21 +12105,7 @@ NamedDecl *Sema::BuildUsingDeclaration(
   if (S) {
     LookupName(Previous, S);
 
-    // It is really dumb that we have to do this.
-    LookupResult::Filter F = Previous.makeFilter();
-    while (F.hasNext()) {
-      NamedDecl *D = F.next();
-      if (!isDeclInScope(D, CurContext, S))
-        F.erase();
-      // If we found a local extern declaration that's not ordinarily visible,
-      // and this declaration is being added to a non-block scope, ignore it.
-      // We're only checking for scope conflicts here, not also for violations
-      // of the linkage rules.
-      else if (!CurContext->isFunctionOrMethod() && D->isLocalExternDecl() &&
-               !(D->getIdentifierNamespace() & Decl::IDNS_Ordinary))
-        F.erase();
-    }
-    F.done();
+    FilterUsingLookup(S, Previous);
   } else {
     assert(IsInstantiation && "no scope in non-instantiation");
     if (CurContext->isRecord())
@@ -12287,6 +12331,61 @@ NamedDecl *Sema::BuildUsingDeclaration(
   return UD;
 }
 
+NamedDecl *Sema::BuildUsingEnumDeclaration(Scope *S, AccessSpecifier AS,
+                                           SourceLocation UsingLoc,
+                                           SourceLocation EnumLoc,
+                                           SourceLocation NameLoc,
+                                           EnumDecl *ED) {
+  bool Invalid = false;
+
+  if (CurContext->getRedeclContext()->isRecord()) {
+    /// In class scope, check if this is a duplicate, for better a diagnostic.
+    DeclarationNameInfo UsingEnumName(ED->getDeclName(), NameLoc);
+    LookupResult Previous(*this, UsingEnumName, LookupUsingDeclName,
+                          ForVisibleRedeclaration);
+
+    LookupName(Previous, S);
+
+    for (NamedDecl *D : Previous)
+      if (UsingEnumDecl *UED = dyn_cast<UsingEnumDecl>(D))
+        if (UED->getEnumDecl() == ED) {
+          Diag(UsingLoc, diag::err_using_enum_decl_redeclaration)
+              << SourceRange(EnumLoc, NameLoc);
+          Diag(D->getLocation(), diag::note_using_enum_decl) << 1;
+          Invalid = true;
+          break;
+        }
+  }
+
+  if (RequireCompleteEnumDecl(ED, NameLoc))
+    Invalid = true;
+
+  UsingEnumDecl *UD = UsingEnumDecl::Create(Context, CurContext, UsingLoc,
+                                            EnumLoc, NameLoc, ED);
+  UD->setAccess(AS);
+  CurContext->addDecl(UD);
+
+  if (Invalid) {
+    UD->setInvalidDecl();
+    return UD;
+  }
+
+  // Create the shadow decls for each enumerator
+  for (EnumConstantDecl *EC : ED->enumerators()) {
+    UsingShadowDecl *PrevDecl = nullptr;
+    DeclarationNameInfo DNI(EC->getDeclName(), EC->getLocation());
+    LookupResult Previous(*this, DNI, LookupOrdinaryName,
+                          ForVisibleRedeclaration);
+    LookupName(Previous, S);
+    FilterUsingLookup(S, Previous);
+
+    if (!CheckUsingShadowDecl(UD, EC, Previous, PrevDecl))
+      BuildUsingShadowDecl(S, UD, EC, PrevDecl);
+  }
+
+  return UD;
+}
+
 NamedDecl *Sema::BuildUsingPackDecl(NamedDecl *InstantiatedFrom,
                                     ArrayRef<NamedDecl *> Expansions) {
   assert(isa<UnresolvedUsingValueDecl>(InstantiatedFrom) ||

diff  --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index f1b34693f45d..10945963352b 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -3013,6 +3013,53 @@ Decl *TemplateDeclInstantiator::VisitUsingDirectiveDecl(UsingDirectiveDecl *D) {
   return Inst;
 }
 
+Decl *TemplateDeclInstantiator::VisitBaseUsingDecls(BaseUsingDecl *D,
+                                                    BaseUsingDecl *Inst,
+                                                    LookupResult *Lookup) {
+
+  bool isFunctionScope = Owner->isFunctionOrMethod();
+
+  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();
+    if (auto *CUSD = dyn_cast<ConstructorUsingShadowDecl>(Shadow))
+      if (auto *BaseShadow = CUSD->getNominatedBaseClassShadowDecl())
+        OldTarget = BaseShadow;
+
+    NamedDecl *InstTarget = nullptr;
+    if (auto *EmptyD =
+            dyn_cast<UnresolvedUsingIfExistsDecl>(Shadow->getTargetDecl())) {
+      InstTarget = UnresolvedUsingIfExistsDecl::Create(
+          SemaRef.Context, Owner, EmptyD->getLocation(), EmptyD->getDeclName());
+    } else {
+      InstTarget = cast_or_null<NamedDecl>(SemaRef.FindInstantiatedDecl(
+          Shadow->getLocation(), OldTarget, TemplateArgs));
+    }
+    if (!InstTarget)
+      return nullptr;
+
+    UsingShadowDecl *PrevDecl = nullptr;
+    if (Lookup &&
+        SemaRef.CheckUsingShadowDecl(Inst, InstTarget, *Lookup, PrevDecl))
+      continue;
+
+    if (UsingShadowDecl *OldPrev = getPreviousDeclForInstantiation(Shadow))
+      PrevDecl = cast_or_null<UsingShadowDecl>(SemaRef.FindInstantiatedDecl(
+          Shadow->getLocation(), OldPrev, TemplateArgs));
+
+    UsingShadowDecl *InstShadow = SemaRef.BuildUsingShadowDecl(
+        /*Scope*/ nullptr, Inst, InstTarget, PrevDecl);
+    SemaRef.Context.setInstantiatedFromUsingShadowDecl(InstShadow, Shadow);
+
+    if (isFunctionScope)
+      SemaRef.CurrentInstantiationScope->InstantiatedLocal(Shadow, InstShadow);
+  }
+
+  return Inst;
+}
+
 Decl *TemplateDeclInstantiator::VisitUsingDecl(UsingDecl *D) {
 
   // The nested name specifier may be dependent, for example
@@ -3038,11 +3085,9 @@ Decl *TemplateDeclInstantiator::VisitUsingDecl(UsingDecl *D) {
       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).
+  // 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);
 
@@ -3063,13 +3108,13 @@ Decl *TemplateDeclInstantiator::VisitUsingDecl(UsingDecl *D) {
                                             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);
@@ -3083,49 +3128,34 @@ Decl *TemplateDeclInstantiator::VisitUsingDecl(UsingDecl *D) {
   if (NameInfo.getName().getNameKind() == DeclarationName::CXXConstructorName)
     SemaRef.CheckInheritingConstructorUsingDecl(NewUD);
 
-  bool isFunctionScope = Owner->isFunctionOrMethod();
+  return VisitBaseUsingDecls(D, NewUD, CheckRedeclaration ? &Prev : nullptr);
+}
 
-  // 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.
-    NamedDecl *OldTarget = Shadow->getTargetDecl();
-    if (auto *CUSD = dyn_cast<ConstructorUsingShadowDecl>(Shadow))
-      if (auto *BaseShadow = CUSD->getNominatedBaseClassShadowDecl())
-        OldTarget = BaseShadow;
+Decl *TemplateDeclInstantiator::VisitUsingEnumDecl(UsingEnumDecl *D) {
+  // Cannot be a dependent type, but still could be an instantiation
+  EnumDecl *EnumD = cast_or_null<EnumDecl>(SemaRef.FindInstantiatedDecl(
+      D->getLocation(), D->getEnumDecl(), TemplateArgs));
 
-    NamedDecl *InstTarget = nullptr;
-    if (auto *EmptyD =
-            dyn_cast<UnresolvedUsingIfExistsDecl>(Shadow->getTargetDecl())) {
-      InstTarget = UnresolvedUsingIfExistsDecl::Create(
-          SemaRef.Context, Owner, EmptyD->getLocation(), EmptyD->getDeclName());
-    } else {
-      InstTarget = cast_or_null<NamedDecl>(SemaRef.FindInstantiatedDecl(
-          Shadow->getLocation(), OldTarget, TemplateArgs));
-    }
-    if (!InstTarget)
-      return nullptr;
+  if (SemaRef.RequireCompleteEnumDecl(EnumD, EnumD->getLocation()))
+    return nullptr;
 
-    UsingShadowDecl *PrevDecl = nullptr;
-    if (CheckRedeclaration) {
-      if (SemaRef.CheckUsingShadowDecl(NewUD, InstTarget, Prev, PrevDecl))
-        continue;
-    } else if (UsingShadowDecl *OldPrev =
-                   getPreviousDeclForInstantiation(Shadow)) {
-      PrevDecl = cast_or_null<UsingShadowDecl>(SemaRef.FindInstantiatedDecl(
-          Shadow->getLocation(), OldPrev, TemplateArgs));
-    }
+  UsingEnumDecl *NewUD =
+      UsingEnumDecl::Create(SemaRef.Context, Owner, D->getUsingLoc(),
+                            D->getEnumLoc(), D->getLocation(), EnumD);
 
-    UsingShadowDecl *InstShadow =
-        SemaRef.BuildUsingShadowDecl(/*Scope*/nullptr, NewUD, InstTarget,
-                                     PrevDecl);
-    SemaRef.Context.setInstantiatedFromUsingShadowDecl(InstShadow, Shadow);
+  SemaRef.Context.setInstantiatedFromUsingEnumDecl(NewUD, D);
+  NewUD->setAccess(D->getAccess());
+  Owner->addDecl(NewUD);
 
-    if (isFunctionScope)
-      SemaRef.CurrentInstantiationScope->InstantiatedLocal(Shadow, InstShadow);
-  }
+  // Don't process the shadow decls for an invalid decl.
+  if (NewUD->isInvalidDecl())
+    return NewUD;
+
+  // We don't have to recheck for duplication of the UsingEnumDecl itself, as it
+  // cannot be dependent, and will therefore have been checked during template
+  // definition.
 
-  return NewUD;
+  return VisitBaseUsingDecls(D, NewUD, nullptr);
 }
 
 Decl *TemplateDeclInstantiator::VisitUsingShadowDecl(UsingShadowDecl *D) {

diff  --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp
index fb0438db7605..5fe1f96327dd 100644
--- a/clang/lib/Serialization/ASTCommon.cpp
+++ b/clang/lib/Serialization/ASTCommon.cpp
@@ -394,6 +394,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) {
   case Decl::NonTypeTemplateParm:
   case Decl::TemplateTemplateParm:
   case Decl::Using:
+  case Decl::UsingEnum:
   case Decl::UsingPack:
   case Decl::ObjCMethod:
   case Decl::ObjCCategory:

diff  --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 41b44c0e5260..1d9bae5d3129 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -390,6 +390,7 @@ namespace clang {
     void VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D);
     void VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D);
     void VisitUsingDecl(UsingDecl *D);
+    void VisitUsingEnumDecl(UsingEnumDecl *D);
     void VisitUsingPackDecl(UsingPackDecl *D);
     void VisitUsingShadowDecl(UsingShadowDecl *D);
     void VisitConstructorUsingShadowDecl(ConstructorUsingShadowDecl *D);
@@ -1652,6 +1653,17 @@ void ASTDeclReader::VisitUsingDecl(UsingDecl *D) {
   mergeMergeable(D);
 }
 
+void ASTDeclReader::VisitUsingEnumDecl(UsingEnumDecl *D) {
+  VisitNamedDecl(D);
+  D->setUsingLoc(readSourceLocation());
+  D->setEnumLoc(readSourceLocation());
+  D->Enum = readDeclAs<EnumDecl>();
+  D->FirstUsingShadow.setPointer(readDeclAs<UsingShadowDecl>());
+  if (auto *Pattern = readDeclAs<UsingEnumDecl>())
+    Reader.getContext().setInstantiatedFromUsingEnumDecl(D, Pattern);
+  mergeMergeable(D);
+}
+
 void ASTDeclReader::VisitUsingPackDecl(UsingPackDecl *D) {
   VisitNamedDecl(D);
   D->InstantiatedFrom = readDeclAs<NamedDecl>();
@@ -3844,6 +3856,9 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) {
   case DECL_USING_SHADOW:
     D = UsingShadowDecl::CreateDeserialized(Context, ID);
     break;
+  case DECL_USING_ENUM:
+    D = UsingEnumDecl::CreateDeserialized(Context, ID);
+    break;
   case DECL_CONSTRUCTOR_USING_SHADOW:
     D = ConstructorUsingShadowDecl::CreateDeserialized(Context, ID);
     break;

diff  --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 03f2a0c451c8..e9315f67d553 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -114,6 +114,7 @@ namespace clang {
     void VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D);
     void VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D);
     void VisitUsingDecl(UsingDecl *D);
+    void VisitUsingEnumDecl(UsingEnumDecl *D);
     void VisitUsingPackDecl(UsingPackDecl *D);
     void VisitUsingShadowDecl(UsingShadowDecl *D);
     void VisitConstructorUsingShadowDecl(ConstructorUsingShadowDecl *D);
@@ -1278,6 +1279,16 @@ void ASTDeclWriter::VisitUsingDecl(UsingDecl *D) {
   Code = serialization::DECL_USING;
 }
 
+void ASTDeclWriter::VisitUsingEnumDecl(UsingEnumDecl *D) {
+  VisitNamedDecl(D);
+  Record.AddSourceLocation(D->getUsingLoc());
+  Record.AddSourceLocation(D->getEnumLoc());
+  Record.AddDeclRef(D->getEnumDecl());
+  Record.AddDeclRef(D->FirstUsingShadow.getPointer());
+  Record.AddDeclRef(Context.getInstantiatedFromUsingEnumDecl(D));
+  Code = serialization::DECL_USING_ENUM;
+}
+
 void ASTDeclWriter::VisitUsingPackDecl(UsingPackDecl *D) {
   Record.push_back(D->NumExpansions);
   VisitNamedDecl(D);

diff  --git a/clang/test/AST/ast-dump-using-enum.cpp b/clang/test/AST/ast-dump-using-enum.cpp
new file mode 100644
index 000000000000..3f848ca96626
--- /dev/null
+++ b/clang/test/AST/ast-dump-using-enum.cpp
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c++20 -ast-dump -ast-dump-filter Foo %s | FileCheck -strict-whitespace %s
+
+// Test with serialization:
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown -emit-pch -o %t %s
+// RUN: %clang_cc1 -x c++ -std=c++20 -triple x86_64-unknown-unknown -include-pch %t \
+// RUN: -ast-dump-all -ast-dump-filter Foo /dev/null \
+// RUN: | FileCheck --strict-whitespace %s
+
+namespace Bob {
+enum class Foo {
+  Foo_a,
+  Foo_b
+};
+}; // namespace Bob
+
+using enum Bob::Foo;
+
+// CHECK-LABEL: Dumping Bob::Foo
+// CHECK-NEXT: EnumDecl {{.*}} class Foo 'int'
+// CHECK-NEXT: |-EnumConstantDecl {{.*}} Foo_a 'Bob::Foo'
+// CHECK-NEXT: `-EnumConstantDecl {{.*}} Foo_b 'Bob::Foo'
+
+// CHECK-LABEL: Dumping Foo:
+// CHECK-NEXT: UsingEnumDecl {{.*}} Enum {{.*}} 'Foo'
+
+// CHECK-LABEL: Dumping Foo_a:
+// CHECK-NEXT: UsingShadowDecl {{.*}} implicit EnumConstant {{.*}} 'Foo_a' 'Bob::Foo'
+
+// CHECK-LABEL: Dumping Foo_b:
+// CHECK-NEXT: UsingShadowDecl {{.*}} implicit EnumConstant {{.*}} 'Foo_b' 'Bob::Foo'

diff  --git a/clang/test/SemaCXX/cxx20-using-enum.cpp b/clang/test/SemaCXX/cxx20-using-enum.cpp
new file mode 100644
index 000000000000..daf35ebfefbb
--- /dev/null
+++ b/clang/test/SemaCXX/cxx20-using-enum.cpp
@@ -0,0 +1,233 @@
+// 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 at -2{{is a C++20 extension}}
+#endif
+using enum Bob::B; // expected-error{{no enum named 'B'}}
+#if __cplusplus < 202002
+// expected-warning at -2{{is a C++20 extension}}
+#endif
+using enum Bob::C; // expected-error{{tag type that does not match}}
+#if __cplusplus < 202002
+// expected-warning at -2{{is a C++20 extension}}
+#endif
+auto v = a;
+
+A g; // expected-error{{unknown type name 'A'}}
+
+int A;
+
+using enum Bob::D;
+#if __cplusplus < 202002
+// expected-warning at -2{{is a C++20 extension}}
+#endif
+} // namespace One
+
+namespace Two {
+namespace Kevin {
+enum class B { d,
+               e,
+               f };
+}
+
+using enum Kevin::B;
+#if __cplusplus < 202002
+// expected-warning at -2{{is a C++20 extension}}
+#endif
+auto w = e;
+
+} // namespace Two
+
+#if __cplusplus >= 202002
+// Now only check c++20 onwards
+
+namespace Three {
+namespace Stuart {
+enum class C : int; // expected-note{{declared here}}
+}
+
+using enum Stuart::C; // expected-error{{is incomplete}}
+} // 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;
+using enum Dave::E; // expected-error{{is a private member}}
+
+} // namespace Four
+
+namespace Five {
+enum class A { b,
+               c };
+class Dave {
+public:
+  using enum A;
+  A f = b;
+};
+
+} // namespace Five
+
+namespace Six {
+template <typename T> class TPL;
+template <> class TPL<int> {
+public:
+  enum A { a };
+};
+
+template <typename T> class USR {
+  using enum TPL<T>::B; // expected-error{{cannot name a dependent type}}
+  using enum TPL<int>::A;
+};
+} // 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;
+  return int(b);
+}
+static_assert(Bar() == 1);
+
+template <int I> constexpr int Foo() {
+  using enum Stuart::A;
+  return int(b) + I;
+}
+
+static_assert(Foo<10>() == 11);
+
+template <int I> struct C {
+  using enum Stuart::A;
+  static constexpr int V = int(c) + I;
+
+  enum class D { d,
+                 e,
+                 f };
+  using enum D;
+
+  static constexpr int W = int(f) + I;
+};
+
+static_assert(C<2>::V == 4);
+static_assert(C<20>::W == 22);
+
+} // namespace Seven
+
+namespace Eight {
+enum class Bob : int {};
+using enum Bob;
+} // namespace Eight
+
+namespace Nine {
+template <int I> struct C {
+  enum class D { i = I };
+  enum class E : int; // expected-note{{declared here}}
+};
+
+using enum C<2>::D;
+
+constexpr auto d = i;
+static_assert(unsigned(d) == 2);
+
+using enum C<2>::E; // expected-error{{instantiation of undefined member}}
+} // namespace Nine
+
+namespace Ten {
+enum class Bob { a };
+
+void Foo() {
+  extern void a();
+}
+
+// We don't see the hidden extern a fn!
+using enum Bob;
+
+auto v = a;
+} // namespace Ten
+
+namespace Eleven {
+enum class Bob { a }; // expected-note{{conflicting declaration}}
+
+struct Base {
+  enum { a }; // expected-note{{target of using}}
+};
+
+template <typename B>
+class TPLa : B {
+  using enum Bob;
+  using B::a; // expected-error{{target of using declaration}}
+};
+
+TPLa<Base> a; // expected-note{{in instantiation}}
+
+} // namespace Eleven
+
+namespace Twelve {
+enum class Bob { a }; // expected-note{{target of using}}
+
+struct Base {
+  enum { a };
+};
+
+template <typename B>
+class TPLb : B {
+  using B::a;     // expected-note{{conflicting declaration}}
+  using enum Bob; // expected-error{{target of using declaration}}
+};
+
+TPLb<Base> b;
+
+} // namespace Twelve
+
+namespace Thirteen {
+enum class Bob { a };
+class Foo {
+  using enum Bob; // expected-note{{previous using-enum}}
+  using enum Bob; // expected-error{{redeclaration of using-enum}}
+};
+
+template <typename B>
+class TPLa {
+  using enum Bob; // expected-note{{previous using-enum}}
+  using enum Bob; // expected-error{{redeclaration of using-enum}}
+};
+
+TPLa<int> a;
+
+} // namespace Thirteen
+
+#endif

diff  --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 3ec41c9b025c..89f7063b7364 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -6541,6 +6541,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) {
   }
 
   case Decl::Using:
+  case Decl::UsingEnum:
     return MakeCursorOverloadedDeclRef(cast<BaseUsingDecl>(D), D->getLocation(),
                                        TU);
 

diff  --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp
index 4ed4a09b03b9..040adec3d5d7 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -844,7 +844,15 @@ TEST_P(ImportDecl, ImportUsingDecl) {
   testImport("namespace foo { int bar; }"
              "void declToImport() { using foo::bar; }",
              Lang_CXX03, "", Lang_CXX03, Verifier,
-             functionDecl(hasDescendant(usingDecl())));
+             functionDecl(hasDescendant(usingDecl(hasName("bar")))));
+}
+
+TEST_P(ImportDecl, ImportUsingEnumDecl) {
+  MatchVerifier<Decl> Verifier;
+  testImport("namespace foo { enum bar { baz, toto, quux }; }"
+             "void declToImport() { using enum foo::bar; }",
+             Lang_CXX20, "", Lang_CXX20, Verifier,
+             functionDecl(hasDescendant(usingEnumDecl(hasName("bar")))));
 }
 
 /// \brief Matches shadow declarations introduced into a scope by a
@@ -862,10 +870,16 @@ const internal::VariadicDynCastAllOfMatcher<Decl,
 
 TEST_P(ImportDecl, ImportUsingShadowDecl) {
   MatchVerifier<Decl> Verifier;
+  // from using-decl
   testImport("namespace foo { int bar; }"
              "namespace declToImport { using foo::bar; }",
              Lang_CXX03, "", Lang_CXX03, Verifier,
-             namespaceDecl(has(usingShadowDecl())));
+             namespaceDecl(has(usingShadowDecl(hasName("bar")))));
+  // from using-enum-decl
+  testImport("namespace foo { enum bar {baz, toto, quux }; }"
+             "namespace declToImport { using enum foo::bar; }",
+             Lang_CXX20, "", Lang_CXX20, Verifier,
+             namespaceDecl(has(usingShadowDecl(hasName("baz")))));
 }
 
 TEST_P(ImportExpr, ImportUnresolvedLookupExpr) {

diff  --git a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
index c179ce11fefb..99d4b9bc626d 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
@@ -1428,6 +1428,22 @@ TEST_P(ASTMatchersTest, UsingDecl_MatchesShadowUsingDelcarations) {
                       usingDecl(hasAnyUsingShadowDecl(hasName("a")))));
 }
 
+TEST_P(ASTMatchersTest, UsingEnumDecl_MatchesUsingEnumDeclarations) {
+  if (!GetParam().isCXX20OrLater()) {
+    return;
+  }
+  EXPECT_TRUE(
+      matches("namespace X { enum x {}; } using enum X::x;", usingEnumDecl()));
+}
+
+TEST_P(ASTMatchersTest, UsingEnumDecl_MatchesShadowUsingDeclarations) {
+  if (!GetParam().isCXX20OrLater()) {
+    return;
+  }
+  EXPECT_TRUE(matches("namespace f { enum a {b}; } using enum f::a;",
+                      usingEnumDecl(hasAnyUsingShadowDecl(hasName("b")))));
+}
+
 TEST_P(ASTMatchersTest, UsingDirectiveDecl_MatchesUsingNamespace) {
   if (!GetParam().isCXX()) {
     return;


        


More information about the cfe-commits mailing list