c++: Dependent using enum [PR97874]
authorJason Merrill <jason@redhat.com>
Wed, 27 Jan 2021 05:51:01 +0000 (00:51 -0500)
committerJason Merrill <jason@redhat.com>
Wed, 27 Jan 2021 20:08:05 +0000 (15:08 -0500)
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.

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
gcc/cp/pt.c
gcc/testsuite/g++.dg/cpp0x/decltype37.C
gcc/testsuite/g++.dg/cpp0x/inh-ctor36.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/inh-ctor39.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/using-enum-7.C [new file with mode: 0644]
gcc/testsuite/g++.dg/lookup/using4.C
gcc/testsuite/g++.dg/template/crash75.C
gcc/testsuite/g++.dg/template/crash76.C

index 0fb0036c4f38bb7c9a4fcf5ae482feaf38f36514..52e4a630e25df584242a883f9173deeec93b49a7 100644 (file)
@@ -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);
+
+         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)))
            {
-             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 (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)
-       {
-         if (binfo)
-           lookup.value = lookup_member (binfo, lookup.name, /*protect=*/2,
-                                         /*want_type=*/false, tf_none);
+       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 (!depscope && 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);
     }
 }
index 63a0a1104404109d486bfe515bdbc41cf71a4514..8f05ce28899e46dde03226a8c5b61dfd8d6d3ca1 100644 (file)
@@ -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))
index c885e9a0769871f350b0a06813e966d3cc0ea069..5d0f085f433ba43432f50659982b5094203a11ba 100644 (file)
@@ -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 (file)
index 0000000..c531af9
--- /dev/null
@@ -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 (file)
index 0000000..3e6356c
--- /dev/null
@@ -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 (file)
index 0000000..4ba3b9e
--- /dev/null
@@ -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>();
+}
index facb2b4fd31d981a4b8db3a732bc7c711381769b..a9a8ec21c4dd9644c078c7697561a68a21551532 100644 (file)
@@ -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 } }
         }
 };
index 462be95b2f7fc6d3dc9be21ba4b3630c7dbc2b81..2bdc3950f541d6b7fee9f859f3a3bca73859c3b8 100644 (file)
@@ -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;
index 851cdd8c436d31c6be2d18a6b03fde4ee65a03a9..2711749eca97b97898ddb5e15504b01477ee1eeb 100644 (file)
@@ -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;