* [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: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
* 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
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).