The handling of dependent scopes and unsuitable scopes in lookup_using_decl
was a bit convoluted; I tweaked it for a while and then eventually
reorganized much of the function to hopefully be clearer.  Along the way I
noticed a couple of ways we were mishandling inherited constructors.

The local binding for a dependent using is the USING_DECL.

Implement instantiation of a dependent USING_DECL at function scope.

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

gcc/cp/ChangeLog:

        PR c++/97874
        * name-lookup.c (lookup_using_decl): Clean up handling
        of dependency and inherited constructors.
        (finish_nonmember_using_decl): Handle DECL_DEPENDENT_P.
        * pt.c (tsubst_expr): Handle DECL_DEPENDENT_P.

gcc/testsuite/ChangeLog:

        PR c++/97874
        * g++.dg/lookup/using4.C: No error in C++20.
        * g++.dg/cpp0x/decltype37.C: Adjust message.
        * g++.dg/template/crash75.C: Adjust message.
        * g++.dg/template/crash76.C: Adjust message.
        * g++.dg/cpp0x/inh-ctor36.C: New test.
        * g++.dg/cpp1z/inh-ctor39.C: New test.
        * g++.dg/cpp2a/using-enum-7.C: New test.
---
 gcc/cp/name-lookup.c                      | 144 +++++++++++-----------
 gcc/cp/pt.c                               |  41 +++---
 gcc/testsuite/g++.dg/cpp0x/decltype37.C   |   2 +-
 gcc/testsuite/g++.dg/cpp0x/inh-ctor36.C   |  10 ++
 gcc/testsuite/g++.dg/cpp1z/inh-ctor39.C   |  12 ++
 gcc/testsuite/g++.dg/cpp2a/using-enum-7.C |  27 ++++
 gcc/testsuite/g++.dg/lookup/using4.C      |   2 +-
 gcc/testsuite/g++.dg/template/crash75.C   |   4 +-
 gcc/testsuite/g++.dg/template/crash76.C   |   2 +-
 9 files changed, 154 insertions(+), 90 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/inh-ctor36.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1z/inh-ctor39.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/using-enum-7.C

diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index 0fb0036c4f3..52e4a630e25 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -5729,6 +5729,16 @@ lookup_using_decl (tree scope, name_lookup &lookup)
       /* Naming a class member.  This is awkward in C++20, because we
         might be naming an enumerator of an unrelated class.  */
 
+      tree npscope = scope;
+      if (PACK_EXPANSION_P (scope))
+       npscope = PACK_EXPANSION_PATTERN (scope);
+
+      if (!MAYBE_CLASS_TYPE_P (npscope))
+       {
+         error ("%qT is not a class, namespace, or enumeration", npscope);
+         return NULL_TREE;
+       }
+
       /* You cannot using-decl a destructor.  */
       if (TREE_CODE (lookup.name) == BIT_NOT_EXPR)
        {
@@ -5737,14 +5747,13 @@ lookup_using_decl (tree scope, name_lookup &lookup)
        }
 
       /* Using T::T declares inheriting ctors, even if T is a typedef.  */
-      if (MAYBE_CLASS_TYPE_P (scope)
-         && (lookup.name == TYPE_IDENTIFIER (scope)
-             || constructor_name_p (lookup.name, scope)))
+      if (lookup.name == TYPE_IDENTIFIER (npscope)
+         || constructor_name_p (lookup.name, npscope))
        {
          if (!TYPE_P (current))
            {
              error ("non-member using-declaration names constructor of %qT",
-                    scope);
+                    npscope);
              return NULL_TREE;
            }
          maybe_warn_cpp0x (CPP0X_INHERITING_CTORS);
@@ -5752,88 +5761,79 @@ lookup_using_decl (tree scope, name_lookup &lookup)
          CLASSTYPE_NON_AGGREGATE (current) = true;
        }
 
-      if (!MAYBE_CLASS_TYPE_P (scope))
-       ;
+      if (!TYPE_P (current) && cxx_dialect < cxx20)
+       {
+         error ("using-declaration for member at non-class scope");
+         return NULL_TREE;
+       }
+
+      bool depscope = dependent_scope_p (scope);
+
+      if (depscope)
+       /* Leave binfo null.  */;
       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);
+         binfo = lookup_base (current, scope, ba_any, &b_kind, tf_none);
+         gcc_checking_assert (b_kind >= bk_not_base);
 
-             if (lookup.name == ctor_identifier)
+         if (b_kind == bk_not_base && any_dependent_bases_p ())
+           /* Treat as-if dependent.  */
+           depscope = true;
+         else if (lookup.name == ctor_identifier
+                  && (b_kind < bk_proper_base || !binfo_direct_p (binfo)))
+           {
+             if (any_dependent_bases_p ())
+               depscope = true;
+             else
                {
-                 /* 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;
-                   }
+                 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;
            }
+
+         if (b_kind < bk_proper_base)
+           binfo = TYPE_BINFO (scope);
        }
       else
        binfo = TYPE_BINFO (scope);
 
+      dependent_p = (depscope
+                    || (IDENTIFIER_CONV_OP_P (lookup.name)
+                        && dependent_type_p (TREE_TYPE (lookup.name))));
+
       if (!dependent_p)
+       lookup.value = lookup_member (binfo, lookup.name, /*protect=*/2,
+                                     /*want_type=*/false, tf_none);
+
+      if (!depscope && b_kind < bk_proper_base)
        {
-         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 && lookup.value
+             && TREE_CODE (lookup.value) == CONST_DECL)
            {
-             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;
+             /* 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);
            }
-
-         if (!lookup.value)
+         else if (!TYPE_P (current))
            {
-             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;
-                   }
-               }
+             error ("using-declaration for member at non-class scope");
+             return NULL_TREE;
+           }
+         else
+           {
+             auto_diagnostic_group g;
+             error_not_base_type (scope, current);
+             if (lookup.value && DECL_IMPLICIT_TYPEDEF_P (lookup.value)
+                 && TREE_CODE (TREE_TYPE (lookup.value)) == ENUMERAL_TYPE)
+               inform (input_location,
+                       "did you mean %<using enum %T::%D%>?",
+                       scope, lookup.name);
+             return NULL_TREE;
            }
        }
     }
@@ -6455,6 +6455,8 @@ finish_nonmember_using_decl (tree scope, tree name)
   else
     {
       add_decl_expr (using_decl);
+      if (DECL_DEPENDENT_P (using_decl))
+       lookup.value = using_decl;
       push_using_decl_bindings (&lookup, name, NULL_TREE);
     }
 }
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 63a0a110440..8f05ce28899 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -18130,22 +18130,33 @@ tsubst_expr (tree t, tree args, tsubst_flags_t 
complain, tree in_decl,
          finish_label_decl (DECL_NAME (decl));
        else if (TREE_CODE (decl) == USING_DECL)
          {
-           /* We cannot have a member-using decl here (until 'using
-              enum T' is a thing).  */
-           gcc_checking_assert (!DECL_DEPENDENT_P (decl));
-
-           /* This must be a non-dependent using-decl, and we'll have
-              used the names it found during template parsing.  We do
-              not want to do the lookup again, because we might not
-              find the things we found then.  (Again, using enum T
-              might mean we have to do things here.)  */
            tree scope = USING_DECL_SCOPE (decl);
-           gcc_checking_assert (scope
-                                == tsubst (scope, args, complain, in_decl));
-           /* We still need to push the bindings so that we can look up
-              this name later.  */
-           push_using_decl_bindings (DECL_NAME (decl),
-                                     USING_DECL_DECLS (decl));
+           if (DECL_DEPENDENT_P (decl))
+             {
+               scope = tsubst (scope, args, complain, in_decl);
+               if (!MAYBE_CLASS_TYPE_P (scope)
+                   && TREE_CODE (scope) != ENUMERAL_TYPE)
+                 {
+                   if (complain & tf_error)
+                     error_at (DECL_SOURCE_LOCATION (decl), "%qT is not a "
+                               "class, namespace, or enumeration", scope);
+                   return error_mark_node;
+                 }
+               finish_nonmember_using_decl (scope, DECL_NAME (decl));
+             }
+           else
+             {
+               /* This is a non-dependent using-decl, and we'll have
+                  used the names it found during template parsing.  We do
+                  not want to do the lookup again, because we might not
+                  find the things we found then.  */
+               gcc_checking_assert (scope == tsubst (scope, args,
+                                                     complain, in_decl));
+               /* We still need to push the bindings so that we can look up
+                  this name later.  */
+               push_using_decl_bindings (DECL_NAME (decl),
+                                         USING_DECL_DECLS (decl));
+             }
          }
        else if (is_capture_proxy (decl)
                 && !DECL_TEMPLATE_INSTANTIATION (current_function_decl))
diff --git a/gcc/testsuite/g++.dg/cpp0x/decltype37.C 
b/gcc/testsuite/g++.dg/cpp0x/decltype37.C
index c885e9a0769..5d0f085f433 100644
--- a/gcc/testsuite/g++.dg/cpp0x/decltype37.C
+++ b/gcc/testsuite/g++.dg/cpp0x/decltype37.C
@@ -8,7 +8,7 @@ template<typename T> auto foo(T* t) -> wrap<T>* { return 0; }
 template<typename T>
 struct holder : decltype(*foo((T*)0)) // { dg-error "class type" }
 {
-    using decltype(*foo((T*)0))::bar; // { dg-error "is not a base" }
+    using decltype(*foo((T*)0))::bar; // { dg-error "is not a class" }
 };
 
 holder<int> h;
diff --git a/gcc/testsuite/g++.dg/cpp0x/inh-ctor36.C 
b/gcc/testsuite/g++.dg/cpp0x/inh-ctor36.C
new file mode 100644
index 00000000000..c531af9bd27
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/inh-ctor36.C
@@ -0,0 +1,10 @@
+// { dg-do compile { target c++11 } }
+
+struct A { A(); A(int); };
+
+template <class... T> struct C: T...
+{
+  using A::A;
+};
+
+C<A> c1(42);
diff --git a/gcc/testsuite/g++.dg/cpp1z/inh-ctor39.C 
b/gcc/testsuite/g++.dg/cpp1z/inh-ctor39.C
new file mode 100644
index 00000000000..3e6356c9935
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/inh-ctor39.C
@@ -0,0 +1,12 @@
+// { dg-do compile { target c++17 } }
+
+struct A { A(); A(int); };
+struct B { B(); B(void*); };
+
+template <class... T> struct C: T...
+{
+  using T::T...;
+};
+
+C<A,B> c1(42);
+C<A,B> c2(nullptr);
diff --git a/gcc/testsuite/g++.dg/cpp2a/using-enum-7.C 
b/gcc/testsuite/g++.dg/cpp2a/using-enum-7.C
new file mode 100644
index 00000000000..4ba3b9ea893
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/using-enum-7.C
@@ -0,0 +1,27 @@
+// PR c++/97874
+// { dg-do compile { target c++20 } }
+
+struct A { enum E { kl }; };
+
+template <typename UQ>
+int
+v4 ()
+{
+  using UQ::kl;
+  return kl;
+}
+
+template <typename UQ>
+int
+v5 ()
+{
+  using UQ::kl;                        // { dg-error "not a class" }
+  return kl;                   // { dg-error "not declared" }
+}
+
+int main()
+{
+  v4<A>();
+  v4<A::E>();
+  v5<int>();
+}
diff --git a/gcc/testsuite/g++.dg/lookup/using4.C 
b/gcc/testsuite/g++.dg/lookup/using4.C
index facb2b4fd31..a9a8ec21c4d 100644
--- a/gcc/testsuite/g++.dg/lookup/using4.C
+++ b/gcc/testsuite/g++.dg/lookup/using4.C
@@ -10,6 +10,6 @@ template <class T>
 struct Bar : public Foo<T> {
         void foo()
         {
-                using Foo<T>::i;       // { dg-error "member at non-class 
scope" }
+                using Foo<T>::i;       // { dg-error "member at non-class 
scope" "" { target c++17_down } }
         }
 };
diff --git a/gcc/testsuite/g++.dg/template/crash75.C 
b/gcc/testsuite/g++.dg/template/crash75.C
index 462be95b2f7..2bdc3950f54 100644
--- a/gcc/testsuite/g++.dg/template/crash75.C
+++ b/gcc/testsuite/g++.dg/template/crash75.C
@@ -2,7 +2,9 @@
 
 template<typename T> struct A
 {
-  T::X<0> x; // { dg-error "non-template|T::template|base type" }
+  T::X<0> x; // { dg-error "non-template" }
+  // { dg-message "T::template" "" { target *-*-* } .-1 }
+  // { dg-prune-output "is not a class" }
 };
 
 A<int*> a;
diff --git a/gcc/testsuite/g++.dg/template/crash76.C 
b/gcc/testsuite/g++.dg/template/crash76.C
index 851cdd8c436..2711749eca9 100644
--- a/gcc/testsuite/g++.dg/template/crash76.C
+++ b/gcc/testsuite/g++.dg/template/crash76.C
@@ -7,7 +7,7 @@ template<typename> struct A
 
 template<typename T> struct B
 {
-  using A<T>::X::Y; // { dg-error "not a base type" }
+  using A<T>::X::Y; // { dg-error "not a class" }
 };
 
 B<int> b;

base-commit: 7a279bed24e1b2a628767a60a20f3dcf6f1088cb
-- 
2.27.0

Reply via email to