[clang] 012898b - [clang] p1099 using enum part 1

Nathan Sidwell via cfe-commits cfe-commits at lists.llvm.org
Tue Jun 8 04:41:03 PDT 2021


Author: Nathan Sidwell
Date: 2021-06-08T04:40:42-07:00
New Revision: 012898b92cad00e230a960a08a3f418628bec060

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

LOG: [clang] p1099 using enum part 1

This adds support for p1099's 'using SCOPED_ENUM::MEMBER;'
functionality, bringing a member of an enumerator into the current
scope. The novel feature here, is that there need not be a class
hierarchical relationship between the current scope and the scope of
the SCOPED_ENUM. That's a new thing, the closest equivalent is a
typedef or alias declaration. But this means that
Sema::CheckUsingDeclQualifier needs adjustment. (a) one can't call it
until one knows the set of decls that are being referenced -- if
exactly one is an enumerator, we're in the new territory. Thus it
needs calling later in some cases. Also (b) there are two ways we hold
the set of such decls. During parsing (or instantiating a dependent
scope) we have a lookup result, and during instantiation we have a set
of shadow decls. Thus two optional arguments, at most one of which
should be non-null.

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

Added: 
    clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p7-cxx20.cpp

Modified: 
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Sema/Sema.h
    clang/lib/Sema/SemaDeclCXX.cpp
    clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
    clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3.cpp
    clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p7.cpp
    clang/test/SemaCXX/enum-scoped.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 82dae0fa01d22..21672ed095591 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -536,6 +536,10 @@ def err_using_dependent_value_is_type : Error<
   "dependent using declaration resolved to type without 'typename'">;
 def err_using_decl_nested_name_specifier_is_not_class : Error<
   "using declaration in class refers into '%0', which is not a class">;
+def warn_cxx17_compat_using_decl_non_member_enumerator : Warning<
+  "member using declaration naming non-class '%0' enumerator is "
+  "incompatible with C++ standards before C++20">, InGroup<CXXPre20Compat>,
+  DefaultIgnore;
 def err_using_decl_nested_name_specifier_is_current_class : Error<
   "using declaration refers to its own class">;
 def err_using_decl_nested_name_specifier_is_not_base_class : Error<
@@ -544,6 +548,16 @@ def err_using_decl_constructor_not_in_direct_base : Error<
   "%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 warn_cxx17_compat_using_decl_class_member_enumerator : Warning<
+  "member using declaration naming a non-member enumerator is incompatible "
+  "with C++ standards before C++20">, InGroup<CXXPre20Compat>, DefaultIgnore;
+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<
   "constructor of %0 inherited from multiple base class subobjects">;
 def note_ambiguous_inherited_constructor_using : Note<
@@ -553,8 +567,12 @@ def note_using_decl_class_member_workaround : Note<
   "a const variable|a constexpr variable}0 instead">;
 def err_using_decl_can_not_refer_to_namespace : Error<
   "using declaration cannot refer to a namespace">;
-def err_using_decl_can_not_refer_to_scoped_enum : Error<
-  "using declaration cannot refer to a scoped enumerator">;
+def warn_cxx17_compat_using_decl_scoped_enumerator: Warning<
+  "using declaration naming a scoped enumerator is incompatible with "
+  "C++ standards before C++20">, InGroup<CXXPre20Compat>, DefaultIgnore;
+def ext_using_decl_scoped_enumerator : ExtWarn<
+  "using declaration naming a scoped enumerator is a C++20 extension">,
+  InGroup<CXX20>;
 def err_using_decl_constructor : Error<
   "using declaration cannot refer to a constructor">;
 def warn_cxx98_compat_using_decl_constructor : Warning<

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 4e6d73cbc1aff..c122b991af9e9 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -5714,11 +5714,12 @@ class Sema final {
                                    const CXXScopeSpec &SS,
                                    SourceLocation NameLoc,
                                    const LookupResult &Previous);
-  bool CheckUsingDeclQualifier(SourceLocation UsingLoc,
-                               bool HasTypename,
+  bool CheckUsingDeclQualifier(SourceLocation UsingLoc, bool HasTypename,
                                const CXXScopeSpec &SS,
                                const DeclarationNameInfo &NameInfo,
-                               SourceLocation NameLoc);
+                               SourceLocation NameLoc,
+                               const LookupResult *R = nullptr,
+                               const UsingDecl *UD = nullptr);
 
   NamedDecl *BuildUsingDeclaration(Scope *S, AccessSpecifier AS,
                                    SourceLocation UsingLoc,

diff  --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index e25a5f03658c5..1a8745ea9a279 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -11659,7 +11659,7 @@ bool Sema::CheckUsingShadowDecl(BaseUsingDecl *BUD, NamedDecl *Orig,
   // This is invalid (during instantiation) in C++03 because B::foo
   // resolves to the using decl in B, which is not a base class of D<T>.
   // We can't diagnose it immediately because C<T> is an unknown
-  // specialization.  The UsingShadowDecl in D<T> then points directly
+  // specialization. The UsingShadowDecl in D<T> then points directly
   // to A::foo, which will look well-formed when we instantiate.
   // The right solution is to not collapse the shadow-decl chain.
   if (!getLangOpts().CPlusPlus11 && CurContext->isRecord())
@@ -12088,11 +12088,6 @@ NamedDecl *Sema::BuildUsingDeclaration(
                                   SS, IdentLoc, Previous))
     return nullptr;
 
-  // Check for bad qualifiers.
-  if (CheckUsingDeclQualifier(UsingLoc, HasTypenameKeyword, SS, NameInfo,
-                              IdentLoc))
-    return nullptr;
-
   // 'using_if_exists' doesn't make sense on an inherited constructor.
   if (IsUsingIfExists && UsingName.getName().getNameKind() ==
                              DeclarationName::CXXConstructorName) {
@@ -12104,6 +12099,11 @@ NamedDecl *Sema::BuildUsingDeclaration(
   NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context);
   if (!LookupContext || EllipsisLoc.isValid()) {
     NamedDecl *D;
+    // Dependent scope, or an unexpanded pack
+    if (!LookupContext && CheckUsingDeclQualifier(UsingLoc, HasTypenameKeyword,
+                                                  SS, NameInfo, IdentLoc))
+      return nullptr;
+
     if (HasTypenameKeyword) {
       // FIXME: not all declaration name kinds are legal here
       D = UnresolvedUsingTypenameDecl::Create(Context, CurContext,
@@ -12156,6 +12156,11 @@ NamedDecl *Sema::BuildUsingDeclaration(
 
   LookupQualifiedName(R, LookupContext);
 
+  // Validate the context, now we have a lookup
+  if (CheckUsingDeclQualifier(UsingLoc, HasTypenameKeyword, SS, NameInfo,
+                              IdentLoc, &R))
+    return nullptr;
+
   if (R.empty() && IsUsingIfExists)
     R.addDecl(UnresolvedUsingIfExistsDecl::Create(Context, CurContext, UsingLoc,
                                                   UsingName.getName()),
@@ -12261,16 +12266,6 @@ NamedDecl *Sema::BuildUsingDeclaration(
     return BuildInvalid();
   }
 
-  // C++14 [namespace.udecl]p7:
-  // A using-declaration shall not name a scoped enumerator.
-  if (auto *ED = R.getAsSingle<EnumConstantDecl>()) {
-    if (cast<EnumDecl>(ED->getDeclContext())->isScoped()) {
-      Diag(IdentLoc, diag::err_using_decl_can_not_refer_to_scoped_enum)
-        << SS.getRange();
-      return BuildInvalid();
-    }
-  }
-
   UsingDecl *UD = BuildValid();
 
   // Some additional rules apply to inheriting constructors.
@@ -12410,48 +12405,83 @@ bool Sema::CheckUsingDeclRedeclaration(SourceLocation UsingLoc,
   return false;
 }
 
-
 /// Checks that the given nested-name qualifier used in a using decl
 /// in the current context is appropriately related to the current
 /// scope.  If an error is found, diagnoses it and returns true.
-bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc,
-                                   bool HasTypename,
+/// R is nullptr, if the caller has not (yet) done a lookup, otherwise it's the
+/// result of that lookup. UD is likewise nullptr, except when we have an
+/// already-populated UsingDecl whose shadow decls contain the same information
+/// (i.e. we're instantiating a UsingDecl with non-dependent scope).
+bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc, bool HasTypename,
                                    const CXXScopeSpec &SS,
                                    const DeclarationNameInfo &NameInfo,
-                                   SourceLocation NameLoc) {
+                                   SourceLocation NameLoc,
+                                   const LookupResult *R, const UsingDecl *UD) {
   DeclContext *NamedContext = computeDeclContext(SS);
+  assert(bool(NamedContext) == (R || UD) && !(R && UD) &&
+         "resolvable context must have exactly one set of decls");
+
+  // C++ 20 permits using an enumerator that does not have a class-hierarchy
+  // relationship.
+  bool Cxx20Enumerator = false;
+  if (NamedContext) {
+    EnumConstantDecl *EC = nullptr;
+    if (R)
+      EC = R->getAsSingle<EnumConstantDecl>();
+    else if (UD && UD->shadow_size() == 1)
+      EC = dyn_cast<EnumConstantDecl>(UD->shadow_begin()->getTargetDecl());
+    if (EC)
+      Cxx20Enumerator = getLangOpts().CPlusPlus20;
+
+    if (auto *ED = dyn_cast<EnumDecl>(NamedContext)) {
+      // C++14 [namespace.udecl]p7:
+      // A using-declaration shall not name a scoped enumerator.
+      // C++20 p1099 permits enumerators.
+      if (EC && R && ED->isScoped())
+        Diag(SS.getBeginLoc(),
+             getLangOpts().CPlusPlus20
+                 ? diag::warn_cxx17_compat_using_decl_scoped_enumerator
+                 : diag::ext_using_decl_scoped_enumerator)
+            << SS.getRange();
+
+      // We want to consider the scope of the enumerator
+      NamedContext = ED->getDeclContext();
+    }
+  }
 
   if (!CurContext->isRecord()) {
     // C++03 [namespace.udecl]p3:
     // C++0x [namespace.udecl]p8:
     //   A using-declaration for a class member shall be a member-declaration.
+    // C++20 [namespace.udecl]p7
+    //   ... other than an enumerator ...
 
     // If we weren't able to compute a valid scope, it might validly be a
-    // dependent class scope or a dependent enumeration unscoped scope. If
-    // we have a 'typename' keyword, the scope must resolve to a class type.
-    if ((HasTypename && !NamedContext) ||
-        (NamedContext && NamedContext->getRedeclContext()->isRecord())) {
-      auto *RD = NamedContext
-                     ? cast<CXXRecordDecl>(NamedContext->getRedeclContext())
-                     : nullptr;
-      if (RD && RequireCompleteDeclContext(const_cast<CXXScopeSpec&>(SS), RD))
-        RD = nullptr;
-
-      Diag(NameLoc, diag::err_using_decl_can_not_refer_to_class_member)
+    // dependent class or enumeration scope. If we have a 'typename' keyword,
+    // the scope must resolve to a class type.
+    if (NamedContext ? !NamedContext->getRedeclContext()->isRecord()
+                     : !HasTypename)
+      return false; // OK
+
+    Diag(NameLoc,
+         Cxx20Enumerator
+             ? diag::warn_cxx17_compat_using_decl_class_member_enumerator
+             : diag::err_using_decl_can_not_refer_to_class_member)
         << SS.getRange();
 
-      // If we have a complete, non-dependent source type, try to suggest a
-      // way to get the same effect.
-      if (!RD)
-        return true;
+    if (Cxx20Enumerator)
+      return false; // OK
 
-      // Find what this using-declaration was referring to.
-      LookupResult R(*this, NameInfo, LookupOrdinaryName);
-      R.setHideTags(false);
-      R.suppressDiagnostics();
-      LookupQualifiedName(R, RD);
+    auto *RD = NamedContext
+                   ? cast<CXXRecordDecl>(NamedContext->getRedeclContext())
+                   : nullptr;
+    if (RD && !RequireCompleteDeclContext(const_cast<CXXScopeSpec &>(SS), RD)) {
+      // See if there's a helpful fixit
 
-      if (R.getAsSingle<TypeDecl>()) {
+      if (!R) {
+        // We will have already diagnosed the problem on the template
+        // definition,  Maybe we should do so again?
+      } else if (R->getAsSingle<TypeDecl>()) {
         if (getLangOpts().CPlusPlus11) {
           // Convert 'using X::Y;' to 'using Y = X::Y;'.
           Diag(SS.getBeginLoc(), diag::note_using_decl_class_member_workaround)
@@ -12468,7 +12498,7 @@ bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc,
             << FixItHint::CreateInsertion(
                    InsertLoc, " " + NameInfo.getName().getAsString());
         }
-      } else if (R.getAsSingle<VarDecl>()) {
+      } else if (R->getAsSingle<VarDecl>()) {
         // Don't provide a fixit outside C++11 mode; we don't want to suggest
         // repeating the type of the static data member here.
         FixItHint FixIt;
@@ -12481,7 +12511,7 @@ bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc,
         Diag(UsingLoc, diag::note_using_decl_class_member_workaround)
           << 2 // reference declaration
           << FixIt;
-      } else if (R.getAsSingle<EnumConstantDecl>()) {
+      } else if (R->getAsSingle<EnumConstantDecl>()) {
         // Don't provide a fixit outside C++11 mode; we don't want to suggest
         // repeating the type of the enumeration here, and we can't do so if
         // the type is anonymous.
@@ -12497,15 +12527,11 @@ bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc,
           << (getLangOpts().CPlusPlus11 ? 4 : 3) // const[expr] variable
           << FixIt;
       }
-      return true;
     }
 
-    // Otherwise, this might be valid.
-    return false;
+    return true; // Fail
   }
 
-  // The current scope is a record.
-
   // If the named context is dependent, we can't decide much.
   if (!NamedContext) {
     // FIXME: in C++0x, we can diagnose if we can prove that the
@@ -12517,12 +12543,19 @@ bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc,
     return false;
   }
 
+  // The current scope is a record.
   if (!NamedContext->isRecord()) {
     // Ideally this would point at the last name in the specifier,
     // but we don't have that level of source info.
-    Diag(SS.getRange().getBegin(),
-         diag::err_using_decl_nested_name_specifier_is_not_class)
-      << SS.getScopeRep() << SS.getRange();
+    Diag(SS.getBeginLoc(),
+         Cxx20Enumerator
+             ? diag::warn_cxx17_compat_using_decl_non_member_enumerator
+             : diag::err_using_decl_nested_name_specifier_is_not_class)
+        << SS.getScopeRep() << SS.getRange();
+
+    if (Cxx20Enumerator)
+      return false; // OK
+
     return true;
   }
 
@@ -12538,19 +12571,25 @@ bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc,
 
     if (cast<CXXRecordDecl>(CurContext)->isProvablyNotDerivedFrom(
                                  cast<CXXRecordDecl>(NamedContext))) {
+
+      if (Cxx20Enumerator) {
+        Diag(NameLoc, diag::warn_cxx17_compat_using_decl_non_member_enumerator)
+            << SS.getRange();
+        return false;
+      }
+
       if (CurContext == NamedContext) {
-        Diag(NameLoc,
+        Diag(SS.getBeginLoc(),
              diag::err_using_decl_nested_name_specifier_is_current_class)
-          << SS.getRange();
-        return true;
+            << SS.getRange();
+        return !getLangOpts().CPlusPlus20;
       }
 
       if (!cast<CXXRecordDecl>(NamedContext)->isInvalidDecl()) {
-        Diag(SS.getRange().getBegin(),
+        Diag(SS.getBeginLoc(),
              diag::err_using_decl_nested_name_specifier_is_not_base_class)
-          << SS.getScopeRep()
-          << cast<CXXRecordDecl>(CurContext)
-          << SS.getRange();
+            << SS.getScopeRep() << cast<CXXRecordDecl>(CurContext)
+            << SS.getRange();
       }
       return true;
     }

diff  --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 92a2cc4210bf5..f1b34693f45d3 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -3067,10 +3067,9 @@ Decl *TemplateDeclInstantiator::VisitUsingDecl(UsingDecl *D) {
   }
 
   if (!NewUD->isInvalidDecl() &&
-      SemaRef.CheckUsingDeclQualifier(D->getUsingLoc(), D->hasTypename(),
-                                      SS, NameInfo, D->getLocation()))
+      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);
@@ -3079,6 +3078,8 @@ Decl *TemplateDeclInstantiator::VisitUsingDecl(UsingDecl *D) {
   if (NewUD->isInvalidDecl())
     return NewUD;
 
+  // If the using scope was dependent, or we had dependent bases, we need to
+  // recheck the inheritance
   if (NameInfo.getName().getNameKind() == DeclarationName::CXXConstructorName)
     SemaRef.CheckInheritingConstructorUsingDecl(NewUD);
 

diff  --git a/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3.cpp b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3.cpp
index 6c505a55c2afc..3351666525374 100644
--- a/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3.cpp
+++ b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3.cpp
@@ -17,6 +17,7 @@ struct B {
 };
 
 class C {
+public:
   int g();
 };
 
@@ -42,7 +43,7 @@ class D2 : public B {
 #endif
 
   using B::EC;
-  using B::EC::ec; // expected-error {{not a class}} expected-warning 0-1 {{C++11}}
+  using B::EC::ec; // expected-warning {{a C++20 extension}} expected-warning 0-1 {{C++11}}
 };
 
 namespace test1 {

diff  --git a/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p7-cxx20.cpp b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p7-cxx20.cpp
new file mode 100644
index 0000000000000..f70bb887fba0c
--- /dev/null
+++ b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p7-cxx20.cpp
@@ -0,0 +1,271 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++17 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
+
+// p1099 'using SCOPEDENUM::MEMBER;'
+
+namespace Zero {
+namespace Bob {
+enum class Kevin {
+  Stuart,
+  AlsoStuart
+#if __cplusplus >= 202002L
+// expected-note at -3{{target of using declaration}}
+// expected-note at -3{{target of using declaration}}
+#endif
+};
+} // namespace Bob
+
+using Bob::Kevin::Stuart;
+#if __cplusplus < 202002L
+// expected-warning at -2{{using declaration naming a scoped enumerator is a C++20 extension}}
+#else
+using Bob::Kevin::Stuart;
+
+auto b = Stuart;
+
+namespace Foo {
+int Stuart;               // expected-note{{conflicting declaration}}
+using Bob::Kevin::Stuart; // expected-error{{target of using declaration conflicts}}
+
+using Bob::Kevin::AlsoStuart; // expected-note{{using declaration}}
+int AlsoStuart;               // expected-error{{declaration conflicts with target}}
+} // namespace Foo
+#endif
+
+} // namespace Zero
+
+namespace One {
+
+// derived from [namespace.udecl]/3
+enum class button { up,
+                    down };
+struct S {
+  using button::up;
+#if __cplusplus < 202002L
+  // expected-warning at -2{{a C++20 extension}}
+  // expected-error at -3{{using declaration in class}}
+#else
+  button b = up;
+#endif
+};
+
+#if __cplusplus >= 202002L
+// some more
+struct T : S {
+  button c = up;
+};
+#endif
+enum E2 { e2 };
+} // namespace One
+
+namespace Two {
+enum class E1 { e1 };
+
+struct S {
+  using One::e2;
+#if __cplusplus < 202002L
+  // expected-error at -2{{using declaration in class}}
+#else
+  One::E2 c = e2;
+#endif
+};
+
+} // namespace Two
+
+namespace Three {
+
+enum E3 { e3 };
+struct e3;
+
+struct S {
+  using Three::e3; // expected-error{{using declaration in class}}
+
+  enum class E4 { e4 };
+  enum E5 { e5 };
+};
+
+using S::e5;
+using S::E4::e4;
+#if __cplusplus < 202002L
+// expected-error at -3{{using declaration cannot refer to class member}}
+// expected-note at -4{{use a constexpr variable instead}}
+// expected-warning at -4{{a C++20 extension}}
+// expected-error at -5{{using declaration cannot refer to class member}}
+// expected-note at -6{{use a constexpr variable instead}}
+#else
+auto a = e4;
+auto b = e5;
+#endif
+} // namespace Three
+
+namespace Four {
+
+template <typename T>
+struct TPL {
+  enum class E1 { e1 };
+  struct IN {
+    enum class E2 { e2 };
+  };
+
+protected:
+  enum class E3 { e3 }; // expected-note{{declared protected here}}
+};
+
+using TPL<int>::E1::e1;
+#if __cplusplus < 202002L
+// expected-warning at -2{{a C++20 extension}}
+// expected-error at -3{{using declaration cannot refer to class member}}
+// expected-note at -4{{use a constexpr variable instead}}
+#else
+using TPL<float>::IN::E2::e2;
+
+auto a = e1;
+auto b = e2;
+#endif
+
+enum class E4 { e4 };
+template <typename T>
+struct DER : TPL<int> {
+  using TPL<T>::E1::e1;
+#if __cplusplus < 202002L
+  // expected-warning at -2{{a C++20 extension}}
+  // expected-warning at -3{{using declaration naming a scoped}}
+  // expected-error at -4{{which is not a base}}
+#endif
+  using TPL<T>::E3::e3; // expected-error{{is a protected member}}
+#if __cplusplus < 202002L
+  // expected-warning at -2 2{{using declaration naming a scoped}}
+  // expected-error at -3{{which is not a base}}
+#endif
+
+  using E4::e4;
+#if __cplusplus < 202002L
+  // expected-warning at -2{{a C++20 extension}}
+  // expected-error at -3{{which is not a class}}
+#else
+  auto Foo() { return e1; }
+  auto Bar() { return e2; }
+#endif
+};
+
+DER<float> x; // expected-note{{requested here}}
+DER<int> y;
+#if __cplusplus < 202002L
+// expected-note at -2{{requested here}}
+#else
+auto y1 = y.Foo();
+auto y2 = y.Bar();
+#endif
+} // namespace Four
+
+namespace Five {
+template <unsigned I, unsigned K>
+struct Quux {
+  enum class Q : unsigned; // expected-note{{member is declared here}}
+  enum class R : unsigned { i = I,
+                            k = K };
+};
+
+using Quux<1, 2>::Q::nothing; // expected-error{{implicit instantiation of undefined}}
+using Quux<1, 2>::R::i;
+#if __cplusplus < 202002L
+// expected-warning at -2{{a C++20 extension}}
+// expected-error at -3{{using declaration cannot refer to class member}}
+// expected-note at -4{{use a constexpr variable instead}}
+#endif
+
+} // namespace Five
+
+namespace Six {
+template <unsigned I, unsigned K>
+struct Quux {
+  enum class Q : unsigned; // expected-note{{member is declared here}}
+  enum class R : unsigned { i = I,
+                            k = K };
+};
+
+template <unsigned I> struct Fido {
+  using Quux<I, I>::Q::nothing; // expected-error{{implicit instantiation of undefined}}
+};
+
+Fido<2> a; // expected-note{{in instantiation}}
+
+} // namespace Six
+
+namespace Seven {
+template <unsigned I, unsigned K>
+struct Quux {
+  enum class R : unsigned { i = I,
+                            k = K };
+};
+
+template <unsigned I> struct Toto {
+  using Quux<I, I>::R::i;
+#if __cplusplus < 202002L
+  // expected-warning at -2{{a C++20 extension}}
+// expected-error at -3{{refers into}}
+#else
+  static_assert(unsigned(i) == I);
+#endif
+};
+
+Toto<2> b;
+#if __cplusplus < 202002L
+// expected-note at -2{{in instantiation}}
+#endif
+
+} // namespace Seven
+
+namespace Eight {
+struct Kevin {
+  enum class B { a };
+  enum a {};
+};
+
+using Kevin::B::a;
+#if __cplusplus < 202002L
+// expected-warning at -2{{a C++20 extension}}
+// expected-error at -3{{using declaration cannot refer to class member}}
+// expected-note at -4{{use a constexpr variable instead}}
+#endif
+using Kevin::B::a;
+#if __cplusplus < 202002L
+// expected-warning at -2{{a C++20 extension}}
+// expected-error at -3{{using declaration cannot refer to class member}}
+// expected-note at -4{{use a constexpr variable instead}}
+#endif
+
+class X : Kevin {
+  using Kevin::B::a; // expected-note{{previous using declaration}}
+#if __cplusplus < 202002L
+// expected-warning at -2{{a C++20 extension}}
+#endif
+  using Kevin::a;
+  using Kevin::B::a; // expected-error{{redeclaration of using declaration}}
+};
+
+} // namespace Eight
+
+namespace Nine {
+namespace Q {
+enum class Bob { a };
+using Bob::a;
+#if __cplusplus < 202002L
+// expected-warning at -2{{a C++20 extension}}
+#endif
+} // namespace Q
+
+using Q::a;
+using Q::Bob::a;
+#if __cplusplus < 202002L
+// expected-warning at -2{{a C++20 extension}}
+#endif
+
+#if __cplusplus >= 202002L
+struct Foo {
+  using Q::a; // expected-note{{previous using declaration}}
+  using Q::Bob::a;
+  using Q::a; // expected-error{{redeclaration of using declaration}}
+};
+#endif
+} // namespace Nine

diff  --git a/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p7.cpp b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p7.cpp
index 6c9379fac27fd..5a7bb3268b651 100644
--- a/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p7.cpp
+++ b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p7.cpp
@@ -1,4 +1,11 @@
 // RUN: %clang_cc1 -std=c++11 -verify %s
+// RUN: %clang_cc1 -std=c++17 -verify %s
+// RUN: %clang_cc1 -std=c++20 -verify %s
 
 enum class EC { ec };
-using EC::ec; // expected-error {{using declaration cannot refer to a scoped enumerator}}
+using EC::ec;
+#if __cplusplus < 202002
+// expected-warning at -2 {{using declaration naming a scoped enumerator is a C++20 extension}}
+#else
+// expected-no-diagnostics
+#endif

diff  --git a/clang/test/SemaCXX/enum-scoped.cpp b/clang/test/SemaCXX/enum-scoped.cpp
index 10403bfa51253..ae2fe37153e86 100644
--- a/clang/test/SemaCXX/enum-scoped.cpp
+++ b/clang/test/SemaCXX/enum-scoped.cpp
@@ -301,8 +301,8 @@ namespace PR18044 {
   int E::*p; // expected-error {{does not point into a class}}
   using E::f; // expected-error {{no member named 'f'}}
 
-  using E::a; // expected-error {{using declaration cannot refer to a scoped enumerator}}
-  E b = a; // expected-error {{undeclared}}
+  using E::a; // expected-warning {{using declaration naming a scoped enumerator is a C++20 extension}}
+  E b = a;
 }
 
 namespace test11 {


        


More information about the cfe-commits mailing list