c++: ICE with USING_DECL redeclaration [PR98687]
authorMarek Polacek <polacek@redhat.com>
Fri, 15 Jan 2021 03:14:38 +0000 (22:14 -0500)
committerMarek Polacek <polacek@redhat.com>
Tue, 19 Jan 2021 22:13:00 +0000 (17:13 -0500)
My recent patch that introduced push_using_decl_bindings didn't
handle USING_DECL redeclaration, therefore things broke.  This patch
amends that by breaking out a part of finish_nonmember_using_decl
out to a separate function, push_using_decl_bindings, and calling it.
It needs an overload, because name_lookup is only available inside
of name-lookup.c.

gcc/cp/ChangeLog:

PR c++/98687
* name-lookup.c (push_using_decl_bindings): New, broken out of...
(finish_nonmember_using_decl): ...here.
* name-lookup.h (push_using_decl_bindings): Update declaration.
* pt.c (tsubst_expr): Update the call to push_using_decl_bindings.

gcc/testsuite/ChangeLog:

PR c++/98687
* g++.dg/lookup/using64.C: New test.
* g++.dg/lookup/using65.C: New test.

gcc/cp/name-lookup.c
gcc/cp/name-lookup.h
gcc/cp/pt.c
gcc/testsuite/g++.dg/lookup/using64.C [new file with mode: 0644]
gcc/testsuite/g++.dg/lookup/using65.C [new file with mode: 0644]

index b4b6c0b81b50124e9888980b893b5bf80e20ee48..843e5f305c0ef13c16a1a479fdc02c51c252c2f3 100644 (file)
@@ -6279,6 +6279,61 @@ pushdecl_namespace_level (tree x, bool hiding)
   return t;
 }
 
+/* Wrapper around push_local_binding to push the bindings for
+   a non-member USING_DECL with NAME and VALUE.  LOOKUP, if non-null,
+   is the result of name lookup during template parsing.  */
+
+static void
+push_using_decl_bindings (name_lookup *lookup, tree name, tree value)
+{
+  tree type = NULL_TREE;
+
+  cxx_binding *binding = find_local_binding (current_binding_level, name);
+  if (binding)
+    {
+      value = binding->value;
+      type = binding->type;
+    }
+
+  /* DR 36 questions why using-decls at function scope may not be
+     duplicates.  Disallow it, as C++11 claimed and PR 20420
+     implemented.  */
+  if (lookup)
+    do_nonmember_using_decl (*lookup, true, true, &value, &type);
+
+  if (!value)
+    ;
+  else if (binding && value == binding->value)
+    /* Redeclaration of this USING_DECL.  */;
+  else if (binding && binding->value && TREE_CODE (value) == OVERLOAD)
+    {
+      /* We already have this binding, so replace it.  */
+      update_local_overload (IDENTIFIER_BINDING (name), value);
+      IDENTIFIER_BINDING (name)->value = value;
+    }
+  else
+    /* Install the new binding.  */
+    push_local_binding (name, value, /*using=*/true);
+
+  if (!type)
+    ;
+  else if (binding && type == binding->type)
+    ;
+  else
+    {
+      push_local_binding (name, type, /*using=*/true);
+      set_identifier_type_value (name, type);
+    }
+}
+
+/* Overload for push_using_decl_bindings that doesn't take a name_lookup.  */
+
+void
+push_using_decl_bindings (tree name, tree value)
+{
+  push_using_decl_bindings (nullptr, name, value);
+}
+
 /* Process a using declaration in non-class scope.  */
 
 void
@@ -6395,43 +6450,7 @@ finish_nonmember_using_decl (tree scope, tree name)
   else
     {
       add_decl_expr (using_decl);
-
-      cxx_binding *binding = find_local_binding (current_binding_level, name);
-      tree value = NULL;
-      tree type = NULL;
-      if (binding)
-       {
-         value = binding->value;
-         type = binding->type;
-       }
-
-      /* DR 36 questions why using-decls at function scope may not be
-        duplicates.  Disallow it, as C++11 claimed and PR 20420
-        implemented.  */
-      do_nonmember_using_decl (lookup, true, true, &value, &type);
-
-      if (!value)
-       ;
-      else if (binding && value == binding->value)
-       ;
-      else if (binding && binding->value && TREE_CODE (value) == OVERLOAD)
-       {
-         update_local_overload (IDENTIFIER_BINDING (name), value);
-         IDENTIFIER_BINDING (name)->value = value;
-       }
-      else
-       /* Install the new binding.  */
-       push_local_binding (name, value, true);
-
-      if (!type)
-       ;
-      else if (binding && type == binding->type)
-       ;
-      else
-       {
-         push_local_binding (name, type, true);
-         set_identifier_type_value (name, type);
-       }
+      push_using_decl_bindings (&lookup, name, NULL_TREE);
     }
 }
 
@@ -9279,14 +9298,4 @@ push_operator_bindings ()
        }
 }
 
-/* Wrapper around push_local_binding to push the bindings for
-   a non-member USING_DECL DECL that was found during template parsing.  */
-
-void
-push_using_decl_bindings (tree decl)
-{
-  push_local_binding (DECL_NAME (decl), USING_DECL_DECLS (decl),
-                     /*using*/true);
-}
-
 #include "gt-cp-name-lookup.h"
index bac3fa71fc90b43b9a4795f5f447b1c0ab83e01d..75db5b38061d94d0e5b6853fa57ceb595279291c 100644 (file)
@@ -478,7 +478,7 @@ extern void push_to_top_level (void);
 extern void pop_from_top_level (void);
 extern void maybe_save_operator_binding (tree);
 extern void push_operator_bindings (void);
-extern void push_using_decl_bindings (tree);
+extern void push_using_decl_bindings (tree, tree);
 extern void discard_operator_bindings (tree);
 
 /* Lower level interface for modules. */
index 957140115e49e0a1606c6d8e01120de5930791be..12d084031b1c772ba3916e0b0b11e7b602d9e710 100644 (file)
@@ -18136,7 +18136,8 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
                                 == 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);
+           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/lookup/using64.C b/gcc/testsuite/g++.dg/lookup/using64.C
new file mode 100644 (file)
index 0000000..a50cd27
--- /dev/null
@@ -0,0 +1,69 @@
+// PR c++/98687
+// { dg-do compile }
+
+struct S { };
+
+namespace N {
+  template <typename T>
+  bool operator==(T, int);
+
+  template <typename T>
+  void X(T);
+}
+
+namespace M {
+  template <typename T>
+  bool operator==(T, double);
+}
+
+template<typename T>
+bool fn1 (T t)
+{
+  using N::operator==;
+  return t == 1;
+}
+
+template<typename T>
+bool fn2 (T t)
+{
+  // Redeclaration.
+  using N::operator==;
+  using N::operator==;
+  return t == 1;
+}
+
+template<typename T>
+bool fn3 (T t)
+{
+  // Need update_local_overload.
+  using N::operator==;
+  using M::operator==;
+  return t == 1;
+}
+
+template<typename T>
+void fn4 (T)
+{
+  struct X { };
+  using N::X;
+  X(1);
+}
+
+template<typename T>
+void fn5 (T)
+{
+  int S;
+  using ::S;
+  struct S s;
+}
+
+void
+g ()
+{
+  S s;
+  fn1 (s);
+  fn2 (s);
+  fn3 (s);
+  fn4 (s);
+  fn5 (s);
+}
diff --git a/gcc/testsuite/g++.dg/lookup/using65.C b/gcc/testsuite/g++.dg/lookup/using65.C
new file mode 100644 (file)
index 0000000..bc6c086
--- /dev/null
@@ -0,0 +1,17 @@
+// PR c++/98687
+// { dg-do compile }
+
+extern "C" namespace std {
+  double log1p(double);
+}
+namespace std_fallback {
+  template <typename> void log1p();
+}
+template <typename> struct log1p_impl {
+  static int run() {
+    using std::log1p;
+    using std_fallback::log1p;
+    return 0;
+  }
+};
+void log1p() { log1p_impl<int>::run(); }