[PATCH v2] c++: ICE with USING_DECL redeclaration [PR98687]

Marek Polacek polacek@redhat.com
Tue Jan 19 20:47:17 GMT 2021


On Mon, Jan 18, 2021 at 05:18:46PM -0500, Jason Merrill wrote:
> On 1/15/21 12:26 AM, Marek Polacek wrote:
> > My recent patch that introduced push_using_decl_bindings didn't
> > handle USING_DECL redeclaration, therefore things broke.  This
> > patch amends that.  Note that I don't know if the other parts of
> > finish_nonmember_using_decl are needed (e.g. the binding->type
> > setting) -- I couldn't trigger it by any of my hand-made testcases.
> 
> I'd expect it to be exercised by something along the lines of
> 
> struct A { };
> 
> void f()
> {
>   int A;
>   using ::A;
>   struct A a;
> }

Hmm, I already had a test for the struct stat hack, but I've added this one
to using64.C, thanks.
 
> Let's factor the code out of finish_nonmember_using_decl rather than copy
> it.

Done here.  A small complication was that name_lookup is local to
name-lookup.c so I had to add an overload to handle this.

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
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                  | 103 ++++++++++++++------------
 gcc/cp/name-lookup.h                  |   2 +-
 gcc/cp/pt.c                           |   3 +-
 gcc/testsuite/g++.dg/lookup/using64.C |  69 +++++++++++++++++
 gcc/testsuite/g++.dg/lookup/using65.C |  17 +++++
 5 files changed, 145 insertions(+), 49 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/lookup/using64.C
 create mode 100644 gcc/testsuite/g++.dg/lookup/using65.C

diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index b4b6c0b81b5..843e5f305c0 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -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"
diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h
index bac3fa71fc9..75db5b38061 100644
--- a/gcc/cp/name-lookup.h
+++ b/gcc/cp/name-lookup.h
@@ -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. */
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 957140115e4..12d084031b1 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -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
index 00000000000..a50cd273c89
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/using64.C
@@ -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
index 00000000000..bc6c086197f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/using65.C
@@ -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(); }

base-commit: 7266ff2a243715e20882850b2fc4211ac7db4d34
-- 
2.29.2



More information about the Gcc-patches mailing list