This patch resolves 87531 by injecting an artificial dependent 'using TPL::operator=' into template class TPL's field list. This makes lookups of (unqualified) operator= dependent, and hence resolved at instantiation time. Prior to fixing 15272 we happened to 'work' for the cases that looked sufficiently dependent as we redid the lookup completely at instantiation time.

The artificial using decl is not added to the template decl list, so the usual lazy creation of these operators happens in the instantiation.

The std doesn't appear to cover this case, and I have raised a question with the Core WG.

Jason, I decided this approach was cheap enough (and simple) to a rather more involved solution involving marking the template class as having a lazy assign op of some kind, and injecting the using decl at that point.

nathan
--
Nathan Sidwell
2018-11-28  Nathan Sidwell  <[email protected]>

	PR c++/87531
	* class.c (finish_struct): In a template, add artificial using
	decl for operator=.

	* g++.dg/lookup/pr87531.C: New.

Index: gcc/cp/class.c
===================================================================
--- gcc/cp/class.c	(revision 266557)
+++ gcc/cp/class.c	(working copy)
@@ -7150,6 +7150,19 @@ finish_struct (tree t, tree attributes)
 	else if (DECL_DECLARES_FUNCTION_P (x))
 	  DECL_IN_AGGR_P (x) = false;
 
+      /* Also add a USING_DECL for operator=.  We know there'll be (at
+	 least) one, but we don't know the signature(s).  We want name
+	 lookup not to fail or recurse into bases.  This isn't added
+	 to the template decl list so we drop this at instantiation
+	 time.  */
+      tree ass_op = build_lang_decl (USING_DECL, assign_op_identifier,
+				     NULL_TREE);
+      USING_DECL_SCOPE (ass_op) = t;
+      DECL_DEPENDENT_P (ass_op) = true;
+      DECL_ARTIFICIAL (ass_op) = true;
+      DECL_CHAIN (ass_op) = TYPE_FIELDS (t);
+      TYPE_FIELDS (t) = ass_op;
+
       TYPE_SIZE (t) = bitsize_zero_node;
       TYPE_SIZE_UNIT (t) = size_zero_node;
       /* COMPLETE_TYPE_P is now true.  */
Index: gcc/testsuite/g++.dg/lookup/pr87531.C
===================================================================
--- gcc/testsuite/g++.dg/lookup/pr87531.C	(revision 0)
+++ gcc/testsuite/g++.dg/lookup/pr87531.C	(working copy)
@@ -0,0 +1,73 @@
+// PR c+/87531 lookup of operator= in templates
+// { dg-do run }
+
+struct Base {
+  void operator= (Base const&);
+};
+
+void Base::operator= (Base const &)
+{
+}
+
+template <typename T>
+struct Derived : Base
+{
+  T v;
+
+  Derived() : v (0) {}
+  Derived(T v_) : v (v_) {}
+
+  T &assign1 (Derived const& rhs)
+  {
+    operator=(rhs); // erroneously bound to Base::operator=
+    return v;
+  }
+
+  T &assign2 (Derived const& rhs)
+  {
+    this->operator=(rhs); // erroneously bound to Base::operator=
+    return v;
+  }
+};
+
+template <typename T>
+struct Single
+{
+  T v;
+
+  Single () : v (0) {}
+  Single (T v_) : v (v_) {}
+
+  T &assign1 (Single const& rhs)
+  {
+    operator=(rhs); // lookup failed
+    return v;
+  }
+
+  T &assign2 (Single const& rhs)
+  {
+    this->operator=(rhs); // Marked as dependent, happened to work
+    return v;
+  }
+};
+
+int main()
+{
+  Derived<int> a, b(123);
+
+  if (a.assign1 (b) != 123)
+    return 1;
+
+  if (a.assign2 (b) != 123)
+    return 2;
+
+  Single<int> c, d(123);
+  
+  if (c.assign1 (d) != 123)
+    return 3;
+
+  if (c.assign2 (d) != 123)
+    return 4;
+
+  return 0;
+}

Reply via email to