public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] Fix PR c++/21802 (two-stage name lookup fails for operators)
@ 2015-12-10 21:43 Patrick Palka
  2015-12-11 15:19 ` Patrick Palka
  0 siblings, 1 reply; 15+ messages in thread
From: Patrick Palka @ 2015-12-10 21:43 UTC (permalink / raw)
  To: gcc-patches; +Cc: jason, Patrick Palka

This patch fixes name-lookup of operators in template definitions whose
operands are non-dependent expressions, i.e. PR c++/21802 (and
incidentally 53223).

The approach that this patch takes is to detect when build_new_op()
returns a call to an overloaded function and to store a call to this
overload intothe template AST instead of storing the raw operator
(an operator would be erroneously subject to overload resolution during
instantiation).

The new function build_min_non_dep_op_overload is the workhorse of the
patch.  It reconstructs the CALL_EXPR that would have been built had an
explicit operator+, operator* etc call been used, i.e. had the overload
gone through finish_call_expr() / build_new_method_call() instead of
through build_new_op().  The parameter OVERLOAD of this new function is
probably not strictly necessary -- one can probably just look at the
CALL_EXPR_FN of the parameter NON_DEP to figure out the overload to use
-- but since the requisite plumbing from build_new_op() already existed
to conveniently get at the overload information I thought I might as
well use it.

I have also created a test case that hopefully exercises all the changes
that were made and to verify that these operator calls are being built
correctly.

Does this approach seem adequate?  Bootstrap and regtesting in progress
on x86_64, OK to commit if testing succeeds?

gcc/cp/ChangeLog:

	PR c++/21802
	PR c++/53223
	* cp-tree.h (build_min_non_dep_op_overload): Declare.
	* tree.c (build_min_non_dep_op_overload): Define.
	* typeck.c (build_x_indirect_ref): Use
	build_min_non_dep_op_overload when the given expression
	has been resolved to an operator overload.
	(build_x_binary_op): Likewise.
	(build_x_array_ref): Likewise.
	(build_x_unary_op): Likewise.
	(build_x_compound_expr): Likewise.
	(build_x_modify_expr): Likewise.

gcc/testsuite/ChangeLog:

	PR c++/21802
	PR c++/53223
	* g++.dg/cpp0x/pr53223.C: New test.
	* g++.dg/lookup/pr21802.C: New test.
	* g++.dg/lookup/two-stage4.C: Remove XFAIL.
---
 gcc/cp/cp-tree.h                         |   1 +
 gcc/cp/tree.c                            |  64 ++++++++
 gcc/cp/typeck.c                          | 100 +++++++++---
 gcc/testsuite/g++.dg/cpp0x/pr53223.C     |  35 ++++
 gcc/testsuite/g++.dg/lookup/pr21802.C    | 271 +++++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/lookup/two-stage4.C |   2 +-
 6 files changed, 453 insertions(+), 20 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/pr53223.C
 create mode 100644 gcc/testsuite/g++.dg/lookup/pr21802.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 6190f4e..3487d77 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6513,6 +6513,7 @@ extern tree build_min				(enum tree_code, tree, ...);
 extern tree build_min_nt_loc			(location_t, enum tree_code,
 						 ...);
 extern tree build_min_non_dep			(enum tree_code, tree, ...);
+extern tree build_min_non_dep_op_overload	(enum tree_code, tree, tree, ...);
 extern tree build_min_non_dep_call_vec		(tree, tree, vec<tree, va_gc> *);
 extern tree build_cplus_new			(tree, tree, tsubst_flags_t);
 extern tree build_aggr_init_expr		(tree, tree);
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 5dad0a7..2635736 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -2744,6 +2744,70 @@ build_min_non_dep_call_vec (tree non_dep, tree fn, vec<tree, va_gc> *argvec)
   return convert_from_reference (t);
 }
 
+/* Similar to build_min_non_dep, but for expressions that have been resolved to
+   a call to an operator overload.  OP is the operator that has been
+   overloaded.  NON_DEP is the non-dependent expression that's been built,
+   which should be a CALL_EXPR or an INDIRECT_REF to a CALL_EXPR.  OVERLOAD is
+   the overload that NON_DEP is calling.  */
+
+tree
+build_min_non_dep_op_overload (enum tree_code op,
+			       tree non_dep,
+			       tree overload, ...)
+{
+  va_list p;
+  int nargs;
+  tree fn, call;
+  vec<tree, va_gc> *args;
+
+  if (REFERENCE_REF_P (non_dep))
+    non_dep = TREE_OPERAND (non_dep, 0);
+
+  nargs = call_expr_nargs (non_dep);
+
+  if (op == PREINCREMENT_EXPR
+      || op == PREDECREMENT_EXPR)
+    gcc_assert (nargs == 1);
+  else if (op == MODOP_EXPR)
+    gcc_assert (nargs == 2);
+  else
+    gcc_assert (nargs == TREE_CODE_LENGTH (op));
+
+  args = make_tree_vector ();
+  va_start (p, overload);
+
+  if (TREE_CODE (TREE_TYPE (overload)) == FUNCTION_TYPE)
+    {
+      fn = overload;
+      for (int i = 0; i < nargs; i++)
+	{
+	  tree arg = va_arg (p, tree);
+	  vec_safe_push (args, arg);
+	}
+    }
+  else if (TREE_CODE (TREE_TYPE (overload)) == METHOD_TYPE)
+    {
+      tree object = va_arg (p, tree);
+      tree binfo = TYPE_BINFO (TREE_TYPE (object));
+      tree method = build_baselink (binfo, binfo, overload, NULL_TREE);
+      fn = build_min (COMPONENT_REF, TREE_TYPE (overload),
+		      object, method, NULL_TREE);
+      for (int i = 1; i < nargs; i++)
+	{
+	  tree arg = va_arg (p, tree);
+	  vec_safe_push (args, arg);
+	}
+    }
+  else
+   gcc_unreachable ();
+
+  va_end (p);
+  call = build_min_non_dep_call_vec (non_dep, fn, args);
+  release_tree_vector (args);
+
+  return call;
+}
+
 tree
 get_type_decl (tree t)
 {
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 17671ee..2e5e46e 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -2905,6 +2905,7 @@ build_x_indirect_ref (location_t loc, tree expr, ref_operator errorstring,
 {
   tree orig_expr = expr;
   tree rval;
+  tree overload = NULL_TREE;
 
   if (processing_template_decl)
     {
@@ -2917,12 +2918,18 @@ build_x_indirect_ref (location_t loc, tree expr, ref_operator errorstring,
     }
 
   rval = build_new_op (loc, INDIRECT_REF, LOOKUP_NORMAL, expr,
-		       NULL_TREE, NULL_TREE, /*overload=*/NULL, complain);
+		       NULL_TREE, NULL_TREE, &overload, complain);
   if (!rval)
     rval = cp_build_indirect_ref (expr, errorstring, complain);
 
   if (processing_template_decl && rval != error_mark_node)
-    return build_min_non_dep (INDIRECT_REF, rval, orig_expr);
+    {
+      if (overload != NULL_TREE)
+	return (build_min_non_dep_op_overload
+		(INDIRECT_REF, rval, overload, orig_expr));
+
+      return build_min_non_dep (INDIRECT_REF, rval, orig_expr);
+    }
   else
     return rval;
 }
@@ -3814,12 +3821,13 @@ convert_arguments (tree typelist, vec<tree, va_gc> **values, tree fndecl,
 tree
 build_x_binary_op (location_t loc, enum tree_code code, tree arg1,
 		   enum tree_code arg1_code, tree arg2,
-		   enum tree_code arg2_code, tree *overload,
+		   enum tree_code arg2_code, tree *overload_p,
 		   tsubst_flags_t complain)
 {
   tree orig_arg1;
   tree orig_arg2;
   tree expr;
+  tree overload = NULL_TREE;
 
   orig_arg1 = arg1;
   orig_arg2 = arg2;
@@ -3837,7 +3845,10 @@ build_x_binary_op (location_t loc, enum tree_code code, tree arg1,
     expr = build_m_component_ref (arg1, arg2, complain);
   else
     expr = build_new_op (loc, code, LOOKUP_NORMAL, arg1, arg2, NULL_TREE,
-			 overload, complain);
+			 &overload, complain);
+
+  if (overload_p != NULL)
+    *overload_p = overload;
 
   /* Check for cases such as x+y<<z which users are likely to
      misinterpret.  But don't warn about obj << x + y, since that is a
@@ -3853,7 +3864,13 @@ build_x_binary_op (location_t loc, enum tree_code code, tree arg1,
 			    arg2_code, orig_arg2);
 
   if (processing_template_decl && expr != error_mark_node)
-    return build_min_non_dep (code, expr, orig_arg1, orig_arg2);
+    {
+      if (overload != NULL_TREE)
+	return (build_min_non_dep_op_overload
+		(code, expr, overload, orig_arg1, orig_arg2));
+
+      return build_min_non_dep (code, expr, orig_arg1, orig_arg2);
+    }
 
   return expr;
 }
@@ -3867,6 +3884,7 @@ build_x_array_ref (location_t loc, tree arg1, tree arg2,
   tree orig_arg1 = arg1;
   tree orig_arg2 = arg2;
   tree expr;
+  tree overload = NULL_TREE;
 
   if (processing_template_decl)
     {
@@ -3879,11 +3897,17 @@ build_x_array_ref (location_t loc, tree arg1, tree arg2,
     }
 
   expr = build_new_op (loc, ARRAY_REF, LOOKUP_NORMAL, arg1, arg2,
-		       NULL_TREE, /*overload=*/NULL, complain);
+		       NULL_TREE, &overload, complain);
 
   if (processing_template_decl && expr != error_mark_node)
-    return build_min_non_dep (ARRAY_REF, expr, orig_arg1, orig_arg2,
-			      NULL_TREE, NULL_TREE);
+    {
+      if (overload != NULL_TREE)
+	return (build_min_non_dep_op_overload
+		(ARRAY_REF, expr, overload, orig_arg1, orig_arg2));
+
+      return build_min_non_dep (ARRAY_REF, expr, orig_arg1, orig_arg2,
+				NULL_TREE, NULL_TREE);
+    }
   return expr;
 }
 
@@ -5278,6 +5302,7 @@ build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg,
   tree orig_expr = xarg;
   tree exp;
   int ptrmem = 0;
+  tree overload = NULL_TREE;
 
   if (processing_template_decl)
     {
@@ -5305,7 +5330,8 @@ build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg,
     /* Don't look for a function.  */;
   else
     exp = build_new_op (loc, code, LOOKUP_NORMAL, xarg, NULL_TREE,
-			NULL_TREE, /*overload=*/NULL, complain);
+			NULL_TREE, &overload, complain);
+
   if (!exp && code == ADDR_EXPR)
     {
       if (is_overloaded_fn (xarg))
@@ -5371,8 +5397,14 @@ build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg,
     }
 
   if (processing_template_decl && exp != error_mark_node)
-    exp = build_min_non_dep (code, exp, orig_expr,
-			     /*For {PRE,POST}{INC,DEC}REMENT_EXPR*/NULL_TREE);
+    {
+      if (overload != NULL_TREE)
+	return (build_min_non_dep_op_overload
+		(code, exp, overload, orig_expr, integer_zero_node));
+
+      exp = build_min_non_dep (code, exp, orig_expr,
+			       /*For {PRE,POST}{INC,DEC}REMENT_EXPR*/NULL_TREE);
+    }
   if (TREE_CODE (exp) == ADDR_EXPR)
     PTRMEM_OK_P (exp) = ptrmem;
   return exp;
@@ -6335,6 +6367,7 @@ build_x_compound_expr (location_t loc, tree op1, tree op2,
   tree result;
   tree orig_op1 = op1;
   tree orig_op2 = op2;
+  tree overload = NULL_TREE;
 
   if (processing_template_decl)
     {
@@ -6346,12 +6379,18 @@ build_x_compound_expr (location_t loc, tree op1, tree op2,
     }
 
   result = build_new_op (loc, COMPOUND_EXPR, LOOKUP_NORMAL, op1, op2,
-			 NULL_TREE, /*overload=*/NULL, complain);
+			 NULL_TREE, &overload, complain);
   if (!result)
     result = cp_build_compound_expr (op1, op2, complain);
 
   if (processing_template_decl && result != error_mark_node)
-    return build_min_non_dep (COMPOUND_EXPR, result, orig_op1, orig_op2);
+    {
+      if (overload != NULL_TREE)
+	return (build_min_non_dep_op_overload
+		(COMPOUND_EXPR, result, overload, orig_op1, orig_op2));
+
+      return build_min_non_dep (COMPOUND_EXPR, result, orig_op1, orig_op2);
+    }
 
   return result;
 }
@@ -7794,19 +7833,42 @@ cp_expr
 build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
 		     tree rhs, tsubst_flags_t complain)
 {
+  tree orig_lhs = lhs;
+  tree orig_rhs = rhs;
+  tree overload = NULL_TREE;
+  tree op = build_nt (modifycode, NULL_TREE, NULL_TREE);
+
   if (processing_template_decl)
-    return build_min_nt_loc (loc, MODOP_EXPR, lhs,
-			     build_min_nt_loc (loc, modifycode, NULL_TREE,
-					       NULL_TREE), rhs);
+    {
+      if (modifycode == NOP_EXPR
+	  || type_dependent_expression_p (lhs)
+	  || type_dependent_expression_p (rhs))
+        return build_min_nt_loc (loc, MODOP_EXPR, lhs,
+				 build_min_nt_loc (loc, modifycode, NULL_TREE,
+						   NULL_TREE), rhs);
+
+      lhs = build_non_dependent_expr (lhs);
+      rhs = build_non_dependent_expr (rhs);
+    }
 
   if (modifycode != NOP_EXPR)
     {
-      tree rval = build_new_op (loc, MODIFY_EXPR, LOOKUP_NORMAL, lhs, rhs,
-				make_node (modifycode), /*overload=*/NULL,
-				complain);
+      tree rval = build_new_op (loc, MODIFY_EXPR, LOOKUP_NORMAL,
+				lhs, rhs, op, &overload, complain);
       if (rval)
 	{
+	  if (rval == error_mark_node)
+	    return rval;
 	  TREE_NO_WARNING (rval) = 1;
+	  if (processing_template_decl)
+	    {
+	      if (overload != NULL_TREE)
+		return (build_min_non_dep_op_overload
+			(MODOP_EXPR, rval, overload, orig_lhs, orig_rhs));
+
+	      return (build_min_non_dep
+		      (MODOP_EXPR, rval, orig_lhs, op, orig_rhs));
+	    }
 	  return rval;
 	}
     }
diff --git a/gcc/testsuite/g++.dg/cpp0x/pr53223.C b/gcc/testsuite/g++.dg/cpp0x/pr53223.C
new file mode 100644
index 0000000..4ca2da1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/pr53223.C
@@ -0,0 +1,35 @@
+// PR c++/53223
+// { dg-do compile { target c++11 } }
+
+struct A
+{
+  int good() const;
+  int operator *() const;
+  int operator ++() const;
+};
+
+int operator-- (const A&);
+
+template<typename T>
+void func(T t)
+{
+  A x;
+  auto &&g1 = x.good();
+  auto &&g2 = x.operator*();
+  auto &&error1 = *x; // { dg-bogus "invalid initialization" }
+  auto &&error2 = ++x; // { dg-bogus "invalid initialization" }
+  auto &&error3 = --x; // { dg-bogus "invalid initialization" }
+}
+
+void func2(int)
+{
+  A x;
+  auto &&g = *x;
+}
+
+int main()
+{
+  func(0);
+  func2(0);
+}
+
diff --git a/gcc/testsuite/g++.dg/lookup/pr21802.C b/gcc/testsuite/g++.dg/lookup/pr21802.C
new file mode 100644
index 0000000..4909bad
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/pr21802.C
@@ -0,0 +1,271 @@
+// PR c++/21802
+// { dg-do run }
+#include <cassert>
+
+struct X;
+int I = 6;
+
+/* A mostly exhaustive and ad-hoc assortment of operator overloads and calls
+   thereof, to stress-test two-stage name lookup of operators inside template
+   definitions and then to verify that the calls get built correctly.  */
+
+inline int operator+(const X &, int x) { return x; }
+inline int operator-(const X &, int x) { return x; }
+inline int operator*(const X &, int x) { return x; }
+inline int operator/(const X &, int x) { return x; }
+inline int operator+=(const X &, int x) { return x; }
+
+struct X
+{
+  X () : m (1) { }
+  int operator%(int x) { return m + x; }
+  virtual int operator>>(int x) { return m + x; }
+  int operator<<(int x) { return m + x; }
+  int operator&(int x) { return m + x; }
+  int operator|(int x) { return m + x; }
+  int operator^(int x) { return m + x; }
+  int operator&&(int x) { return m + x; }
+  int operator||(int x) { return m + x; }
+  int operator==(int x) { return m + x; }
+  int operator!=(int x) { return m + x; }
+  int operator<(int x) { return m + x; }
+  int operator<=(int x) { return m + x; }
+  int operator>(int x) { return m + x; }
+  int operator>=(int x) { return m + x; }
+  int operator*() { return m + I; }
+  int operator!() { return m + I; }
+  int operator~() { return m + I; }
+  int operator++() { return m + I + 100; }
+  int operator--() { return m + I + 100; }
+  int operator++(int) { return m + I; }
+  int operator--(int) { return m + I; }
+  int operator()() { return m + I; }
+  int operator,(int x) { return m + x; }
+  int operator[](int x) { return m + x; }
+  int operator*=(int x) { return m + x; }
+  int operator-=(int x) { return m + x; }
+  int operator/=(int x) { return m + x; }
+  virtual int operator& () { return m + I; }
+  int m;
+};
+struct Y : virtual X
+{
+  /* Virtual override.  */
+  int operator>>(int x) { return m + x + 1; }
+  int operator& () { return m + I + 1; }
+
+  /* Not virtual.  */
+  int operator&(int x) { return m + x + 1; }
+};
+
+/* The folloiwng "FooN" functions each contain a different way to call and to
+   resolve these operator overloads.  */
+
+template <typename T>
+void
+Foo1 (T)
+{
+  Y x;
+  { int t = x + I; assert (t == 6); }
+  { int t = x - I; assert (t == 6); }
+  { int t = x * I; assert (t == 6); }
+  { int t = x / I; assert (t == 6); }
+  { int t = (x+=I); assert (t == 6); }
+
+  { int t = x % I; assert (t == 7); }
+  { int t = x << I; assert (t == 7); }
+  { int t = x | I; assert (t == 7); }
+  { int t = x && I; assert (t == 7); }
+  { int t = x || I; assert (t == 7); }
+  { int t = x == I; assert (t == 7); }
+  { int t = x != I; assert (t == 7); }
+  { int t = x < I; assert (t == 7); }
+  { int t = x <= I; assert (t == 7); }
+  { int t = x > I; assert (t == 7); }
+  { int t = x >= I; assert (t == 7); }
+  { int t = *x; assert (t == 7); }
+  { int t = !x; assert (t == 7); }
+  { int t = ~x; assert (t == 7); }
+  { int t = x++; assert (t == 7); }
+  { int t = x--; assert (t == 7); }
+  { int t = ++x; assert (t == 107); }
+  { int t = --x; assert (t == 107); }
+  { int t = x (); assert (t == 7); }
+  { int t = (x, I); assert (t == 7); }
+  { int t = x[I]; assert (t == 7); }
+  { int t = (x-=I); assert (t == 7); }
+  { int t = (x/=I); assert (t == 7); }
+  { int t = (x*=I); assert (t == 7); }
+
+  { int t = x >> I; assert (t == 8); }
+  { int t = x & I; assert (t == 8); }
+  { int t = &x; assert (t == 8); }
+}
+
+template <typename T>
+void
+Foo2 (T)
+{
+  X x;
+  { int t = x + I; assert (t == 6); }
+  { int t = x - I; assert (t == 6); }
+  { int t = x * I; assert (t == 6); }
+  { int t = x / I; assert (t == 6); }
+  { int t = (x+=I); assert (t == 6); }
+
+  { int t = x % I; assert (t == 7); }
+  { int t = x >> I; assert (t == 7); }
+  { int t = x << I; assert (t == 7); }
+  { int t = x | I; assert (t == 7); }
+  { int t = x && I; assert (t == 7); }
+  { int t = x || I; assert (t == 7); }
+  { int t = x == I; assert (t == 7); }
+  { int t = x != I; assert (t == 7); }
+  { int t = x < I; assert (t == 7); }
+  { int t = x <= I; assert (t == 7); }
+  { int t = x > I; assert (t == 7); }
+  { int t = x >= I; assert (t == 7); }
+  { int t = *x; assert (t == 7); }
+  { int t = !x; assert (t == 7); }
+  { int t = ~x; assert (t == 7); }
+  { int t = x++; assert (t == 7); }
+  { int t = x--; assert (t == 7); }
+  { int t = ++x; assert (t == 107); }
+  { int t = --x; assert (t == 107); }
+  { int t = x (); assert (t == 7); }
+  { int t = (x, I); assert (t == 7); }
+  { int t = x[I]; assert (t == 7); }
+  { int t = &x; assert (t == 7); }
+  { int t = (x-=I); assert (t == 7); }
+  { int t = (x/=I); assert (t == 7); }
+  { int t = (x*=I); assert (t == 7); }
+  { int t = x & I; assert (t == 7); }
+}
+
+template <typename T>
+void
+Foo3 (T)
+{
+  Y o;
+  X &x = o;
+  { int t = x + I; assert (t == 6); }
+  { int t = x - I; assert (t == 6); }
+  { int t = x * I; assert (t == 6); }
+  { int t = x / I; assert (t == 6); }
+  { int t = (x+=I); assert (t == 6); }
+
+  { int t = x % I; assert (t == 7); }
+  { int t = x << I; assert (t == 7); }
+  { int t = x | I; assert (t == 7); }
+  { int t = x && I; assert (t == 7); }
+  { int t = x || I; assert (t == 7); }
+  { int t = x == I; assert (t == 7); }
+  { int t = x != I; assert (t == 7); }
+  { int t = x < I; assert (t == 7); }
+  { int t = x <= I; assert (t == 7); }
+  { int t = x > I; assert (t == 7); }
+  { int t = x >= I; assert (t == 7); }
+  { int t = *x; assert (t == 7); }
+  { int t = !x; assert (t == 7); }
+  { int t = ~x; assert (t == 7); }
+  { int t = x++; assert (t == 7); }
+  { int t = x--; assert (t == 7); }
+  { int t = ++x; assert (t == 107); }
+  { int t = --x; assert (t == 107); }
+  { int t = x (); assert (t == 7); }
+  { int t = (x, I); assert (t == 7); }
+  { int t = x[I]; assert (t == 7); }
+  { int t = (x-=I); assert (t == 7); }
+  { int t = (x/=I); assert (t == 7); }
+  { int t = (x*=I); assert (t == 7); }
+
+  { int t = x & I; assert (t == 7); }
+  { int t = x >> I; assert (t == 8); }
+  { int t = &x; assert (t == 8); }
+}
+
+template <typename T>
+void
+Foo4 (T)
+{
+  Y x;
+  { int t = operator+ (x, I); assert (t == 6); }
+  { int t = operator- (x, I); assert (t == 6); }
+  { int t = operator* (x, I); assert (t == 6); }
+  { int t = operator/ (x, I); assert (t == 6); }
+  { int t = operator+= (x, I); assert (t == 6); }
+
+  { int t = x.operator% (I); assert (t == 7); }
+  { int t = x.operator<< (I); assert (t == 7); }
+  { int t = x.operator| (I); assert (t == 7); }
+  { int t = x.operator&& (I); assert (t == 7); }
+  { int t = x.operator|| (I); assert (t == 7); }
+  { int t = x.operator== (I); assert (t == 7); }
+  { int t = x.operator!= (I); assert (t == 7); }
+  { int t = x.operator< (I); assert (t == 7); }
+  { int t = x.operator<= (I); assert (t == 7); }
+  { int t = x.operator> (I); assert (t == 7); }
+  { int t = x.operator>= (I); assert (t == 7); }
+  { int t = x.operator* (); assert (t == 7); }
+  { int t = x.operator! (); assert (t == 7); }
+  { int t = x.operator~ (); assert (t == 7); }
+  { int t = x.operator++ (0); assert (t == 7); }
+  { int t = x.operator-- (0); assert (t == 7); }
+  { int t = x.operator++ (); assert (t == 107); }
+  { int t = x.operator-- (); assert (t == 107); }
+  { int t = x.operator() (); assert (t == 7); }
+  { int t = x.operator, (I); assert (t == 7); }
+  { int t = x.operator[] (I); assert (t == 7); }
+  { int t = x.operator-= (I); assert (t == 7); }
+  { int t = x.operator/= (I); assert (t == 7); }
+  { int t = x.operator*= (I); assert (t == 7); }
+
+  { int t = x.operator>> (I); assert (t == 8); }
+  { int t = x.operator& (); assert (t == 8); }
+  { int t = x.operator& (I); assert (t == 8); }
+}
+
+
+/* These definitions should be irrelevant to operator lookup of non-dependent
+   expressions inside the above templates since they are not in scope at
+   template-definition time (but are in scope at instantiation time).  */
+inline int operator+(const Y&, int) { return 11; }
+inline int operator-(const Y&, int) { return 11; }
+inline int operator*(const Y&, int) { return 11; }
+inline int operator/(const Y&, int) { return 11; }
+inline int operator%(const Y&, int) { return 11; }
+inline int operator>>(const Y&, int) { return 11; }
+inline int operator<<(const Y&, int) { return 11; }
+inline int operator&(const Y&, int) { return 11; }
+inline int operator|(const Y&, int) { return 11; }
+inline int operator^(const Y&, int) { return 11; }
+inline int operator&&(const Y&, int) { return 11; }
+inline int operator||(const Y&, int) { return 11; }
+inline int operator==(const Y&, int) { return 11; }
+inline int operator!=(const Y&, int) { return 11; }
+inline int operator<(const Y&, int) { return 11; }
+inline int operator<=(const Y&, int) { return 11; }
+inline int operator>(const Y&, int) { return 11; }
+inline int operator>=(const Y&, int) { return 11; }
+inline int operator*(const Y&) { return 11; }
+inline int operator!(const Y&) { return 11; }
+inline int operator~(const Y&) { return 11; }
+inline int operator++(const Y&) { return 11; }
+inline int operator--(const Y&) { return 11; }
+inline int operator++(const Y&, int) { return 11; }
+inline int operator--(const Y&, int) { return 11; }
+inline int operator,(const Y&, int) { return 11; }
+inline int operator&(const Y&) { return 11; }
+inline int operator+=(const Y&, int x) { return 11; }
+inline int operator*=(const Y&, int x) { return 11; }
+inline int operator-=(const Y&, int x) { return 11; }
+inline int operator/=(const Y&, int x) { return 11; }
+
+int
+main ()
+{
+  Foo1 (0);
+  Foo2 (0);
+  Foo3 (0);
+  Foo4 (0);
+}
diff --git a/gcc/testsuite/g++.dg/lookup/two-stage4.C b/gcc/testsuite/g++.dg/lookup/two-stage4.C
index 7d97109..a89e618 100644
--- a/gcc/testsuite/g++.dg/lookup/two-stage4.C
+++ b/gcc/testsuite/g++.dg/lookup/two-stage4.C
@@ -8,7 +8,7 @@ template<typename T> bool operator==(wrap<T>, wrap<T>);
 template<typename T>
 void g(T, wrap<wrap<int> > x)
 {
-  bool b = x == x; // { dg-bogus "" "" { xfail *-*-* } }
+  bool b = x == x; // { dg-bogus "" "" }
 }
 
 template<typename T> void operator==(wrap<wrap<T> >, wrap<wrap<T> >);
-- 
2.6.4.491.gda30757.dirty

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] Fix PR c++/21802 (two-stage name lookup fails for operators)
  2015-12-10 21:43 [PATCH] Fix PR c++/21802 (two-stage name lookup fails for operators) Patrick Palka
@ 2015-12-11 15:19 ` Patrick Palka
  2015-12-11 19:43   ` Patrick Palka
  0 siblings, 1 reply; 15+ messages in thread
From: Patrick Palka @ 2015-12-11 15:19 UTC (permalink / raw)
  To: GCC Patches; +Cc: Jason Merrill, Patrick Palka

On Thu, Dec 10, 2015 at 4:43 PM, Patrick Palka <patrick@parcs.ath.cx> wrote:
> This patch fixes name-lookup of operators in template definitions whose
> operands are non-dependent expressions, i.e. PR c++/21802 (and
> incidentally 53223).
>
> The approach that this patch takes is to detect when build_new_op()
> returns a call to an overloaded function and to store a call to this
> overload intothe template AST instead of storing the raw operator
> (an operator would be erroneously subject to overload resolution during
> instantiation).
>
> The new function build_min_non_dep_op_overload is the workhorse of the
> patch.  It reconstructs the CALL_EXPR that would have been built had an
> explicit operator+, operator* etc call been used, i.e. had the overload
> gone through finish_call_expr() / build_new_method_call() instead of
> through build_new_op().  The parameter OVERLOAD of this new function is
> probably not strictly necessary -- one can probably just look at the
> CALL_EXPR_FN of the parameter NON_DEP to figure out the overload to use
> -- but since the requisite plumbing from build_new_op() already existed
> to conveniently get at the overload information I thought I might as
> well use it.
>
> I have also created a test case that hopefully exercises all the changes
> that were made and to verify that these operator calls are being built
> correctly.
>
> Does this approach seem adequate?  Bootstrap and regtesting in progress
> on x86_64, OK to commit if testing succeeds?

Unfortunately this patch doesn't work properly on operator overloads
that are defined as friend functions.  E.g. the following now fails to
compile:

struct A
{
  friend int operator* (A);
};

template <typename T>
void func(T t)
{
  A x;
  int y = *x;
}

int main()
{
  func(0);
}

I think this happens because KOENIG_LOOKUP_P is not being properly set
in in the CALL_EXPR we are reconstructing.  Not sure how to fix that
yet.


>
> gcc/cp/ChangeLog:
>
>         PR c++/21802
>         PR c++/53223
>         * cp-tree.h (build_min_non_dep_op_overload): Declare.
>         * tree.c (build_min_non_dep_op_overload): Define.
>         * typeck.c (build_x_indirect_ref): Use
>         build_min_non_dep_op_overload when the given expression
>         has been resolved to an operator overload.
>         (build_x_binary_op): Likewise.
>         (build_x_array_ref): Likewise.
>         (build_x_unary_op): Likewise.
>         (build_x_compound_expr): Likewise.
>         (build_x_modify_expr): Likewise.
>
> gcc/testsuite/ChangeLog:
>
>         PR c++/21802
>         PR c++/53223
>         * g++.dg/cpp0x/pr53223.C: New test.
>         * g++.dg/lookup/pr21802.C: New test.
>         * g++.dg/lookup/two-stage4.C: Remove XFAIL.
> ---
>  gcc/cp/cp-tree.h                         |   1 +
>  gcc/cp/tree.c                            |  64 ++++++++
>  gcc/cp/typeck.c                          | 100 +++++++++---
>  gcc/testsuite/g++.dg/cpp0x/pr53223.C     |  35 ++++
>  gcc/testsuite/g++.dg/lookup/pr21802.C    | 271 +++++++++++++++++++++++++++++++
>  gcc/testsuite/g++.dg/lookup/two-stage4.C |   2 +-
>  6 files changed, 453 insertions(+), 20 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/cpp0x/pr53223.C
>  create mode 100644 gcc/testsuite/g++.dg/lookup/pr21802.C
>
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 6190f4e..3487d77 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -6513,6 +6513,7 @@ extern tree build_min                             (enum tree_code, tree, ...);
>  extern tree build_min_nt_loc                   (location_t, enum tree_code,
>                                                  ...);
>  extern tree build_min_non_dep                  (enum tree_code, tree, ...);
> +extern tree build_min_non_dep_op_overload      (enum tree_code, tree, tree, ...);
>  extern tree build_min_non_dep_call_vec         (tree, tree, vec<tree, va_gc> *);
>  extern tree build_cplus_new                    (tree, tree, tsubst_flags_t);
>  extern tree build_aggr_init_expr               (tree, tree);
> diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
> index 5dad0a7..2635736 100644
> --- a/gcc/cp/tree.c
> +++ b/gcc/cp/tree.c
> @@ -2744,6 +2744,70 @@ build_min_non_dep_call_vec (tree non_dep, tree fn, vec<tree, va_gc> *argvec)
>    return convert_from_reference (t);
>  }
>
> +/* Similar to build_min_non_dep, but for expressions that have been resolved to
> +   a call to an operator overload.  OP is the operator that has been
> +   overloaded.  NON_DEP is the non-dependent expression that's been built,
> +   which should be a CALL_EXPR or an INDIRECT_REF to a CALL_EXPR.  OVERLOAD is
> +   the overload that NON_DEP is calling.  */
> +
> +tree
> +build_min_non_dep_op_overload (enum tree_code op,
> +                              tree non_dep,
> +                              tree overload, ...)
> +{
> +  va_list p;
> +  int nargs;
> +  tree fn, call;
> +  vec<tree, va_gc> *args;
> +
> +  if (REFERENCE_REF_P (non_dep))
> +    non_dep = TREE_OPERAND (non_dep, 0);
> +
> +  nargs = call_expr_nargs (non_dep);
> +
> +  if (op == PREINCREMENT_EXPR
> +      || op == PREDECREMENT_EXPR)
> +    gcc_assert (nargs == 1);
> +  else if (op == MODOP_EXPR)
> +    gcc_assert (nargs == 2);
> +  else
> +    gcc_assert (nargs == TREE_CODE_LENGTH (op));
> +
> +  args = make_tree_vector ();
> +  va_start (p, overload);
> +
> +  if (TREE_CODE (TREE_TYPE (overload)) == FUNCTION_TYPE)
> +    {
> +      fn = overload;
> +      for (int i = 0; i < nargs; i++)
> +       {
> +         tree arg = va_arg (p, tree);
> +         vec_safe_push (args, arg);
> +       }
> +    }
> +  else if (TREE_CODE (TREE_TYPE (overload)) == METHOD_TYPE)
> +    {
> +      tree object = va_arg (p, tree);
> +      tree binfo = TYPE_BINFO (TREE_TYPE (object));
> +      tree method = build_baselink (binfo, binfo, overload, NULL_TREE);
> +      fn = build_min (COMPONENT_REF, TREE_TYPE (overload),
> +                     object, method, NULL_TREE);
> +      for (int i = 1; i < nargs; i++)
> +       {
> +         tree arg = va_arg (p, tree);
> +         vec_safe_push (args, arg);
> +       }
> +    }
> +  else
> +   gcc_unreachable ();
> +
> +  va_end (p);
> +  call = build_min_non_dep_call_vec (non_dep, fn, args);
> +  release_tree_vector (args);
> +
> +  return call;
> +}
> +
>  tree
>  get_type_decl (tree t)
>  {
> diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
> index 17671ee..2e5e46e 100644
> --- a/gcc/cp/typeck.c
> +++ b/gcc/cp/typeck.c
> @@ -2905,6 +2905,7 @@ build_x_indirect_ref (location_t loc, tree expr, ref_operator errorstring,
>  {
>    tree orig_expr = expr;
>    tree rval;
> +  tree overload = NULL_TREE;
>
>    if (processing_template_decl)
>      {
> @@ -2917,12 +2918,18 @@ build_x_indirect_ref (location_t loc, tree expr, ref_operator errorstring,
>      }
>
>    rval = build_new_op (loc, INDIRECT_REF, LOOKUP_NORMAL, expr,
> -                      NULL_TREE, NULL_TREE, /*overload=*/NULL, complain);
> +                      NULL_TREE, NULL_TREE, &overload, complain);
>    if (!rval)
>      rval = cp_build_indirect_ref (expr, errorstring, complain);
>
>    if (processing_template_decl && rval != error_mark_node)
> -    return build_min_non_dep (INDIRECT_REF, rval, orig_expr);
> +    {
> +      if (overload != NULL_TREE)
> +       return (build_min_non_dep_op_overload
> +               (INDIRECT_REF, rval, overload, orig_expr));
> +
> +      return build_min_non_dep (INDIRECT_REF, rval, orig_expr);
> +    }
>    else
>      return rval;
>  }
> @@ -3814,12 +3821,13 @@ convert_arguments (tree typelist, vec<tree, va_gc> **values, tree fndecl,
>  tree
>  build_x_binary_op (location_t loc, enum tree_code code, tree arg1,
>                    enum tree_code arg1_code, tree arg2,
> -                  enum tree_code arg2_code, tree *overload,
> +                  enum tree_code arg2_code, tree *overload_p,
>                    tsubst_flags_t complain)
>  {
>    tree orig_arg1;
>    tree orig_arg2;
>    tree expr;
> +  tree overload = NULL_TREE;
>
>    orig_arg1 = arg1;
>    orig_arg2 = arg2;
> @@ -3837,7 +3845,10 @@ build_x_binary_op (location_t loc, enum tree_code code, tree arg1,
>      expr = build_m_component_ref (arg1, arg2, complain);
>    else
>      expr = build_new_op (loc, code, LOOKUP_NORMAL, arg1, arg2, NULL_TREE,
> -                        overload, complain);
> +                        &overload, complain);
> +
> +  if (overload_p != NULL)
> +    *overload_p = overload;
>
>    /* Check for cases such as x+y<<z which users are likely to
>       misinterpret.  But don't warn about obj << x + y, since that is a
> @@ -3853,7 +3864,13 @@ build_x_binary_op (location_t loc, enum tree_code code, tree arg1,
>                             arg2_code, orig_arg2);
>
>    if (processing_template_decl && expr != error_mark_node)
> -    return build_min_non_dep (code, expr, orig_arg1, orig_arg2);
> +    {
> +      if (overload != NULL_TREE)
> +       return (build_min_non_dep_op_overload
> +               (code, expr, overload, orig_arg1, orig_arg2));
> +
> +      return build_min_non_dep (code, expr, orig_arg1, orig_arg2);
> +    }
>
>    return expr;
>  }
> @@ -3867,6 +3884,7 @@ build_x_array_ref (location_t loc, tree arg1, tree arg2,
>    tree orig_arg1 = arg1;
>    tree orig_arg2 = arg2;
>    tree expr;
> +  tree overload = NULL_TREE;
>
>    if (processing_template_decl)
>      {
> @@ -3879,11 +3897,17 @@ build_x_array_ref (location_t loc, tree arg1, tree arg2,
>      }
>
>    expr = build_new_op (loc, ARRAY_REF, LOOKUP_NORMAL, arg1, arg2,
> -                      NULL_TREE, /*overload=*/NULL, complain);
> +                      NULL_TREE, &overload, complain);
>
>    if (processing_template_decl && expr != error_mark_node)
> -    return build_min_non_dep (ARRAY_REF, expr, orig_arg1, orig_arg2,
> -                             NULL_TREE, NULL_TREE);
> +    {
> +      if (overload != NULL_TREE)
> +       return (build_min_non_dep_op_overload
> +               (ARRAY_REF, expr, overload, orig_arg1, orig_arg2));
> +
> +      return build_min_non_dep (ARRAY_REF, expr, orig_arg1, orig_arg2,
> +                               NULL_TREE, NULL_TREE);
> +    }
>    return expr;
>  }
>
> @@ -5278,6 +5302,7 @@ build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg,
>    tree orig_expr = xarg;
>    tree exp;
>    int ptrmem = 0;
> +  tree overload = NULL_TREE;
>
>    if (processing_template_decl)
>      {
> @@ -5305,7 +5330,8 @@ build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg,
>      /* Don't look for a function.  */;
>    else
>      exp = build_new_op (loc, code, LOOKUP_NORMAL, xarg, NULL_TREE,
> -                       NULL_TREE, /*overload=*/NULL, complain);
> +                       NULL_TREE, &overload, complain);
> +
>    if (!exp && code == ADDR_EXPR)
>      {
>        if (is_overloaded_fn (xarg))
> @@ -5371,8 +5397,14 @@ build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg,
>      }
>
>    if (processing_template_decl && exp != error_mark_node)
> -    exp = build_min_non_dep (code, exp, orig_expr,
> -                            /*For {PRE,POST}{INC,DEC}REMENT_EXPR*/NULL_TREE);
> +    {
> +      if (overload != NULL_TREE)
> +       return (build_min_non_dep_op_overload
> +               (code, exp, overload, orig_expr, integer_zero_node));
> +
> +      exp = build_min_non_dep (code, exp, orig_expr,
> +                              /*For {PRE,POST}{INC,DEC}REMENT_EXPR*/NULL_TREE);
> +    }
>    if (TREE_CODE (exp) == ADDR_EXPR)
>      PTRMEM_OK_P (exp) = ptrmem;
>    return exp;
> @@ -6335,6 +6367,7 @@ build_x_compound_expr (location_t loc, tree op1, tree op2,
>    tree result;
>    tree orig_op1 = op1;
>    tree orig_op2 = op2;
> +  tree overload = NULL_TREE;
>
>    if (processing_template_decl)
>      {
> @@ -6346,12 +6379,18 @@ build_x_compound_expr (location_t loc, tree op1, tree op2,
>      }
>
>    result = build_new_op (loc, COMPOUND_EXPR, LOOKUP_NORMAL, op1, op2,
> -                        NULL_TREE, /*overload=*/NULL, complain);
> +                        NULL_TREE, &overload, complain);
>    if (!result)
>      result = cp_build_compound_expr (op1, op2, complain);
>
>    if (processing_template_decl && result != error_mark_node)
> -    return build_min_non_dep (COMPOUND_EXPR, result, orig_op1, orig_op2);
> +    {
> +      if (overload != NULL_TREE)
> +       return (build_min_non_dep_op_overload
> +               (COMPOUND_EXPR, result, overload, orig_op1, orig_op2));
> +
> +      return build_min_non_dep (COMPOUND_EXPR, result, orig_op1, orig_op2);
> +    }
>
>    return result;
>  }
> @@ -7794,19 +7833,42 @@ cp_expr
>  build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
>                      tree rhs, tsubst_flags_t complain)
>  {
> +  tree orig_lhs = lhs;
> +  tree orig_rhs = rhs;
> +  tree overload = NULL_TREE;
> +  tree op = build_nt (modifycode, NULL_TREE, NULL_TREE);
> +
>    if (processing_template_decl)
> -    return build_min_nt_loc (loc, MODOP_EXPR, lhs,
> -                            build_min_nt_loc (loc, modifycode, NULL_TREE,
> -                                              NULL_TREE), rhs);
> +    {
> +      if (modifycode == NOP_EXPR
> +         || type_dependent_expression_p (lhs)
> +         || type_dependent_expression_p (rhs))
> +        return build_min_nt_loc (loc, MODOP_EXPR, lhs,
> +                                build_min_nt_loc (loc, modifycode, NULL_TREE,
> +                                                  NULL_TREE), rhs);
> +
> +      lhs = build_non_dependent_expr (lhs);
> +      rhs = build_non_dependent_expr (rhs);
> +    }
>
>    if (modifycode != NOP_EXPR)
>      {
> -      tree rval = build_new_op (loc, MODIFY_EXPR, LOOKUP_NORMAL, lhs, rhs,
> -                               make_node (modifycode), /*overload=*/NULL,
> -                               complain);
> +      tree rval = build_new_op (loc, MODIFY_EXPR, LOOKUP_NORMAL,
> +                               lhs, rhs, op, &overload, complain);
>        if (rval)
>         {
> +         if (rval == error_mark_node)
> +           return rval;
>           TREE_NO_WARNING (rval) = 1;
> +         if (processing_template_decl)
> +           {
> +             if (overload != NULL_TREE)
> +               return (build_min_non_dep_op_overload
> +                       (MODOP_EXPR, rval, overload, orig_lhs, orig_rhs));
> +
> +             return (build_min_non_dep
> +                     (MODOP_EXPR, rval, orig_lhs, op, orig_rhs));
> +           }
>           return rval;
>         }
>      }
> diff --git a/gcc/testsuite/g++.dg/cpp0x/pr53223.C b/gcc/testsuite/g++.dg/cpp0x/pr53223.C
> new file mode 100644
> index 0000000..4ca2da1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp0x/pr53223.C
> @@ -0,0 +1,35 @@
> +// PR c++/53223
> +// { dg-do compile { target c++11 } }
> +
> +struct A
> +{
> +  int good() const;
> +  int operator *() const;
> +  int operator ++() const;
> +};
> +
> +int operator-- (const A&);
> +
> +template<typename T>
> +void func(T t)
> +{
> +  A x;
> +  auto &&g1 = x.good();
> +  auto &&g2 = x.operator*();
> +  auto &&error1 = *x; // { dg-bogus "invalid initialization" }
> +  auto &&error2 = ++x; // { dg-bogus "invalid initialization" }
> +  auto &&error3 = --x; // { dg-bogus "invalid initialization" }
> +}
> +
> +void func2(int)
> +{
> +  A x;
> +  auto &&g = *x;
> +}
> +
> +int main()
> +{
> +  func(0);
> +  func2(0);
> +}
> +
> diff --git a/gcc/testsuite/g++.dg/lookup/pr21802.C b/gcc/testsuite/g++.dg/lookup/pr21802.C
> new file mode 100644
> index 0000000..4909bad
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/lookup/pr21802.C
> @@ -0,0 +1,271 @@
> +// PR c++/21802
> +// { dg-do run }
> +#include <cassert>
> +
> +struct X;
> +int I = 6;
> +
> +/* A mostly exhaustive and ad-hoc assortment of operator overloads and calls
> +   thereof, to stress-test two-stage name lookup of operators inside template
> +   definitions and then to verify that the calls get built correctly.  */
> +
> +inline int operator+(const X &, int x) { return x; }
> +inline int operator-(const X &, int x) { return x; }
> +inline int operator*(const X &, int x) { return x; }
> +inline int operator/(const X &, int x) { return x; }
> +inline int operator+=(const X &, int x) { return x; }
> +
> +struct X
> +{
> +  X () : m (1) { }
> +  int operator%(int x) { return m + x; }
> +  virtual int operator>>(int x) { return m + x; }
> +  int operator<<(int x) { return m + x; }
> +  int operator&(int x) { return m + x; }
> +  int operator|(int x) { return m + x; }
> +  int operator^(int x) { return m + x; }
> +  int operator&&(int x) { return m + x; }
> +  int operator||(int x) { return m + x; }
> +  int operator==(int x) { return m + x; }
> +  int operator!=(int x) { return m + x; }
> +  int operator<(int x) { return m + x; }
> +  int operator<=(int x) { return m + x; }
> +  int operator>(int x) { return m + x; }
> +  int operator>=(int x) { return m + x; }
> +  int operator*() { return m + I; }
> +  int operator!() { return m + I; }
> +  int operator~() { return m + I; }
> +  int operator++() { return m + I + 100; }
> +  int operator--() { return m + I + 100; }
> +  int operator++(int) { return m + I; }
> +  int operator--(int) { return m + I; }
> +  int operator()() { return m + I; }
> +  int operator,(int x) { return m + x; }
> +  int operator[](int x) { return m + x; }
> +  int operator*=(int x) { return m + x; }
> +  int operator-=(int x) { return m + x; }
> +  int operator/=(int x) { return m + x; }
> +  virtual int operator& () { return m + I; }
> +  int m;
> +};
> +struct Y : virtual X
> +{
> +  /* Virtual override.  */
> +  int operator>>(int x) { return m + x + 1; }
> +  int operator& () { return m + I + 1; }
> +
> +  /* Not virtual.  */
> +  int operator&(int x) { return m + x + 1; }
> +};
> +
> +/* The folloiwng "FooN" functions each contain a different way to call and to
> +   resolve these operator overloads.  */
> +
> +template <typename T>
> +void
> +Foo1 (T)
> +{
> +  Y x;
> +  { int t = x + I; assert (t == 6); }
> +  { int t = x - I; assert (t == 6); }
> +  { int t = x * I; assert (t == 6); }
> +  { int t = x / I; assert (t == 6); }
> +  { int t = (x+=I); assert (t == 6); }
> +
> +  { int t = x % I; assert (t == 7); }
> +  { int t = x << I; assert (t == 7); }
> +  { int t = x | I; assert (t == 7); }
> +  { int t = x && I; assert (t == 7); }
> +  { int t = x || I; assert (t == 7); }
> +  { int t = x == I; assert (t == 7); }
> +  { int t = x != I; assert (t == 7); }
> +  { int t = x < I; assert (t == 7); }
> +  { int t = x <= I; assert (t == 7); }
> +  { int t = x > I; assert (t == 7); }
> +  { int t = x >= I; assert (t == 7); }
> +  { int t = *x; assert (t == 7); }
> +  { int t = !x; assert (t == 7); }
> +  { int t = ~x; assert (t == 7); }
> +  { int t = x++; assert (t == 7); }
> +  { int t = x--; assert (t == 7); }
> +  { int t = ++x; assert (t == 107); }
> +  { int t = --x; assert (t == 107); }
> +  { int t = x (); assert (t == 7); }
> +  { int t = (x, I); assert (t == 7); }
> +  { int t = x[I]; assert (t == 7); }
> +  { int t = (x-=I); assert (t == 7); }
> +  { int t = (x/=I); assert (t == 7); }
> +  { int t = (x*=I); assert (t == 7); }
> +
> +  { int t = x >> I; assert (t == 8); }
> +  { int t = x & I; assert (t == 8); }
> +  { int t = &x; assert (t == 8); }
> +}
> +
> +template <typename T>
> +void
> +Foo2 (T)
> +{
> +  X x;
> +  { int t = x + I; assert (t == 6); }
> +  { int t = x - I; assert (t == 6); }
> +  { int t = x * I; assert (t == 6); }
> +  { int t = x / I; assert (t == 6); }
> +  { int t = (x+=I); assert (t == 6); }
> +
> +  { int t = x % I; assert (t == 7); }
> +  { int t = x >> I; assert (t == 7); }
> +  { int t = x << I; assert (t == 7); }
> +  { int t = x | I; assert (t == 7); }
> +  { int t = x && I; assert (t == 7); }
> +  { int t = x || I; assert (t == 7); }
> +  { int t = x == I; assert (t == 7); }
> +  { int t = x != I; assert (t == 7); }
> +  { int t = x < I; assert (t == 7); }
> +  { int t = x <= I; assert (t == 7); }
> +  { int t = x > I; assert (t == 7); }
> +  { int t = x >= I; assert (t == 7); }
> +  { int t = *x; assert (t == 7); }
> +  { int t = !x; assert (t == 7); }
> +  { int t = ~x; assert (t == 7); }
> +  { int t = x++; assert (t == 7); }
> +  { int t = x--; assert (t == 7); }
> +  { int t = ++x; assert (t == 107); }
> +  { int t = --x; assert (t == 107); }
> +  { int t = x (); assert (t == 7); }
> +  { int t = (x, I); assert (t == 7); }
> +  { int t = x[I]; assert (t == 7); }
> +  { int t = &x; assert (t == 7); }
> +  { int t = (x-=I); assert (t == 7); }
> +  { int t = (x/=I); assert (t == 7); }
> +  { int t = (x*=I); assert (t == 7); }
> +  { int t = x & I; assert (t == 7); }
> +}
> +
> +template <typename T>
> +void
> +Foo3 (T)
> +{
> +  Y o;
> +  X &x = o;
> +  { int t = x + I; assert (t == 6); }
> +  { int t = x - I; assert (t == 6); }
> +  { int t = x * I; assert (t == 6); }
> +  { int t = x / I; assert (t == 6); }
> +  { int t = (x+=I); assert (t == 6); }
> +
> +  { int t = x % I; assert (t == 7); }
> +  { int t = x << I; assert (t == 7); }
> +  { int t = x | I; assert (t == 7); }
> +  { int t = x && I; assert (t == 7); }
> +  { int t = x || I; assert (t == 7); }
> +  { int t = x == I; assert (t == 7); }
> +  { int t = x != I; assert (t == 7); }
> +  { int t = x < I; assert (t == 7); }
> +  { int t = x <= I; assert (t == 7); }
> +  { int t = x > I; assert (t == 7); }
> +  { int t = x >= I; assert (t == 7); }
> +  { int t = *x; assert (t == 7); }
> +  { int t = !x; assert (t == 7); }
> +  { int t = ~x; assert (t == 7); }
> +  { int t = x++; assert (t == 7); }
> +  { int t = x--; assert (t == 7); }
> +  { int t = ++x; assert (t == 107); }
> +  { int t = --x; assert (t == 107); }
> +  { int t = x (); assert (t == 7); }
> +  { int t = (x, I); assert (t == 7); }
> +  { int t = x[I]; assert (t == 7); }
> +  { int t = (x-=I); assert (t == 7); }
> +  { int t = (x/=I); assert (t == 7); }
> +  { int t = (x*=I); assert (t == 7); }
> +
> +  { int t = x & I; assert (t == 7); }
> +  { int t = x >> I; assert (t == 8); }
> +  { int t = &x; assert (t == 8); }
> +}
> +
> +template <typename T>
> +void
> +Foo4 (T)
> +{
> +  Y x;
> +  { int t = operator+ (x, I); assert (t == 6); }
> +  { int t = operator- (x, I); assert (t == 6); }
> +  { int t = operator* (x, I); assert (t == 6); }
> +  { int t = operator/ (x, I); assert (t == 6); }
> +  { int t = operator+= (x, I); assert (t == 6); }
> +
> +  { int t = x.operator% (I); assert (t == 7); }
> +  { int t = x.operator<< (I); assert (t == 7); }
> +  { int t = x.operator| (I); assert (t == 7); }
> +  { int t = x.operator&& (I); assert (t == 7); }
> +  { int t = x.operator|| (I); assert (t == 7); }
> +  { int t = x.operator== (I); assert (t == 7); }
> +  { int t = x.operator!= (I); assert (t == 7); }
> +  { int t = x.operator< (I); assert (t == 7); }
> +  { int t = x.operator<= (I); assert (t == 7); }
> +  { int t = x.operator> (I); assert (t == 7); }
> +  { int t = x.operator>= (I); assert (t == 7); }
> +  { int t = x.operator* (); assert (t == 7); }
> +  { int t = x.operator! (); assert (t == 7); }
> +  { int t = x.operator~ (); assert (t == 7); }
> +  { int t = x.operator++ (0); assert (t == 7); }
> +  { int t = x.operator-- (0); assert (t == 7); }
> +  { int t = x.operator++ (); assert (t == 107); }
> +  { int t = x.operator-- (); assert (t == 107); }
> +  { int t = x.operator() (); assert (t == 7); }
> +  { int t = x.operator, (I); assert (t == 7); }
> +  { int t = x.operator[] (I); assert (t == 7); }
> +  { int t = x.operator-= (I); assert (t == 7); }
> +  { int t = x.operator/= (I); assert (t == 7); }
> +  { int t = x.operator*= (I); assert (t == 7); }
> +
> +  { int t = x.operator>> (I); assert (t == 8); }
> +  { int t = x.operator& (); assert (t == 8); }
> +  { int t = x.operator& (I); assert (t == 8); }
> +}
> +
> +
> +/* These definitions should be irrelevant to operator lookup of non-dependent
> +   expressions inside the above templates since they are not in scope at
> +   template-definition time (but are in scope at instantiation time).  */
> +inline int operator+(const Y&, int) { return 11; }
> +inline int operator-(const Y&, int) { return 11; }
> +inline int operator*(const Y&, int) { return 11; }
> +inline int operator/(const Y&, int) { return 11; }
> +inline int operator%(const Y&, int) { return 11; }
> +inline int operator>>(const Y&, int) { return 11; }
> +inline int operator<<(const Y&, int) { return 11; }
> +inline int operator&(const Y&, int) { return 11; }
> +inline int operator|(const Y&, int) { return 11; }
> +inline int operator^(const Y&, int) { return 11; }
> +inline int operator&&(const Y&, int) { return 11; }
> +inline int operator||(const Y&, int) { return 11; }
> +inline int operator==(const Y&, int) { return 11; }
> +inline int operator!=(const Y&, int) { return 11; }
> +inline int operator<(const Y&, int) { return 11; }
> +inline int operator<=(const Y&, int) { return 11; }
> +inline int operator>(const Y&, int) { return 11; }
> +inline int operator>=(const Y&, int) { return 11; }
> +inline int operator*(const Y&) { return 11; }
> +inline int operator!(const Y&) { return 11; }
> +inline int operator~(const Y&) { return 11; }
> +inline int operator++(const Y&) { return 11; }
> +inline int operator--(const Y&) { return 11; }
> +inline int operator++(const Y&, int) { return 11; }
> +inline int operator--(const Y&, int) { return 11; }
> +inline int operator,(const Y&, int) { return 11; }
> +inline int operator&(const Y&) { return 11; }
> +inline int operator+=(const Y&, int x) { return 11; }
> +inline int operator*=(const Y&, int x) { return 11; }
> +inline int operator-=(const Y&, int x) { return 11; }
> +inline int operator/=(const Y&, int x) { return 11; }
> +
> +int
> +main ()
> +{
> +  Foo1 (0);
> +  Foo2 (0);
> +  Foo3 (0);
> +  Foo4 (0);
> +}
> diff --git a/gcc/testsuite/g++.dg/lookup/two-stage4.C b/gcc/testsuite/g++.dg/lookup/two-stage4.C
> index 7d97109..a89e618 100644
> --- a/gcc/testsuite/g++.dg/lookup/two-stage4.C
> +++ b/gcc/testsuite/g++.dg/lookup/two-stage4.C
> @@ -8,7 +8,7 @@ template<typename T> bool operator==(wrap<T>, wrap<T>);
>  template<typename T>
>  void g(T, wrap<wrap<int> > x)
>  {
> -  bool b = x == x; // { dg-bogus "" "" { xfail *-*-* } }
> +  bool b = x == x; // { dg-bogus "" "" }
>  }
>
>  template<typename T> void operator==(wrap<wrap<T> >, wrap<wrap<T> >);
> --
> 2.6.4.491.gda30757.dirty
>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH] Fix PR c++/21802 (two-stage name lookup fails for operators)
  2015-12-11 15:19 ` Patrick Palka
@ 2015-12-11 19:43   ` Patrick Palka
  2015-12-11 20:40     ` Jason Merrill
  0 siblings, 1 reply; 15+ messages in thread
From: Patrick Palka @ 2015-12-11 19:43 UTC (permalink / raw)
  To: gcc-patches; +Cc: jason, Patrick Palka

> Unfortunately this patch doesn't work properly on operator overloads
> that are defined as friend functions.  E.g. the following now fails to
> compile:
>
> struct A
> {
>  friend int operator* (A);
> };
>
> template <typename T>
> void func(T t)
> {
>  A x;
>  int y = *x;
> }
>
> int main()
> {
>  func(0);
> }
>
> I think this happens because KOENIG_LOOKUP_P is not being properly set
> in in the CALL_EXPR we are reconstructing.  Not sure how to fix that
> yet.

Here is what I came up with.  We need to specify to perform ADL during
instantiation (by setting the KOENIG_LOOKUP_P flag) if the candidate
operator overload we chose is a friend function.  Here's an incremental
diff, followed by patch v2 (test case is updated too):

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 117dd79..a1c0b14 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -5630,6 +5630,17 @@ build_new_op_1 (location_t loc, enum tree_code code, int flags, tree arg1,
 	    result = error_mark_node;
 	  else
 	    result = build_over_call (cand, LOOKUP_NORMAL, complain);
+
+	  if (processing_template_decl
+	      && result != NULL_TREE
+	      && result != error_mark_node
+	      && DECL_HIDDEN_FRIEND_P (cand->fn))
+	    {
+	      tree call = result;
+	      if (REFERENCE_REF_P (call))
+		call = TREE_OPERAND (call, 0);
+	      KOENIG_LOOKUP_P (call) = 1;
+	    }
 	}
       else
 	{
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 2635736..4194b1a 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -2741,6 +2741,7 @@ build_min_non_dep_call_vec (tree non_dep, tree fn, vec<tree, va_gc> *argvec)
     non_dep = TREE_OPERAND (non_dep, 0);
   TREE_TYPE (t) = TREE_TYPE (non_dep);
   TREE_SIDE_EFFECTS (t) = TREE_SIDE_EFFECTS (non_dep);
+  KOENIG_LOOKUP_P (t) = KOENIG_LOOKUP_P (non_dep);
   return convert_from_reference (t);
 }

gcc/cp/ChangeLog:

	PR c++/21802
	PR c++/53223
	* cp-tree.h (build_min_non_dep_op_overload): Declare.
	* tree.c (build_min_non_dep_op_overload): Define.
	(build_win_non_dep_call_vec): Copy KOENIG_LOOKUP_P flag.
	* typeck.c (build_x_indirect_ref): Use
	build_min_non_dep_op_overload when the given expression
	has been resolved to an operator overload.
	(build_x_binary_op): Likewise.
	(build_x_array_ref): Likewise.
	(build_x_unary_op): Likewise.
	(build_x_compound_expr): Likewise.
	(build_x_modify_expr): Likewise.
	* call.c (build_new_op_1): If during template processing we
	chose an operator overload that is a hidden friend function, set
	the call's KOENIG_LOOKUP_P flag to 1.

gcc/testsuite/ChangeLog:

	PR c++/21802
	PR c++/53223
	* g++.dg/cpp0x/pr53223.C: New test.
	* g++.dg/lookup/pr21802.C: New test.
	* g++.dg/lookup/two-stage4.C: Remove XFAIL.
---
 gcc/cp/call.c                            |  11 ++
 gcc/cp/cp-tree.h                         |   1 +
 gcc/cp/tree.c                            |  65 ++++++++
 gcc/cp/typeck.c                          | 100 ++++++++---
 gcc/testsuite/g++.dg/cpp0x/pr53223.C     |  35 ++++
 gcc/testsuite/g++.dg/lookup/pr21802.C    | 276 +++++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/lookup/two-stage4.C |   2 +-
 7 files changed, 470 insertions(+), 20 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/pr53223.C
 create mode 100644 gcc/testsuite/g++.dg/lookup/pr21802.C

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 117dd79..a1c0b14 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -5630,6 +5630,17 @@ build_new_op_1 (location_t loc, enum tree_code code, int flags, tree arg1,
 	    result = error_mark_node;
 	  else
 	    result = build_over_call (cand, LOOKUP_NORMAL, complain);
+
+	  if (processing_template_decl
+	      && result != NULL_TREE
+	      && result != error_mark_node
+	      && DECL_HIDDEN_FRIEND_P (cand->fn))
+	    {
+	      tree call = result;
+	      if (REFERENCE_REF_P (call))
+		call = TREE_OPERAND (call, 0);
+	      KOENIG_LOOKUP_P (call) = 1;
+	    }
 	}
       else
 	{
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 6190f4e..3487d77 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6513,6 +6513,7 @@ extern tree build_min				(enum tree_code, tree, ...);
 extern tree build_min_nt_loc			(location_t, enum tree_code,
 						 ...);
 extern tree build_min_non_dep			(enum tree_code, tree, ...);
+extern tree build_min_non_dep_op_overload	(enum tree_code, tree, tree, ...);
 extern tree build_min_non_dep_call_vec		(tree, tree, vec<tree, va_gc> *);
 extern tree build_cplus_new			(tree, tree, tsubst_flags_t);
 extern tree build_aggr_init_expr		(tree, tree);
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 5dad0a7..4194b1a 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -2741,9 +2741,74 @@ build_min_non_dep_call_vec (tree non_dep, tree fn, vec<tree, va_gc> *argvec)
     non_dep = TREE_OPERAND (non_dep, 0);
   TREE_TYPE (t) = TREE_TYPE (non_dep);
   TREE_SIDE_EFFECTS (t) = TREE_SIDE_EFFECTS (non_dep);
+  KOENIG_LOOKUP_P (t) = KOENIG_LOOKUP_P (non_dep);
   return convert_from_reference (t);
 }
 
+/* Similar to build_min_non_dep, but for expressions that have been resolved to
+   a call to an operator overload.  OP is the operator that has been
+   overloaded.  NON_DEP is the non-dependent expression that's been built,
+   which should be a CALL_EXPR or an INDIRECT_REF to a CALL_EXPR.  OVERLOAD is
+   the overload that NON_DEP is calling.  */
+
+tree
+build_min_non_dep_op_overload (enum tree_code op,
+			       tree non_dep,
+			       tree overload, ...)
+{
+  va_list p;
+  int nargs;
+  tree fn, call;
+  vec<tree, va_gc> *args;
+
+  if (REFERENCE_REF_P (non_dep))
+    non_dep = TREE_OPERAND (non_dep, 0);
+
+  nargs = call_expr_nargs (non_dep);
+
+  if (op == PREINCREMENT_EXPR
+      || op == PREDECREMENT_EXPR)
+    gcc_assert (nargs == 1);
+  else if (op == MODOP_EXPR)
+    gcc_assert (nargs == 2);
+  else
+    gcc_assert (nargs == TREE_CODE_LENGTH (op));
+
+  args = make_tree_vector ();
+  va_start (p, overload);
+
+  if (TREE_CODE (TREE_TYPE (overload)) == FUNCTION_TYPE)
+    {
+      fn = overload;
+      for (int i = 0; i < nargs; i++)
+	{
+	  tree arg = va_arg (p, tree);
+	  vec_safe_push (args, arg);
+	}
+    }
+  else if (TREE_CODE (TREE_TYPE (overload)) == METHOD_TYPE)
+    {
+      tree object = va_arg (p, tree);
+      tree binfo = TYPE_BINFO (TREE_TYPE (object));
+      tree method = build_baselink (binfo, binfo, overload, NULL_TREE);
+      fn = build_min (COMPONENT_REF, TREE_TYPE (overload),
+		      object, method, NULL_TREE);
+      for (int i = 1; i < nargs; i++)
+	{
+	  tree arg = va_arg (p, tree);
+	  vec_safe_push (args, arg);
+	}
+    }
+  else
+   gcc_unreachable ();
+
+  va_end (p);
+  call = build_min_non_dep_call_vec (non_dep, fn, args);
+  release_tree_vector (args);
+
+  return call;
+}
+
 tree
 get_type_decl (tree t)
 {
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 17671ee..2e5e46e 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -2905,6 +2905,7 @@ build_x_indirect_ref (location_t loc, tree expr, ref_operator errorstring,
 {
   tree orig_expr = expr;
   tree rval;
+  tree overload = NULL_TREE;
 
   if (processing_template_decl)
     {
@@ -2917,12 +2918,18 @@ build_x_indirect_ref (location_t loc, tree expr, ref_operator errorstring,
     }
 
   rval = build_new_op (loc, INDIRECT_REF, LOOKUP_NORMAL, expr,
-		       NULL_TREE, NULL_TREE, /*overload=*/NULL, complain);
+		       NULL_TREE, NULL_TREE, &overload, complain);
   if (!rval)
     rval = cp_build_indirect_ref (expr, errorstring, complain);
 
   if (processing_template_decl && rval != error_mark_node)
-    return build_min_non_dep (INDIRECT_REF, rval, orig_expr);
+    {
+      if (overload != NULL_TREE)
+	return (build_min_non_dep_op_overload
+		(INDIRECT_REF, rval, overload, orig_expr));
+
+      return build_min_non_dep (INDIRECT_REF, rval, orig_expr);
+    }
   else
     return rval;
 }
@@ -3814,12 +3821,13 @@ convert_arguments (tree typelist, vec<tree, va_gc> **values, tree fndecl,
 tree
 build_x_binary_op (location_t loc, enum tree_code code, tree arg1,
 		   enum tree_code arg1_code, tree arg2,
-		   enum tree_code arg2_code, tree *overload,
+		   enum tree_code arg2_code, tree *overload_p,
 		   tsubst_flags_t complain)
 {
   tree orig_arg1;
   tree orig_arg2;
   tree expr;
+  tree overload = NULL_TREE;
 
   orig_arg1 = arg1;
   orig_arg2 = arg2;
@@ -3837,7 +3845,10 @@ build_x_binary_op (location_t loc, enum tree_code code, tree arg1,
     expr = build_m_component_ref (arg1, arg2, complain);
   else
     expr = build_new_op (loc, code, LOOKUP_NORMAL, arg1, arg2, NULL_TREE,
-			 overload, complain);
+			 &overload, complain);
+
+  if (overload_p != NULL)
+    *overload_p = overload;
 
   /* Check for cases such as x+y<<z which users are likely to
      misinterpret.  But don't warn about obj << x + y, since that is a
@@ -3853,7 +3864,13 @@ build_x_binary_op (location_t loc, enum tree_code code, tree arg1,
 			    arg2_code, orig_arg2);
 
   if (processing_template_decl && expr != error_mark_node)
-    return build_min_non_dep (code, expr, orig_arg1, orig_arg2);
+    {
+      if (overload != NULL_TREE)
+	return (build_min_non_dep_op_overload
+		(code, expr, overload, orig_arg1, orig_arg2));
+
+      return build_min_non_dep (code, expr, orig_arg1, orig_arg2);
+    }
 
   return expr;
 }
@@ -3867,6 +3884,7 @@ build_x_array_ref (location_t loc, tree arg1, tree arg2,
   tree orig_arg1 = arg1;
   tree orig_arg2 = arg2;
   tree expr;
+  tree overload = NULL_TREE;
 
   if (processing_template_decl)
     {
@@ -3879,11 +3897,17 @@ build_x_array_ref (location_t loc, tree arg1, tree arg2,
     }
 
   expr = build_new_op (loc, ARRAY_REF, LOOKUP_NORMAL, arg1, arg2,
-		       NULL_TREE, /*overload=*/NULL, complain);
+		       NULL_TREE, &overload, complain);
 
   if (processing_template_decl && expr != error_mark_node)
-    return build_min_non_dep (ARRAY_REF, expr, orig_arg1, orig_arg2,
-			      NULL_TREE, NULL_TREE);
+    {
+      if (overload != NULL_TREE)
+	return (build_min_non_dep_op_overload
+		(ARRAY_REF, expr, overload, orig_arg1, orig_arg2));
+
+      return build_min_non_dep (ARRAY_REF, expr, orig_arg1, orig_arg2,
+				NULL_TREE, NULL_TREE);
+    }
   return expr;
 }
 
@@ -5278,6 +5302,7 @@ build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg,
   tree orig_expr = xarg;
   tree exp;
   int ptrmem = 0;
+  tree overload = NULL_TREE;
 
   if (processing_template_decl)
     {
@@ -5305,7 +5330,8 @@ build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg,
     /* Don't look for a function.  */;
   else
     exp = build_new_op (loc, code, LOOKUP_NORMAL, xarg, NULL_TREE,
-			NULL_TREE, /*overload=*/NULL, complain);
+			NULL_TREE, &overload, complain);
+
   if (!exp && code == ADDR_EXPR)
     {
       if (is_overloaded_fn (xarg))
@@ -5371,8 +5397,14 @@ build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg,
     }
 
   if (processing_template_decl && exp != error_mark_node)
-    exp = build_min_non_dep (code, exp, orig_expr,
-			     /*For {PRE,POST}{INC,DEC}REMENT_EXPR*/NULL_TREE);
+    {
+      if (overload != NULL_TREE)
+	return (build_min_non_dep_op_overload
+		(code, exp, overload, orig_expr, integer_zero_node));
+
+      exp = build_min_non_dep (code, exp, orig_expr,
+			       /*For {PRE,POST}{INC,DEC}REMENT_EXPR*/NULL_TREE);
+    }
   if (TREE_CODE (exp) == ADDR_EXPR)
     PTRMEM_OK_P (exp) = ptrmem;
   return exp;
@@ -6335,6 +6367,7 @@ build_x_compound_expr (location_t loc, tree op1, tree op2,
   tree result;
   tree orig_op1 = op1;
   tree orig_op2 = op2;
+  tree overload = NULL_TREE;
 
   if (processing_template_decl)
     {
@@ -6346,12 +6379,18 @@ build_x_compound_expr (location_t loc, tree op1, tree op2,
     }
 
   result = build_new_op (loc, COMPOUND_EXPR, LOOKUP_NORMAL, op1, op2,
-			 NULL_TREE, /*overload=*/NULL, complain);
+			 NULL_TREE, &overload, complain);
   if (!result)
     result = cp_build_compound_expr (op1, op2, complain);
 
   if (processing_template_decl && result != error_mark_node)
-    return build_min_non_dep (COMPOUND_EXPR, result, orig_op1, orig_op2);
+    {
+      if (overload != NULL_TREE)
+	return (build_min_non_dep_op_overload
+		(COMPOUND_EXPR, result, overload, orig_op1, orig_op2));
+
+      return build_min_non_dep (COMPOUND_EXPR, result, orig_op1, orig_op2);
+    }
 
   return result;
 }
@@ -7794,19 +7833,42 @@ cp_expr
 build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
 		     tree rhs, tsubst_flags_t complain)
 {
+  tree orig_lhs = lhs;
+  tree orig_rhs = rhs;
+  tree overload = NULL_TREE;
+  tree op = build_nt (modifycode, NULL_TREE, NULL_TREE);
+
   if (processing_template_decl)
-    return build_min_nt_loc (loc, MODOP_EXPR, lhs,
-			     build_min_nt_loc (loc, modifycode, NULL_TREE,
-					       NULL_TREE), rhs);
+    {
+      if (modifycode == NOP_EXPR
+	  || type_dependent_expression_p (lhs)
+	  || type_dependent_expression_p (rhs))
+        return build_min_nt_loc (loc, MODOP_EXPR, lhs,
+				 build_min_nt_loc (loc, modifycode, NULL_TREE,
+						   NULL_TREE), rhs);
+
+      lhs = build_non_dependent_expr (lhs);
+      rhs = build_non_dependent_expr (rhs);
+    }
 
   if (modifycode != NOP_EXPR)
     {
-      tree rval = build_new_op (loc, MODIFY_EXPR, LOOKUP_NORMAL, lhs, rhs,
-				make_node (modifycode), /*overload=*/NULL,
-				complain);
+      tree rval = build_new_op (loc, MODIFY_EXPR, LOOKUP_NORMAL,
+				lhs, rhs, op, &overload, complain);
       if (rval)
 	{
+	  if (rval == error_mark_node)
+	    return rval;
 	  TREE_NO_WARNING (rval) = 1;
+	  if (processing_template_decl)
+	    {
+	      if (overload != NULL_TREE)
+		return (build_min_non_dep_op_overload
+			(MODOP_EXPR, rval, overload, orig_lhs, orig_rhs));
+
+	      return (build_min_non_dep
+		      (MODOP_EXPR, rval, orig_lhs, op, orig_rhs));
+	    }
 	  return rval;
 	}
     }
diff --git a/gcc/testsuite/g++.dg/cpp0x/pr53223.C b/gcc/testsuite/g++.dg/cpp0x/pr53223.C
new file mode 100644
index 0000000..4ca2da1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/pr53223.C
@@ -0,0 +1,35 @@
+// PR c++/53223
+// { dg-do compile { target c++11 } }
+
+struct A
+{
+  int good() const;
+  int operator *() const;
+  int operator ++() const;
+};
+
+int operator-- (const A&);
+
+template<typename T>
+void func(T t)
+{
+  A x;
+  auto &&g1 = x.good();
+  auto &&g2 = x.operator*();
+  auto &&error1 = *x; // { dg-bogus "invalid initialization" }
+  auto &&error2 = ++x; // { dg-bogus "invalid initialization" }
+  auto &&error3 = --x; // { dg-bogus "invalid initialization" }
+}
+
+void func2(int)
+{
+  A x;
+  auto &&g = *x;
+}
+
+int main()
+{
+  func(0);
+  func2(0);
+}
+
diff --git a/gcc/testsuite/g++.dg/lookup/pr21802.C b/gcc/testsuite/g++.dg/lookup/pr21802.C
new file mode 100644
index 0000000..139f7b4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/pr21802.C
@@ -0,0 +1,276 @@
+// PR c++/21802
+// { dg-do run }
+#include <cassert>
+
+struct X;
+int I = 6;
+
+/* A mostly exhaustive and ad-hoc assortment of operator overloads and calls
+   thereof, to stress-test two-stage name lookup of operators inside template
+   definitions and then to verify that the calls get built correctly.  */
+
+template <typename T>
+inline int operator+(const X &, T x) { return x; }
+inline int operator-(const X &, int x) { return x; }
+inline int operator*(const X &, int x) { return x; }
+inline int operator/(const X &, int x) { return x; }
+inline int operator+=(const X &, int x) { return x; }
+
+struct X
+{
+  X () : m (1) { }
+  template <typename T>
+  int operator%(T x) { return m + x; }
+  virtual int operator>>(int x) { return m + x; }
+  int operator<<(int x) { return m + x; }
+  int operator&(int x) { return m + x; }
+  int operator|(int x) { return m + x; }
+  int operator^(int x) { return m + x; }
+  int operator&&(int x) { return m + x; }
+  int operator||(int x) { return m + x; }
+  friend int operator==(X o, int x) { return o.m + x; }
+  int operator!=(int x) { return m + x; }
+  int operator<(int x) { return m + x; }
+  int operator<=(int x) { return m + x; }
+  int operator>(int x) { return m + x; }
+  int operator>=(int x) { return m + x; }
+  int operator*() { return m + I; }
+  int operator!() { return m + I; }
+  int operator~() { return m + I; }
+  int operator++() { return m + I + 100; }
+  int operator--() { return m + I + 100; }
+  int operator++(int) { return m + I; }
+  int operator--(int) { return m + I; }
+  int operator()() { return m + I; }
+  int operator,(int x) { return m + x; }
+  int operator[](int x) { return m + x; }
+  int operator*=(int x) { return m + x; }
+  int operator-=(int x) { return m + x; }
+  int operator/=(int x) { return m + x; }
+  virtual int operator& () { return m + I; }
+  int m;
+};
+struct Y : virtual X
+{
+  /* Virtual override.  */
+  int operator>>(int x) { return m + x + 1; }
+  int operator& () { return m + I + 1; }
+
+  /* Not virtual.  */
+  template <typename T>
+  int operator&(T x) { return m + x + 1; }
+  friend int operator==(Y o, int x) { return o.m + x + 1; }
+};
+
+/* The folloiwng "FooN" functions each contain a different way to call and to
+   resolve these operator overloads.  */
+
+template <typename T>
+void
+Foo1 (T)
+{
+  Y x;
+  { int t = x + I; assert (t == 6); }
+  { int t = x - I; assert (t == 6); }
+  { int t = x * I; assert (t == 6); }
+  { int t = x / I; assert (t == 6); }
+  { int t = (x+=I); assert (t == 6); }
+
+  { int t = x % I; assert (t == 7); }
+  { int t = x << I; assert (t == 7); }
+  { int t = x | I; assert (t == 7); }
+  { int t = x && I; assert (t == 7); }
+  { int t = x || I; assert (t == 7); }
+  { int t = x != I; assert (t == 7); }
+  { int t = x < I; assert (t == 7); }
+  { int t = x <= I; assert (t == 7); }
+  { int t = x > I; assert (t == 7); }
+  { int t = x >= I; assert (t == 7); }
+  { int t = *x; assert (t == 7); }
+  { int t = !x; assert (t == 7); }
+  { int t = ~x; assert (t == 7); }
+  { int t = x++; assert (t == 7); }
+  { int t = x--; assert (t == 7); }
+  { int t = ++x; assert (t == 107); }
+  { int t = --x; assert (t == 107); }
+  { int t = x (); assert (t == 7); }
+  { int t = (x, I); assert (t == 7); }
+  { int t = x[I]; assert (t == 7); }
+  { int t = (x-=I); assert (t == 7); }
+  { int t = (x/=I); assert (t == 7); }
+  { int t = (x*=I); assert (t == 7); }
+
+  { int t = x >> I; assert (t == 8); }
+  { int t = x & I; assert (t == 8); }
+  { int t = &x; assert (t == 8); }
+  { int t = x == I; assert (t == 8); }
+}
+
+template <typename T>
+void
+Foo2 (T)
+{
+  X x;
+  { int t = x + I; assert (t == 6); }
+  { int t = x - I; assert (t == 6); }
+  { int t = x * I; assert (t == 6); }
+  { int t = x / I; assert (t == 6); }
+  { int t = (x+=I); assert (t == 6); }
+
+  { int t = x % I; assert (t == 7); }
+  { int t = x >> I; assert (t == 7); }
+  { int t = x << I; assert (t == 7); }
+  { int t = x | I; assert (t == 7); }
+  { int t = x && I; assert (t == 7); }
+  { int t = x || I; assert (t == 7); }
+  { int t = x == I; assert (t == 7); }
+  { int t = x != I; assert (t == 7); }
+  { int t = x < I; assert (t == 7); }
+  { int t = x <= I; assert (t == 7); }
+  { int t = x > I; assert (t == 7); }
+  { int t = x >= I; assert (t == 7); }
+  { int t = *x; assert (t == 7); }
+  { int t = !x; assert (t == 7); }
+  { int t = ~x; assert (t == 7); }
+  { int t = x++; assert (t == 7); }
+  { int t = x--; assert (t == 7); }
+  { int t = ++x; assert (t == 107); }
+  { int t = --x; assert (t == 107); }
+  { int t = x (); assert (t == 7); }
+  { int t = (x, I); assert (t == 7); }
+  { int t = x[I]; assert (t == 7); }
+  { int t = &x; assert (t == 7); }
+  { int t = (x-=I); assert (t == 7); }
+  { int t = (x/=I); assert (t == 7); }
+  { int t = (x*=I); assert (t == 7); }
+  { int t = x & I; assert (t == 7); }
+}
+
+template <typename T>
+void
+Foo3 (T)
+{
+  Y o;
+  X &x = o;
+  { int t = x + I; assert (t == 6); }
+  { int t = x - I; assert (t == 6); }
+  { int t = x * I; assert (t == 6); }
+  { int t = x / I; assert (t == 6); }
+  { int t = (x+=I); assert (t == 6); }
+
+  { int t = x % I; assert (t == 7); }
+  { int t = x << I; assert (t == 7); }
+  { int t = x | I; assert (t == 7); }
+  { int t = x && I; assert (t == 7); }
+  { int t = x || I; assert (t == 7); }
+  { int t = x == I; assert (t == 7); }
+  { int t = x != I; assert (t == 7); }
+  { int t = x < I; assert (t == 7); }
+  { int t = x <= I; assert (t == 7); }
+  { int t = x > I; assert (t == 7); }
+  { int t = x >= I; assert (t == 7); }
+  { int t = *x; assert (t == 7); }
+  { int t = !x; assert (t == 7); }
+  { int t = ~x; assert (t == 7); }
+  { int t = x++; assert (t == 7); }
+  { int t = x--; assert (t == 7); }
+  { int t = ++x; assert (t == 107); }
+  { int t = --x; assert (t == 107); }
+  { int t = x (); assert (t == 7); }
+  { int t = (x, I); assert (t == 7); }
+  { int t = x[I]; assert (t == 7); }
+  { int t = (x-=I); assert (t == 7); }
+  { int t = (x/=I); assert (t == 7); }
+  { int t = (x*=I); assert (t == 7); }
+
+  { int t = x & I; assert (t == 7); }
+  { int t = x >> I; assert (t == 8); }
+  { int t = &x; assert (t == 8); }
+}
+
+template <typename T>
+void
+Foo4 (T)
+{
+  Y x;
+  { int t = operator+ (x, I); assert (t == 6); }
+  { int t = operator- (x, I); assert (t == 6); }
+  { int t = operator* (x, I); assert (t == 6); }
+  { int t = operator/ (x, I); assert (t == 6); }
+  { int t = operator+= (x, I); assert (t == 6); }
+
+  { int t = x.operator% (I); assert (t == 7); }
+  { int t = x.operator<< (I); assert (t == 7); }
+  { int t = x.operator| (I); assert (t == 7); }
+  { int t = x.operator&& (I); assert (t == 7); }
+  { int t = x.operator|| (I); assert (t == 7); }
+  { int t = x.operator!= (I); assert (t == 7); }
+  { int t = x.operator< (I); assert (t == 7); }
+  { int t = x.operator<= (I); assert (t == 7); }
+  { int t = x.operator> (I); assert (t == 7); }
+  { int t = x.operator>= (I); assert (t == 7); }
+  { int t = x.operator* (); assert (t == 7); }
+  { int t = x.operator! (); assert (t == 7); }
+  { int t = x.operator~ (); assert (t == 7); }
+  { int t = x.operator++ (0); assert (t == 7); }
+  { int t = x.operator-- (0); assert (t == 7); }
+  { int t = x.operator++ (); assert (t == 107); }
+  { int t = x.operator-- (); assert (t == 107); }
+  { int t = x.operator() (); assert (t == 7); }
+  { int t = x.operator, (I); assert (t == 7); }
+  { int t = x.operator[] (I); assert (t == 7); }
+  { int t = x.operator-= (I); assert (t == 7); }
+  { int t = x.operator/= (I); assert (t == 7); }
+  { int t = x.operator*= (I); assert (t == 7); }
+
+  { int t = x.operator>> (I); assert (t == 8); }
+  { int t = x.operator& (); assert (t == 8); }
+  { int t = x.operator& (I); assert (t == 8); }
+  { int t = operator== (x, I); assert (t == 8); }
+}
+
+
+/* These definitions should be irrelevant to operator lookup of non-dependent
+   expressions inside the above templates since they are not in scope at
+   template-definition time (even though they are in scope at instantiation
+   time).  */
+inline int operator+(const Y&, int) { return 11; }
+inline int operator-(const Y&, int) { return 11; }
+inline int operator*(const Y&, int) { return 11; }
+inline int operator/(const Y&, int) { return 11; }
+inline int operator%(const Y&, int) { return 11; }
+inline int operator>>(const Y&, int) { return 11; }
+inline int operator<<(const Y&, int) { return 11; }
+inline int operator&(const Y&, int) { return 11; }
+inline int operator|(const Y&, int) { return 11; }
+inline int operator^(const Y&, int) { return 11; }
+inline int operator&&(const Y&, int) { return 11; }
+inline int operator||(const Y&, int) { return 11; }
+inline int operator==(const Y&, int) { return 11; }
+inline int operator!=(const Y&, int) { return 11; }
+inline int operator<(const Y&, int) { return 11; }
+inline int operator<=(const Y&, int) { return 11; }
+inline int operator>(const Y&, int) { return 11; }
+inline int operator>=(const Y&, int) { return 11; }
+inline int operator*(const Y&) { return 11; }
+inline int operator!(const Y&) { return 11; }
+inline int operator~(const Y&) { return 11; }
+inline int operator++(const Y&) { return 11; }
+inline int operator--(const Y&) { return 11; }
+inline int operator++(const Y&, int) { return 11; }
+inline int operator--(const Y&, int) { return 11; }
+inline int operator,(const Y&, int) { return 11; }
+inline int operator&(const Y&) { return 11; }
+inline int operator+=(const Y&, int x) { return 11; }
+inline int operator*=(const Y&, int x) { return 11; }
+inline int operator-=(const Y&, int x) { return 11; }
+inline int operator/=(const Y&, int x) { return 11; }
+
+int
+main ()
+{
+  Foo1 (0);
+  Foo2 (0);
+  Foo3 (0);
+  Foo4 (0);
+}
diff --git a/gcc/testsuite/g++.dg/lookup/two-stage4.C b/gcc/testsuite/g++.dg/lookup/two-stage4.C
index 7d97109..a89e618 100644
--- a/gcc/testsuite/g++.dg/lookup/two-stage4.C
+++ b/gcc/testsuite/g++.dg/lookup/two-stage4.C
@@ -8,7 +8,7 @@ template<typename T> bool operator==(wrap<T>, wrap<T>);
 template<typename T>
 void g(T, wrap<wrap<int> > x)
 {
-  bool b = x == x; // { dg-bogus "" "" { xfail *-*-* } }
+  bool b = x == x; // { dg-bogus "" "" }
 }
 
 template<typename T> void operator==(wrap<wrap<T> >, wrap<wrap<T> >);
-- 
2.7.0.rc0.50.g1470d8f.dirty

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] Fix PR c++/21802 (two-stage name lookup fails for operators)
  2015-12-11 19:43   ` Patrick Palka
@ 2015-12-11 20:40     ` Jason Merrill
  2015-12-12 23:32       ` Patrick Palka
  0 siblings, 1 reply; 15+ messages in thread
From: Jason Merrill @ 2015-12-11 20:40 UTC (permalink / raw)
  To: Patrick Palka, gcc-patches

On 12/11/2015 02:43 PM, Patrick Palka wrote:
> +	  if (processing_template_decl
> +	      && result != NULL_TREE
> +	      && result != error_mark_node
> +	      && DECL_HIDDEN_FRIEND_P (cand->fn))
> +	    {
> +	      tree call = result;
> +	      if (REFERENCE_REF_P (call))
> +		call = TREE_OPERAND (call, 0);
> +	      KOENIG_LOOKUP_P (call) = 1;
> +	    }

This should have a comment explaining that this prevents 
build_new_function_call from discarding the function.

> +  if (op == PREINCREMENT_EXPR
> +      || op == PREDECREMENT_EXPR)
> +    gcc_assert (nargs == 1);
> +  else if (op == MODOP_EXPR)
> +    gcc_assert (nargs == 2);
> +  else
> +    gcc_assert (nargs == TREE_CODE_LENGTH (op));

This should use cp_tree_operand_length.

Jason

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH] Fix PR c++/21802 (two-stage name lookup fails for operators)
  2015-12-11 20:40     ` Jason Merrill
@ 2015-12-12 23:32       ` Patrick Palka
  2015-12-14 20:42         ` Jason Merrill
  0 siblings, 1 reply; 15+ messages in thread
From: Patrick Palka @ 2015-12-12 23:32 UTC (permalink / raw)
  To: gcc-patches; +Cc: jason, Patrick Palka

On Fri, 11 Dec 2015, Jason Merrill wrote:

> On 12/11/2015 02:43 PM, Patrick Palka wrote:
>> +	  if (processing_template_decl
>> +	      && result != NULL_TREE
>> +	      && result != error_mark_node
>> +	      && DECL_HIDDEN_FRIEND_P (cand->fn))
>> +	    {
>> +	      tree call = result;
>> +	      if (REFERENCE_REF_P (call))
>> +		call = TREE_OPERAND (call, 0);
>> +	      KOENIG_LOOKUP_P (call) = 1;
>> +	    }
>
> This should have a comment explaining that this prevents
> build_new_function_call from discarding the function.

Consider it done.

>
>> +  if (op == PREINCREMENT_EXPR
>> +      || op == PREDECREMENT_EXPR)
>> +    gcc_assert (nargs == 1);
>> +  else if (op == MODOP_EXPR)
>> +    gcc_assert (nargs == 2);
>> +  else
>> +    gcc_assert (nargs == TREE_CODE_LENGTH (op));
>
> This should use cp_tree_operand_length.

Hmm, I don't immediately see how I can use this function here.  It
expects a tree but I dont have an appropriate tree to give to it, only a
tree_code.  And I can't overload it easily because it calls
TREE_OPERAND_LENGTH which expects a tree as well.

Your pointing out of this function fortunately made me uncover a bug in
my patch: that the function build_min_non_dep_op_overload expects the
wrong number of arguments to an operator[] overload.  It is expecting 4
(i.e. TREE_CODE_LENGTH (ARRAY_REF)) instead of 2.

This should have caused an ICE but it turns out that the test case was
not using the code I added in build_x_array_ref at all, because during
template processing, ARRAY_REFS seem to get processed via
grok_array_decl -- a caller of build_new_op that I had forgotten to
change.

This patch v3 fixes this issue by adjusting the functions
build_min_non_dep_op_overload and grok_array_decl accordingly.  It also
improves the pr53223.C test case to use static_assert and std::is_same<>
to explicitly confirm that the auto types in the test case are correctly
deduced.

gcc/cp/ChangeLog:

	PR c++/21802
	PR c++/53223
	* cp-tree.h (build_min_non_dep_op_overload): Declare.
	* tree.c (build_min_non_dep_op_overload): Define.
	(build_win_non_dep_call_vec): Copy KOENIG_LOOKUP_P flag.
	* typeck.c (build_x_indirect_ref): Use
	build_min_non_dep_op_overload when the given expression
	has been resolved to an operator overload.
	(build_x_binary_op): Likewise.
	(build_x_array_ref): Likewise.
	(build_x_unary_op): Likewise.
	(build_x_compound_expr): Likewise.
	(build_x_modify_expr): Likewise.
	* decl2.c (grok_array_decl): Likewise.
	* call.c (build_new_op_1): If during template processing we
	chose an operator overload that is a hidden friend function, set
	the call's KOENIG_LOOKUP_P flag to 1.

gcc/testsuite/ChangeLog:

	PR c++/21802
	PR c++/53223
	* g++.dg/cpp0x/pr53223.C: New test.
	* g++.dg/lookup/pr21802.C: New test.
	* g++.dg/lookup/two-stage4.C: Remove XFAIL.
---
 gcc/cp/call.c                            |  13 ++
 gcc/cp/cp-tree.h                         |   1 +
 gcc/cp/decl2.c                           |  13 +-
 gcc/cp/tree.c                            |  67 ++++++++
 gcc/cp/typeck.c                          | 100 ++++++++---
 gcc/testsuite/g++.dg/cpp0x/pr53223.C     |  45 +++++
 gcc/testsuite/g++.dg/lookup/pr21802.C    | 276 +++++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/lookup/two-stage4.C |   2 +-
 8 files changed, 494 insertions(+), 23 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/pr53223.C
 create mode 100644 gcc/testsuite/g++.dg/lookup/pr21802.C

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 117dd79..cdfa01a 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -5630,6 +5630,19 @@ build_new_op_1 (location_t loc, enum tree_code code, int flags, tree arg1,
 	    result = error_mark_node;
 	  else
 	    result = build_over_call (cand, LOOKUP_NORMAL, complain);
+
+	  if (processing_template_decl
+	      && result != NULL_TREE
+	      && result != error_mark_node
+	      && DECL_HIDDEN_FRIEND_P (cand->fn))
+	    {
+	      tree call = result;
+	      if (REFERENCE_REF_P (call))
+		call = TREE_OPERAND (call, 0);
+	      /* This prevents build_new_function_call from discarding this
+		 function during instantiation of the enclosing template.  */
+	      KOENIG_LOOKUP_P (call) = 1;
+	    }
 	}
       else
 	{
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 6190f4e..3487d77 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6513,6 +6513,7 @@ extern tree build_min				(enum tree_code, tree, ...);
 extern tree build_min_nt_loc			(location_t, enum tree_code,
 						 ...);
 extern tree build_min_non_dep			(enum tree_code, tree, ...);
+extern tree build_min_non_dep_op_overload	(enum tree_code, tree, tree, ...);
 extern tree build_min_non_dep_call_vec		(tree, tree, vec<tree, va_gc> *);
 extern tree build_cplus_new			(tree, tree, tsubst_flags_t);
 extern tree build_aggr_init_expr		(tree, tree);
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 2cc51d6..5ae6266 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -354,6 +354,7 @@ grok_array_decl (location_t loc, tree array_expr, tree index_exp,
   tree expr;
   tree orig_array_expr = array_expr;
   tree orig_index_exp = index_exp;
+  tree overload = NULL_TREE;
 
   if (error_operand_p (array_expr) || error_operand_p (index_exp))
     return error_mark_node;
@@ -379,7 +380,7 @@ grok_array_decl (location_t loc, tree array_expr, tree index_exp,
       if (decltype_p)
 	complain |= tf_decltype;
       expr = build_new_op (loc, ARRAY_REF, LOOKUP_NORMAL, array_expr,
-			   index_exp, NULL_TREE, /*overload=*/NULL, complain);
+			   index_exp, NULL_TREE, &overload, complain);
     }
   else
     {
@@ -424,8 +425,14 @@ grok_array_decl (location_t loc, tree array_expr, tree index_exp,
       expr = build_array_ref (input_location, array_expr, index_exp);
     }
   if (processing_template_decl && expr != error_mark_node)
-    return build_min_non_dep (ARRAY_REF, expr, orig_array_expr, orig_index_exp,
-			      NULL_TREE, NULL_TREE);
+    {
+      if (overload != NULL_TREE)
+	return (build_min_non_dep_op_overload
+		(ARRAY_REF, expr, overload, orig_array_expr, orig_index_exp));
+
+      return build_min_non_dep (ARRAY_REF, expr, orig_array_expr, orig_index_exp,
+				NULL_TREE, NULL_TREE);
+    }
   return expr;
 }
 
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 5dad0a7..76c4ffd 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -2741,9 +2741,76 @@ build_min_non_dep_call_vec (tree non_dep, tree fn, vec<tree, va_gc> *argvec)
     non_dep = TREE_OPERAND (non_dep, 0);
   TREE_TYPE (t) = TREE_TYPE (non_dep);
   TREE_SIDE_EFFECTS (t) = TREE_SIDE_EFFECTS (non_dep);
+  KOENIG_LOOKUP_P (t) = KOENIG_LOOKUP_P (non_dep);
   return convert_from_reference (t);
 }
 
+/* Similar to build_min_non_dep, but for expressions that have been resolved to
+   a call to an operator overload.  OP is the operator that has been
+   overloaded.  NON_DEP is the non-dependent expression that's been built,
+   which should be a CALL_EXPR or an INDIRECT_REF to a CALL_EXPR.  OVERLOAD is
+   the overload that NON_DEP is calling.  */
+
+tree
+build_min_non_dep_op_overload (enum tree_code op,
+			       tree non_dep,
+			       tree overload, ...)
+{
+  va_list p;
+  int nargs, expected_nargs;
+  tree fn, call;
+  vec<tree, va_gc> *args;
+
+  if (REFERENCE_REF_P (non_dep))
+    non_dep = TREE_OPERAND (non_dep, 0);
+
+  nargs = call_expr_nargs (non_dep);
+
+  if (op == PREINCREMENT_EXPR
+      || op == PREDECREMENT_EXPR)
+    expected_nargs = 1;
+  else if (op == MODOP_EXPR
+	   || op == ARRAY_REF)
+    expected_nargs = 2;
+  else
+    expected_nargs = TREE_CODE_LENGTH (op)
+  gcc_assert (nargs == expected_nargs);
+
+  args = make_tree_vector ();
+  va_start (p, overload);
+
+  if (TREE_CODE (TREE_TYPE (overload)) == FUNCTION_TYPE)
+    {
+      fn = overload;
+      for (int i = 0; i < nargs; i++)
+	{
+	  tree arg = va_arg (p, tree);
+	  vec_safe_push (args, arg);
+	}
+    }
+  else if (TREE_CODE (TREE_TYPE (overload)) == METHOD_TYPE)
+    {
+      tree object = va_arg (p, tree);
+      tree binfo = TYPE_BINFO (TREE_TYPE (object));
+      tree method = build_baselink (binfo, binfo, overload, NULL_TREE);
+      fn = build_min (COMPONENT_REF, TREE_TYPE (overload),
+		      object, method, NULL_TREE);
+      for (int i = 1; i < nargs; i++)
+	{
+	  tree arg = va_arg (p, tree);
+	  vec_safe_push (args, arg);
+	}
+    }
+  else
+   gcc_unreachable ();
+
+  va_end (p);
+  call = build_min_non_dep_call_vec (non_dep, fn, args);
+  release_tree_vector (args);
+
+  return call;
+}
+
 tree
 get_type_decl (tree t)
 {
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 17671ee..2e5e46e 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -2905,6 +2905,7 @@ build_x_indirect_ref (location_t loc, tree expr, ref_operator errorstring,
 {
   tree orig_expr = expr;
   tree rval;
+  tree overload = NULL_TREE;
 
   if (processing_template_decl)
     {
@@ -2917,12 +2918,18 @@ build_x_indirect_ref (location_t loc, tree expr, ref_operator errorstring,
     }
 
   rval = build_new_op (loc, INDIRECT_REF, LOOKUP_NORMAL, expr,
-		       NULL_TREE, NULL_TREE, /*overload=*/NULL, complain);
+		       NULL_TREE, NULL_TREE, &overload, complain);
   if (!rval)
     rval = cp_build_indirect_ref (expr, errorstring, complain);
 
   if (processing_template_decl && rval != error_mark_node)
-    return build_min_non_dep (INDIRECT_REF, rval, orig_expr);
+    {
+      if (overload != NULL_TREE)
+	return (build_min_non_dep_op_overload
+		(INDIRECT_REF, rval, overload, orig_expr));
+
+      return build_min_non_dep (INDIRECT_REF, rval, orig_expr);
+    }
   else
     return rval;
 }
@@ -3814,12 +3821,13 @@ convert_arguments (tree typelist, vec<tree, va_gc> **values, tree fndecl,
 tree
 build_x_binary_op (location_t loc, enum tree_code code, tree arg1,
 		   enum tree_code arg1_code, tree arg2,
-		   enum tree_code arg2_code, tree *overload,
+		   enum tree_code arg2_code, tree *overload_p,
 		   tsubst_flags_t complain)
 {
   tree orig_arg1;
   tree orig_arg2;
   tree expr;
+  tree overload = NULL_TREE;
 
   orig_arg1 = arg1;
   orig_arg2 = arg2;
@@ -3837,7 +3845,10 @@ build_x_binary_op (location_t loc, enum tree_code code, tree arg1,
     expr = build_m_component_ref (arg1, arg2, complain);
   else
     expr = build_new_op (loc, code, LOOKUP_NORMAL, arg1, arg2, NULL_TREE,
-			 overload, complain);
+			 &overload, complain);
+
+  if (overload_p != NULL)
+    *overload_p = overload;
 
   /* Check for cases such as x+y<<z which users are likely to
      misinterpret.  But don't warn about obj << x + y, since that is a
@@ -3853,7 +3864,13 @@ build_x_binary_op (location_t loc, enum tree_code code, tree arg1,
 			    arg2_code, orig_arg2);
 
   if (processing_template_decl && expr != error_mark_node)
-    return build_min_non_dep (code, expr, orig_arg1, orig_arg2);
+    {
+      if (overload != NULL_TREE)
+	return (build_min_non_dep_op_overload
+		(code, expr, overload, orig_arg1, orig_arg2));
+
+      return build_min_non_dep (code, expr, orig_arg1, orig_arg2);
+    }
 
   return expr;
 }
@@ -3867,6 +3884,7 @@ build_x_array_ref (location_t loc, tree arg1, tree arg2,
   tree orig_arg1 = arg1;
   tree orig_arg2 = arg2;
   tree expr;
+  tree overload = NULL_TREE;
 
   if (processing_template_decl)
     {
@@ -3879,11 +3897,17 @@ build_x_array_ref (location_t loc, tree arg1, tree arg2,
     }
 
   expr = build_new_op (loc, ARRAY_REF, LOOKUP_NORMAL, arg1, arg2,
-		       NULL_TREE, /*overload=*/NULL, complain);
+		       NULL_TREE, &overload, complain);
 
   if (processing_template_decl && expr != error_mark_node)
-    return build_min_non_dep (ARRAY_REF, expr, orig_arg1, orig_arg2,
-			      NULL_TREE, NULL_TREE);
+    {
+      if (overload != NULL_TREE)
+	return (build_min_non_dep_op_overload
+		(ARRAY_REF, expr, overload, orig_arg1, orig_arg2));
+
+      return build_min_non_dep (ARRAY_REF, expr, orig_arg1, orig_arg2,
+				NULL_TREE, NULL_TREE);
+    }
   return expr;
 }
 
@@ -5278,6 +5302,7 @@ build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg,
   tree orig_expr = xarg;
   tree exp;
   int ptrmem = 0;
+  tree overload = NULL_TREE;
 
   if (processing_template_decl)
     {
@@ -5305,7 +5330,8 @@ build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg,
     /* Don't look for a function.  */;
   else
     exp = build_new_op (loc, code, LOOKUP_NORMAL, xarg, NULL_TREE,
-			NULL_TREE, /*overload=*/NULL, complain);
+			NULL_TREE, &overload, complain);
+
   if (!exp && code == ADDR_EXPR)
     {
       if (is_overloaded_fn (xarg))
@@ -5371,8 +5397,14 @@ build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg,
     }
 
   if (processing_template_decl && exp != error_mark_node)
-    exp = build_min_non_dep (code, exp, orig_expr,
-			     /*For {PRE,POST}{INC,DEC}REMENT_EXPR*/NULL_TREE);
+    {
+      if (overload != NULL_TREE)
+	return (build_min_non_dep_op_overload
+		(code, exp, overload, orig_expr, integer_zero_node));
+
+      exp = build_min_non_dep (code, exp, orig_expr,
+			       /*For {PRE,POST}{INC,DEC}REMENT_EXPR*/NULL_TREE);
+    }
   if (TREE_CODE (exp) == ADDR_EXPR)
     PTRMEM_OK_P (exp) = ptrmem;
   return exp;
@@ -6335,6 +6367,7 @@ build_x_compound_expr (location_t loc, tree op1, tree op2,
   tree result;
   tree orig_op1 = op1;
   tree orig_op2 = op2;
+  tree overload = NULL_TREE;
 
   if (processing_template_decl)
     {
@@ -6346,12 +6379,18 @@ build_x_compound_expr (location_t loc, tree op1, tree op2,
     }
 
   result = build_new_op (loc, COMPOUND_EXPR, LOOKUP_NORMAL, op1, op2,
-			 NULL_TREE, /*overload=*/NULL, complain);
+			 NULL_TREE, &overload, complain);
   if (!result)
     result = cp_build_compound_expr (op1, op2, complain);
 
   if (processing_template_decl && result != error_mark_node)
-    return build_min_non_dep (COMPOUND_EXPR, result, orig_op1, orig_op2);
+    {
+      if (overload != NULL_TREE)
+	return (build_min_non_dep_op_overload
+		(COMPOUND_EXPR, result, overload, orig_op1, orig_op2));
+
+      return build_min_non_dep (COMPOUND_EXPR, result, orig_op1, orig_op2);
+    }
 
   return result;
 }
@@ -7794,19 +7833,42 @@ cp_expr
 build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
 		     tree rhs, tsubst_flags_t complain)
 {
+  tree orig_lhs = lhs;
+  tree orig_rhs = rhs;
+  tree overload = NULL_TREE;
+  tree op = build_nt (modifycode, NULL_TREE, NULL_TREE);
+
   if (processing_template_decl)
-    return build_min_nt_loc (loc, MODOP_EXPR, lhs,
-			     build_min_nt_loc (loc, modifycode, NULL_TREE,
-					       NULL_TREE), rhs);
+    {
+      if (modifycode == NOP_EXPR
+	  || type_dependent_expression_p (lhs)
+	  || type_dependent_expression_p (rhs))
+        return build_min_nt_loc (loc, MODOP_EXPR, lhs,
+				 build_min_nt_loc (loc, modifycode, NULL_TREE,
+						   NULL_TREE), rhs);
+
+      lhs = build_non_dependent_expr (lhs);
+      rhs = build_non_dependent_expr (rhs);
+    }
 
   if (modifycode != NOP_EXPR)
     {
-      tree rval = build_new_op (loc, MODIFY_EXPR, LOOKUP_NORMAL, lhs, rhs,
-				make_node (modifycode), /*overload=*/NULL,
-				complain);
+      tree rval = build_new_op (loc, MODIFY_EXPR, LOOKUP_NORMAL,
+				lhs, rhs, op, &overload, complain);
       if (rval)
 	{
+	  if (rval == error_mark_node)
+	    return rval;
 	  TREE_NO_WARNING (rval) = 1;
+	  if (processing_template_decl)
+	    {
+	      if (overload != NULL_TREE)
+		return (build_min_non_dep_op_overload
+			(MODOP_EXPR, rval, overload, orig_lhs, orig_rhs));
+
+	      return (build_min_non_dep
+		      (MODOP_EXPR, rval, orig_lhs, op, orig_rhs));
+	    }
 	  return rval;
 	}
     }
diff --git a/gcc/testsuite/g++.dg/cpp0x/pr53223.C b/gcc/testsuite/g++.dg/cpp0x/pr53223.C
new file mode 100644
index 0000000..b6ccd50
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/pr53223.C
@@ -0,0 +1,45 @@
+// PR c++/53223
+// { dg-do compile { target c++11 } }
+
+#include <type_traits>
+
+#define SA(x) static_assert ((x), #x)
+
+struct A
+{
+  int good() const;
+  int operator *() const;
+  int operator ++() const;
+  int operator [](int) const;
+};
+
+int operator-- (const A&);
+
+template<typename T>
+void func(T t)
+{
+  A x;
+  auto &&g1 = x.good();
+  auto &&g2 = x.operator*();
+  auto &&error1 = *x;
+  auto &&error2 = ++x;
+  auto &&error3 = --x;
+  auto &&error4 = x[5];
+  SA ((std::is_same<int &&, decltype (error1)>::value));
+  SA ((std::is_same<int &&, decltype (error2)>::value));
+  SA ((std::is_same<int &&, decltype (error3)>::value));
+  SA ((std::is_same<int &&, decltype (error4)>::value));
+}
+
+void func2(int)
+{
+  A x;
+  auto &&g = *x;
+}
+
+int main()
+{
+  func(0);
+  func2(0);
+}
+
diff --git a/gcc/testsuite/g++.dg/lookup/pr21802.C b/gcc/testsuite/g++.dg/lookup/pr21802.C
new file mode 100644
index 0000000..139f7b4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/pr21802.C
@@ -0,0 +1,276 @@
+// PR c++/21802
+// { dg-do run }
+#include <cassert>
+
+struct X;
+int I = 6;
+
+/* A mostly exhaustive and ad-hoc assortment of operator overloads and calls
+   thereof, to stress-test two-stage name lookup of operators inside template
+   definitions and then to verify that the calls get built correctly.  */
+
+template <typename T>
+inline int operator+(const X &, T x) { return x; }
+inline int operator-(const X &, int x) { return x; }
+inline int operator*(const X &, int x) { return x; }
+inline int operator/(const X &, int x) { return x; }
+inline int operator+=(const X &, int x) { return x; }
+
+struct X
+{
+  X () : m (1) { }
+  template <typename T>
+  int operator%(T x) { return m + x; }
+  virtual int operator>>(int x) { return m + x; }
+  int operator<<(int x) { return m + x; }
+  int operator&(int x) { return m + x; }
+  int operator|(int x) { return m + x; }
+  int operator^(int x) { return m + x; }
+  int operator&&(int x) { return m + x; }
+  int operator||(int x) { return m + x; }
+  friend int operator==(X o, int x) { return o.m + x; }
+  int operator!=(int x) { return m + x; }
+  int operator<(int x) { return m + x; }
+  int operator<=(int x) { return m + x; }
+  int operator>(int x) { return m + x; }
+  int operator>=(int x) { return m + x; }
+  int operator*() { return m + I; }
+  int operator!() { return m + I; }
+  int operator~() { return m + I; }
+  int operator++() { return m + I + 100; }
+  int operator--() { return m + I + 100; }
+  int operator++(int) { return m + I; }
+  int operator--(int) { return m + I; }
+  int operator()() { return m + I; }
+  int operator,(int x) { return m + x; }
+  int operator[](int x) { return m + x; }
+  int operator*=(int x) { return m + x; }
+  int operator-=(int x) { return m + x; }
+  int operator/=(int x) { return m + x; }
+  virtual int operator& () { return m + I; }
+  int m;
+};
+struct Y : virtual X
+{
+  /* Virtual override.  */
+  int operator>>(int x) { return m + x + 1; }
+  int operator& () { return m + I + 1; }
+
+  /* Not virtual.  */
+  template <typename T>
+  int operator&(T x) { return m + x + 1; }
+  friend int operator==(Y o, int x) { return o.m + x + 1; }
+};
+
+/* The folloiwng "FooN" functions each contain a different way to call and to
+   resolve these operator overloads.  */
+
+template <typename T>
+void
+Foo1 (T)
+{
+  Y x;
+  { int t = x + I; assert (t == 6); }
+  { int t = x - I; assert (t == 6); }
+  { int t = x * I; assert (t == 6); }
+  { int t = x / I; assert (t == 6); }
+  { int t = (x+=I); assert (t == 6); }
+
+  { int t = x % I; assert (t == 7); }
+  { int t = x << I; assert (t == 7); }
+  { int t = x | I; assert (t == 7); }
+  { int t = x && I; assert (t == 7); }
+  { int t = x || I; assert (t == 7); }
+  { int t = x != I; assert (t == 7); }
+  { int t = x < I; assert (t == 7); }
+  { int t = x <= I; assert (t == 7); }
+  { int t = x > I; assert (t == 7); }
+  { int t = x >= I; assert (t == 7); }
+  { int t = *x; assert (t == 7); }
+  { int t = !x; assert (t == 7); }
+  { int t = ~x; assert (t == 7); }
+  { int t = x++; assert (t == 7); }
+  { int t = x--; assert (t == 7); }
+  { int t = ++x; assert (t == 107); }
+  { int t = --x; assert (t == 107); }
+  { int t = x (); assert (t == 7); }
+  { int t = (x, I); assert (t == 7); }
+  { int t = x[I]; assert (t == 7); }
+  { int t = (x-=I); assert (t == 7); }
+  { int t = (x/=I); assert (t == 7); }
+  { int t = (x*=I); assert (t == 7); }
+
+  { int t = x >> I; assert (t == 8); }
+  { int t = x & I; assert (t == 8); }
+  { int t = &x; assert (t == 8); }
+  { int t = x == I; assert (t == 8); }
+}
+
+template <typename T>
+void
+Foo2 (T)
+{
+  X x;
+  { int t = x + I; assert (t == 6); }
+  { int t = x - I; assert (t == 6); }
+  { int t = x * I; assert (t == 6); }
+  { int t = x / I; assert (t == 6); }
+  { int t = (x+=I); assert (t == 6); }
+
+  { int t = x % I; assert (t == 7); }
+  { int t = x >> I; assert (t == 7); }
+  { int t = x << I; assert (t == 7); }
+  { int t = x | I; assert (t == 7); }
+  { int t = x && I; assert (t == 7); }
+  { int t = x || I; assert (t == 7); }
+  { int t = x == I; assert (t == 7); }
+  { int t = x != I; assert (t == 7); }
+  { int t = x < I; assert (t == 7); }
+  { int t = x <= I; assert (t == 7); }
+  { int t = x > I; assert (t == 7); }
+  { int t = x >= I; assert (t == 7); }
+  { int t = *x; assert (t == 7); }
+  { int t = !x; assert (t == 7); }
+  { int t = ~x; assert (t == 7); }
+  { int t = x++; assert (t == 7); }
+  { int t = x--; assert (t == 7); }
+  { int t = ++x; assert (t == 107); }
+  { int t = --x; assert (t == 107); }
+  { int t = x (); assert (t == 7); }
+  { int t = (x, I); assert (t == 7); }
+  { int t = x[I]; assert (t == 7); }
+  { int t = &x; assert (t == 7); }
+  { int t = (x-=I); assert (t == 7); }
+  { int t = (x/=I); assert (t == 7); }
+  { int t = (x*=I); assert (t == 7); }
+  { int t = x & I; assert (t == 7); }
+}
+
+template <typename T>
+void
+Foo3 (T)
+{
+  Y o;
+  X &x = o;
+  { int t = x + I; assert (t == 6); }
+  { int t = x - I; assert (t == 6); }
+  { int t = x * I; assert (t == 6); }
+  { int t = x / I; assert (t == 6); }
+  { int t = (x+=I); assert (t == 6); }
+
+  { int t = x % I; assert (t == 7); }
+  { int t = x << I; assert (t == 7); }
+  { int t = x | I; assert (t == 7); }
+  { int t = x && I; assert (t == 7); }
+  { int t = x || I; assert (t == 7); }
+  { int t = x == I; assert (t == 7); }
+  { int t = x != I; assert (t == 7); }
+  { int t = x < I; assert (t == 7); }
+  { int t = x <= I; assert (t == 7); }
+  { int t = x > I; assert (t == 7); }
+  { int t = x >= I; assert (t == 7); }
+  { int t = *x; assert (t == 7); }
+  { int t = !x; assert (t == 7); }
+  { int t = ~x; assert (t == 7); }
+  { int t = x++; assert (t == 7); }
+  { int t = x--; assert (t == 7); }
+  { int t = ++x; assert (t == 107); }
+  { int t = --x; assert (t == 107); }
+  { int t = x (); assert (t == 7); }
+  { int t = (x, I); assert (t == 7); }
+  { int t = x[I]; assert (t == 7); }
+  { int t = (x-=I); assert (t == 7); }
+  { int t = (x/=I); assert (t == 7); }
+  { int t = (x*=I); assert (t == 7); }
+
+  { int t = x & I; assert (t == 7); }
+  { int t = x >> I; assert (t == 8); }
+  { int t = &x; assert (t == 8); }
+}
+
+template <typename T>
+void
+Foo4 (T)
+{
+  Y x;
+  { int t = operator+ (x, I); assert (t == 6); }
+  { int t = operator- (x, I); assert (t == 6); }
+  { int t = operator* (x, I); assert (t == 6); }
+  { int t = operator/ (x, I); assert (t == 6); }
+  { int t = operator+= (x, I); assert (t == 6); }
+
+  { int t = x.operator% (I); assert (t == 7); }
+  { int t = x.operator<< (I); assert (t == 7); }
+  { int t = x.operator| (I); assert (t == 7); }
+  { int t = x.operator&& (I); assert (t == 7); }
+  { int t = x.operator|| (I); assert (t == 7); }
+  { int t = x.operator!= (I); assert (t == 7); }
+  { int t = x.operator< (I); assert (t == 7); }
+  { int t = x.operator<= (I); assert (t == 7); }
+  { int t = x.operator> (I); assert (t == 7); }
+  { int t = x.operator>= (I); assert (t == 7); }
+  { int t = x.operator* (); assert (t == 7); }
+  { int t = x.operator! (); assert (t == 7); }
+  { int t = x.operator~ (); assert (t == 7); }
+  { int t = x.operator++ (0); assert (t == 7); }
+  { int t = x.operator-- (0); assert (t == 7); }
+  { int t = x.operator++ (); assert (t == 107); }
+  { int t = x.operator-- (); assert (t == 107); }
+  { int t = x.operator() (); assert (t == 7); }
+  { int t = x.operator, (I); assert (t == 7); }
+  { int t = x.operator[] (I); assert (t == 7); }
+  { int t = x.operator-= (I); assert (t == 7); }
+  { int t = x.operator/= (I); assert (t == 7); }
+  { int t = x.operator*= (I); assert (t == 7); }
+
+  { int t = x.operator>> (I); assert (t == 8); }
+  { int t = x.operator& (); assert (t == 8); }
+  { int t = x.operator& (I); assert (t == 8); }
+  { int t = operator== (x, I); assert (t == 8); }
+}
+
+
+/* These definitions should be irrelevant to operator lookup of non-dependent
+   expressions inside the above templates since they are not in scope at
+   template-definition time (even though they are in scope at instantiation
+   time).  */
+inline int operator+(const Y&, int) { return 11; }
+inline int operator-(const Y&, int) { return 11; }
+inline int operator*(const Y&, int) { return 11; }
+inline int operator/(const Y&, int) { return 11; }
+inline int operator%(const Y&, int) { return 11; }
+inline int operator>>(const Y&, int) { return 11; }
+inline int operator<<(const Y&, int) { return 11; }
+inline int operator&(const Y&, int) { return 11; }
+inline int operator|(const Y&, int) { return 11; }
+inline int operator^(const Y&, int) { return 11; }
+inline int operator&&(const Y&, int) { return 11; }
+inline int operator||(const Y&, int) { return 11; }
+inline int operator==(const Y&, int) { return 11; }
+inline int operator!=(const Y&, int) { return 11; }
+inline int operator<(const Y&, int) { return 11; }
+inline int operator<=(const Y&, int) { return 11; }
+inline int operator>(const Y&, int) { return 11; }
+inline int operator>=(const Y&, int) { return 11; }
+inline int operator*(const Y&) { return 11; }
+inline int operator!(const Y&) { return 11; }
+inline int operator~(const Y&) { return 11; }
+inline int operator++(const Y&) { return 11; }
+inline int operator--(const Y&) { return 11; }
+inline int operator++(const Y&, int) { return 11; }
+inline int operator--(const Y&, int) { return 11; }
+inline int operator,(const Y&, int) { return 11; }
+inline int operator&(const Y&) { return 11; }
+inline int operator+=(const Y&, int x) { return 11; }
+inline int operator*=(const Y&, int x) { return 11; }
+inline int operator-=(const Y&, int x) { return 11; }
+inline int operator/=(const Y&, int x) { return 11; }
+
+int
+main ()
+{
+  Foo1 (0);
+  Foo2 (0);
+  Foo3 (0);
+  Foo4 (0);
+}
diff --git a/gcc/testsuite/g++.dg/lookup/two-stage4.C b/gcc/testsuite/g++.dg/lookup/two-stage4.C
index 7d97109..a89e618 100644
--- a/gcc/testsuite/g++.dg/lookup/two-stage4.C
+++ b/gcc/testsuite/g++.dg/lookup/two-stage4.C
@@ -8,7 +8,7 @@ template<typename T> bool operator==(wrap<T>, wrap<T>);
 template<typename T>
 void g(T, wrap<wrap<int> > x)
 {
-  bool b = x == x; // { dg-bogus "" "" { xfail *-*-* } }
+  bool b = x == x; // { dg-bogus "" "" }
 }
 
 template<typename T> void operator==(wrap<wrap<T> >, wrap<wrap<T> >);
-- 
2.7.0.rc0.50.g1470d8f.dirty

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] Fix PR c++/21802 (two-stage name lookup fails for operators)
  2015-12-12 23:32       ` Patrick Palka
@ 2015-12-14 20:42         ` Jason Merrill
  2015-12-15  0:14           ` Patrick Palka
  0 siblings, 1 reply; 15+ messages in thread
From: Jason Merrill @ 2015-12-14 20:42 UTC (permalink / raw)
  To: Patrick Palka, gcc-patches

On 12/12/2015 06:32 PM, Patrick Palka wrote:
>> >This should use cp_tree_operand_length.
> Hmm, I don't immediately see how I can use this function here.  It
> expects a tree but I dont have an appropriate tree to give to it, only a
> tree_code.

True.  So let's introduce cp_tree_code_length next to 
cp_tree_operand_length.

Jason

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH] Fix PR c++/21802 (two-stage name lookup fails for operators)
  2015-12-14 20:42         ` Jason Merrill
@ 2015-12-15  0:14           ` Patrick Palka
  2015-12-15  0:34             ` Jason Merrill
  2015-12-16 19:25             ` [PATCH] Fix PR c++/21802 (two-stage name lookup fails for operators) Michael Matz
  0 siblings, 2 replies; 15+ messages in thread
From: Patrick Palka @ 2015-12-15  0:14 UTC (permalink / raw)
  To: gcc-patches; +Cc: jason, Patrick Palka

On Mon, 14 Dec 2015, Jason Merrill wrote:

> On 12/12/2015 06:32 PM, Patrick Palka wrote:
>>> >This should use cp_tree_operand_length.
>> Hmm, I don't immediately see how I can use this function here.  It
>> expects a tree but I dont have an appropriate tree to give to it, only a
>> tree_code.
>
> True.  So let's introduce cp_tree_code_length next to cp_tree_operand_length.
>
> Jason
>
>

Like this?  Incremental diff followed by patch v4:

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
 index 3487d77..59c70b2 100644
 --- a/gcc/cp/cp-tree.h
 +++ b/gcc/cp/cp-tree.h
 @@ -6477,6 +6477,7 @@ extern bool is_lambda_ignored_entity            (tree);

 /* in tree.c */
 extern int cp_tree_operand_length		(const_tree);
+extern int cp_tree_code_length			(enum tree_code);
 void cp_free_lang_data 				(tree t);
 extern tree force_target_expr			(tree, tree, tsubst_flags_t);
 extern tree build_target_expr_with_type		(tree, tree, tsubst_flags_t);
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
 index ca72877..6e671b7 100644
 --- a/gcc/cp/tree.c
 +++ b/gcc/cp/tree.c
 @@ -2766,14 +2766,10 @@ build_min_non_dep_op_overload (enum tree_code op,

   nargs = call_expr_nargs (non_dep);

-  if (op == PREINCREMENT_EXPR
-      || op == PREDECREMENT_EXPR)
-    expected_nargs = 1;
-  else if (op == MODOP_EXPR
-	   || op == ARRAY_REF)
-    expected_nargs = 2;
-  else
-    expected_nargs = TREE_CODE_LENGTH (op);
+  expected_nargs = cp_tree_code_length (op);
+  if (op == POSTINCREMENT_EXPR
+      || op == POSTDECREMENT_EXPR)
+    expected_nargs += 1;
   gcc_assert (nargs == expected_nargs);

   args = make_tree_vector ();
 @@ -4450,6 +4446,32 @@ cp_tree_operand_length (const_tree t)
     }
 }

+/* Like cp_tree_operand_length, but takes a tree_code CODE.  */
+
+int
+cp_tree_code_length (enum tree_code code)
+{
+  gcc_assert (TREE_CODE_CLASS (code) != tcc_vl_exp);
+
+  switch (code)
+    {
+    case PREINCREMENT_EXPR:
+    case PREDECREMENT_EXPR:
+    case POSTINCREMENT_EXPR:
+    case POSTDECREMENT_EXPR:
+      return 1;
+
+    case ARRAY_REF:
+      return 2;
+
+    case EXPR_PACK_EXPANSION:
+      return 1;
+
+    default:
+      return TREE_CODE_LENGTH (code);
+    }
+}
+
 /* Implement -Wzero_as_null_pointer_constant.  Return true if the
    conditions for the warning hold, false otherwise.  */
 bool
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
 index 2e5e46e..39c1af2 100644
 --- a/gcc/cp/typeck.c
 +++ b/gcc/cp/typeck.c
 @@ -7864,7 +7864,7 @@ build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
 	    {
 	      if (overload != NULL_TREE)
 		return (build_min_non_dep_op_overload
-			(MODOP_EXPR, rval, overload, orig_lhs, orig_rhs));
+			(MODIFY_EXPR, rval, overload, orig_lhs, orig_rhs));

 	      return (build_min_non_dep
 		      (MODOP_EXPR, rval, orig_lhs, op, orig_rhs));

-- 8< --

Bootstrapped and regtested on x86_64-pc-linux-gnu.

gcc/cp/ChangeLog:

	PR c++/21802
	PR c++/53223
	* cp-tree.h (cp_tree_code_length): Declare.
	(build_min_non_dep_op_overload): Declare.
	* tree.c (cp_tree_code_length): Define.
	(build_min_non_dep_op_overload): Define.
	(build_win_non_dep_call_vec): Copy the KOENIG_LOOKUP_P flag.
	* typeck.c (build_x_indirect_ref): Use
	build_min_non_dep_op_overload when the given expression
	has been resolved to an operator overload.
	(build_x_binary_op): Likewise.
	(build_x_array_ref): Likewise.
	(build_x_unary_op): Likewise.
	(build_x_compound_expr): Likewise.
	(build_x_modify_expr): Likewise.
	* decl2.c (grok_array_decl): Likewise.
	* call.c (build_new_op_1): If during template processing we
	chose an operator overload that is a hidden friend function, set
	the call's KOENIG_LOOKUP_P flag to 1.

gcc/testsuite/ChangeLog:

	PR c++/21802
	PR c++/53223
	* g++.dg/cpp0x/pr53223.C: New test.
	* g++.dg/lookup/pr21802.C: New test.
	* g++.dg/lookup/two-stage4.C: Remove XFAIL.
---
 gcc/cp/call.c                            |  13 ++
 gcc/cp/cp-tree.h                         |   2 +
 gcc/cp/decl2.c                           |  13 +-
 gcc/cp/tree.c                            |  89 ++++++++++
 gcc/cp/typeck.c                          | 100 ++++++++---
 gcc/testsuite/g++.dg/cpp0x/pr53223.C     |  45 +++++
 gcc/testsuite/g++.dg/lookup/pr21802.C    | 276 +++++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/lookup/two-stage4.C |   2 +-
 8 files changed, 517 insertions(+), 23 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/pr53223.C
 create mode 100644 gcc/testsuite/g++.dg/lookup/pr21802.C

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 117dd79..cdfa01a 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -5630,6 +5630,19 @@ build_new_op_1 (location_t loc, enum tree_code code, int flags, tree arg1,
 	    result = error_mark_node;
 	  else
 	    result = build_over_call (cand, LOOKUP_NORMAL, complain);
+
+	  if (processing_template_decl
+	      && result != NULL_TREE
+	      && result != error_mark_node
+	      && DECL_HIDDEN_FRIEND_P (cand->fn))
+	    {
+	      tree call = result;
+	      if (REFERENCE_REF_P (call))
+		call = TREE_OPERAND (call, 0);
+	      /* This prevents build_new_function_call from discarding this
+		 function during instantiation of the enclosing template.  */
+	      KOENIG_LOOKUP_P (call) = 1;
+	    }
 	}
       else
 	{
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 6190f4e..d5e3acd 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6477,6 +6477,7 @@ extern bool is_lambda_ignored_entity            (tree);
 
 /* in tree.c */
 extern int cp_tree_operand_length		(const_tree);
+extern int cp_tree_code_length			(enum tree_code);
 void cp_free_lang_data 				(tree t);
 extern tree force_target_expr			(tree, tree, tsubst_flags_t);
 extern tree build_target_expr_with_type		(tree, tree, tsubst_flags_t);
@@ -6513,6 +6514,7 @@ extern tree build_min				(enum tree_code, tree, ...);
 extern tree build_min_nt_loc			(location_t, enum tree_code,
 						 ...);
 extern tree build_min_non_dep			(enum tree_code, tree, ...);
+extern tree build_min_non_dep_op_overload	(enum tree_code, tree, tree, ...);
 extern tree build_min_non_dep_call_vec		(tree, tree, vec<tree, va_gc> *);
 extern tree build_cplus_new			(tree, tree, tsubst_flags_t);
 extern tree build_aggr_init_expr		(tree, tree);
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 2cc51d6..5ae6266 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -354,6 +354,7 @@ grok_array_decl (location_t loc, tree array_expr, tree index_exp,
   tree expr;
   tree orig_array_expr = array_expr;
   tree orig_index_exp = index_exp;
+  tree overload = NULL_TREE;
 
   if (error_operand_p (array_expr) || error_operand_p (index_exp))
     return error_mark_node;
@@ -379,7 +380,7 @@ grok_array_decl (location_t loc, tree array_expr, tree index_exp,
       if (decltype_p)
 	complain |= tf_decltype;
       expr = build_new_op (loc, ARRAY_REF, LOOKUP_NORMAL, array_expr,
-			   index_exp, NULL_TREE, /*overload=*/NULL, complain);
+			   index_exp, NULL_TREE, &overload, complain);
     }
   else
     {
@@ -424,8 +425,14 @@ grok_array_decl (location_t loc, tree array_expr, tree index_exp,
       expr = build_array_ref (input_location, array_expr, index_exp);
     }
   if (processing_template_decl && expr != error_mark_node)
-    return build_min_non_dep (ARRAY_REF, expr, orig_array_expr, orig_index_exp,
-			      NULL_TREE, NULL_TREE);
+    {
+      if (overload != NULL_TREE)
+	return (build_min_non_dep_op_overload
+		(ARRAY_REF, expr, overload, orig_array_expr, orig_index_exp));
+
+      return build_min_non_dep (ARRAY_REF, expr, orig_array_expr, orig_index_exp,
+				NULL_TREE, NULL_TREE);
+    }
   return expr;
 }
 
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 5dad0a7..0c0987d 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -2741,9 +2741,72 @@ build_min_non_dep_call_vec (tree non_dep, tree fn, vec<tree, va_gc> *argvec)
     non_dep = TREE_OPERAND (non_dep, 0);
   TREE_TYPE (t) = TREE_TYPE (non_dep);
   TREE_SIDE_EFFECTS (t) = TREE_SIDE_EFFECTS (non_dep);
+  KOENIG_LOOKUP_P (t) = KOENIG_LOOKUP_P (non_dep);
   return convert_from_reference (t);
 }
 
+/* Similar to build_min_non_dep, but for expressions that have been resolved to
+   a call to an operator overload.  OP is the operator that has been
+   overloaded.  NON_DEP is the non-dependent expression that's been built,
+   which should be a CALL_EXPR or an INDIRECT_REF to a CALL_EXPR.  OVERLOAD is
+   the overload that NON_DEP is calling.  */
+
+tree
+build_min_non_dep_op_overload (enum tree_code op,
+			       tree non_dep,
+			       tree overload, ...)
+{
+  va_list p;
+  int nargs, expected_nargs;
+  tree fn, call;
+  vec<tree, va_gc> *args;
+
+  if (REFERENCE_REF_P (non_dep))
+    non_dep = TREE_OPERAND (non_dep, 0);
+
+  nargs = call_expr_nargs (non_dep);
+
+  expected_nargs = cp_tree_code_length (op);
+  if (op == POSTINCREMENT_EXPR
+      || op == POSTDECREMENT_EXPR)
+    expected_nargs += 1;
+  gcc_assert (nargs == expected_nargs);
+
+  args = make_tree_vector ();
+  va_start (p, overload);
+
+  if (TREE_CODE (TREE_TYPE (overload)) == FUNCTION_TYPE)
+    {
+      fn = overload;
+      for (int i = 0; i < nargs; i++)
+	{
+	  tree arg = va_arg (p, tree);
+	  vec_safe_push (args, arg);
+	}
+    }
+  else if (TREE_CODE (TREE_TYPE (overload)) == METHOD_TYPE)
+    {
+      tree object = va_arg (p, tree);
+      tree binfo = TYPE_BINFO (TREE_TYPE (object));
+      tree method = build_baselink (binfo, binfo, overload, NULL_TREE);
+      fn = build_min (COMPONENT_REF, TREE_TYPE (overload),
+		      object, method, NULL_TREE);
+      for (int i = 1; i < nargs; i++)
+	{
+	  tree arg = va_arg (p, tree);
+	  vec_safe_push (args, arg);
+	}
+    }
+  else
+   gcc_unreachable ();
+
+  va_end (p);
+  call = build_min_non_dep_call_vec (non_dep, fn, args);
+  release_tree_vector (args);
+
+  return call;
+}
+
 tree
 get_type_decl (tree t)
 {
@@ -4383,6 +4446,32 @@ cp_tree_operand_length (const_tree t)
     }
 }
 
+/* Like cp_tree_operand_length, but takes a tree_code CODE.  */
+
+int
+cp_tree_code_length (enum tree_code code)
+{
+  gcc_assert (TREE_CODE_CLASS (code) != tcc_vl_exp);
+
+  switch (code)
+    {
+    case PREINCREMENT_EXPR:
+    case PREDECREMENT_EXPR:
+    case POSTINCREMENT_EXPR:
+    case POSTDECREMENT_EXPR:
+      return 1;
+
+    case ARRAY_REF:
+      return 2;
+
+    case EXPR_PACK_EXPANSION:
+      return 1;
+
+    default:
+      return TREE_CODE_LENGTH (code);
+    }
+}
+
 /* Implement -Wzero_as_null_pointer_constant.  Return true if the
    conditions for the warning hold, false otherwise.  */
 bool
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 17671ee..39c1af2 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -2905,6 +2905,7 @@ build_x_indirect_ref (location_t loc, tree expr, ref_operator errorstring,
 {
   tree orig_expr = expr;
   tree rval;
+  tree overload = NULL_TREE;
 
   if (processing_template_decl)
     {
@@ -2917,12 +2918,18 @@ build_x_indirect_ref (location_t loc, tree expr, ref_operator errorstring,
     }
 
   rval = build_new_op (loc, INDIRECT_REF, LOOKUP_NORMAL, expr,
-		       NULL_TREE, NULL_TREE, /*overload=*/NULL, complain);
+		       NULL_TREE, NULL_TREE, &overload, complain);
   if (!rval)
     rval = cp_build_indirect_ref (expr, errorstring, complain);
 
   if (processing_template_decl && rval != error_mark_node)
-    return build_min_non_dep (INDIRECT_REF, rval, orig_expr);
+    {
+      if (overload != NULL_TREE)
+	return (build_min_non_dep_op_overload
+		(INDIRECT_REF, rval, overload, orig_expr));
+
+      return build_min_non_dep (INDIRECT_REF, rval, orig_expr);
+    }
   else
     return rval;
 }
@@ -3814,12 +3821,13 @@ convert_arguments (tree typelist, vec<tree, va_gc> **values, tree fndecl,
 tree
 build_x_binary_op (location_t loc, enum tree_code code, tree arg1,
 		   enum tree_code arg1_code, tree arg2,
-		   enum tree_code arg2_code, tree *overload,
+		   enum tree_code arg2_code, tree *overload_p,
 		   tsubst_flags_t complain)
 {
   tree orig_arg1;
   tree orig_arg2;
   tree expr;
+  tree overload = NULL_TREE;
 
   orig_arg1 = arg1;
   orig_arg2 = arg2;
@@ -3837,7 +3845,10 @@ build_x_binary_op (location_t loc, enum tree_code code, tree arg1,
     expr = build_m_component_ref (arg1, arg2, complain);
   else
     expr = build_new_op (loc, code, LOOKUP_NORMAL, arg1, arg2, NULL_TREE,
-			 overload, complain);
+			 &overload, complain);
+
+  if (overload_p != NULL)
+    *overload_p = overload;
 
   /* Check for cases such as x+y<<z which users are likely to
      misinterpret.  But don't warn about obj << x + y, since that is a
@@ -3853,7 +3864,13 @@ build_x_binary_op (location_t loc, enum tree_code code, tree arg1,
 			    arg2_code, orig_arg2);
 
   if (processing_template_decl && expr != error_mark_node)
-    return build_min_non_dep (code, expr, orig_arg1, orig_arg2);
+    {
+      if (overload != NULL_TREE)
+	return (build_min_non_dep_op_overload
+		(code, expr, overload, orig_arg1, orig_arg2));
+
+      return build_min_non_dep (code, expr, orig_arg1, orig_arg2);
+    }
 
   return expr;
 }
@@ -3867,6 +3884,7 @@ build_x_array_ref (location_t loc, tree arg1, tree arg2,
   tree orig_arg1 = arg1;
   tree orig_arg2 = arg2;
   tree expr;
+  tree overload = NULL_TREE;
 
   if (processing_template_decl)
     {
@@ -3879,11 +3897,17 @@ build_x_array_ref (location_t loc, tree arg1, tree arg2,
     }
 
   expr = build_new_op (loc, ARRAY_REF, LOOKUP_NORMAL, arg1, arg2,
-		       NULL_TREE, /*overload=*/NULL, complain);
+		       NULL_TREE, &overload, complain);
 
   if (processing_template_decl && expr != error_mark_node)
-    return build_min_non_dep (ARRAY_REF, expr, orig_arg1, orig_arg2,
-			      NULL_TREE, NULL_TREE);
+    {
+      if (overload != NULL_TREE)
+	return (build_min_non_dep_op_overload
+		(ARRAY_REF, expr, overload, orig_arg1, orig_arg2));
+
+      return build_min_non_dep (ARRAY_REF, expr, orig_arg1, orig_arg2,
+				NULL_TREE, NULL_TREE);
+    }
   return expr;
 }
 
@@ -5278,6 +5302,7 @@ build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg,
   tree orig_expr = xarg;
   tree exp;
   int ptrmem = 0;
+  tree overload = NULL_TREE;
 
   if (processing_template_decl)
     {
@@ -5305,7 +5330,8 @@ build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg,
     /* Don't look for a function.  */;
   else
     exp = build_new_op (loc, code, LOOKUP_NORMAL, xarg, NULL_TREE,
-			NULL_TREE, /*overload=*/NULL, complain);
+			NULL_TREE, &overload, complain);
+
   if (!exp && code == ADDR_EXPR)
     {
       if (is_overloaded_fn (xarg))
@@ -5371,8 +5397,14 @@ build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg,
     }
 
   if (processing_template_decl && exp != error_mark_node)
-    exp = build_min_non_dep (code, exp, orig_expr,
-			     /*For {PRE,POST}{INC,DEC}REMENT_EXPR*/NULL_TREE);
+    {
+      if (overload != NULL_TREE)
+	return (build_min_non_dep_op_overload
+		(code, exp, overload, orig_expr, integer_zero_node));
+
+      exp = build_min_non_dep (code, exp, orig_expr,
+			       /*For {PRE,POST}{INC,DEC}REMENT_EXPR*/NULL_TREE);
+    }
   if (TREE_CODE (exp) == ADDR_EXPR)
     PTRMEM_OK_P (exp) = ptrmem;
   return exp;
@@ -6335,6 +6367,7 @@ build_x_compound_expr (location_t loc, tree op1, tree op2,
   tree result;
   tree orig_op1 = op1;
   tree orig_op2 = op2;
+  tree overload = NULL_TREE;
 
   if (processing_template_decl)
     {
@@ -6346,12 +6379,18 @@ build_x_compound_expr (location_t loc, tree op1, tree op2,
     }
 
   result = build_new_op (loc, COMPOUND_EXPR, LOOKUP_NORMAL, op1, op2,
-			 NULL_TREE, /*overload=*/NULL, complain);
+			 NULL_TREE, &overload, complain);
   if (!result)
     result = cp_build_compound_expr (op1, op2, complain);
 
   if (processing_template_decl && result != error_mark_node)
-    return build_min_non_dep (COMPOUND_EXPR, result, orig_op1, orig_op2);
+    {
+      if (overload != NULL_TREE)
+	return (build_min_non_dep_op_overload
+		(COMPOUND_EXPR, result, overload, orig_op1, orig_op2));
+
+      return build_min_non_dep (COMPOUND_EXPR, result, orig_op1, orig_op2);
+    }
 
   return result;
 }
@@ -7794,19 +7833,42 @@ cp_expr
 build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
 		     tree rhs, tsubst_flags_t complain)
 {
+  tree orig_lhs = lhs;
+  tree orig_rhs = rhs;
+  tree overload = NULL_TREE;
+  tree op = build_nt (modifycode, NULL_TREE, NULL_TREE);
+
   if (processing_template_decl)
-    return build_min_nt_loc (loc, MODOP_EXPR, lhs,
-			     build_min_nt_loc (loc, modifycode, NULL_TREE,
-					       NULL_TREE), rhs);
+    {
+      if (modifycode == NOP_EXPR
+	  || type_dependent_expression_p (lhs)
+	  || type_dependent_expression_p (rhs))
+        return build_min_nt_loc (loc, MODOP_EXPR, lhs,
+				 build_min_nt_loc (loc, modifycode, NULL_TREE,
+						   NULL_TREE), rhs);
+
+      lhs = build_non_dependent_expr (lhs);
+      rhs = build_non_dependent_expr (rhs);
+    }
 
   if (modifycode != NOP_EXPR)
     {
-      tree rval = build_new_op (loc, MODIFY_EXPR, LOOKUP_NORMAL, lhs, rhs,
-				make_node (modifycode), /*overload=*/NULL,
-				complain);
+      tree rval = build_new_op (loc, MODIFY_EXPR, LOOKUP_NORMAL,
+				lhs, rhs, op, &overload, complain);
       if (rval)
 	{
+	  if (rval == error_mark_node)
+	    return rval;
 	  TREE_NO_WARNING (rval) = 1;
+	  if (processing_template_decl)
+	    {
+	      if (overload != NULL_TREE)
+		return (build_min_non_dep_op_overload
+			(MODIFY_EXPR, rval, overload, orig_lhs, orig_rhs));
+
+	      return (build_min_non_dep
+		      (MODOP_EXPR, rval, orig_lhs, op, orig_rhs));
+	    }
 	  return rval;
 	}
     }
diff --git a/gcc/testsuite/g++.dg/cpp0x/pr53223.C b/gcc/testsuite/g++.dg/cpp0x/pr53223.C
new file mode 100644
index 0000000..b6ccd50
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/pr53223.C
@@ -0,0 +1,45 @@
+// PR c++/53223
+// { dg-do compile { target c++11 } }
+
+#include <type_traits>
+
+#define SA(x) static_assert ((x), #x)
+
+struct A
+{
+  int good() const;
+  int operator *() const;
+  int operator ++() const;
+  int operator [](int) const;
+};
+
+int operator-- (const A&);
+
+template<typename T>
+void func(T t)
+{
+  A x;
+  auto &&g1 = x.good();
+  auto &&g2 = x.operator*();
+  auto &&error1 = *x;
+  auto &&error2 = ++x;
+  auto &&error3 = --x;
+  auto &&error4 = x[5];
+  SA ((std::is_same<int &&, decltype (error1)>::value));
+  SA ((std::is_same<int &&, decltype (error2)>::value));
+  SA ((std::is_same<int &&, decltype (error3)>::value));
+  SA ((std::is_same<int &&, decltype (error4)>::value));
+}
+
+void func2(int)
+{
+  A x;
+  auto &&g = *x;
+}
+
+int main()
+{
+  func(0);
+  func2(0);
+}
+
diff --git a/gcc/testsuite/g++.dg/lookup/pr21802.C b/gcc/testsuite/g++.dg/lookup/pr21802.C
new file mode 100644
index 0000000..139f7b4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/pr21802.C
@@ -0,0 +1,276 @@
+// PR c++/21802
+// { dg-do run }
+#include <cassert>
+
+struct X;
+int I = 6;
+
+/* A mostly exhaustive and ad-hoc assortment of operator overloads and calls
+   thereof, to stress-test two-stage name lookup of operators inside template
+   definitions and then to verify that the calls get built correctly.  */
+
+template <typename T>
+inline int operator+(const X &, T x) { return x; }
+inline int operator-(const X &, int x) { return x; }
+inline int operator*(const X &, int x) { return x; }
+inline int operator/(const X &, int x) { return x; }
+inline int operator+=(const X &, int x) { return x; }
+
+struct X
+{
+  X () : m (1) { }
+  template <typename T>
+  int operator%(T x) { return m + x; }
+  virtual int operator>>(int x) { return m + x; }
+  int operator<<(int x) { return m + x; }
+  int operator&(int x) { return m + x; }
+  int operator|(int x) { return m + x; }
+  int operator^(int x) { return m + x; }
+  int operator&&(int x) { return m + x; }
+  int operator||(int x) { return m + x; }
+  friend int operator==(X o, int x) { return o.m + x; }
+  int operator!=(int x) { return m + x; }
+  int operator<(int x) { return m + x; }
+  int operator<=(int x) { return m + x; }
+  int operator>(int x) { return m + x; }
+  int operator>=(int x) { return m + x; }
+  int operator*() { return m + I; }
+  int operator!() { return m + I; }
+  int operator~() { return m + I; }
+  int operator++() { return m + I + 100; }
+  int operator--() { return m + I + 100; }
+  int operator++(int) { return m + I; }
+  int operator--(int) { return m + I; }
+  int operator()() { return m + I; }
+  int operator,(int x) { return m + x; }
+  int operator[](int x) { return m + x; }
+  int operator*=(int x) { return m + x; }
+  int operator-=(int x) { return m + x; }
+  int operator/=(int x) { return m + x; }
+  virtual int operator& () { return m + I; }
+  int m;
+};
+struct Y : virtual X
+{
+  /* Virtual override.  */
+  int operator>>(int x) { return m + x + 1; }
+  int operator& () { return m + I + 1; }
+
+  /* Not virtual.  */
+  template <typename T>
+  int operator&(T x) { return m + x + 1; }
+  friend int operator==(Y o, int x) { return o.m + x + 1; }
+};
+
+/* The folloiwng "FooN" functions each contain a different way to call and to
+   resolve these operator overloads.  */
+
+template <typename T>
+void
+Foo1 (T)
+{
+  Y x;
+  { int t = x + I; assert (t == 6); }
+  { int t = x - I; assert (t == 6); }
+  { int t = x * I; assert (t == 6); }
+  { int t = x / I; assert (t == 6); }
+  { int t = (x+=I); assert (t == 6); }
+
+  { int t = x % I; assert (t == 7); }
+  { int t = x << I; assert (t == 7); }
+  { int t = x | I; assert (t == 7); }
+  { int t = x && I; assert (t == 7); }
+  { int t = x || I; assert (t == 7); }
+  { int t = x != I; assert (t == 7); }
+  { int t = x < I; assert (t == 7); }
+  { int t = x <= I; assert (t == 7); }
+  { int t = x > I; assert (t == 7); }
+  { int t = x >= I; assert (t == 7); }
+  { int t = *x; assert (t == 7); }
+  { int t = !x; assert (t == 7); }
+  { int t = ~x; assert (t == 7); }
+  { int t = x++; assert (t == 7); }
+  { int t = x--; assert (t == 7); }
+  { int t = ++x; assert (t == 107); }
+  { int t = --x; assert (t == 107); }
+  { int t = x (); assert (t == 7); }
+  { int t = (x, I); assert (t == 7); }
+  { int t = x[I]; assert (t == 7); }
+  { int t = (x-=I); assert (t == 7); }
+  { int t = (x/=I); assert (t == 7); }
+  { int t = (x*=I); assert (t == 7); }
+
+  { int t = x >> I; assert (t == 8); }
+  { int t = x & I; assert (t == 8); }
+  { int t = &x; assert (t == 8); }
+  { int t = x == I; assert (t == 8); }
+}
+
+template <typename T>
+void
+Foo2 (T)
+{
+  X x;
+  { int t = x + I; assert (t == 6); }
+  { int t = x - I; assert (t == 6); }
+  { int t = x * I; assert (t == 6); }
+  { int t = x / I; assert (t == 6); }
+  { int t = (x+=I); assert (t == 6); }
+
+  { int t = x % I; assert (t == 7); }
+  { int t = x >> I; assert (t == 7); }
+  { int t = x << I; assert (t == 7); }
+  { int t = x | I; assert (t == 7); }
+  { int t = x && I; assert (t == 7); }
+  { int t = x || I; assert (t == 7); }
+  { int t = x == I; assert (t == 7); }
+  { int t = x != I; assert (t == 7); }
+  { int t = x < I; assert (t == 7); }
+  { int t = x <= I; assert (t == 7); }
+  { int t = x > I; assert (t == 7); }
+  { int t = x >= I; assert (t == 7); }
+  { int t = *x; assert (t == 7); }
+  { int t = !x; assert (t == 7); }
+  { int t = ~x; assert (t == 7); }
+  { int t = x++; assert (t == 7); }
+  { int t = x--; assert (t == 7); }
+  { int t = ++x; assert (t == 107); }
+  { int t = --x; assert (t == 107); }
+  { int t = x (); assert (t == 7); }
+  { int t = (x, I); assert (t == 7); }
+  { int t = x[I]; assert (t == 7); }
+  { int t = &x; assert (t == 7); }
+  { int t = (x-=I); assert (t == 7); }
+  { int t = (x/=I); assert (t == 7); }
+  { int t = (x*=I); assert (t == 7); }
+  { int t = x & I; assert (t == 7); }
+}
+
+template <typename T>
+void
+Foo3 (T)
+{
+  Y o;
+  X &x = o;
+  { int t = x + I; assert (t == 6); }
+  { int t = x - I; assert (t == 6); }
+  { int t = x * I; assert (t == 6); }
+  { int t = x / I; assert (t == 6); }
+  { int t = (x+=I); assert (t == 6); }
+
+  { int t = x % I; assert (t == 7); }
+  { int t = x << I; assert (t == 7); }
+  { int t = x | I; assert (t == 7); }
+  { int t = x && I; assert (t == 7); }
+  { int t = x || I; assert (t == 7); }
+  { int t = x == I; assert (t == 7); }
+  { int t = x != I; assert (t == 7); }
+  { int t = x < I; assert (t == 7); }
+  { int t = x <= I; assert (t == 7); }
+  { int t = x > I; assert (t == 7); }
+  { int t = x >= I; assert (t == 7); }
+  { int t = *x; assert (t == 7); }
+  { int t = !x; assert (t == 7); }
+  { int t = ~x; assert (t == 7); }
+  { int t = x++; assert (t == 7); }
+  { int t = x--; assert (t == 7); }
+  { int t = ++x; assert (t == 107); }
+  { int t = --x; assert (t == 107); }
+  { int t = x (); assert (t == 7); }
+  { int t = (x, I); assert (t == 7); }
+  { int t = x[I]; assert (t == 7); }
+  { int t = (x-=I); assert (t == 7); }
+  { int t = (x/=I); assert (t == 7); }
+  { int t = (x*=I); assert (t == 7); }
+
+  { int t = x & I; assert (t == 7); }
+  { int t = x >> I; assert (t == 8); }
+  { int t = &x; assert (t == 8); }
+}
+
+template <typename T>
+void
+Foo4 (T)
+{
+  Y x;
+  { int t = operator+ (x, I); assert (t == 6); }
+  { int t = operator- (x, I); assert (t == 6); }
+  { int t = operator* (x, I); assert (t == 6); }
+  { int t = operator/ (x, I); assert (t == 6); }
+  { int t = operator+= (x, I); assert (t == 6); }
+
+  { int t = x.operator% (I); assert (t == 7); }
+  { int t = x.operator<< (I); assert (t == 7); }
+  { int t = x.operator| (I); assert (t == 7); }
+  { int t = x.operator&& (I); assert (t == 7); }
+  { int t = x.operator|| (I); assert (t == 7); }
+  { int t = x.operator!= (I); assert (t == 7); }
+  { int t = x.operator< (I); assert (t == 7); }
+  { int t = x.operator<= (I); assert (t == 7); }
+  { int t = x.operator> (I); assert (t == 7); }
+  { int t = x.operator>= (I); assert (t == 7); }
+  { int t = x.operator* (); assert (t == 7); }
+  { int t = x.operator! (); assert (t == 7); }
+  { int t = x.operator~ (); assert (t == 7); }
+  { int t = x.operator++ (0); assert (t == 7); }
+  { int t = x.operator-- (0); assert (t == 7); }
+  { int t = x.operator++ (); assert (t == 107); }
+  { int t = x.operator-- (); assert (t == 107); }
+  { int t = x.operator() (); assert (t == 7); }
+  { int t = x.operator, (I); assert (t == 7); }
+  { int t = x.operator[] (I); assert (t == 7); }
+  { int t = x.operator-= (I); assert (t == 7); }
+  { int t = x.operator/= (I); assert (t == 7); }
+  { int t = x.operator*= (I); assert (t == 7); }
+
+  { int t = x.operator>> (I); assert (t == 8); }
+  { int t = x.operator& (); assert (t == 8); }
+  { int t = x.operator& (I); assert (t == 8); }
+  { int t = operator== (x, I); assert (t == 8); }
+}
+
+
+/* These definitions should be irrelevant to operator lookup of non-dependent
+   expressions inside the above templates since they are not in scope at
+   template-definition time (even though they are in scope at instantiation
+   time).  */
+inline int operator+(const Y&, int) { return 11; }
+inline int operator-(const Y&, int) { return 11; }
+inline int operator*(const Y&, int) { return 11; }
+inline int operator/(const Y&, int) { return 11; }
+inline int operator%(const Y&, int) { return 11; }
+inline int operator>>(const Y&, int) { return 11; }
+inline int operator<<(const Y&, int) { return 11; }
+inline int operator&(const Y&, int) { return 11; }
+inline int operator|(const Y&, int) { return 11; }
+inline int operator^(const Y&, int) { return 11; }
+inline int operator&&(const Y&, int) { return 11; }
+inline int operator||(const Y&, int) { return 11; }
+inline int operator==(const Y&, int) { return 11; }
+inline int operator!=(const Y&, int) { return 11; }
+inline int operator<(const Y&, int) { return 11; }
+inline int operator<=(const Y&, int) { return 11; }
+inline int operator>(const Y&, int) { return 11; }
+inline int operator>=(const Y&, int) { return 11; }
+inline int operator*(const Y&) { return 11; }
+inline int operator!(const Y&) { return 11; }
+inline int operator~(const Y&) { return 11; }
+inline int operator++(const Y&) { return 11; }
+inline int operator--(const Y&) { return 11; }
+inline int operator++(const Y&, int) { return 11; }
+inline int operator--(const Y&, int) { return 11; }
+inline int operator,(const Y&, int) { return 11; }
+inline int operator&(const Y&) { return 11; }
+inline int operator+=(const Y&, int x) { return 11; }
+inline int operator*=(const Y&, int x) { return 11; }
+inline int operator-=(const Y&, int x) { return 11; }
+inline int operator/=(const Y&, int x) { return 11; }
+
+int
+main ()
+{
+  Foo1 (0);
+  Foo2 (0);
+  Foo3 (0);
+  Foo4 (0);
+}
diff --git a/gcc/testsuite/g++.dg/lookup/two-stage4.C b/gcc/testsuite/g++.dg/lookup/two-stage4.C
index 7d97109..a89e618 100644
--- a/gcc/testsuite/g++.dg/lookup/two-stage4.C
+++ b/gcc/testsuite/g++.dg/lookup/two-stage4.C
@@ -8,7 +8,7 @@ template<typename T> bool operator==(wrap<T>, wrap<T>);
 template<typename T>
 void g(T, wrap<wrap<int> > x)
 {
-  bool b = x == x; // { dg-bogus "" "" { xfail *-*-* } }
+  bool b = x == x; // { dg-bogus "" "" }
 }
 
 template<typename T> void operator==(wrap<wrap<T> >, wrap<wrap<T> >);
-- 
2.7.0.rc0.50.g1470d8f.dirty

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] Fix PR c++/21802 (two-stage name lookup fails for operators)
  2015-12-15  0:14           ` Patrick Palka
@ 2015-12-15  0:34             ` Jason Merrill
  2015-12-16 21:46               ` Markus Trippelsdorf
  2015-12-16 19:25             ` [PATCH] Fix PR c++/21802 (two-stage name lookup fails for operators) Michael Matz
  1 sibling, 1 reply; 15+ messages in thread
From: Jason Merrill @ 2015-12-15  0:34 UTC (permalink / raw)
  To: Patrick Palka, gcc-patches

OK.

Jason

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] Fix PR c++/21802 (two-stage name lookup fails for operators)
  2015-12-15  0:14           ` Patrick Palka
  2015-12-15  0:34             ` Jason Merrill
@ 2015-12-16 19:25             ` Michael Matz
  2015-12-16 21:31               ` Patrick Palka
  1 sibling, 1 reply; 15+ messages in thread
From: Michael Matz @ 2015-12-16 19:25 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches, jason

Hi,

On Mon, 14 Dec 2015, Patrick Palka wrote:

> >>> >This should use cp_tree_operand_length.
> >> Hmm, I don't immediately see how I can use this function here.  It
> >> expects a tree but I dont have an appropriate tree to give to it, only a
> >> tree_code.
> >
> > True.  So let's introduce cp_tree_code_length next to cp_tree_operand_length.
> >
> > Jason
> >
> >
> 
> Like this?  Incremental diff followed by patch v4:

Not my turf, but if I may make a suggestion: please use the new function 
in the old one instead of duplicating the implementation.


Ciao,
Michael.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] Fix PR c++/21802 (two-stage name lookup fails for operators)
  2015-12-16 19:25             ` [PATCH] Fix PR c++/21802 (two-stage name lookup fails for operators) Michael Matz
@ 2015-12-16 21:31               ` Patrick Palka
  2015-12-16 21:33                 ` Jason Merrill
  0 siblings, 1 reply; 15+ messages in thread
From: Patrick Palka @ 2015-12-16 21:31 UTC (permalink / raw)
  To: Michael Matz; +Cc: Patrick Palka, gcc-patches, jason

On Wed, 16 Dec 2015, Michael Matz wrote:

> Hi,
>
> On Mon, 14 Dec 2015, Patrick Palka wrote:
>
>>>>>> This should use cp_tree_operand_length.
>>>> Hmm, I don't immediately see how I can use this function here.  It
>>>> expects a tree but I dont have an appropriate tree to give to it, only a
>>>> tree_code.
>>>
>>> True.  So let's introduce cp_tree_code_length next to cp_tree_operand_length.
>>>
>>> Jason
>>>
>>>
>>
>> Like this?  Incremental diff followed by patch v4:
>
> Not my turf, but if I may make a suggestion: please use the new function
> in the old one instead of duplicating the implementation.

The implementation is not exactly a duplicate since the old function
returns TREE_OPERAND_LENGTH in the default case and the new function
returns TREE_CODE_LENGTH in the default case.

It's still doable though. Defining one in terms of the other would look
like this.  Is this patch OK to commit after testing?

-- 8< --

Subject: [PATCH] Avoid code duplication in cp_tree_[operand|code]_length

gcc/cp/ChangeLog:

 	* tree.c (cp_tree_operand_length): Define in terms of
 	cp_tree_code_length.
---
  gcc/cp/tree.c | 19 +++----------------
  1 file changed, 3 insertions(+), 16 deletions(-)

diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 0c0987d..ae176d0 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -4427,23 +4427,10 @@ cp_tree_operand_length (const_tree t)
  {
    enum tree_code code = TREE_CODE (t);

-  switch (code)
-    {
-    case PREINCREMENT_EXPR:
-    case PREDECREMENT_EXPR:
-    case POSTINCREMENT_EXPR:
-    case POSTDECREMENT_EXPR:
-      return 1;
+  if (TREE_CODE_CLASS (code) == tcc_vl_exp)
+    return VL_EXP_OPERAND_LENGTH (t);

-    case ARRAY_REF:
-      return 2;
-
-    case EXPR_PACK_EXPANSION:
-      return 1;
-
-    default:
-      return TREE_OPERAND_LENGTH (t);
-    }
+  return cp_tree_code_length (code);
  }

  /* Like cp_tree_operand_length, but takes a tree_code CODE.  */
-- 
2.7.0.rc0.50.g1470d8f.dirty



>
>
> Ciao,
> Michael.
>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] Fix PR c++/21802 (two-stage name lookup fails for operators)
  2015-12-16 21:31               ` Patrick Palka
@ 2015-12-16 21:33                 ` Jason Merrill
  0 siblings, 0 replies; 15+ messages in thread
From: Jason Merrill @ 2015-12-16 21:33 UTC (permalink / raw)
  To: Patrick Palka, Michael Matz; +Cc: gcc-patches

Looks good.

Jason

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] Fix PR c++/21802 (two-stage name lookup fails for operators)
  2015-12-15  0:34             ` Jason Merrill
@ 2015-12-16 21:46               ` Markus Trippelsdorf
  2016-01-15  0:40                 ` Ryan Burn
  0 siblings, 1 reply; 15+ messages in thread
From: Markus Trippelsdorf @ 2015-12-16 21:46 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Patrick Palka, gcc-patches

On 2015.12.14 at 19:34 -0500, Jason Merrill wrote:
> OK.

This patch caused https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68936 

-- 
Markus

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] Fix PR c++/21802 (two-stage name lookup fails for operators)
  2015-12-16 21:46               ` Markus Trippelsdorf
@ 2016-01-15  0:40                 ` Ryan Burn
  2016-01-15  4:10                   ` [PATCH] Fix PR c++/21802 (two-stage name lookup fails for operators Patrick Palka
  0 siblings, 1 reply; 15+ messages in thread
From: Ryan Burn @ 2016-01-15  0:40 UTC (permalink / raw)
  To: Markus Trippelsdorf; +Cc: Jason Merrill, Patrick Palka, gcc-patches

Also caused https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69091

On Wed, Dec 16, 2015 at 4:46 PM, Markus Trippelsdorf
<markus@trippelsdorf.de> wrote:
> On 2015.12.14 at 19:34 -0500, Jason Merrill wrote:
>> OK.
>
> This patch caused https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68936
>
> --
> Markus

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] Fix PR c++/21802 (two-stage name lookup fails for operators
  2016-01-15  0:40                 ` Ryan Burn
@ 2016-01-15  4:10                   ` Patrick Palka
  2016-04-09  8:11                     ` David Abdurachmanov
  0 siblings, 1 reply; 15+ messages in thread
From: Patrick Palka @ 2016-01-15  4:10 UTC (permalink / raw)
  To: Ryan Burn; +Cc: Markus Trippelsdorf, Jason Merrill, Patrick Palka, gcc-patches

On Thu, 14 Jan 2016, Ryan Burn wrote:

> Also caused https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69091

Thanks for the heads up, I was not aware I had caused this regression.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] Fix PR c++/21802 (two-stage name lookup fails for operators
  2016-01-15  4:10                   ` [PATCH] Fix PR c++/21802 (two-stage name lookup fails for operators Patrick Palka
@ 2016-04-09  8:11                     ` David Abdurachmanov
  0 siblings, 0 replies; 15+ messages in thread
From: David Abdurachmanov @ 2016-04-09  8:11 UTC (permalink / raw)
  To: Patrick Palka; +Cc: Ryan Burn, Markus Trippelsdorf, Jason Merrill, gcc-patches


> On 15 Jan 2016, at 05:10, Patrick Palka <patrick@parcs.ath.cx> wrote:
> 
> On Thu, 14 Jan 2016, Ryan Burn wrote:
> 
>> Also caused https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69091
> 
> Thanks for the heads up, I was not aware I had caused this regression.

While looking at last few failures with GCC 6.0.0 in our software, I found one
compilation error related to PR c++/21802 fix. I am not yet sure if this is a
compiler issue or something else, thus instead just made a comment [1] to PR
c++/21802 with the current details (incl. pre-processed source).

It works fine with GCC 5.3.0, Clang 3.7.0 and ICC (16.0.2 20160204).

david
- - -
[1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=21802#c8

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2016-04-09  8:11 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-12-10 21:43 [PATCH] Fix PR c++/21802 (two-stage name lookup fails for operators) Patrick Palka
2015-12-11 15:19 ` Patrick Palka
2015-12-11 19:43   ` Patrick Palka
2015-12-11 20:40     ` Jason Merrill
2015-12-12 23:32       ` Patrick Palka
2015-12-14 20:42         ` Jason Merrill
2015-12-15  0:14           ` Patrick Palka
2015-12-15  0:34             ` Jason Merrill
2015-12-16 21:46               ` Markus Trippelsdorf
2016-01-15  0:40                 ` Ryan Burn
2016-01-15  4:10                   ` [PATCH] Fix PR c++/21802 (two-stage name lookup fails for operators Patrick Palka
2016-04-09  8:11                     ` David Abdurachmanov
2015-12-16 19:25             ` [PATCH] Fix PR c++/21802 (two-stage name lookup fails for operators) Michael Matz
2015-12-16 21:31               ` Patrick Palka
2015-12-16 21:33                 ` Jason Merrill

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).