This feature allows the programmer to import enumerator names into the
current scope so later mentions don't need to use the fully-qualified name.
These usings are not subject to the usual restrictions on using-declarations:
in particular, they can move between class and non-class scopes, and between
classes that are not related by inheritance.  This last caused difficulty
for our normal approach to using-decls within a class hierarchy, as we
assume that the class where we looked up a used declaration is derived from
the class where it was first declared.  So to simplify things, in that case
we make a clone of the CONST_DECL in the using class.

Thanks to Nathan for the start of this work: in particular, the
lookup_using_decl rewrite.

The changes to dwarf2out revealed an existing issue with the D front-end: we
were doing the wrong thing for importing a D CONST_DECL, because
dwarf2out_imported_module_or_decl_1 was looking through it to its type,
expecting it to be an enumerator, but in one case in thread.d, the constant
had type int.  Adding the ability to import a C++ enumerator also fixed
that, but that led to a crash in force_decl_die, which didn't know what to
do with a CONST_DECL.  So now it does.

Tested x86_64-pc-linux-gnu, applying to trunk.

Co-authored-by: Nathan Sidwell <nat...@acm.org>

gcc/cp/ChangeLog:

        * cp-tree.h (USING_DECL_UNRELATED_P): New.
        (CONST_DECL_USING_P): New.
        * class.c (handle_using_decl): If USING_DECL_UNRELATED_P,
        clone the CONST_DECL.
        * name-lookup.c (supplement_binding_1): A clone hides its
        using-declaration.
        (lookup_using_decl): Rewrite to separate lookup and validation.
        (do_class_using_decl): Adjust.
        (finish_nonmember_using_decl): Adjust.
        * parser.c (make_location): Add cp_token overload.
        (finish_using_decl): Split out from...
        (cp_parser_using_declaration): ...here.  Don't look through enums.
        (cp_parser_using_enum): New.
        (cp_parser_block_declaration): Call it.
        (cp_parser_member_declaration): Call it.
        * semantics.c (finish_id_expression_1): Handle enumerator
        used from class scope.

gcc/ChangeLog:

        * dwarf2out.c (gen_enumeration_type_die): Call
        equate_decl_number_to_die for enumerators.
        (gen_member_die): Don't move enumerators to their
        enclosing class.
        (dwarf2out_imported_module_or_decl_1): Allow importing
        individual enumerators.
        (force_decl_die): Handle CONST_DECL.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp0x/inh-ctor28.C: Adjust expected diagnostic.
        * g++.dg/cpp0x/inh-ctor33.C: Likewise.
        * g++.dg/cpp0x/using-enum-1.C: Add comment.
        * g++.dg/cpp0x/using-enum-2.C: Allowed in C++20.
        * g++.dg/cpp0x/using-enum-3.C: Likewise.
        * g++.dg/cpp1z/class-deduction69.C: Adjust diagnostic.
        * g++.dg/inherit/using5.C: Likewise.
        * g++.dg/cpp2a/using-enum-1.C: New test.
        * g++.dg/cpp2a/using-enum-2.C: New test.
        * g++.dg/cpp2a/using-enum-3.C: New test.
        * g++.dg/cpp2a/using-enum-4.C: New test.
        * g++.dg/cpp2a/using-enum-5.C: New test.
        * g++.dg/cpp2a/using-enum-6.C: New test.
        * g++.dg/debug/dwarf2/using-enum.C: New test.
---
 gcc/cp/cp-tree.h                              |  11 +
 gcc/cp/class.c                                |  17 ++
 gcc/cp/name-lookup.c                          | 278 +++++++++++-------
 gcc/cp/parser.c                               | 145 +++++++--
 gcc/cp/semantics.c                            |  14 +-
 gcc/dwarf2out.c                               |  16 +-
 gcc/testsuite/g++.dg/cpp0x/inh-ctor28.C       |   2 +-
 gcc/testsuite/g++.dg/cpp0x/inh-ctor33.C       |   2 +-
 gcc/testsuite/g++.dg/cpp0x/using-enum-1.C     |   3 +
 gcc/testsuite/g++.dg/cpp0x/using-enum-2.C     |  11 +-
 gcc/testsuite/g++.dg/cpp0x/using-enum-3.C     |  15 +-
 .../g++.dg/cpp1z/class-deduction69.C          |   2 +-
 gcc/testsuite/g++.dg/cpp2a/using-enum-1.C     |  62 ++++
 gcc/testsuite/g++.dg/cpp2a/using-enum-2.C     |  48 +++
 gcc/testsuite/g++.dg/cpp2a/using-enum-3.C     |   6 +
 gcc/testsuite/g++.dg/cpp2a/using-enum-4.C     |  13 +
 gcc/testsuite/g++.dg/cpp2a/using-enum-5.C     | 132 +++++++++
 gcc/testsuite/g++.dg/cpp2a/using-enum-6.C     |   5 +
 .../g++.dg/debug/dwarf2/using-enum.C          |  21 ++
 gcc/testsuite/g++.dg/inherit/using5.C         |   2 +-
 20 files changed, 659 insertions(+), 146 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/using-enum-1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/using-enum-2.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/using-enum-3.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/using-enum-4.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/using-enum-5.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/using-enum-6.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/using-enum.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 63724c0e84f..9ae6ff5f7a2 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -529,6 +529,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
       TEMPLATE_DECL_COMPLEX_ALIAS_P (in TEMPLATE_DECL)
       DECL_INSTANTIATING_NSDMI_P (in a FIELD_DECL)
       LABEL_DECL_CDTOR (in LABEL_DECL)
+      USING_DECL_UNRELATED_P (in USING_DECL)
    3: DECL_IN_AGGR_P.
    4: DECL_C_BIT_FIELD (in a FIELD_DECL)
       DECL_ANON_UNION_VAR_P (in a VAR_DECL)
@@ -3409,6 +3410,16 @@ struct GTY(()) lang_decl {
 /* Non zero if the using decl refers to a dependent type.  */
 #define USING_DECL_TYPENAME_P(NODE) DECL_LANG_FLAG_1 (USING_DECL_CHECK (NODE))
 
+/* True if member using decl NODE refers to a non-inherited NODE.  */
+#define USING_DECL_UNRELATED_P(NODE) DECL_LANG_FLAG_2 (USING_DECL_CHECK (NODE))
+
+/* True iff the CONST_DECL is a class-scope clone from C++20 using enum,
+   created by handle_using_decl.  */
+#define CONST_DECL_USING_P(NODE)                       \
+  (TREE_CODE (NODE) == CONST_DECL                      \
+   && TREE_CODE (TREE_TYPE (NODE)) == ENUMERAL_TYPE    \
+   && DECL_CONTEXT (NODE) != TREE_TYPE (NODE))
+
 /* In a FUNCTION_DECL, this is nonzero if this function was defined in
    the class definition.  We have saved away the text of the function,
    but have not yet processed it.  */
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 7c34d9466fc..ec47b0698ab 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -1331,6 +1331,23 @@ handle_using_decl (tree using_decl, tree t)
        add_method (t, *iter, true);
        alter_access (t, *iter, access);
       }
+  else if (USING_DECL_UNRELATED_P (using_decl))
+    {
+      /* C++20 using enum can import non-inherited enumerators into class
+        scope.  We implement that by making a copy of the CONST_DECL for which
+        CONST_DECL_USING_P is true.  */
+      gcc_assert (TREE_CODE (decl) == CONST_DECL);
+
+      tree copy = copy_decl (decl);
+      DECL_CONTEXT (copy) = t;
+      DECL_ARTIFICIAL (copy) = true;
+      /* We emitted debug info for the USING_DECL above; make sure we don't
+        also emit anything for this clone.  */
+      DECL_IGNORED_P (copy) = true;
+      DECL_SOURCE_LOCATION (copy) = DECL_SOURCE_LOCATION (using_decl);
+      finish_member_declaration (copy);
+      DECL_ABSTRACT_ORIGIN (copy) = decl;
+    }
   else
     alter_access (t, decl, access);
 }
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index 410ec595c82..bf05e7bbcd1 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -2125,6 +2125,10 @@ supplement_binding_1 (cxx_binding *binding, tree decl)
       region to refer only to the namespace to which it already
       refers.  */
     ok = false;
+  else if (TREE_CODE (bval) == USING_DECL
+          && CONST_DECL_USING_P (decl))
+    /* Let the clone hide the using-decl that introduced it.  */
+    binding->value = decl;
   else
     {
       if (!error_operand_p (bval))
@@ -4540,43 +4544,66 @@ push_class_level_binding (tree name, tree x)
 /* Process and lookup a using decl SCOPE::lookup.name, filling in
    lookup.values & lookup.type.  Return true if ok.  */
 
-static bool
+static tree
 lookup_using_decl (tree scope, name_lookup &lookup)
 {
   tree current = current_scope ();
   bool dependent_p = false;
+  tree binfo = NULL_TREE;
+  base_kind b_kind = bk_not_base;
+
+  /* Because C++20 breaks the invariant that only member using-decls
+     refer to members and only non-member using-decls refer to
+     non-members, we first do the lookups, and then do validation that
+     what we found is ok.  */
+
+  if (TREE_CODE (scope) == ENUMERAL_TYPE
+      && cxx_dialect < cxx20
+      && UNSCOPED_ENUM_P (scope)
+      && !TYPE_FUNCTION_SCOPE_P (scope))
+    {
+      /* PR c++/60265 argued that since C++11 added explicit enum scope, we
+        should allow it as meaning the enclosing scope.  I don't see any
+        justification for this in C++11, but let's keep allowing it.  */
+      tree ctx = CP_TYPE_CONTEXT (scope);
+      if (CLASS_TYPE_P (ctx) == CLASS_TYPE_P (current))
+       scope = ctx;
+    }
 
   if (TREE_CODE (scope) == NAMESPACE_DECL)
     {
       /* Naming a namespace member.  */
-      if (TYPE_P (current))
+      qualified_namespace_lookup (scope, &lookup);
+
+      if (TYPE_P (current)
+         && (!lookup.value
+             || lookup.type
+             || cxx_dialect < cxx20
+             || TREE_CODE (lookup.value) != CONST_DECL))
        {
          error ("using-declaration for non-member at class scope");
-         return false;
+         return NULL_TREE;
        }
-
-      qualified_namespace_lookup (scope, &lookup);
     }
   else if (TREE_CODE (scope) == ENUMERAL_TYPE)
     {
-      error ("using-declaration may not name enumerator %<%E::%D%>",
-            scope, lookup.name);
-      return false;
+      /* Naming an enumeration member.  */
+      if (cxx_dialect < cxx20)
+       error ("%<using%> with enumeration scope %q#T "
+              "only available with %<-std=c++20%> or %<-std=gnu++20%>",
+              scope);
+      lookup.value = lookup_enumerator (scope, lookup.name);
     }
   else
     {
-      /* Naming a class member.  */
-      if (!TYPE_P (current))
-       {
-         error ("using-declaration for member at non-class scope");
-         return false;
-       }
+      /* Naming a class member.  This is awkward in C++20, because we
+        might be naming an enumerator of an unrelated class.  */
 
-      /* Make sure the name is not invalid */
+      /* You cannot using-decl a destructor.  */
       if (TREE_CODE (lookup.name) == BIT_NOT_EXPR)
        {
          error ("%<%T::%D%> names destructor", scope, lookup.name);
-         return false;
+         return NULL_TREE;
        }
 
       /* Using T::T declares inheriting ctors, even if T is a typedef.  */
@@ -4584,91 +4611,150 @@ lookup_using_decl (tree scope, name_lookup &lookup)
          && (lookup.name == TYPE_IDENTIFIER (scope)
              || constructor_name_p (lookup.name, scope)))
        {
+         if (!TYPE_P (current))
+           {
+             error ("non-member using-decl names constructor of %qT", scope);
+             return NULL_TREE;
+           }
          maybe_warn_cpp0x (CPP0X_INHERITING_CTORS);
          lookup.name = ctor_identifier;
          CLASSTYPE_NON_AGGREGATE (current) = true;
        }
 
+      if (!MAYBE_CLASS_TYPE_P (scope))
+       ;
+      else if (TYPE_P (current))
+       {
+         dependent_p = dependent_scope_p (scope);
+         if (!dependent_p)
+           {
+             binfo = lookup_base (current, scope, ba_any, &b_kind, tf_none);
+             gcc_checking_assert (b_kind >= bk_not_base);
+
+             if (lookup.name == ctor_identifier)
+               {
+                 /* Even if there are dependent bases, SCOPE will not
+                    be direct base, no matter.  */
+                 if (b_kind < bk_proper_base || !binfo_direct_p (binfo))
+                   {
+                     error ("%qT is not a direct base of %qT", scope, current);
+                     return NULL_TREE;
+                   }
+               }
+             else if (b_kind < bk_proper_base)
+               binfo = TYPE_BINFO (scope);
+             else if (IDENTIFIER_CONV_OP_P (lookup.name)
+                      && dependent_type_p (TREE_TYPE (lookup.name)))
+               dependent_p = true;
+           }
+       }
+      else
+       binfo = TYPE_BINFO (scope);
+
+      if (!dependent_p)
+       {
+         if (binfo)
+           lookup.value = lookup_member (binfo, lookup.name, /*protect=*/2,
+                                         /*want_type=*/false, tf_none);
+
+         tree saved_value = lookup.value;
+         if (lookup.value
+             && b_kind < bk_proper_base)
+           {
+             if (cxx_dialect >= cxx20
+                 && TREE_CODE (lookup.value) == CONST_DECL)
+               {
+                 /* Using an unrelated enum; check access here rather
+                    than separately for class and non-class using.  */
+                 perform_or_defer_access_check
+                   (binfo, lookup.value, lookup.value, tf_warning_or_error);
+                 /* And then if this is a copy from handle_using_decl, look
+                    through to the original enumerator.  */
+                 if (CONST_DECL_USING_P (lookup.value))
+                   lookup.value = DECL_ABSTRACT_ORIGIN (lookup.value);
+               }
+             else
+               lookup.value = NULL_TREE;
+           }
+
+         if (!lookup.value)
+           {
+             if (!TYPE_P (current))
+               {
+                 error ("using-declaration for member at non-class scope");
+                 return NULL_TREE;
+               }
+
+             if (b_kind < bk_proper_base)
+               {
+                 if (b_kind == bk_not_base && any_dependent_bases_p ())
+                   /* Treat as-if dependent.  */
+                   dependent_p = true;
+                 else
+                   {
+                     auto_diagnostic_group g;
+                     error_not_base_type (scope, current);
+                     if (saved_value && DECL_IMPLICIT_TYPEDEF_P (saved_value)
+                         && (TREE_CODE (TREE_TYPE (saved_value))
+                             == ENUMERAL_TYPE))
+                       inform (input_location,
+                               "did you mean %<using enum %T::%D%>?",
+                               scope, lookup.name);
+                     return NULL_TREE;
+                   }
+               }
+           }
+       }
+    }
+
+  /* Did we find anything sane?  */
+  if (dependent_p)
+    ;
+  else if (!lookup.value)
+    {
+      error ("%qD has not been declared in %qD", lookup.name, scope);
+      return NULL_TREE;
+    }
+  else if (TREE_CODE (lookup.value) == TREE_LIST
+          /* We can (independently) have ambiguous implicit typedefs.  */
+          || (lookup.type && TREE_CODE (lookup.type) == TREE_LIST))
+    {
+      error ("reference to %qD is ambiguous", lookup.name);
+      print_candidates (TREE_CODE (lookup.value) == TREE_LIST
+                       ? lookup.value : lookup.type);
+      return NULL_TREE;
+    }
+  else if (TREE_CODE (lookup.value) == NAMESPACE_DECL)
+    {
+      error ("using-declaration may not name namespace %qD", lookup.value);
+      return NULL_TREE;
+    }
+
+  if (TYPE_P (current))
+    {
+      /* In class scope.  */
+
       /* Cannot introduce a constructor name.  */
       if (constructor_name_p (lookup.name, current))
        {
          error ("%<%T::%D%> names constructor in %qT",
                 scope, lookup.name, current);
-         return false;
+         return NULL_TREE;
        }
 
-      /* Member using decls finish processing when completing the
-        class.  */
-      /* From [namespace.udecl]:
-
-         A using-declaration used as a member-declaration shall refer
-         to a member of a base class of the class being defined.
-
-         In general, we cannot check this constraint in a template
-         because we do not know the entire set of base classes of the
-         current class type. Morover, if SCOPE is dependent, it might
-         match a non-dependent base.  */
-
-      dependent_p = dependent_scope_p (scope);
-      if (!dependent_p)
-       {
-         base_kind b_kind;
-         tree binfo = lookup_base (current, scope, ba_any, &b_kind,
-                                   tf_warning_or_error);
-         if (b_kind < bk_proper_base)
-           {
-             /* If there are dependent bases, scope might resolve at
-                instantiation time, even if it isn't exactly one of
-                the dependent bases.  */
-             if (b_kind == bk_same_type || !any_dependent_bases_p ())
-               {
-                 error_not_base_type (scope, current);
-                 return false;
-               }
-             /* Treat as-if dependent.  */
-             dependent_p = true;
-           }
-         else if (lookup.name == ctor_identifier && !binfo_direct_p (binfo))
-           {
-             error ("cannot inherit constructors from indirect base %qT",
-                    scope);
-             return false;
-           }
-         else if (IDENTIFIER_CONV_OP_P (lookup.name)
-                  && dependent_type_p (TREE_TYPE (lookup.name)))
-           dependent_p = true;
-         else
-           lookup.value = lookup_member (binfo, lookup.name, 0,
-                                         false, tf_warning_or_error);
-       }
+      if (lookup.value && BASELINK_P (lookup.value))
+       /* The binfo from which the functions came does not matter.  */
+       lookup.value = BASELINK_FUNCTIONS (lookup.value);
     }
 
-  if (!dependent_p)
-    {
-      if (!lookup.value)
-       {
-         error ("%qD has not been declared in %qE", lookup.name, scope);
-         return false;
-       }
+  tree using_decl = build_lang_decl (USING_DECL, lookup.name, NULL_TREE);
+  USING_DECL_SCOPE (using_decl) = scope;
+  USING_DECL_DECLS (using_decl) = lookup.value;
+  DECL_DEPENDENT_P (using_decl) = dependent_p;
+  if (TYPE_P (current) && b_kind == bk_not_base)
+    USING_DECL_UNRELATED_P (using_decl) = true;
 
-      if (TREE_CODE (lookup.value) == TREE_LIST
-         /* We can (independently) have ambiguous implicit typedefs.  */
-         || (lookup.type && TREE_CODE (lookup.type) == TREE_LIST))
-       {
-         error ("reference to %qD is ambiguous", lookup.name);
-         print_candidates (TREE_CODE (lookup.value) == TREE_LIST
-                           ? lookup.value : lookup.type);
-         return false;
-       }
-
-      if (TREE_CODE (lookup.value) == NAMESPACE_DECL)
-       {
-         error ("using-declaration may not name namespace %qD", lookup.value);
-         return false;
-       }
-    }
-
-  return true;
+  return using_decl;
 }
 
 /* Process "using SCOPE::NAME" in a class scope.  Return the
@@ -4682,20 +4768,7 @@ do_class_using_decl (tree scope, tree name)
     return NULL_TREE;
 
   name_lookup lookup (name);
-  if (!lookup_using_decl (scope, lookup))
-    return NULL_TREE;
-
-  tree found = lookup.value;
-  if (found && BASELINK_P (found))
-    /* The binfo from which the functions came does not matter.  */
-    found = BASELINK_FUNCTIONS (found);
-
-  tree using_decl = build_lang_decl (USING_DECL, lookup.name, NULL_TREE);
-  USING_DECL_SCOPE (using_decl) = scope;
-  USING_DECL_DECLS (using_decl) = found;
-  DECL_DEPENDENT_P (using_decl) = !found;
-
-  return using_decl;
+  return lookup_using_decl (scope, lookup);
 }
 
 
@@ -5076,7 +5149,8 @@ finish_nonmember_using_decl (tree scope, tree name)
 
   name_lookup lookup (name);
 
-  if (!lookup_using_decl (scope, lookup))
+  tree using_decl = lookup_using_decl (scope, lookup);
+  if (!using_decl)
     return;
 
   /* Emit debug info.  */
@@ -5105,8 +5179,6 @@ finish_nonmember_using_decl (tree scope, tree name)
     }
   else
     {
-      tree using_decl = build_lang_decl (USING_DECL, lookup.name, NULL_TREE);
-      USING_DECL_SCOPE (using_decl) = scope;
       add_decl_expr (using_decl);
 
       cxx_binding *binding = find_local_binding (current_binding_level, name);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 68f1cfa23f2..42f705266bb 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -808,6 +808,14 @@ make_location (location_t caret, location_t start, 
cp_lexer *lexer)
   return make_location (caret, start, t->location);
 }
 
+/* Overload for make_location taking tokens instead of locations.  */
+
+static inline location_t
+make_location (cp_token *caret, cp_token *start, cp_token *end)
+{
+  return make_location (caret->location, start->location, end->location);
+}
+
 /* nonzero if we are presently saving tokens.  */
 
 static inline int
@@ -2233,6 +2241,8 @@ static bool cp_parser_using_declaration
   (cp_parser *, bool);
 static void cp_parser_using_directive
   (cp_parser *);
+static void cp_parser_using_enum
+  (cp_parser *);
 static tree cp_parser_alias_declaration
   (cp_parser *);
 static void cp_parser_asm_definition
@@ -13726,6 +13736,8 @@ cp_parser_block_declaration (cp_parser *parser,
       token2 = cp_lexer_peek_nth_token (parser->lexer, 2);
       if (token2->keyword == RID_NAMESPACE)
        cp_parser_using_directive (parser);
+      else if (token2->keyword == RID_ENUM)
+       cp_parser_using_enum (parser);
       /* If the second token after 'using' is '=', then we have an
         alias-declaration.  */
       else if (cxx_dialect >= cxx11
@@ -20010,6 +20022,31 @@ cp_parser_qualified_namespace_specifier (cp_parser* 
parser)
   return cp_parser_namespace_name (parser);
 }
 
+/* Subroutine of cp_parser_using_declaration.  */
+
+static tree
+finish_using_decl (tree qscope, tree identifier, bool typename_p = false)
+{
+  tree decl = NULL_TREE;
+  if (at_class_scope_p ())
+    {
+      /* Create the USING_DECL.  */
+      decl = do_class_using_decl (qscope, identifier);
+
+      if (check_for_bare_parameter_packs (decl))
+       return error_mark_node;
+
+      if (decl && typename_p)
+       USING_DECL_TYPENAME_P (decl) = 1;
+
+      /* Add it to the list of members in this class.  */
+      finish_member_declaration (decl);
+    }
+  else
+    finish_nonmember_using_decl (qscope, identifier);
+  return decl;
+}
+
 /* Parse a using-declaration, or, if ACCESS_DECLARATION_P is true, an
    access declaration.
 
@@ -20029,7 +20066,6 @@ cp_parser_using_declaration (cp_parser* parser,
   cp_token *token;
   bool typename_p = false;
   bool global_scope_p;
-  tree decl;
   tree identifier;
   tree qscope;
   int oldcount = errorcount;
@@ -20088,9 +20124,6 @@ cp_parser_using_declaration (cp_parser* parser,
                                                  /*is_declaration=*/true);
   if (!qscope)
     qscope = global_namespace;
-  else if (UNSCOPED_ENUM_P (qscope)
-          && !TYPE_FUNCTION_SCOPE_P (qscope))
-    qscope = CP_TYPE_CONTEXT (qscope);
 
   cp_warn_deprecated_use_scopes (qscope);
 
@@ -20138,25 +20171,13 @@ cp_parser_using_declaration (cp_parser* parser,
              "a template-id may not appear in a using-declaration");
   else
     {
-      if (at_class_scope_p ())
+      tree decl = finish_using_decl (qscope, identifier, typename_p);
+
+      if (decl == error_mark_node)
        {
-         /* Create the USING_DECL.  */
-         decl = do_class_using_decl (qscope, identifier);
-
-         if (decl && typename_p)
-           USING_DECL_TYPENAME_P (decl) = 1;
-
-         if (check_for_bare_parameter_packs (decl))
-           {
-             cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
-             return false;
-           }
-         else
-           /* Add it to the list of members in this class.  */
-           finish_member_declaration (decl);
+         cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
+         return false;
        }
-      else
-       finish_nonmember_using_decl (qscope, identifier);
     }
 
   if (!access_declaration_p
@@ -20182,6 +20203,76 @@ cp_parser_using_declaration (cp_parser* parser,
   return true;
 }
 
+/* C++20 using enum declaration.
+
+   using-enum-declaration :
+       using elaborated-enum-specifier ;  */
+
+static void
+cp_parser_using_enum (cp_parser *parser)
+{
+  cp_parser_require_keyword (parser, RID_USING, RT_USING);
+
+  /* Using cp_parser_elaborated_type_specifier rejects typedef-names, which
+     breaks one of the motivating examples in using-enum-5.C.
+     cp_parser_simple_type_specifier seems to be closer to what we actually
+     want, though that hasn't been properly specified yet.  */
+
+  /* Consume 'enum'.  */
+  gcc_checking_assert (cp_lexer_next_token_is_keyword (parser->lexer, 
RID_ENUM));
+  cp_lexer_consume_token (parser->lexer);
+
+  cp_token *start = cp_lexer_peek_token (parser->lexer);
+
+  tree type = (cp_parser_simple_type_specifier
+              (parser, NULL, CP_PARSER_FLAGS_TYPENAME_OPTIONAL));
+
+  cp_token *end = cp_lexer_previous_token (parser->lexer);
+
+  if (type == error_mark_node
+      || !cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON))
+    {
+      cp_parser_skip_to_end_of_block_or_statement (parser);
+      return;
+    }
+  if (TREE_CODE (type) == TYPE_DECL)
+    type = TREE_TYPE (type);
+
+  /* The elaborated-enum-specifier shall not name a dependent type and the type
+     shall have a reachable enum-specifier.  */
+  const char *msg = nullptr;
+  if (cxx_dialect < cxx20)
+    msg = _("%<using enum%> "
+           "only available with %<-std=c++20%> or %<-std=gnu++20%>");
+  else if (dependent_type_p (type))
+    msg = _("%<using enum%> of dependent type %qT");
+  else if (TREE_CODE (type) != ENUMERAL_TYPE)
+    msg = _("%<using enum%> of non-enumeration type %q#T");
+  else if (!COMPLETE_TYPE_P (type))
+    msg = _("%<using enum%> of incomplete type %qT");
+  else if (OPAQUE_ENUM_P (type))
+    msg = _("%<using enum%> of %qT before its enum-specifier");
+  if (msg)
+    {
+      location_t loc = make_location (start, start, end);
+      auto_diagnostic_group g;
+      error_at (loc, msg, type);
+      loc = location_of (type);
+      if (cxx_dialect < cxx20 || loc == input_location)
+       ;
+      else if (OPAQUE_ENUM_P (type))
+       inform (loc, "opaque-enum-declaration here");
+      else
+       inform (loc, "declared here");
+    }
+
+  /* A using-enum-declaration introduces the enumerator names of the named
+     enumeration as if by a using-declaration for each enumerator.  */
+  if (TREE_CODE (type) == ENUMERAL_TYPE)
+    for (tree v = TYPE_VALUES (type); v; v = TREE_CHAIN (v))
+      finish_using_decl (type, DECL_NAME (TREE_VALUE (v)));
+}
+
 /* Parse an alias-declaration.
 
    alias-declaration:
@@ -25279,12 +25370,10 @@ cp_parser_member_declaration (cp_parser* parser)
   if (cp_lexer_next_token_is_keyword (parser->lexer, RID_USING))
     {
       if (cxx_dialect < cxx11)
-       {
-         /* Parse the using-declaration.  */
-         cp_parser_using_declaration (parser,
-                                      /*access_declaration_p=*/false);
-         return;
-       }
+       /* Parse the using-declaration.  */
+       cp_parser_using_declaration (parser, /*access_declaration_p=*/false);
+      else if (cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_ENUM))
+       cp_parser_using_enum (parser);
       else
        {
          tree decl;
@@ -25305,8 +25394,8 @@ cp_parser_member_declaration (cp_parser* parser)
          else
            cp_parser_using_declaration (parser,
                                         /*access_declaration_p=*/false);
-         return;
        }
+      return;
     }
 
   /* Check for @defs.  */
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 0389198dab8..5ff70ff4844 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -4019,9 +4019,17 @@ finish_id_expression_1 (tree id_expression,
              if (context != current_class_type)
                {
                  tree path = currently_open_derived_class (context);
-                 perform_or_defer_access_check (TYPE_BINFO (path),
-                                                decl, decl,
-                                                tf_warning_or_error);
+                 if (!path)
+                   /* PATH can be null for using an enum of an unrelated
+                      class; we checked its access in lookup_using_decl.
+
+                      ??? Should this case make a clone instead, like
+                      handle_using_decl?  */
+                   gcc_assert (TREE_CODE (decl) == CONST_DECL);
+                 else
+                   perform_or_defer_access_check (TYPE_BINFO (path),
+                                                  decl, decl,
+                                                  tf_warning_or_error);
                }
            }
 
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index 4452b9fa89e..0e8436e51c4 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -22193,6 +22193,9 @@ gen_enumeration_type_die (tree type, dw_die_ref 
context_die)
          dw_die_ref enum_die = new_die (DW_TAG_enumerator, type_die, link);
          tree value = TREE_VALUE (link);
 
+         if (DECL_P (value))
+           equate_decl_number_to_die (value, enum_die);
+
          gcc_assert (!ENUM_IS_OPAQUE (type));
          add_name_attribute (enum_die,
                              IDENTIFIER_POINTER (TREE_PURPOSE (link)));
@@ -25247,6 +25250,10 @@ gen_member_die (tree type, dw_die_ref context_die)
                  splice = false;
                }
            }
+         else if (child->die_tag == DW_TAG_enumerator)
+           /* Enumerators remain under their enumeration even if
+              their names are introduced in the enclosing scope.  */
+           splice = false;
 
          if (splice)
            splice_child_die (context_die, child);
@@ -26158,6 +26165,13 @@ force_decl_die (tree decl)
            decl_die = comp_unit_die ();
          break;
 
+       case CONST_DECL:
+         /* Enumerators shouldn't need force_decl_die.  */
+         gcc_assert (DECL_CONTEXT (decl) == NULL_TREE
+                     || TREE_CODE (DECL_CONTEXT (decl)) != ENUMERAL_TYPE);
+         gen_decl_die (decl, NULL, NULL, context_die);
+         break;
+
        case TRANSLATION_UNIT_DECL:
          decl_die = comp_unit_die ();
          break;
@@ -26743,7 +26757,7 @@ dwarf2out_imported_module_or_decl_1 (tree decl,
   else
     xloc = expand_location (input_location);
 
-  if (TREE_CODE (decl) == TYPE_DECL || TREE_CODE (decl) == CONST_DECL)
+  if (TREE_CODE (decl) == TYPE_DECL)
     {
       at_import_die = force_type_die (TREE_TYPE (decl));
       /* For namespace N { typedef void T; } using N::T; base_type_die
diff --git a/gcc/testsuite/g++.dg/cpp0x/inh-ctor28.C 
b/gcc/testsuite/g++.dg/cpp0x/inh-ctor28.C
index 90a06c610f3..59801a1d7bc 100644
--- a/gcc/testsuite/g++.dg/cpp0x/inh-ctor28.C
+++ b/gcc/testsuite/g++.dg/cpp0x/inh-ctor28.C
@@ -4,4 +4,4 @@
 struct A {};
 struct B : virtual A {};
 struct C : virtual A {};
-struct D : B,C { using A::A; };        // { dg-error "indirect" }
+struct D : B,C { using A::A; };        // { dg-error "not a direct base" }
diff --git a/gcc/testsuite/g++.dg/cpp0x/inh-ctor33.C 
b/gcc/testsuite/g++.dg/cpp0x/inh-ctor33.C
index 95b7812f054..4e612907a42 100644
--- a/gcc/testsuite/g++.dg/cpp0x/inh-ctor33.C
+++ b/gcc/testsuite/g++.dg/cpp0x/inh-ctor33.C
@@ -10,7 +10,7 @@ public:
 class Y : public X { };
 
 class Z : public Y {
-  using X::X; // { dg-error "cannot inherit constructors from indirect base 
.X." }
+  using X::X; // { dg-error ".X. is not a direct base of .Z." }
 };
 
 int main()
diff --git a/gcc/testsuite/g++.dg/cpp0x/using-enum-1.C 
b/gcc/testsuite/g++.dg/cpp0x/using-enum-1.C
index 9904d59aaaa..bf251babeae 100644
--- a/gcc/testsuite/g++.dg/cpp0x/using-enum-1.C
+++ b/gcc/testsuite/g++.dg/cpp0x/using-enum-1.C
@@ -1,6 +1,9 @@
 // PR c++/60265
 // { dg-do compile { target c++11 } }
 
+// [namespace.udecl]/7 shall not name a scoped enumerator.
+// (so unscoped enumerator is ok)
+
 namespace A
 {
   enum E { V };
diff --git a/gcc/testsuite/g++.dg/cpp0x/using-enum-2.C 
b/gcc/testsuite/g++.dg/cpp0x/using-enum-2.C
index faa38179305..8ea70d79d4d 100644
--- a/gcc/testsuite/g++.dg/cpp0x/using-enum-2.C
+++ b/gcc/testsuite/g++.dg/cpp0x/using-enum-2.C
@@ -1,20 +1,23 @@
 // PR c++/60265
 // { dg-do compile { target c++11 } }
 
+// [namespace.udecl]/7 shall not name a scoped enumerator.
+// (this changes in C++2a)
+
 namespace A
 {
   enum class E { V };
 
-  using E::V;        // { dg-error "name enumerator" }
+  using E::V;        // { dg-error "enum" "" { target { ! c++2a } } }
 }
 
 void foo()
 {
-  using A::E::V;     // { dg-error "name enumerator" }
+  using A::E::V;     // { dg-error "enum" "" { target { ! c++2a } } }
 }
 
-using A::E::V;       // { dg-error "name enumerator" }
+using A::E::V;       // { dg-error "enum" "" { target { ! c++2a } } }
 
 enum class F { U };
 
-using F::U;          // { dg-error "name enumerator" }
+using F::U;          // { dg-error "enum" "" { target { ! c++2a } } }
diff --git a/gcc/testsuite/g++.dg/cpp0x/using-enum-3.C 
b/gcc/testsuite/g++.dg/cpp0x/using-enum-3.C
index ecc4ddcccd8..34f8bf4fa0b 100644
--- a/gcc/testsuite/g++.dg/cpp0x/using-enum-3.C
+++ b/gcc/testsuite/g++.dg/cpp0x/using-enum-3.C
@@ -1,15 +1,24 @@
 // PR c++/89511
 // { dg-do compile { target c++11 } }
 
+// [namespace.udecl] In a using-declaration used as a
+// member-declaration, the nested-name-specifier shall name a base
+// class of the class being defined
+// (this changes in C++2a)
+
 void f ()
 {
   enum e { a };
-  using e::a; // { dg-error "name enumerator" }
+  using e::a;  // { dg-error "redeclaration" }
+  // { dg-error "enum" "" { target { ! c++2a } } .-1 }
 }
 
+enum E { A };
+
 struct S {
   enum E { A };
-  using E::A; // { dg-error "type .S. is not a base type for type .S." }
+  using E::A; // { dg-error "not a base" "" { target { ! c++2a } } }
+  // { dg-error "conflicts" "" { target c++2a } .-1 }
 };
 
 namespace N {
@@ -17,5 +26,5 @@ namespace N {
 }
 
 struct T {
-  using N::E::B; // { dg-error "using-declaration for non-member at class 
scope" }
+  using N::E::B; // { dg-error "enum" "" { target { ! c++2a } } }
 };
diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction69.C 
b/gcc/testsuite/g++.dg/cpp1z/class-deduction69.C
index 8291f4a649f..d3363668cc5 100644
--- a/gcc/testsuite/g++.dg/cpp1z/class-deduction69.C
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction69.C
@@ -6,7 +6,7 @@ namespace a {
   template <typename...> using c = b;
 }
 template <typename... d> struct e : a::c<d...> { // { dg-error "incomplete" }
-  using a::c<>::c;             // { dg-prune-output "not a base" }
+  using a::c<>::c;             // { dg-prune-output "not a direct base" }
 };
 template <template <typename> typename f> void g() { f(); }
 void h() { g<e>(); }
diff --git a/gcc/testsuite/g++.dg/cpp2a/using-enum-1.C 
b/gcc/testsuite/g++.dg/cpp2a/using-enum-1.C
new file mode 100644
index 00000000000..fd34ca8759a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/using-enum-1.C
@@ -0,0 +1,62 @@
+// Test of using an enumerator.
+// { dg-do compile { target c++2a } }
+
+// using ENUM::V;
+enum class E {v};
+
+using E::v;
+using E::v; // OK
+
+E a = v;
+
+class C
+{
+  using E::v;                  // { dg-message "declared private here" }
+
+  static inline const E m = v;
+};
+
+E b = C::v;                    // { dg-error "private" }
+
+struct B
+{
+  enum E {e};
+  enum class EC {f};
+  using EC::f;
+};
+
+struct D
+{
+private:
+  using B::e;                  // { dg-message "declared private here" }
+  using B::f;                  // { dg-message "declared private here" }
+};
+
+struct F : D
+{
+  static inline const auto bad1 = e; // { dg-error "private" }
+  static inline const auto bad2 = f; // { dg-error "private" }
+
+  static inline const auto ok1 = B::e;
+  static inline const auto ok2 = B::f;
+  static inline const auto also_ok1 = B::E::e;
+  static inline const auto also_ok2 = B::EC::f;
+};
+
+using B::e;
+auto bob = e;
+
+struct Q
+{
+  using B::e;
+};
+using Q::e;                    // OK
+
+using D::e;                    // { dg-error "private" }
+
+template <class T>
+struct X : T
+{
+  using T::e;
+};
+auto fob = X<Q>::e;
diff --git a/gcc/testsuite/g++.dg/cpp2a/using-enum-2.C 
b/gcc/testsuite/g++.dg/cpp2a/using-enum-2.C
new file mode 100644
index 00000000000..66b37f9c010
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/using-enum-2.C
@@ -0,0 +1,48 @@
+// Test of 'using enum' in different scopes.
+// { dg-do compile { target c++20 } }
+
+namespace N
+{
+  enum class E { e, f };
+}
+
+int main()
+{
+  using enum N::E;
+  static_assert (e < f);
+}
+
+struct A
+{
+  using enum N::E;
+  static_assert (e < f);
+};
+
+namespace M
+{
+  using enum N::E;
+  static_assert (e < f);
+
+  enum class X: int;           // { dg-message "opaque" }
+  using enum X;                        // { dg-error "enum-specifier" }
+}
+
+template <class T>
+void f()
+{
+  using enum N::E;
+  static_assert (e < f);
+}
+
+template <class T>
+struct AT
+{
+  using enum N::E;
+  static_assert (e < f);
+};
+
+template <class T>
+struct BT
+{
+  using enum T::E;             // { dg-error "dependent" }
+};
diff --git a/gcc/testsuite/g++.dg/cpp2a/using-enum-3.C 
b/gcc/testsuite/g++.dg/cpp2a/using-enum-3.C
new file mode 100644
index 00000000000..d09bd6a4683
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/using-enum-3.C
@@ -0,0 +1,6 @@
+// Test of 'using enum' syntax error recovery.
+// { dg-do compile { target c++20 } }
+
+using enum 2 + garbage3'850%^&;                // { dg-error "" }
+
+void f() {}
diff --git a/gcc/testsuite/g++.dg/cpp2a/using-enum-4.C 
b/gcc/testsuite/g++.dg/cpp2a/using-enum-4.C
new file mode 100644
index 00000000000..03432cf4532
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/using-enum-4.C
@@ -0,0 +1,13 @@
+// Test for suggestion to try 'using enum'.
+// { dg-do compile { target c++20 } }
+
+struct A
+{
+  enum E { e };
+};
+
+struct B
+{
+  using A::E;                  // { dg-error "" }
+  // { dg-message "using enum" "" { target *-*-* } .-1 }
+};
diff --git a/gcc/testsuite/g++.dg/cpp2a/using-enum-5.C 
b/gcc/testsuite/g++.dg/cpp2a/using-enum-5.C
new file mode 100644
index 00000000000..e5fe820925b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/using-enum-5.C
@@ -0,0 +1,132 @@
+// Examples from P1099R5
+// { dg-do compile { target c++20 } }
+
+namespace my_lib {
+
+  enum class errcode
+    {
+     SUCCESS = 0,
+     ENOMEM = 1,
+     EAGAIN = 2,
+     ETOOSLOW = 3
+    };
+  using enum errcode; // import enumerators into namespace
+}
+
+namespace NS {
+  my_lib::errcode get_widget() {
+    using namespace my_lib;
+    return ETOOSLOW; // works, and conversions to int don't.
+    int i = ETOOSLOW;          // { dg-error "" }
+  }
+}
+
+enum class rgba_color_channel { red, green, blue, alpha};
+
+const char * to_string(rgba_color_channel channel) {
+  switch (channel) {
+    using enum rgba_color_channel;
+  case red:   return "red";
+  case green: return "green";
+  case blue:  return "blue";
+  case alpha: return "alpha";
+  }
+  return nullptr;
+}
+
+namespace ns {
+  struct E_detail {
+    enum E { e1, e2 };
+    friend void swap(E&, E&);  // adl-only swap in the only associated scope 
of the enum
+  };
+  using E = E_detail::E;  // import E into ns
+  using enum E;           // expose the enumerators of E in ns. Also note the 
direct reference to E.
+}
+
+int main() {
+  auto x = ns::e1;
+  auto y = ns::e2;
+  swap(x, y); // finds the swap in the associated struct
+}
+
+namespace N0 {
+  enum E { x };
+  struct S {
+    enum H { y };
+    enum class K { z };
+    using E::x; // OK, introduces x into S
+    using E::x; // { dg-error "" } redeclaration in class scope
+    using H::y; // { dg-error "" } redeclaration in class scope
+    using K::z; // OK, introduces z into S
+  };
+  namespace NS {
+    enum H { y };
+    enum class K { z };
+    using E::x; // OK, introduces x into NS
+    using E::x; // OK, just a redeclaration of the same entity
+    using H::y; // OK, redeclaration of the same entity
+    using K::z; // OK, introduces z into NS
+  };
+}
+namespace N1 {
+  struct S {
+    enum E { x };
+    enum class EC { y };
+    using EC::y;
+  };
+
+  void f() {
+    using S::x; // OK
+    x; // resolves to S::E::x;
+    using S::y; // OK
+    y; // resolves to S::EC::y;
+  }
+}
+
+namespace N2 {
+  enum class E { a, b, c };
+  using E::a, E::b, E::c; // OK, imports all three
+  auto x = (a,b,c);
+}
+
+namespace N3 {
+  struct B {
+    enum class E { x };
+  };
+  enum class H { y };
+  struct C : B {
+    using enum B::E; // OK, introduces E::x into C
+    using enum H; // OK, introduces y into C. Does not introduce H
+  };
+  auto i = C::y;  // OK
+  C::H h;        // { dg-error "" }
+}
+
+namespace N4 {
+  enum class button { up, down };
+  struct S {
+    using button::up;
+    button b = up; // OK
+  };
+}
+
+namespace N5 {
+  enum class fruit { orange, apple };
+  struct S {
+    using enum fruit; // OK, introduces orange and apple into S
+  };
+  void f() {
+    S s;
+    s.orange; // OK, names fruit::orange
+    S::orange; // OK, names fruit::orange
+  }
+}
+
+namespace N6 {
+  enum class fruit { orange, apple };
+  enum class color { red, orange };
+  void f() {
+    using enum fruit; // OK
+    using enum color; // { dg-error "" } color::orange and fruit::orange 
conflict
+  }
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/using-enum-6.C 
b/gcc/testsuite/g++.dg/cpp2a/using-enum-6.C
new file mode 100644
index 00000000000..732cdfdcd3c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/using-enum-6.C
@@ -0,0 +1,5 @@
+// { dg-do compile { target c++2a } }
+
+using enum void;               // { dg-error "non-enum" }
+struct A {};                   // { dg-message "declared here" }
+using enum A;                  // { dg-error "non-enum" }
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/using-enum.C 
b/gcc/testsuite/g++.dg/debug/dwarf2/using-enum.C
new file mode 100644
index 00000000000..7663a13cc61
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/using-enum.C
@@ -0,0 +1,21 @@
+// Test of 'using enum' debug info.
+// { dg-do compile { target c++20 } }
+// { dg-options "-g -dA" }
+
+struct A
+{
+  // All the counts are +1 for the abbreviation table.
+  // { dg-final { scan-assembler-times "DW_TAG_enumeration_type" 2 } }
+  // { dg-final { scan-assembler-times "DW_TAG_enumerator" 3 } }
+  enum E { e, f };
+};
+
+struct B
+{
+  // The using-enum-declaration is represented by two
+  // DW_TAG_imported_declaration, one for each enumerator.
+  // { dg-final { scan-assembler-times "DW_TAG_imported_declaration" 3 } }
+  using enum A::E;
+};
+
+B b;
diff --git a/gcc/testsuite/g++.dg/inherit/using5.C 
b/gcc/testsuite/g++.dg/inherit/using5.C
index b8e51078ff9..514cd8daeb8 100644
--- a/gcc/testsuite/g++.dg/inherit/using5.C
+++ b/gcc/testsuite/g++.dg/inherit/using5.C
@@ -6,7 +6,7 @@
 
 template<int> struct A
 {
-  A::A; // { dg-error "constructor|not a base" }
+  A::A; // { dg-error "constructor|not a direct base" }
 };
 
 struct B

base-commit: e3b3b59683c1e7d31a9d313dd97394abebf644be
-- 
2.18.4

Reply via email to