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.
This commit is contained in:
Patrick Palka 2021-05-10 22:38:34 -04:00
parent e7a9f085ff
commit 6ab1176667
3 changed files with 128 additions and 13 deletions

View file

@ -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);

View file

@ -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)

View file

@ -0,0 +1,109 @@
// PR c++/51577
template <class T> 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());
}