diff mbox series

[PR,c++/87531] operator= lookup in templates

Message ID ad246b80-8a99-5290-9dd5-d383ee895030@acm.org
State New
Headers show
Series [PR,c++/87531] operator= lookup in templates | expand

Commit Message

Nathan Sidwell Nov. 28, 2018, 1:28 p.m. UTC
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
diff mbox series

Patch

2018-11-28  Nathan Sidwell  <nathan@acm.org>

	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;
+}