From 6ab1176667734bd6de20833f8d263c03a418c452 Mon Sep 17 00:00:00 2001 From: Patrick Palka Date: Mon, 10 May 2021 22:38:34 -0400 Subject: [PATCH] c++: dependent operator expression lookup [PR51577] This unconditionally enables the maybe_save_operator_binding mechanism for all function templates, so that when resolving a dependent operator expression from a function template we ignore later-declared namespace-scope bindings that weren't visible at template definition time. This patch additionally makes the mechanism apply to dependent comma and compound-assignment operator expressions. Note that this doesn't fix the testcases in PR83035 or PR99692 because there the dependent operator expressions aren't at function scope. I'm not sure how adapt this mechanism for these testcases, since although we'll in both testcases have a TEMPLATE_DECL to associate the lookup result with, at instantiation time we won't have an appropriate binding level to push to. gcc/cp/ChangeLog: PR c++/51577 * name-lookup.c (maybe_save_operator_binding): Unconditionally enable for all function templates, not just generic lambdas. Handle compound-assignment operator expressions. * typeck.c (build_x_compound_expr): Call maybe_save_operator_binding in the type-dependent case. (build_x_modify_expr): Likewise. Move declaration of 'op' closer to its first use. gcc/testsuite/ChangeLog: PR c++/51577 * g++.dg/lookup/operator-3.C: New test. --- gcc/cp/name-lookup.c | 15 ++--- gcc/cp/typeck.c | 17 +++-- gcc/testsuite/g++.dg/lookup/operator-3.C | 109 +++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 13 deletions(-) create mode 100644 gcc/testsuite/g++.dg/lookup/operator-3.C diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index 4e84e2f..a6c9e68 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -9116,7 +9116,7 @@ static const char *const op_bind_attrname = "operator bindings"; void maybe_save_operator_binding (tree e) { - /* This is only useful in a generic lambda. */ + /* This is only useful in a template. */ if (!processing_template_decl) return; @@ -9124,13 +9124,12 @@ maybe_save_operator_binding (tree e) if (!cfn) return; - /* Do this for lambdas and code that will emit a CMI. In a module's - GMF we don't yet know whether there will be a CMI. */ - if (!module_has_cmi_p () && !global_purview_p () && !current_lambda_expr()) - return; - - tree fnname = ovl_op_identifier (false, TREE_CODE (e)); - if (!fnname) + tree fnname; + if(TREE_CODE (e) == MODOP_EXPR) + fnname = ovl_op_identifier (true, TREE_CODE (TREE_OPERAND (e, 1))); + else + fnname = ovl_op_identifier (false, TREE_CODE (e)); + if (!fnname || fnname == assign_op_identifier) return; tree attributes = DECL_ATTRIBUTES (cfn); diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 5af47ce..9002dd1 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -7274,7 +7274,11 @@ build_x_compound_expr (location_t loc, tree op1, tree op2, { if (type_dependent_expression_p (op1) || type_dependent_expression_p (op2)) - return build_min_nt_loc (loc, COMPOUND_EXPR, op1, op2); + { + result = build_min_nt_loc (loc, COMPOUND_EXPR, op1, op2); + maybe_save_operator_binding (result); + return result; + } op1 = build_non_dependent_expr (op1); op2 = build_non_dependent_expr (op2); } @@ -8938,7 +8942,6 @@ build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, tree orig_lhs = lhs; tree orig_rhs = rhs; tree overload = NULL_TREE; - tree op = build_nt (modifycode, NULL_TREE, NULL_TREE); if (lhs == error_mark_node || rhs == error_mark_node) return cp_expr (error_mark_node, loc); @@ -8948,9 +8951,12 @@ build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, 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); + { + tree op = build_min_nt_loc (loc, modifycode, NULL_TREE, NULL_TREE); + tree rval = build_min_nt_loc (loc, MODOP_EXPR, lhs, op, rhs); + maybe_save_operator_binding (rval); + return rval; + } lhs = build_non_dependent_expr (lhs); rhs = build_non_dependent_expr (rhs); @@ -8958,6 +8964,7 @@ build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, if (modifycode != NOP_EXPR) { + tree op = build_nt (modifycode, NULL_TREE, NULL_TREE); tree rval = build_new_op (loc, MODIFY_EXPR, LOOKUP_NORMAL, lhs, rhs, op, &overload, complain); if (rval) diff --git a/gcc/testsuite/g++.dg/lookup/operator-3.C b/gcc/testsuite/g++.dg/lookup/operator-3.C new file mode 100644 index 0000000..bc5eb3d --- /dev/null +++ b/gcc/testsuite/g++.dg/lookup/operator-3.C @@ -0,0 +1,109 @@ +// PR c++/51577 + +template void f (T x) { + +x; // { dg-error "no match" } + -x; // { dg-error "no match" } + *x; // { dg-error "no match" } + ~x; // { dg-error "no match" } + &x; + !x; // { dg-error "no match" } + ++x; // { dg-error "no match" } + --x; // { dg-error "no match" } + x++; // { dg-error "declared for postfix" } + x--; // { dg-error "declared for postfix" } + + x->*x; // { dg-error "no match" } + x / x; // { dg-error "no match" } + x * x; // { dg-error "no match" } + x + x; // { dg-error "no match" } + x - x; // { dg-error "no match" } + x % x; // { dg-error "no match" } + x & x; // { dg-error "no match" } + x | x; // { dg-error "no match" } + x ^ x; // { dg-error "no match" } + x << x; // { dg-error "no match" } + x >> x; // { dg-error "no match" } + x && x; // { dg-error "no match" } + x || x; // { dg-error "no match" } + x, x; + + x == x; // { dg-error "no match" } + x != x; // { dg-error "no match" } + x < x; // { dg-error "no match" } + x > x; // { dg-error "no match" } + x <= x; // { dg-error "no match" } + x >= x; // { dg-error "no match" } +#if __cplusplus > 201703L + x <=> x; // { dg-error "no match" "" { target c++20 } } +#endif + + x += x; // { dg-error "no match" } + x -= x; // { dg-error "no match" } + x *= x; // { dg-error "no match" } + x /= x; // { dg-error "no match" } + x %= x; // { dg-error "no match" } + x |= x; // { dg-error "no match" } + x ^= x; // { dg-error "no match" } + x <<= x; // { dg-error "no match" } + x >>= x; // { dg-error "no match" } +} + +namespace N { struct A { }; } + +void operator+(N::A); +void operator-(N::A); +void operator*(N::A); +void operator~(N::A); +#if __cplusplus >= 201103L +void operator&(N::A) = delete; +#else +void operator&(N::A); +#endif +void operator!(N::A); +void operator++(N::A); +void operator--(N::A); +void operator++(N::A, int); +void operator--(N::A, int); + +void operator->*(N::A, N::A); +void operator/(N::A, N::A); +void operator*(N::A, N::A); +void operator+(N::A, N::A); +void operator-(N::A, N::A); +void operator%(N::A, N::A); +void operator&(N::A, N::A); +void operator|(N::A, N::A); +void operator^(N::A, N::A); +void operator<<(N::A, N::A); +void operator>>(N::A, N::A); +void operator&&(N::A, N::A); +void operator||(N::A, N::A); +#if __cplusplus >= 201103L +void operator,(N::A, N::A) = delete; +#else +void operator,(N::A, N::A); +#endif + +void operator==(N::A, N::A); +void operator!=(N::A, N::A); +void operator<(N::A, N::A); +void operator>(N::A, N::A); +void operator<=(N::A, N::A); +void operator>=(N::A, N::A); +#if __cplusplus > 201703L +void operator<=>(N::A, N::A); +#endif + +void operator+=(N::A, N::A); +void operator-=(N::A, N::A); +void operator*=(N::A, N::A); +void operator/=(N::A, N::A); +void operator%=(N::A, N::A); +void operator|=(N::A, N::A); +void operator^=(N::A, N::A); +void operator<<=(N::A, N::A); +void operator>>=(N::A, N::A); + +int main() { + f(N::A()); +} -- 2.7.4