diff options
author | Patrick Palka <ppalka@redhat.com> | 2021-12-16 13:40:42 -0500 |
---|---|---|
committer | Patrick Palka <ppalka@redhat.com> | 2021-12-16 13:40:42 -0500 |
commit | bb2a7f80a98de3febefbb32b1e4898062bdb6af8 (patch) | |
tree | 755d59d7edc782cd013dcc67e5d706e4a6a63af7 | |
parent | 271e36d9d5b3a75e7f1a927e594477e6a5dd6fc0 (diff) | |
download | gcc-bb2a7f80a98de3febefbb32b1e4898062bdb6af8.zip gcc-bb2a7f80a98de3febefbb32b1e4898062bdb6af8.tar.gz gcc-bb2a7f80a98de3febefbb32b1e4898062bdb6af8.tar.bz2 |
c++: two-stage name lookup for overloaded operators [PR51577]
In order to properly implement two-stage name lookup for dependent
operator expressions, we need to remember the result of unqualified
lookup of the operator at template definition time, and reuse that
result rather than performing another unqualified lookup at
instantiation time.
Ideally we could just store the lookup in the expression directly, but
as pointed out in r9-6405 this isn't really possible since we use the
standard tree codes to represent most dependent operator expressions.
We could perhaps create a new tree code to represent dependent operator
expressions, with enough operands to store the lookup along with
everything else, but that'd require a lot of careful work to make sure
we handle this new tree code properly across the frontend.
But currently type-dependent operator (and call) expressions are given
an empty TREE_TYPE, which dependent_type_p treats as dependent, so this
field is effectively unused except to signal that the expression is
type-dependent. It'd be convenient if we could store the lookup there
while preserving the dependent-ness of the expression.
To that end, this patch creates a new kind of type, called
DEPENDENT_OPERATOR_TYPE, which we give to dependent operator expressions
and into which we can store the result of operator lookup at template
definition time (DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS). Since this
type is always dependent (by definition), and since the frontend doesn't
seem to care much about the exact type of a type-dependent expression,
using this type in place of a NULL_TREE type seems to "just work"; only
dependent_type_p and WILDCARD_TYPE_P need to be adjusted to return true
for this new type.
The rest of the patch mostly consists of adding the necessary plumbing
to pass DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS to add_operator_candidates,
adjusting all callers of build_x_* appropriately, and removing the now
unnecessary push_operator_bindings mechanism.
In passing, this patch simplifies finish_constraint_binary_op to avoid
using build_x_binary_op for building a binary constraint-expr; we don't
need to consider operator overloads here, as the &&/|| inside a
constraint effectively always has the built-in meaning (since atomic
constraints must have bool type).
This patch also makes FOLD_EXPR_OP yield a tree_code instead of a raw
INTEGER_CST.
Finally, this patch adds the XFAILed test operator-8.C which is about
broken two-stage name lookup for rewritten non-dependent operator
expressions, an existing bug that's otherwise only documented in
build_new_op.
PR c++/51577
PR c++/83035
PR c++/100465
gcc/cp/ChangeLog:
* call.c (add_operator_candidates): Add lookups parameter.
Use it to avoid performing a second unqualified lookup when
instantiating a dependent operator expression.
(build_new_op): Add lookups parameter and pass it appropriately.
* constraint.cc (finish_constraint_binary_op): Use
build_min_nt_loc instead of build_x_binary_op.
* coroutines.cc (build_co_await): Adjust call to build_new_op.
* cp-objcp-common.c (cp_common_init_ts): Mark
DEPENDENT_OPERATOR_TYPE appropriately.
* cp-tree.def (DEPENDENT_OPERATOR_TYPE): Define.
* cp-tree.h (WILDCARD_TYPE_P): Accept DEPENDENT_OPERATOR_TYPE.
(FOLD_EXPR_OP_RAW): New, renamed from ...
(FOLD_EXPR_OP): ... this. Change this to return the tree_code directly.
(DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS): Define.
(templated_operator_saved_lookups): Define.
(build_new_op): Add lookups parameter.
(build_dependent_operator_type): Declare.
(build_x_indirect_ref): Add lookups parameter.
(build_x_binary_op): Likewise.
(build_x_unary_op): Likewise.
(build_x_compound_expr): Likewise.
(build_x_modify_expr): Likewise.
* cxx-pretty-print.c (get_fold_operator): Adjust after
FOLD_EXPR_OP change.
* decl.c (start_preparsed_function): Don't call
push_operator_bindings.
* decl2.c (grok_array_decl): Adjust calls to build_new_op.
* method.c (do_one_comp): Likewise.
(build_comparison_op): Likewise.
* module.cc (trees_out::type_node): Handle DEPENDENT_OPERATOR_TYPE.
(trees_in::tree_node): Likewise.
* name-lookup.c (lookup_name): Revert r11-2876 change.
(op_unqualified_lookup): Remove.
(maybe_save_operator_binding): Remove.
(discard_operator_bindings): Remove.
(push_operator_bindings): Remove.
* name-lookup.h (maybe_save_operator_binding): Remove.
(push_operator_bindings): Remove.
(discard_operator_bindings): Remove.
* parser.c (cp_parser_unary_expression): Adjust calls to build_x_*.
(cp_parser_binary_expression): Likewise.
(cp_parser_assignment_expression): Likewise.
(cp_parser_expression): Likewise.
(do_range_for_auto_deduction): Likewise.
(cp_convert_range_for): Likewise.
(cp_parser_perform_range_for_lookup): Likewise.
(cp_parser_template_argument): Likewise.
(cp_parser_omp_for_cond): Likewise.
(cp_parser_omp_for_incr): Likewise.
(cp_parser_omp_for_loop_init): Likewise.
(cp_convert_omp_range_for): Likewise.
(cp_finish_omp_range_for): Likewise.
* pt.c (fold_expression): Adjust after FOLD_EXPR_OP change. Pass
templated_operator_saved_lookups to build_x_*.
(tsubst_omp_for_iterator): Adjust call to build_x_modify_expr.
(tsubst_expr) <case COMPOUND_EXPR>: Pass
templated_operator_saved_lookups to build_x_*.
(tsubst_copy_and_build) <case INDIRECT_REF>: Likewise.
<case tcc_unary>: Likewise.
<case tcc_binary>: Likewise.
<case MODOP_EXPR>: Likewise.
<case COMPOUND_EXPR>: Likewise.
(dependent_type_p_r): Return true for DEPENDENT_OPERATOR_TYPE.
* ptree.c (cxx_print_type): Handle DEPENDENT_OPERATOR_TYPE.
* semantics.c (finish_increment_expr): Adjust call to
build_x_unary_op.
(finish_unary_op_expr): Likewise.
(handle_omp_for_class_iterator): Adjust calls to build_x_*.
(finish_omp_cancel): Likewise.
(finish_unary_fold_expr): Use build_dependent_operator_type.
(finish_binary_fold_expr): Likewise.
* tree.c (cp_free_lang_data): Don't call discard_operator_bindings.
* typeck.c (rationalize_conditional_expr): Adjust call to
build_x_binary_op.
(op_unqualified_lookup): Define.
(build_dependent_operator_type): Define.
(build_x_indirect_ref): Add lookups parameter and use
build_dependent_operator_type.
(build_x_binary_op): Likewise.
(build_x_array_ref): Likewise.
(build_x_unary_op): Likewise.
(build_x_compound_expr_from_list): Adjust call to
build_x_compound_expr.
(build_x_compound_expr_from_vec): Likewise.
(build_x_compound_expr): Add lookups parameter and use
build_dependent_operator_type.
(cp_build_modify_expr): Adjust call to build_new_op.
(build_x_modify_expr): Add lookups parameter and use
build_dependent_operator_type.
* typeck2.c (build_x_arrow): Adjust call to build_new_op.
libcc1/ChangeLog:
* libcp1plugin.cc (plugin_build_unary_expr): Adjust call to
build_x_unary_op.
(plugin_build_binary_expr): Adjust call to build_x_binary_op.
gcc/testsuite/ChangeLog:
* g++.dg/lookup/operator-3.C: Split out operator overload
declarations into ...
* g++.dg/lookup/operator-3-ops.h: ... here.
* g++.dg/lookup/operator-3a.C: New test.
* g++.dg/lookup/operator-4.C: New test.
* g++.dg/lookup/operator-4a.C: New test.
* g++.dg/lookup/operator-5.C: New test.
* g++.dg/lookup/operator-5a.C: New test.
* g++.dg/lookup/operator-6.C: New test.
* g++.dg/lookup/operator-7.C: New test.
* g++.dg/lookup/operator-8.C: New test.
31 files changed, 796 insertions, 300 deletions
diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 347df5d..495dcdd 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -6285,12 +6285,17 @@ op_is_ordered (tree_code code) /* Subroutine of build_new_op: Add to CANDIDATES all candidates for the operator indicated by CODE/CODE2. This function calls itself recursively to - handle C++20 rewritten comparison operator candidates. */ + handle C++20 rewritten comparison operator candidates. + + LOOKUPS, if non-NULL, is the set of pertinent namespace-scope operator + overloads to consider. This parameter is used when instantiating a + dependent operator expression and has the same structure as + DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS. */ static tree add_operator_candidates (z_candidate **candidates, tree_code code, tree_code code2, - vec<tree, va_gc> *arglist, + vec<tree, va_gc> *arglist, tree lookups, int flags, tsubst_flags_t complain) { z_candidate *start_candidates = *candidates; @@ -6326,7 +6331,15 @@ add_operator_candidates (z_candidate **candidates, consider. */ if (!memonly) { - tree fns = lookup_name (fnname, LOOK_where::BLOCK_NAMESPACE); + tree fns; + if (!lookups) + fns = lookup_name (fnname, LOOK_where::BLOCK_NAMESPACE); + /* If LOOKUPS is non-NULL, then we're instantiating a dependent operator + expression, and LOOKUPS is the result of stage 1 name lookup. */ + else if (tree found = purpose_member (fnname, lookups)) + fns = TREE_VALUE (found); + else + fns = NULL_TREE; fns = lookup_arg_dependent (fnname, fns, arglist); add_candidates (fns, NULL_TREE, arglist, NULL_TREE, NULL_TREE, false, NULL_TREE, NULL_TREE, @@ -6429,7 +6442,7 @@ add_operator_candidates (z_candidate **candidates, if (rewrite_code != code) /* Add rewritten candidates in same order. */ add_operator_candidates (candidates, rewrite_code, ERROR_MARK, - arglist, flags, complain); + arglist, lookups, flags, complain); z_candidate *save_cand = *candidates; @@ -6439,7 +6452,7 @@ add_operator_candidates (z_candidate **candidates, revlist->quick_push ((*arglist)[1]); revlist->quick_push ((*arglist)[0]); add_operator_candidates (candidates, rewrite_code, ERROR_MARK, - revlist, flags, complain); + revlist, lookups, flags, complain); /* Release the vec if we didn't add a candidate that uses it. */ for (z_candidate *c = *candidates; c != save_cand; c = c->next) @@ -6457,8 +6470,8 @@ add_operator_candidates (z_candidate **candidates, tree build_new_op (const op_location_t &loc, enum tree_code code, int flags, - tree arg1, tree arg2, tree arg3, tree *overload, - tsubst_flags_t complain) + tree arg1, tree arg2, tree arg3, tree lookups, + tree *overload, tsubst_flags_t complain) { struct z_candidate *candidates = 0, *cand; releasing_vec arglist; @@ -6552,7 +6565,7 @@ build_new_op (const op_location_t &loc, enum tree_code code, int flags, p = conversion_obstack_alloc (0); result = add_operator_candidates (&candidates, code, code2, arglist, - flags, complain); + lookups, flags, complain); if (result == error_mark_node) goto user_defined_result_ready; @@ -6608,7 +6621,7 @@ build_new_op (const op_location_t &loc, enum tree_code code, int flags, else code = PREDECREMENT_EXPR; result = build_new_op (loc, code, flags, arg1, NULL_TREE, - NULL_TREE, overload, complain); + NULL_TREE, lookups, overload, complain); break; /* The caller will deal with these. */ @@ -6765,7 +6778,7 @@ build_new_op (const op_location_t &loc, enum tree_code code, int flags, warning_sentinel ws (warn_zero_as_null_pointer_constant); result = build_new_op (loc, code, LOOKUP_NORMAL|LOOKUP_REWRITTEN, - lhs, rhs, NULL_TREE, + lhs, rhs, NULL_TREE, lookups, NULL, complain); } break; diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 566f4e3..8e25ae2 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -202,15 +202,8 @@ finish_constraint_binary_op (location_t loc, return error_mark_node; if (!check_constraint_operands (loc, lhs, rhs)) return error_mark_node; - tree overload; - cp_expr expr = build_x_binary_op (loc, code, - lhs, TREE_CODE (lhs), - rhs, TREE_CODE (rhs), - &overload, tf_none); - /* When either operand is dependent, the overload set may be non-empty. */ - if (expr == error_mark_node) - return error_mark_node; - expr.set_location (loc); + cp_expr expr + = build_min_nt_loc (loc, code, lhs.get_value (), rhs.get_value ()); expr.set_range (lhs.get_start (), rhs.get_finish ()); return expr; } diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index 9017902..c00672e 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -912,7 +912,7 @@ build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind) if (MAYBE_CLASS_TYPE_P (TREE_TYPE (a))) { o = build_new_op (loc, CO_AWAIT_EXPR, LOOKUP_NORMAL, a, NULL_TREE, - NULL_TREE, NULL, tf_warning_or_error); + NULL_TREE, NULL_TREE, NULL, tf_warning_or_error); /* If no viable functions are found, o is a. */ if (!o || o == error_mark_node) o = a; diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c index 38eae88..36e04cd 100644 --- a/gcc/cp/cp-objcp-common.c +++ b/gcc/cp/cp-objcp-common.c @@ -484,6 +484,7 @@ cp_common_init_ts (void) /* New Types. */ MARK_TS_TYPE_COMMON (UNBOUND_CLASS_TEMPLATE); MARK_TS_TYPE_COMMON (TYPE_ARGUMENT_PACK); + MARK_TS_TYPE_COMMON (DEPENDENT_OPERATOR_TYPE); MARK_TS_TYPE_NON_COMMON (DECLTYPE_TYPE); MARK_TS_TYPE_NON_COMMON (TYPENAME_TYPE); diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def index 725139b..6fb838c 100644 --- a/gcc/cp/cp-tree.def +++ b/gcc/cp/cp-tree.def @@ -476,6 +476,11 @@ DEFTREECODE (UNDERLYING_TYPE, "underlying_type", tcc_type, 0) BASES_TYPE is the type in question. */ DEFTREECODE (BASES, "bases", tcc_type, 0) +/* Dependent operator expressions are given this type rather than a NULL_TREE + type so that we have somewhere to stash the result of phase 1 name lookup + (namely into DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS). */ +DEFTREECODE (DEPENDENT_OPERATOR_TYPE, "dependent_operator_type", tcc_type, 0) + /* Used to represent the template information stored by template specializations. The accessors are: diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 7f32cf5..9eff434 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -2185,7 +2185,8 @@ enum languages { lang_c, lang_cplusplus }; || TREE_CODE (T) == TYPENAME_TYPE \ || TREE_CODE (T) == TYPEOF_TYPE \ || TREE_CODE (T) == BOUND_TEMPLATE_TEMPLATE_PARM \ - || TREE_CODE (T) == DECLTYPE_TYPE) + || TREE_CODE (T) == DECLTYPE_TYPE \ + || TREE_CODE (T) == DEPENDENT_OPERATOR_TYPE) /* Nonzero if T is a class (or struct or union) type. Also nonzero for template type parameters, typename types, and instantiated @@ -3978,9 +3979,13 @@ struct GTY(()) lang_decl { TREE_LANG_FLAG_0 (FOLD_EXPR_CHECK (NODE)) /* An INTEGER_CST containing the tree code of the folded operator. */ -#define FOLD_EXPR_OP(NODE) \ +#define FOLD_EXPR_OP_RAW(NODE) \ TREE_OPERAND (FOLD_EXPR_CHECK (NODE), 0) +/* The tree code of the folded operator. */ +#define FOLD_EXPR_OP(NODE) \ + ((enum tree_code) TREE_INT_CST_LOW (FOLD_EXPR_OP_RAW (NODE))) + /* The expression containing an unexpanded parameter pack. */ #define FOLD_EXPR_PACK(NODE) \ TREE_OPERAND (FOLD_EXPR_CHECK (NODE), 1) @@ -4035,6 +4040,26 @@ struct GTY(()) lang_decl { #define CALL_EXPR_OPERATOR_SYNTAX(NODE) \ TREE_LANG_FLAG_6 (CALL_OR_AGGR_INIT_CHECK (NODE)) +/* A TREE_LIST containing the result of phase 1 name lookup of the operator + overloads that are pertinent to the dependent operator expression whose + type is NODE. Each TREE_PURPOSE is an IDENTIFIER_NODE and TREE_VALUE is + the corresponding (possibly empty) lookup result. The TREE_TYPE of the + first TREE_LIST node points back to NODE. */ +#define DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS(NODE) \ + TYPE_VALUES_RAW (DEPENDENT_OPERATOR_TYPE_CHECK (NODE)) + +/* Guarded helper for the above accessor macro that takes a (templated) + operator expression instead of the type thereof. */ +inline tree +templated_operator_saved_lookups (tree t) +{ + tree type = TREE_TYPE (EXPR_CHECK (t)); + if (type && TREE_CODE (type) == DEPENDENT_OPERATOR_TYPE) + return DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS (type); + else + return NULL_TREE; +} + /* Indicates whether a string literal has been parenthesized. Such usages are disallowed in certain circumstances. */ @@ -6464,14 +6489,15 @@ extern tree build_special_member_call (tree, tree, tree, int, tsubst_flags_t); extern tree build_new_op (const op_location_t &, enum tree_code, - int, tree, tree, tree, tree *, - tsubst_flags_t); + int, tree, tree, tree, tree, + tree *, tsubst_flags_t); /* Wrapper that leaves out the usually-null op3 and overload parms. */ inline tree build_new_op (const op_location_t &loc, enum tree_code code, int flags, tree arg1, tree arg2, tsubst_flags_t complain) { - return build_new_op (loc, code, flags, arg1, arg2, NULL_TREE, NULL, complain); + return build_new_op (loc, code, flags, arg1, arg2, NULL_TREE, NULL_TREE, + NULL, complain); } extern tree build_op_call (tree, vec<tree, va_gc> **, tsubst_flags_t); @@ -7875,8 +7901,9 @@ extern tree build_class_member_access_expr (cp_expr, tree, tree, bool, extern tree finish_class_member_access_expr (cp_expr, tree, bool, tsubst_flags_t); extern tree lookup_destructor (tree, tree, tree, tsubst_flags_t); +extern tree build_dependent_operator_type (tree, enum tree_code, bool); extern tree build_x_indirect_ref (location_t, tree, - ref_operator, + ref_operator, tree, tsubst_flags_t); extern tree cp_build_indirect_ref (location_t, tree, ref_operator, @@ -7894,20 +7921,20 @@ extern tree cp_build_function_call_vec (tree, vec<tree, va_gc> **, extern tree build_x_binary_op (const op_location_t &, enum tree_code, tree, enum tree_code, tree, - enum tree_code, tree *, - tsubst_flags_t); + enum tree_code, tree, + tree *, tsubst_flags_t); inline tree build_x_binary_op (const op_location_t &loc, enum tree_code code, tree arg1, tree arg2, tsubst_flags_t complain) { return build_x_binary_op (loc, code, arg1, TREE_CODE (arg1), arg2, - TREE_CODE (arg2), NULL, complain); + TREE_CODE (arg2), NULL_TREE, NULL, complain); } extern tree build_x_array_ref (location_t, tree, tree, tsubst_flags_t); extern tree build_x_unary_op (location_t, enum tree_code, cp_expr, - tsubst_flags_t); + tree, tsubst_flags_t); extern tree cp_build_addressof (location_t, tree, tsubst_flags_t); extern tree cp_build_addr_expr (tree, tsubst_flags_t); @@ -7922,7 +7949,7 @@ extern tree build_x_compound_expr_from_list (tree, expr_list_kind, extern tree build_x_compound_expr_from_vec (vec<tree, va_gc> *, const char *, tsubst_flags_t); extern tree build_x_compound_expr (location_t, tree, tree, - tsubst_flags_t); + tree, tsubst_flags_t); extern tree build_compound_expr (location_t, tree, tree); extern tree cp_build_compound_expr (tree, tree, tsubst_flags_t); extern tree build_static_cast (location_t, tree, tree, @@ -7938,7 +7965,7 @@ extern tree cp_build_c_cast (location_t, tree, tree, tsubst_flags_t); extern cp_expr build_x_modify_expr (location_t, tree, enum tree_code, tree, - tsubst_flags_t); + tree, tsubst_flags_t); extern tree cp_build_modify_expr (location_t, tree, enum tree_code, tree, tsubst_flags_t); diff --git a/gcc/cp/cxx-pretty-print.c b/gcc/cp/cxx-pretty-print.c index 3ea357d..6af009c 100644 --- a/gcc/cp/cxx-pretty-print.c +++ b/gcc/cp/cxx-pretty-print.c @@ -2541,8 +2541,8 @@ pp_cxx_addressof_expression (cxx_pretty_printer *pp, tree t) static char const* get_fold_operator (tree t) { - int op = int_cst_value (FOLD_EXPR_OP (t)); - ovl_op_info_t *info = OVL_OP_INFO (FOLD_EXPR_MODIFY_P (t), op); + ovl_op_info_t *info = OVL_OP_INFO (FOLD_EXPR_MODIFY_P (t), + FOLD_EXPR_OP (t)); return info->name; } diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 2e03398..7ca8770 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -17118,8 +17118,6 @@ start_preparsed_function (tree decl1, tree attrs, int flags) store_parm_decls (current_function_parms); - push_operator_bindings (); - if (!processing_template_decl && (flag_lifetime_dse > 1) && DECL_CONSTRUCTOR_P (decl1) diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index 99f5dc7..062c175 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -417,7 +417,8 @@ grok_array_decl (location_t loc, tree array_expr, tree index_exp, { if (index_exp) expr = build_new_op (loc, ARRAY_REF, LOOKUP_NORMAL, array_expr, - index_exp, NULL_TREE, &overload, complain); + index_exp, NULL_TREE, NULL_TREE, + &overload, complain); else if ((*index_exp_list)->is_empty ()) expr = build_op_subscript (loc, array_expr, index_exp_list, &overload, complain); @@ -431,7 +432,7 @@ grok_array_decl (location_t loc, tree array_expr, tree index_exp, tf_none); if (idx != error_mark_node) expr = build_new_op (loc, ARRAY_REF, LOOKUP_NORMAL, array_expr, - idx, NULL_TREE, &overload, + idx, NULL_TREE, NULL_TREE, &overload, complain & tf_decltype); if (expr == error_mark_node) { diff --git a/gcc/cp/method.c b/gcc/cp/method.c index 935946f..44439ba 100644 --- a/gcc/cp/method.c +++ b/gcc/cp/method.c @@ -1372,7 +1372,7 @@ do_one_comp (location_t loc, const comp_info &info, tree sub, tree lhs, tree rhs to </=, so don't give an error yet if <=> lookup fails. */ bool tentative = retcat != cc_last; tree comp = build_new_op (loc, code, flags, lhs, rhs, - NULL_TREE, &overload, + NULL_TREE, NULL_TREE, &overload, tentative ? tf_none : complain); if (code != SPACESHIP_EXPR) @@ -1684,8 +1684,8 @@ build_comparison_op (tree fndecl, bool defining, tsubst_flags_t complain) comp = retval = var; } eq = build_new_op (info.loc, EQ_EXPR, flags, comp, - integer_zero_node, NULL_TREE, NULL, - complain); + integer_zero_node, NULL_TREE, NULL_TREE, + NULL, complain); } tree ceq = contextual_conv_bool (eq, complain); info.check (ceq); @@ -1720,7 +1720,7 @@ build_comparison_op (tree fndecl, bool defining, tsubst_flags_t complain) else if (code == NE_EXPR) { tree comp = build_new_op (info.loc, EQ_EXPR, flags, lhs, rhs, - NULL_TREE, NULL, complain); + NULL_TREE, NULL_TREE, NULL, complain); comp = contextual_conv_bool (comp, complain); info.check (comp); if (defining) @@ -1732,9 +1732,9 @@ build_comparison_op (tree fndecl, bool defining, tsubst_flags_t complain) else { tree comp = build_new_op (info.loc, SPACESHIP_EXPR, flags, lhs, rhs, - NULL_TREE, NULL, complain); + NULL_TREE, NULL_TREE, NULL, complain); tree comp2 = build_new_op (info.loc, code, flags, comp, integer_zero_node, - NULL_TREE, NULL, complain); + NULL_TREE, NULL_TREE, NULL, complain); info.check (comp2); if (defining) finish_return_stmt (comp2); diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index 9266055..f3e7af2 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -8789,6 +8789,7 @@ trees_out::type_node (tree type) case DECLTYPE_TYPE: case TYPEOF_TYPE: case UNDERLYING_TYPE: + case DEPENDENT_OPERATOR_TYPE: tree_node (TYPE_VALUES_RAW (type)); if (TREE_CODE (type) == DECLTYPE_TYPE) /* We stash a whole bunch of things into decltype's @@ -9311,6 +9312,7 @@ trees_in::tree_node (bool is_use) case DECLTYPE_TYPE: case TYPEOF_TYPE: case UNDERLYING_TYPE: + case DEPENDENT_OPERATOR_TYPE: { tree expr = tree_node (); if (!get_overrun ()) diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index 6b5e434..3bd7b20 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -7725,20 +7725,14 @@ lookup_name (tree name, LOOK_where where, LOOK_want want) if (binding) { - /* The saved lookups for an operator record 'nothing - found' as error_mark_node. We need to stop the search - here, but not return the error mark node. */ - if (binding == error_mark_node) - binding = NULL_TREE; - val = binding; - goto found; + break; } } } /* Now lookup in namespace scopes. */ - if (bool (where & LOOK_where::NAMESPACE)) + if (!val && bool (where & LOOK_where::NAMESPACE)) { name_lookup lookup (name, want); if (lookup.search_unqualified @@ -7746,8 +7740,6 @@ lookup_name (tree name, LOOK_where where, LOOK_want want) val = lookup.value; } - found:; - /* If we have a known type overload, pull it out. This can happen for both using decls and unhidden functions. */ if (val && TREE_CODE (val) == OVERLOAD && TREE_TYPE (val) != unknown_type_node) @@ -8949,125 +8941,4 @@ cp_emit_debug_info_for_using (tree t, tree context) } } -/* Return the result of unqualified lookup for the overloaded operator - designated by CODE, if we are in a template and the binding we find is - not. */ - -static tree -op_unqualified_lookup (tree fnname) -{ - if (cxx_binding *binding = IDENTIFIER_BINDING (fnname)) - { - cp_binding_level *l = binding->scope; - while (l && !l->this_entity) - l = l->level_chain; - - if (l && uses_template_parms (l->this_entity)) - /* Don't preserve decls from an uninstantiated template, - wait until that template is instantiated. */ - return NULL_TREE; - } - - tree fns = lookup_name (fnname); - if (!fns) - /* Remember we found nothing! */ - return error_mark_node; - - tree d = fns; - if (TREE_CODE (d) == TREE_LIST) - d = TREE_VALUE (d); - if (is_overloaded_fn (d)) - d = get_first_fn (d); - if (DECL_CLASS_SCOPE_P (d)) - /* We don't need to remember class-scope functions or declarations, - normal unqualified lookup will find them again. */ - return NULL_TREE; - - return fns; -} - -/* E is an expression representing an operation with dependent type, so we - don't know yet whether it will use the built-in meaning of the operator or a - function. Remember declarations of that operator in scope. - - We then inject a fake binding of that lookup into the - instantiation's parameter scope. This approach fails if the user - has different using declarations or directives in different local - binding of the current function from whence we need to do lookups - (we'll cache what we see on the first lookup). */ - -static const char *const op_bind_attrname = "operator bindings"; - -void -maybe_save_operator_binding (tree e) -{ - /* This is only useful in a template. */ - if (!processing_template_decl) - return; - - tree cfn = current_function_decl; - if (!cfn) - return; - - 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); - tree op_attr = lookup_attribute (op_bind_attrname, attributes); - if (!op_attr) - { - tree *ap = &DECL_ATTRIBUTES (cfn); - while (*ap && ATTR_IS_DEPENDENT (*ap)) - ap = &TREE_CHAIN (*ap); - op_attr = tree_cons (get_identifier (op_bind_attrname), - NULL_TREE, *ap); - *ap = op_attr; - } - - tree op_bind = purpose_member (fnname, TREE_VALUE (op_attr)); - if (!op_bind) - { - tree fns = op_unqualified_lookup (fnname); - - /* Always record, so we don't keep looking for this - operator. */ - TREE_VALUE (op_attr) = tree_cons (fnname, fns, TREE_VALUE (op_attr)); - } -} - -/* Called from cp_free_lang_data so we don't put this into LTO. */ - -void -discard_operator_bindings (tree decl) -{ - DECL_ATTRIBUTES (decl) = remove_attribute (op_bind_attrname, - DECL_ATTRIBUTES (decl)); -} - -/* Subroutine of start_preparsed_function: push the bindings we saved away in - maybe_save_op_lookup into the function parameter binding level. */ - -void -push_operator_bindings () -{ - tree decl1 = current_function_decl; - if (tree attr = lookup_attribute (op_bind_attrname, - DECL_ATTRIBUTES (decl1))) - for (tree binds = TREE_VALUE (attr); binds; binds = TREE_CHAIN (binds)) - if (tree val = TREE_VALUE (binds)) - { - tree name = TREE_PURPOSE (binds); - if (TREE_CODE (val) == TREE_LIST) - for (tree v = val; v; v = TREE_CHAIN (v)) - push_local_binding (name, TREE_VALUE (v), /*using*/true); - else - push_local_binding (name, val, /*using*/true); - } -} - #include "gt-cp-name-lookup.h" diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h index f63c4f5..db705d2 100644 --- a/gcc/cp/name-lookup.h +++ b/gcc/cp/name-lookup.h @@ -465,10 +465,7 @@ extern void push_nested_namespace (tree); extern void pop_nested_namespace (tree); extern void push_to_top_level (void); extern void pop_from_top_level (void); -extern void maybe_save_operator_binding (tree); -extern void push_operator_bindings (void); extern void push_using_decl_bindings (tree, tree); -extern void discard_operator_bindings (tree); /* Lower level interface for modules. */ extern tree *mergeable_namespace_slots (tree ns, tree name, bool is_global, diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index c2564e5..5d72201 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -8731,7 +8731,7 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk, return build_x_unary_op (token->location, (keyword == RID_REALPART ? REALPART_EXPR : IMAGPART_EXPR), - expression, + expression, NULL_TREE, tf_warning_or_error); } break; @@ -8908,7 +8908,7 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk, case INDIRECT_REF: non_constant_p = NIC_STAR; expression = build_x_indirect_ref (loc, cast_expression, - RO_UNARY_STAR, + RO_UNARY_STAR, NULL_TREE, complain); /* TODO: build_x_indirect_ref does not always honor the location, so ensure it is set. */ @@ -8921,7 +8921,7 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk, case BIT_NOT_EXPR: expression = build_x_unary_op (loc, unary_operator, cast_expression, - complain); + NULL_TREE, complain); /* TODO: build_x_unary_op does not always honor the location, so ensure it is set. */ expression.set_location (loc); @@ -10149,7 +10149,7 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p, op_location_t op_loc (current.loc, combined_loc); current.lhs = build_x_binary_op (op_loc, current.tree_type, current.lhs, current.lhs_type, - rhs, rhs_type, &overload, + rhs, rhs_type, NULL_TREE, &overload, complain_flags (decltype_p)); /* TODO: build_x_binary_op doesn't always honor the location. */ current.lhs.set_location (combined_loc); @@ -10328,7 +10328,7 @@ cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk, rhs.get_finish ()); expr = build_x_modify_expr (loc, expr, assignment_operator, - rhs, + rhs, NULL_TREE, complain_flags (decltype_p)); /* TODO: build_x_modify_expr doesn't honor the location, so we must set it here. */ @@ -10480,7 +10480,7 @@ cp_parser_expression (cp_parser* parser, cp_id_kind * pidk, expression.get_start (), assignment_expression.get_finish ()); expression = build_x_compound_expr (loc, expression, - assignment_expression, + assignment_expression, NULL_TREE, complain_flags (decltype_p)); expression.set_location (loc); } @@ -13617,7 +13617,7 @@ do_range_for_auto_deduction (tree decl, tree range_expr) iter_decl = build_decl (input_location, VAR_DECL, NULL_TREE, iter_type); iter_decl = build_x_indirect_ref (input_location, iter_decl, - RO_UNARY_STAR, + RO_UNARY_STAR, NULL_TREE, tf_warning_or_error); TREE_TYPE (decl) = do_auto_deduction (TREE_TYPE (decl), iter_decl, auto_node, @@ -13804,7 +13804,7 @@ cp_convert_range_for (tree statement, tree range_decl, tree range_expr, condition = build_x_binary_op (input_location, NE_EXPR, begin, ERROR_MARK, end, ERROR_MARK, - NULL, tf_warning_or_error); + NULL_TREE, NULL, tf_warning_or_error); finish_for_cond (condition, statement, ivdep, unroll); /* The new increment expression. */ @@ -13818,7 +13818,7 @@ cp_convert_range_for (tree statement, tree range_decl, tree range_expr, /* The declaration is initialized with *__begin inside the loop body. */ tree deref_begin = build_x_indirect_ref (input_location, begin, RO_UNARY_STAR, - tf_warning_or_error); + NULL_TREE, tf_warning_or_error); cp_finish_decl (range_decl, deref_begin, /*is_constant_init*/false, NULL_TREE, LOOKUP_ONLYCONVERTING); @@ -13924,7 +13924,7 @@ cp_parser_perform_range_for_lookup (tree range, tree *begin, tree *end) && (build_x_binary_op (input_location, NE_EXPR, *begin, ERROR_MARK, *end, ERROR_MARK, - NULL, tf_none) + NULL_TREE, NULL, tf_none) != error_mark_node)) /* P0184R0 allows __begin and __end to have different types, but make sure they are comparable so we can give a better @@ -18924,7 +18924,7 @@ cp_parser_template_argument (cp_parser* parser) { if (address_p) argument = build_x_unary_op (loc, ADDR_EXPR, argument, - tf_warning_or_error); + NULL_TREE, tf_warning_or_error); else argument = convert_from_reference (argument); return argument; @@ -41564,7 +41564,7 @@ cp_parser_omp_for_cond (cp_parser *parser, tree decl, enum tree_code code) TREE_CODE (cond), TREE_OPERAND (cond, 0), ERROR_MARK, TREE_OPERAND (cond, 1), ERROR_MARK, - /*overload=*/NULL, tf_warning_or_error); + NULL_TREE, /*overload=*/NULL, tf_warning_or_error); } /* Helper function, to parse omp for increment expression. */ @@ -41641,11 +41641,13 @@ cp_parser_omp_for_incr (cp_parser *parser, tree decl) lhs = rhs; else lhs = build_x_unary_op (input_location, NEGATE_EXPR, rhs, - tf_warning_or_error); + NULL_TREE, tf_warning_or_error); } else - lhs = build_x_binary_op (input_location, op, lhs, ERROR_MARK, rhs, - ERROR_MARK, NULL, tf_warning_or_error); + lhs = build_x_binary_op (input_location, op, + lhs, ERROR_MARK, + rhs, ERROR_MARK, + NULL_TREE, NULL, tf_warning_or_error); } } while (token->type == CPP_PLUS || token->type == CPP_MINUS); @@ -41873,7 +41875,7 @@ cp_parser_omp_for_loop_init (cp_parser *parser, orig_init = rhs; finish_expr_stmt (build_x_modify_expr (EXPR_LOCATION (rhs), decl, NOP_EXPR, - rhs, + rhs, NULL_TREE, tf_warning_or_error)); if (!add_private_clause) add_private_clause = decl; @@ -41995,7 +41997,7 @@ cp_convert_omp_range_for (tree &this_pre_body, vec<tree, va_gc> *for_block, cond = build_x_binary_op (input_location, NE_EXPR, begin, ERROR_MARK, end, ERROR_MARK, - NULL, tf_warning_or_error); + NULL_TREE, NULL, tf_warning_or_error); /* The new increment expression. */ if (CLASS_TYPE_P (iter_type)) @@ -42033,7 +42035,7 @@ cp_convert_omp_range_for (tree &this_pre_body, vec<tree, va_gc> *for_block, if (auto_node) { tree t = build_x_indirect_ref (input_location, begin, RO_UNARY_STAR, - tf_none); + NULL_TREE, tf_none); if (!error_operand_p (t)) TREE_TYPE (orig_decl) = do_auto_deduction (TREE_TYPE (orig_decl), t, auto_node); @@ -42073,7 +42075,7 @@ cp_finish_omp_range_for (tree orig, tree begin) /* The declaration is initialized with *__begin inside the loop body. */ cp_finish_decl (decl, build_x_indirect_ref (input_location, begin, RO_UNARY_STAR, - tf_warning_or_error), + NULL_TREE, tf_warning_or_error), /*is_constant_init*/false, NULL_TREE, LOOKUP_ONLYCONVERTING); if (VAR_P (decl) && DECL_DECOMPOSITION_P (decl)) diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 62a058d..18c6f11 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -12640,23 +12640,26 @@ expand_empty_fold (tree t, tsubst_flags_t complain) static tree fold_expression (tree t, tree left, tree right, tsubst_flags_t complain) { - tree op = FOLD_EXPR_OP (t); - tree_code code = (tree_code)TREE_INT_CST_LOW (op); + tree_code code = FOLD_EXPR_OP (t); + + tree lookups = templated_operator_saved_lookups (t); // Handle compound assignment operators. if (FOLD_EXPR_MODIFY_P (t)) - return build_x_modify_expr (input_location, left, code, right, complain); + return build_x_modify_expr (input_location, left, code, right, + lookups, complain); warning_sentinel s(warn_parentheses); switch (code) { case COMPOUND_EXPR: - return build_x_compound_expr (input_location, left, right, complain); + return build_x_compound_expr (input_location, left, right, + lookups, complain); default: return build_x_binary_op (input_location, code, left, TREE_CODE (left), right, TREE_CODE (right), - /*overload=*/NULL, + lookups, /*overload=*/NULL, complain); } } @@ -17891,7 +17894,7 @@ tsubst_omp_for_iterator (tree t, int i, tree declv, tree &orig_declv, tree lhs = RECUR (TREE_OPERAND (incr, 0)); tree rhs = RECUR (TREE_OPERAND (incr, 1)); incr = build_x_modify_expr (EXPR_LOCATION (incr), lhs, - NOP_EXPR, rhs, complain); + NOP_EXPR, rhs, NULL_TREE, complain); } else incr = RECUR (incr); @@ -19204,6 +19207,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl, RETURN (RECUR (TREE_OPERAND (t, 1))); RETURN (build_x_compound_expr (EXPR_LOCATION (t), tmp, RECUR (TREE_OPERAND (t, 1)), + templated_operator_saved_lookups (t), complain)); case ANNOTATE_EXPR: @@ -19855,6 +19859,7 @@ tsubst_copy_and_build (tree t, } else r = build_x_indirect_ref (input_location, r, RO_UNARY_STAR, + templated_operator_saved_lookups (t), complain|decltype_flag); if (REF_PARENTHESIZED_P (t)) @@ -19965,6 +19970,7 @@ tsubst_copy_and_build (tree t, op1 = tsubst_non_call_postfix_expression (TREE_OPERAND (t, 0), args, complain, in_decl); RETURN (build_x_unary_op (input_location, TREE_CODE (t), op1, + templated_operator_saved_lookups (t), complain|decltype_flag)); case PREDECREMENT_EXPR: @@ -19978,6 +19984,7 @@ tsubst_copy_and_build (tree t, case IMAGPART_EXPR: RETURN (build_x_unary_op (input_location, TREE_CODE (t), RECUR (TREE_OPERAND (t, 0)), + templated_operator_saved_lookups (t), complain|decltype_flag)); case FIX_TRUNC_EXPR: @@ -19996,6 +20003,7 @@ tsubst_copy_and_build (tree t, op1 = tsubst_non_call_postfix_expression (op1, args, complain, in_decl); RETURN (build_x_unary_op (input_location, ADDR_EXPR, op1, + templated_operator_saved_lookups (t), complain|decltype_flag)); case PLUS_EXPR: @@ -20060,6 +20068,7 @@ tsubst_copy_and_build (tree t, (warning_suppressed_p (TREE_OPERAND (t, 1)) ? ERROR_MARK : TREE_CODE (TREE_OPERAND (t, 1))), + templated_operator_saved_lookups (t), /*overload=*/NULL, complain|decltype_flag); if (EXPR_P (r)) @@ -20212,8 +20221,10 @@ tsubst_copy_and_build (tree t, warning_sentinel s(warn_div_by_zero); tree lhs = RECUR (TREE_OPERAND (t, 0)); tree rhs = RECUR (TREE_OPERAND (t, 2)); + tree r = build_x_modify_expr (EXPR_LOCATION (t), lhs, TREE_CODE (TREE_OPERAND (t, 1)), rhs, + templated_operator_saved_lookups (t), complain|decltype_flag); /* TREE_NO_WARNING must be set if either the expression was parenthesized or it uses an operator such as >>= rather @@ -20314,6 +20325,7 @@ tsubst_copy_and_build (tree t, RETURN (build_x_compound_expr (EXPR_LOCATION (t), op0, RECUR (TREE_OPERAND (t, 1)), + templated_operator_saved_lookups (t), complain|decltype_flag)); } @@ -26994,6 +27006,9 @@ dependent_type_p_r (tree type) if (TREE_CODE (type) == TYPE_PACK_EXPANSION) return true; + if (TREE_CODE (type) == DEPENDENT_OPERATOR_TYPE) + return true; + if (any_dependent_type_attributes_p (TYPE_ATTRIBUTES (type))) return true; diff --git a/gcc/cp/ptree.c b/gcc/cp/ptree.c index d514aa2..f7ddae7 100644 --- a/gcc/cp/ptree.c +++ b/gcc/cp/ptree.c @@ -151,6 +151,12 @@ cxx_print_type (FILE *file, tree node, int indent) print_node (file, "expr", DECLTYPE_TYPE_EXPR (node), indent + 4); return; + case DEPENDENT_OPERATOR_TYPE: + print_node (file, "saved_lookups", + DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS (node), + indent + 4); + return; + case TYPENAME_TYPE: print_node (file, "fullname", TYPENAME_TYPE_FULLNAME (node), indent + 4); diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 356fb83..6603066 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -2920,7 +2920,7 @@ finish_increment_expr (cp_expr expr, enum tree_code code) expr.get_start (), get_finish (input_location)); cp_expr result = build_x_unary_op (combined_loc, code, expr, - tf_warning_or_error); + NULL_TREE, tf_warning_or_error); /* TODO: build_x_unary_op doesn't honor the location, so set it here. */ result.set_location (combined_loc); return result; @@ -3031,7 +3031,8 @@ finish_unary_op_expr (location_t op_loc, enum tree_code code, cp_expr expr, of the operator token to the end of EXPR. */ location_t combined_loc = make_location (op_loc, op_loc, expr.get_finish ()); - cp_expr result = build_x_unary_op (combined_loc, code, expr, complain); + cp_expr result = build_x_unary_op (combined_loc, code, expr, + NULL_TREE, complain); /* TODO: build_x_unary_op doesn't always honor the location. */ result.set_location (combined_loc); @@ -9884,7 +9885,7 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, TREE_CODE (cond), iter, ERROR_MARK, TREE_OPERAND (cond, 1), ERROR_MARK, - NULL, tf_warning_or_error); + NULL_TREE, NULL, tf_warning_or_error); if (error_operand_p (tem)) return true; } @@ -9898,9 +9899,10 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, error_at (elocus, "invalid controlling predicate"); return true; } - diff = build_x_binary_op (elocus, MINUS_EXPR, TREE_OPERAND (cond, 1), - ERROR_MARK, iter, ERROR_MARK, NULL, - tf_warning_or_error); + diff = build_x_binary_op (elocus, MINUS_EXPR, + TREE_OPERAND (cond, 1), ERROR_MARK, + iter, ERROR_MARK, + NULL_TREE, NULL, tf_warning_or_error); diff = cp_fully_fold (diff); if (error_operand_p (diff)) return true; @@ -9928,7 +9930,7 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, } iter_incr = build_x_unary_op (EXPR_LOCATION (incr), TREE_CODE (incr), iter, - tf_warning_or_error); + NULL_TREE, tf_warning_or_error); if (error_operand_p (iter_incr)) return true; else if (TREE_CODE (incr) == PREINCREMENT_EXPR @@ -9954,6 +9956,7 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, iter_incr = build_x_modify_expr (EXPR_LOCATION (rhs), iter, TREE_CODE (rhs), TREE_OPERAND (rhs, 1), + NULL_TREE, tf_warning_or_error); if (error_operand_p (iter_incr)) return true; @@ -9983,13 +9986,13 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, PLUS_EXPR, TREE_OPERAND (rhs, 0), ERROR_MARK, iter, - ERROR_MARK, NULL, + ERROR_MARK, NULL_TREE, NULL, tf_warning_or_error); if (error_operand_p (iter_incr)) return true; iter_incr = build_x_modify_expr (EXPR_LOCATION (rhs), iter, NOP_EXPR, - iter_incr, + iter_incr, NULL_TREE, tf_warning_or_error); if (error_operand_p (iter_incr)) return true; @@ -10100,7 +10103,7 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, if (init != NULL) finish_expr_stmt (build_x_modify_expr (elocus, iter, NOP_EXPR, init, - tf_warning_or_error)); + NULL_TREE, tf_warning_or_error)); init = build_int_cst (TREE_TYPE (diff), 0); if (c && iter_incr == NULL && (!ordered || (i < collapse && collapse > 1))) @@ -10109,23 +10112,24 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, { finish_expr_stmt (build_x_modify_expr (elocus, incr_var, NOP_EXPR, - incr, tf_warning_or_error)); + incr, NULL_TREE, + tf_warning_or_error)); incr = incr_var; } iter_incr = build_x_modify_expr (elocus, iter, PLUS_EXPR, incr, - tf_warning_or_error); + NULL_TREE, tf_warning_or_error); } if (c && ordered && i < collapse && collapse > 1) iter_incr = incr; finish_expr_stmt (build_x_modify_expr (elocus, last, NOP_EXPR, init, - tf_warning_or_error)); + NULL_TREE, tf_warning_or_error)); if (diffvar) { finish_expr_stmt (build_x_modify_expr (elocus, diffvar, NOP_EXPR, - diff, tf_warning_or_error)); + diff, NULL_TREE, tf_warning_or_error)); diff = diffvar; } *pre_body = pop_stmt_list (*pre_body); @@ -10141,13 +10145,13 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, iter_init = build2 (MINUS_EXPR, TREE_TYPE (diff), decl, last); iter_init = build_x_modify_expr (elocus, iter, PLUS_EXPR, iter_init, - tf_warning_or_error); + NULL_TREE, tf_warning_or_error); if (iter_init != error_mark_node) iter_init = build1 (NOP_EXPR, void_type_node, iter_init); finish_expr_stmt (iter_init); finish_expr_stmt (build_x_modify_expr (elocus, last, NOP_EXPR, decl, - tf_warning_or_error)); + NULL_TREE, tf_warning_or_error)); add_stmt (orig_body); *body = pop_stmt_list (*body); @@ -10165,7 +10169,7 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, iter_init = build2 (MINUS_EXPR, TREE_TYPE (diff), iter_init, last); iter_init = build_x_modify_expr (elocus, iter, PLUS_EXPR, iter_init, - tf_warning_or_error); + NULL_TREE, tf_warning_or_error); if (iter_init != error_mark_node) iter_init = build1 (NOP_EXPR, void_type_node, iter_init); finish_expr_stmt (iter_init); @@ -10876,7 +10880,7 @@ finish_omp_cancel (tree clauses) ifc = build_x_binary_op (OMP_CLAUSE_LOCATION (ifc), NE_EXPR, OMP_CLAUSE_IF_EXPR (ifc), ERROR_MARK, integer_zero_node, ERROR_MARK, - NULL, tf_warning_or_error); + NULL_TREE, NULL, tf_warning_or_error); } else ifc = boolean_true_node; @@ -12128,6 +12132,9 @@ finish_unary_fold_expr (tree expr, int op, tree_code dir) tree code = build_int_cstu (integer_type_node, abs (op)); tree fold = build_min_nt_loc (UNKNOWN_LOCATION, dir, code, pack); FOLD_EXPR_MODIFY_P (fold) = (op < 0); + TREE_TYPE (fold) = build_dependent_operator_type (NULL_TREE, + FOLD_EXPR_OP (fold), + FOLD_EXPR_MODIFY_P (fold)); return fold; } @@ -12154,6 +12161,9 @@ finish_binary_fold_expr (tree pack, tree init, int op, tree_code dir) tree code = build_int_cstu (integer_type_node, abs (op)); tree fold = build_min_nt_loc (UNKNOWN_LOCATION, dir, code, pack, init); FOLD_EXPR_MODIFY_P (fold) = (op < 0); + TREE_TYPE (fold) = build_dependent_operator_type (NULL_TREE, + FOLD_EXPR_OP (fold), + FOLD_EXPR_MODIFY_P (fold)); return fold; } diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 284fb5f..29f3c17 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -5975,8 +5975,6 @@ cp_free_lang_data (tree t) DECL_EXTERNAL (t) = 1; TREE_STATIC (t) = 0; } - if (TREE_CODE (t) == FUNCTION_DECL) - discard_operator_bindings (t); if (TREE_CODE (t) == NAMESPACE_DECL) /* We do not need the leftover chaining of namespaces from the binding level. */ diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 4e60db4..5184b02 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -2602,6 +2602,7 @@ rationalize_conditional_expr (enum tree_code code, tree t, ? LE_EXPR : GE_EXPR), op0, TREE_CODE (op0), op1, TREE_CODE (op1), + NULL_TREE, /*overload=*/NULL, complain), cp_build_unary_op (code, op0, false, complain), @@ -3487,6 +3488,67 @@ build_ptrmemfunc_access_expr (tree ptrmem, tree member_name) return build_simple_component_ref (ptrmem, member); } +/* Return a TREE_LIST of namespace-scope overloads for the given operator, + and for any other relevant operator. */ + +static tree +op_unqualified_lookup (tree_code code, bool is_assign) +{ + tree lookups = NULL_TREE; + + if (cxx_dialect >= cxx20 && !is_assign) + { + if (code == NE_EXPR) + { + /* != can get rewritten in terms of ==. */ + tree fnname = ovl_op_identifier (false, EQ_EXPR); + if (tree fns = lookup_name (fnname, LOOK_where::BLOCK_NAMESPACE)) + lookups = tree_cons (fnname, fns, lookups); + } + else if (code == GT_EXPR || code == LE_EXPR + || code == LT_EXPR || code == GE_EXPR) + { + /* These can get rewritten in terms of <=>. */ + tree fnname = ovl_op_identifier (false, SPACESHIP_EXPR); + if (tree fns = lookup_name (fnname, LOOK_where::BLOCK_NAMESPACE)) + lookups = tree_cons (fnname, fns, lookups); + } + } + + tree fnname = ovl_op_identifier (is_assign, code); + if (tree fns = lookup_name (fnname, LOOK_where::BLOCK_NAMESPACE)) + lookups = tree_cons (fnname, fns, lookups); + + if (lookups) + return lookups; + else + return build_tree_list (NULL_TREE, NULL_TREE); +} + +/* Create a DEPENDENT_OPERATOR_TYPE for a dependent operator expression of + the given operator. LOOKUPS, if non-NULL, is the result of phase 1 + name lookup for the given operator. */ + +tree +build_dependent_operator_type (tree lookups, tree_code code, bool is_assign) +{ + if (lookups) + /* We're partially instantiating a dependent operator expression, and + LOOKUPS is the result of phase 1 name lookup that we performed + earlier at template definition time, so just reuse the corresponding + DEPENDENT_OPERATOR_TYPE. */ + return TREE_TYPE (lookups); + + /* Otherwise we're processing a dependent operator expression at template + definition time, so perform phase 1 name lookup now. */ + lookups = op_unqualified_lookup (code, is_assign); + + tree type = cxx_make_type (DEPENDENT_OPERATOR_TYPE); + DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS (type) = lookups; + TREE_TYPE (lookups) = type; + return type; +} + /* Given an expression PTR for a pointer, return an expression for the value pointed to. ERRORSTRING is the name of the operator to appear in error messages. @@ -3496,7 +3558,7 @@ build_ptrmemfunc_access_expr (tree ptrmem, tree member_name) tree build_x_indirect_ref (location_t loc, tree expr, ref_operator errorstring, - tsubst_flags_t complain) + tree lookups, tsubst_flags_t complain) { tree orig_expr = expr; tree rval; @@ -3516,12 +3578,18 @@ build_x_indirect_ref (location_t loc, tree expr, ref_operator errorstring, return build_min (INDIRECT_REF, TREE_TYPE (TREE_TYPE (expr)), expr); } if (type_dependent_expression_p (expr)) - return build_min_nt_loc (loc, INDIRECT_REF, expr); + { + expr = build_min_nt_loc (loc, INDIRECT_REF, expr); + TREE_TYPE (expr) + = build_dependent_operator_type (lookups, INDIRECT_REF, false); + return expr; + } expr = build_non_dependent_expr (expr); } rval = build_new_op (loc, INDIRECT_REF, LOOKUP_NORMAL, expr, - NULL_TREE, NULL_TREE, &overload, complain); + NULL_TREE, NULL_TREE, lookups, + &overload, complain); if (!rval) rval = cp_build_indirect_ref (loc, expr, errorstring, complain); @@ -4458,8 +4526,8 @@ convert_arguments (tree typelist, vec<tree, va_gc> **values, tree fndecl, tree build_x_binary_op (const op_location_t &loc, enum tree_code code, tree arg1, enum tree_code arg1_code, tree arg2, - enum tree_code arg2_code, tree *overload_p, - tsubst_flags_t complain) + enum tree_code arg2_code, tree lookups, + tree *overload_p, tsubst_flags_t complain) { tree orig_arg1; tree orig_arg2; @@ -4475,7 +4543,8 @@ build_x_binary_op (const op_location_t &loc, enum tree_code code, tree arg1, || type_dependent_expression_p (arg2)) { expr = build_min_nt_loc (loc, code, arg1, arg2); - maybe_save_operator_binding (expr); + TREE_TYPE (expr) + = build_dependent_operator_type (lookups, code, false); return expr; } arg1 = build_non_dependent_expr (arg1); @@ -4486,7 +4555,7 @@ build_x_binary_op (const 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); + lookups, &overload, complain); if (overload_p != NULL) *overload_p = overload; @@ -4538,7 +4607,7 @@ 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, complain); + NULL_TREE, NULL_TREE, &overload, complain); if (processing_template_decl && expr != error_mark_node) { @@ -6402,7 +6471,7 @@ pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype, tree build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg, - tsubst_flags_t complain) + tree lookups, tsubst_flags_t complain) { tree orig_expr = xarg; tree exp; @@ -6414,7 +6483,7 @@ build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg, if (type_dependent_expression_p (xarg)) { tree e = build_min_nt_loc (loc, code, xarg.get_value (), NULL_TREE); - maybe_save_operator_binding (e); + TREE_TYPE (e) = build_dependent_operator_type (lookups, code, false); return e; } @@ -6439,7 +6508,7 @@ 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, complain); + NULL_TREE, lookups, &overload, complain); if (!exp && code == ADDR_EXPR) { @@ -7508,7 +7577,8 @@ build_x_compound_expr_from_list (tree list, expr_list_kind exp, for (list = TREE_CHAIN (list); list; list = TREE_CHAIN (list)) expr = build_x_compound_expr (EXPR_LOCATION (TREE_VALUE (list)), - expr, TREE_VALUE (list), complain); + expr, TREE_VALUE (list), NULL_TREE, + complain); } return expr; @@ -7543,7 +7613,7 @@ build_x_compound_expr_from_vec (vec<tree, va_gc> *vec, const char *msg, expr = (*vec)[0]; for (ix = 1; vec->iterate (ix, &t); ++ix) expr = build_x_compound_expr (EXPR_LOCATION (t), expr, - t, complain); + t, NULL_TREE, complain); return expr; } @@ -7553,7 +7623,7 @@ build_x_compound_expr_from_vec (vec<tree, va_gc> *vec, const char *msg, tree build_x_compound_expr (location_t loc, tree op1, tree op2, - tsubst_flags_t complain) + tree lookups, tsubst_flags_t complain) { tree result; tree orig_op1 = op1; @@ -7566,7 +7636,8 @@ build_x_compound_expr (location_t loc, tree op1, tree op2, || type_dependent_expression_p (op2)) { result = build_min_nt_loc (loc, COMPOUND_EXPR, op1, op2); - maybe_save_operator_binding (result); + TREE_TYPE (result) + = build_dependent_operator_type (lookups, COMPOUND_EXPR, false); return result; } op1 = build_non_dependent_expr (op1); @@ -7574,7 +7645,7 @@ 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, complain); + NULL_TREE, lookups, &overload, complain); if (!result) result = cp_build_compound_expr (op1, op2, complain); @@ -9017,8 +9088,8 @@ cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, { result = build_new_op (input_location, MODIFY_EXPR, LOOKUP_NORMAL, lhs, rhs, - make_node (NOP_EXPR), /*overload=*/NULL, - complain); + make_node (NOP_EXPR), NULL_TREE, + /*overload=*/NULL, complain); if (result == NULL_TREE) return error_mark_node; goto ret; @@ -9233,7 +9304,7 @@ cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, cp_expr build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, - tree rhs, tsubst_flags_t complain) + tree rhs, tree lookups, tsubst_flags_t complain) { tree orig_lhs = lhs; tree orig_rhs = rhs; @@ -9250,7 +9321,9 @@ build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, { 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); + if (modifycode != NOP_EXPR) + TREE_TYPE (rval) + = build_dependent_operator_type (lookups, modifycode, true); return rval; } @@ -9262,7 +9335,7 @@ build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, { tree op = build_nt (modifycode, NULL_TREE, NULL_TREE); tree rval = build_new_op (loc, MODIFY_EXPR, LOOKUP_NORMAL, - lhs, rhs, op, &overload, complain); + lhs, rhs, op, lookups, &overload, complain); if (rval) { if (rval == error_mark_node) diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c index 3fb651a..724684c 100644 --- a/gcc/cp/typeck2.c +++ b/gcc/cp/typeck2.c @@ -1956,7 +1956,7 @@ build_x_arrow (location_t loc, tree expr, tsubst_flags_t complain) while ((expr = build_new_op (loc, COMPONENT_REF, LOOKUP_NORMAL, expr, NULL_TREE, NULL_TREE, - &fn, complain))) + NULL_TREE, &fn, complain))) { if (expr == error_mark_node) return error_mark_node; diff --git a/gcc/testsuite/g++.dg/lookup/operator-3-ops.h b/gcc/testsuite/g++.dg/lookup/operator-3-ops.h new file mode 100644 index 0000000..fbd242a --- /dev/null +++ b/gcc/testsuite/g++.dg/lookup/operator-3-ops.h @@ -0,0 +1,53 @@ +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); diff --git a/gcc/testsuite/g++.dg/lookup/operator-3.C b/gcc/testsuite/g++.dg/lookup/operator-3.C index bc5eb3d..ab0257a 100644 --- a/gcc/testsuite/g++.dg/lookup/operator-3.C +++ b/gcc/testsuite/g++.dg/lookup/operator-3.C @@ -1,4 +1,6 @@ // PR c++/51577 +// Verify we don't consider later-declared namespace-scope operator overloads +// when instantiating a dependent operator expression that occurs at block scope. template <class T> void f (T x) { +x; // { dg-error "no match" } @@ -50,59 +52,7 @@ template <class T> void f (T x) { 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); +#include "operator-3-ops.h" int main() { f(N::A()); diff --git a/gcc/testsuite/g++.dg/lookup/operator-3a.C b/gcc/testsuite/g++.dg/lookup/operator-3a.C new file mode 100644 index 0000000..62ae5c3 --- /dev/null +++ b/gcc/testsuite/g++.dg/lookup/operator-3a.C @@ -0,0 +1,61 @@ +// PR c++/51577 +// { dg-do compile { target c++14 } } +// Like operator-3.C but also containing a partial instantiation step. + +template <class...> auto f () { + return [] (auto 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 { }; } + +#include "operator-3-ops.h" + +int main() { + f()(N::A()); +} diff --git a/gcc/testsuite/g++.dg/lookup/operator-4.C b/gcc/testsuite/g++.dg/lookup/operator-4.C new file mode 100644 index 0000000..e0b80a1 --- /dev/null +++ b/gcc/testsuite/g++.dg/lookup/operator-4.C @@ -0,0 +1,74 @@ +// PR c++/51577 +// { dg-do compile { target c++17 } } +// Like operator-3.C but for unary fold expressions. + +template <class... Ts> void f (Ts... xs) { + (xs->*...); // { dg-error "no match" } + (...->*xs); // { dg-error "no match" } + (xs / ...); // { dg-error "no match" } + (... / xs); // { dg-error "no match" } + (xs * ...); // { dg-error "no match" } + (... * xs); // { dg-error "no match" } + (xs + ...); // { dg-error "no match" } + (... + xs); // { dg-error "no match" } + (xs - ...); // { dg-error "no match" } + (... - xs); // { dg-error "no match" } + (xs % ...); // { dg-error "no match" } + (... % xs); // { dg-error "no match" } + (xs & ...); // { dg-error "no match" } + (... & xs); // { dg-error "no match" } + (xs | ...); // { dg-error "no match" } + (... | xs); // { dg-error "no match" } + (xs ^ ...); // { dg-error "no match" } + (... ^ xs); // { dg-error "no match" } + (xs << ...); // { dg-error "no match" } + (... << xs); // { dg-error "no match" } + (xs >> ...); // { dg-error "no match" } + (... >> xs); // { dg-error "no match" } + (xs && ...); // { dg-error "no match" } + (... && xs); // { dg-error "no match" } + (xs || ...); // { dg-error "no match" } + (... || xs); // { dg-error "no match" } + (xs, ...); + (..., xs); + + (xs == ...); // { dg-error "no match" } + (... == xs); // { dg-error "no match" } + (xs != ...); // { dg-error "no match" } + (... != xs); // { dg-error "no match" } + (xs < ...); // { dg-error "no match" } + (... < xs); // { dg-error "no match" } + (xs > ...); // { dg-error "no match" } + (... > xs); // { dg-error "no match" } + (xs <= ...); // { dg-error "no match" } + (... <= xs); // { dg-error "no match" } + (xs >= ...); // { dg-error "no match" } + (... >= xs); // { dg-error "no match" } + + (xs += ...); // { dg-error "no match" } + (... += xs); // { dg-error "no match" } + (xs -= ...); // { dg-error "no match" } + (... -= xs); // { dg-error "no match" } + (xs *= ...); // { dg-error "no match" } + (... *= xs); // { dg-error "no match" } + (xs /= ...); // { dg-error "no match" } + (... /= xs); // { dg-error "no match" } + (xs %= ...); // { dg-error "no match" } + (... %= xs); // { dg-error "no match" } + (xs |= ...); // { dg-error "no match" } + (... |= xs); // { dg-error "no match" } + (xs ^= ...); // { dg-error "no match" } + (... ^= xs); // { dg-error "no match" } + (xs <<= ...); // { dg-error "no match" } + (... <<= xs); // { dg-error "no match" } + (xs >>= ...); // { dg-error "no match" } + (... >>= xs); // { dg-error "no match" } +} + +namespace N { struct A { }; } + +#include "operator-3-ops.h" + +int main() { + f(N::A(), N::A()); +} diff --git a/gcc/testsuite/g++.dg/lookup/operator-4a.C b/gcc/testsuite/g++.dg/lookup/operator-4a.C new file mode 100644 index 0000000..b4a3f94 --- /dev/null +++ b/gcc/testsuite/g++.dg/lookup/operator-4a.C @@ -0,0 +1,76 @@ +// PR c++/51577 +// { dg-do compile { target c++17 } } +// Like operator-4.C but also containing a partial instantiation step. + +template <class...> auto f () { + return [] (auto... xs) { + (xs->*...); // { dg-error "no match" } + (...->*xs); // { dg-error "no match" } + (xs / ...); // { dg-error "no match" } + (... / xs); // { dg-error "no match" } + (xs * ...); // { dg-error "no match" } + (... * xs); // { dg-error "no match" } + (xs + ...); // { dg-error "no match" } + (... + xs); // { dg-error "no match" } + (xs - ...); // { dg-error "no match" } + (... - xs); // { dg-error "no match" } + (xs % ...); // { dg-error "no match" } + (... % xs); // { dg-error "no match" } + (xs & ...); // { dg-error "no match" } + (... & xs); // { dg-error "no match" } + (xs | ...); // { dg-error "no match" } + (... | xs); // { dg-error "no match" } + (xs ^ ...); // { dg-error "no match" } + (... ^ xs); // { dg-error "no match" } + (xs << ...); // { dg-error "no match" } + (... << xs); // { dg-error "no match" } + (xs >> ...); // { dg-error "no match" } + (... >> xs); // { dg-error "no match" } + (xs && ...); // { dg-error "no match" } + (... && xs); // { dg-error "no match" } + (xs || ...); // { dg-error "no match" } + (... || xs); // { dg-error "no match" } + (xs, ...); + (..., xs); + + (xs == ...); // { dg-error "no match" } + (... == xs); // { dg-error "no match" } + (xs != ...); // { dg-error "no match" } + (... != xs); // { dg-error "no match" } + (xs < ...); // { dg-error "no match" } + (... < xs); // { dg-error "no match" } + (xs > ...); // { dg-error "no match" } + (... > xs); // { dg-error "no match" } + (xs <= ...); // { dg-error "no match" } + (... <= xs); // { dg-error "no match" } + (xs >= ...); // { dg-error "no match" } + (... >= xs); // { dg-error "no match" } + + (xs += ...); // { dg-error "no match" } + (... += xs); // { dg-error "no match" } + (xs -= ...); // { dg-error "no match" } + (... -= xs); // { dg-error "no match" } + (xs *= ...); // { dg-error "no match" } + (... *= xs); // { dg-error "no match" } + (xs /= ...); // { dg-error "no match" } + (... /= xs); // { dg-error "no match" } + (xs %= ...); // { dg-error "no match" } + (... %= xs); // { dg-error "no match" } + (xs |= ...); // { dg-error "no match" } + (... |= xs); // { dg-error "no match" } + (xs ^= ...); // { dg-error "no match" } + (... ^= xs); // { dg-error "no match" } + (xs <<= ...); // { dg-error "no match" } + (... <<= xs); // { dg-error "no match" } + (xs >>= ...); // { dg-error "no match" } + (... >>= xs); // { dg-error "no match" } + }; +} + +namespace N { struct A { }; } + +#include "operator-3-ops.h" + +int main() { + f()(N::A(), N::A()); +} diff --git a/gcc/testsuite/g++.dg/lookup/operator-5.C b/gcc/testsuite/g++.dg/lookup/operator-5.C new file mode 100644 index 0000000..2bbb2c4 --- /dev/null +++ b/gcc/testsuite/g++.dg/lookup/operator-5.C @@ -0,0 +1,74 @@ +// PR c++/51577 +// { dg-do compile { target c++17 } } +// Like operator-4.C but for binary fold expressions. + +namespace N { struct A { }; } + +template <class... Ts> void f (Ts... xs) { + (xs->*...->*N::A{}); // { dg-error "no match" } + (N::A{}->*...->*xs); // { dg-error "no match" } + (xs / ... / N::A{}); // { dg-error "no match" } + (N::A{} / ... / xs); // { dg-error "no match" } + (xs * ... * N::A{}); // { dg-error "no match" } + (N::A{} * ... * xs); // { dg-error "no match" } + (xs + ... + N::A{}); // { dg-error "no match" } + (N::A{} + ... + xs); // { dg-error "no match" } + (xs - ... - N::A{}); // { dg-error "no match" } + (N::A{} - ... - xs); // { dg-error "no match" } + (xs % ... % N::A{}); // { dg-error "no match" } + (N::A{} % ... % xs); // { dg-error "no match" } + (xs & ... & N::A{}); // { dg-error "no match" } + (N::A{} & ... & xs); // { dg-error "no match" } + (xs | ... | N::A{}); // { dg-error "no match" } + (N::A{} | ... | xs); // { dg-error "no match" } + (xs ^ ... ^ N::A{}); // { dg-error "no match" } + (N::A{} ^ ... ^ xs); // { dg-error "no match" } + (xs << ... << N::A{}); // { dg-error "no match" } + (N::A{} << ... << xs); // { dg-error "no match" } + (xs >> ... >> N::A{}); // { dg-error "no match" } + (N::A{} >> ... >> xs); // { dg-error "no match" } + (xs && ... && N::A{}); // { dg-error "no match" } + (N::A{} && ... && xs); // { dg-error "no match" } + (xs || ... || N::A{}); // { dg-error "no match" } + (N::A{} || ... || xs); // { dg-error "no match" } + (xs , ... , N::A{}); + (N::A{} , ... , xs); + + (xs == ... == N::A{}); // { dg-error "no match" } + (N::A{} == ... == xs); // { dg-error "no match" } + (xs != ... != N::A{}); // { dg-error "no match" } + (N::A{} != ... != xs); // { dg-error "no match" } + (xs < ... < N::A{}); // { dg-error "no match" } + (N::A{} < ... < xs); // { dg-error "no match" } + (xs > ... > N::A{}); // { dg-error "no match" } + (N::A{} > ... > xs); // { dg-error "no match" } + (xs <= ... <= N::A{}); // { dg-error "no match" } + (N::A{} <= ... <= xs); // { dg-error "no match" } + (xs >= ... >= N::A{}); // { dg-error "no match" } + (N::A{} >= ... >= xs); // { dg-error "no match" } + + (xs += ... += N::A{}); // { dg-error "no match" } + (N::A{} += ... += xs); // { dg-error "no match" } + (xs -= ... -= N::A{}); // { dg-error "no match" } + (N::A{} -= ... -= xs); // { dg-error "no match" } + (xs *= ... *= N::A{}); // { dg-error "no match" } + (N::A{} *= ... *= xs); // { dg-error "no match" } + (xs /= ... /= N::A{}); // { dg-error "no match" } + (N::A{} /= ... /= xs); // { dg-error "no match" } + (xs %= ... %= N::A{}); // { dg-error "no match" } + (N::A{} %= ... %= xs); // { dg-error "no match" } + (xs |= ... |= N::A{}); // { dg-error "no match" } + (N::A{} |= ... |= xs); // { dg-error "no match" } + (xs ^= ... ^= N::A{}); // { dg-error "no match" } + (N::A{} ^= ... ^= xs); // { dg-error "no match" } + (xs <<= ... <<= N::A{}); // { dg-error "no match" } + (N::A{} <<= ... <<= xs); // { dg-error "no match" } + (xs >>= ... >>= N::A{}); // { dg-error "no match" } + (N::A{} >>= ... >>= xs); // { dg-error "no match" } +} + +#include "operator-3-ops.h" + +int main() { + f(N::A()); +} diff --git a/gcc/testsuite/g++.dg/lookup/operator-5a.C b/gcc/testsuite/g++.dg/lookup/operator-5a.C new file mode 100644 index 0000000..6f9ecd6 --- /dev/null +++ b/gcc/testsuite/g++.dg/lookup/operator-5a.C @@ -0,0 +1,76 @@ +// PR c++/51577 +// { dg-do compile { target c++17 } } +// Like operator-5.C but also containing a partial instantiation step. + +namespace N { struct A { }; } + +template <class...> auto f () { + return [] (auto... xs) { + (xs->*...->*N::A{}); // { dg-error "no match" } + (N::A{}->*...->*xs); // { dg-error "no match" } + (xs / ... / N::A{}); // { dg-error "no match" } + (N::A{} / ... / xs); // { dg-error "no match" } + (xs * ... * N::A{}); // { dg-error "no match" } + (N::A{} * ... * xs); // { dg-error "no match" } + (xs + ... + N::A{}); // { dg-error "no match" } + (N::A{} + ... + xs); // { dg-error "no match" } + (xs - ... - N::A{}); // { dg-error "no match" } + (N::A{} - ... - xs); // { dg-error "no match" } + (xs % ... % N::A{}); // { dg-error "no match" } + (N::A{} % ... % xs); // { dg-error "no match" } + (xs & ... & N::A{}); // { dg-error "no match" } + (N::A{} & ... & xs); // { dg-error "no match" } + (xs | ... | N::A{}); // { dg-error "no match" } + (N::A{} | ... | xs); // { dg-error "no match" } + (xs ^ ... ^ N::A{}); // { dg-error "no match" } + (N::A{} ^ ... ^ xs); // { dg-error "no match" } + (xs << ... << N::A{}); // { dg-error "no match" } + (N::A{} << ... << xs); // { dg-error "no match" } + (xs >> ... >> N::A{}); // { dg-error "no match" } + (N::A{} >> ... >> xs); // { dg-error "no match" } + (xs && ... && N::A{}); // { dg-error "no match" } + (N::A{} && ... && xs); // { dg-error "no match" } + (xs || ... || N::A{}); // { dg-error "no match" } + (N::A{} || ... || xs); // { dg-error "no match" } + (xs , ... , N::A{}); + (N::A{} , ... , xs); + + (xs == ... == N::A{}); // { dg-error "no match" } + (N::A{} == ... == xs); // { dg-error "no match" } + (xs != ... != N::A{}); // { dg-error "no match" } + (N::A{} != ... != xs); // { dg-error "no match" } + (xs < ... < N::A{}); // { dg-error "no match" } + (N::A{} < ... < xs); // { dg-error "no match" } + (xs > ... > N::A{}); // { dg-error "no match" } + (N::A{} > ... > xs); // { dg-error "no match" } + (xs <= ... <= N::A{}); // { dg-error "no match" } + (N::A{} <= ... <= xs); // { dg-error "no match" } + (xs >= ... >= N::A{}); // { dg-error "no match" } + (N::A{} >= ... >= xs); // { dg-error "no match" } + + (xs += ... += N::A{}); // { dg-error "no match" } + (N::A{} += ... += xs); // { dg-error "no match" } + (xs -= ... -= N::A{}); // { dg-error "no match" } + (N::A{} -= ... -= xs); // { dg-error "no match" } + (xs *= ... *= N::A{}); // { dg-error "no match" } + (N::A{} *= ... *= xs); // { dg-error "no match" } + (xs /= ... /= N::A{}); // { dg-error "no match" } + (N::A{} /= ... /= xs); // { dg-error "no match" } + (xs %= ... %= N::A{}); // { dg-error "no match" } + (N::A{} %= ... %= xs); // { dg-error "no match" } + (xs |= ... |= N::A{}); // { dg-error "no match" } + (N::A{} |= ... |= xs); // { dg-error "no match" } + (xs ^= ... ^= N::A{}); // { dg-error "no match" } + (N::A{} ^= ... ^= xs); // { dg-error "no match" } + (xs <<= ... <<= N::A{}); // { dg-error "no match" } + (N::A{} <<= ... <<= xs); // { dg-error "no match" } + (xs >>= ... >>= N::A{}); // { dg-error "no match" } + (N::A{} >>= ... >>= xs); // { dg-error "no match" } + }; +} + +#include "operator-3-ops.h" + +int main() { + f()(N::A()); +} diff --git a/gcc/testsuite/g++.dg/lookup/operator-6.C b/gcc/testsuite/g++.dg/lookup/operator-6.C new file mode 100644 index 0000000..b59c137 --- /dev/null +++ b/gcc/testsuite/g++.dg/lookup/operator-6.C @@ -0,0 +1,59 @@ +// PR c++/83035 +// { dg-do compile { target c++11 } } +// Like operator-3.C but where the lookup occurs at non-block scope. + +template<class T, class = void> struct S { + static constexpr bool is_primary = true; +}; + +template<class T> struct S<T, decltype(+T())> { }; +template<class T> struct S<T, decltype(-T())> { }; +template<class T> struct S<T, decltype(*T())> { }; +template<class T> struct S<T, decltype(~T())> { }; +template<class T> struct S<T, decltype(&T())> { }; +template<class T> struct S<T, decltype(!T())> { }; +template<class T> struct S<T, decltype(++T())> { }; +template<class T> struct S<T, decltype(--T())> { }; +template<class T> struct S<T, decltype(T()++)> { }; +template<class T> struct S<T, decltype(T()--)> { }; + +template<class T> struct S<T, decltype(T()->*T())> { }; +template<class T> struct S<T, decltype(T() / T())> { }; +template<class T> struct S<T, decltype(T() * T())> { }; +template<class T> struct S<T, decltype(T() + T())> { }; +template<class T> struct S<T, decltype(T() - T())> { }; +template<class T> struct S<T, decltype(T() % T())> { }; +template<class T> struct S<T, decltype(T() & T())> { }; +template<class T> struct S<T, decltype(T() | T())> { }; +template<class T> struct S<T, decltype(T() ^ T())> { }; +template<class T> struct S<T, decltype(T() << T())> { }; +template<class T> struct S<T, decltype(T() >> T())> { }; +template<class T> struct S<T, decltype(T() && T())> { }; +template<class T> struct S<T, decltype(T() || T())> { }; +template<class T> struct S<T, decltype(T(), T())> { }; + +template<class T> struct S<T, decltype(T() == T())> { }; +template<class T> struct S<T, decltype(T() != T())> { }; +template<class T> struct S<T, decltype(T() < T())> { }; +template<class T> struct S<T, decltype(T() > T())> { }; +template<class T> struct S<T, decltype(T() <= T())> { }; +template<class T> struct S<T, decltype(T() >= T())> { }; +#if __cplusplus > 201703L +template<class T> struct S<T, decltype(T() <=> T())> { }; +#endif + +template<class T> struct S<T, decltype(T() += T())> { }; +template<class T> struct S<T, decltype(T() -= T())> { }; +template<class T> struct S<T, decltype(T() *= T())> { }; +template<class T> struct S<T, decltype(T() /= T())> { }; +template<class T> struct S<T, decltype(T() %= T())> { }; +template<class T> struct S<T, decltype(T() |= T())> { }; +template<class T> struct S<T, decltype(T() ^= T())> { }; +template<class T> struct S<T, decltype(T() <<= T())> { }; +template<class T> struct S<T, decltype(T() >>= T())> { }; + +namespace N { struct A { }; } + +#include "operator-3-ops.h" + +static_assert(S<N::A>::is_primary, ""); diff --git a/gcc/testsuite/g++.dg/lookup/operator-7.C b/gcc/testsuite/g++.dg/lookup/operator-7.C new file mode 100644 index 0000000..546fcb0 --- /dev/null +++ b/gcc/testsuite/g++.dg/lookup/operator-7.C @@ -0,0 +1,27 @@ +// PR c++/100465 + +namespace N +{ + struct string + { + template<typename T> + void operator+=(T); + }; + + struct A { + void operator+=(char); // #1 + + template<typename T> + void f() { + string s; + s += T(); + } + + void g() { + f<char>(); + } + }; +} // namespace N + +template<typename T> +void operator+=(N::string, T); diff --git a/gcc/testsuite/g++.dg/lookup/operator-8.C b/gcc/testsuite/g++.dg/lookup/operator-8.C new file mode 100644 index 0000000..64d8a97 --- /dev/null +++ b/gcc/testsuite/g++.dg/lookup/operator-8.C @@ -0,0 +1,34 @@ +// Verify phase 1 lookup works properly for rewritten non-dependent conditional +// operator expressions. + +// This test currently fails due to build_min_non_dep_op_overload not knowing +// how to handle rewritten operator expressions; see the FIXME in build_new_op. + +// { dg-do compile { target c++20 } } + +#include <compare> + +struct A { + bool operator==(int); + std::strong_ordering operator<=>(int); +}; + +template<class T> +void f() { + A a; + (void)(a != 0, 0 != a); // { dg-bogus "deleted" "" { xfail *-*-* } } + (void)(a < 0, 0 < a); // { dg-bogus "deleted" "" { xfail *-*-* } } + (void)(a <= 0, 0 <= a); // { dg-bogus "deleted" "" { xfail *-*-* } } + (void)(a > 0, 0 > a); // { dg-bogus "deleted" "" { xfail *-*-* } } + (void)(a >= 0, 0 >= a); // { dg-bogus "deleted" "" { xfail *-*-* } } +} + +// These later-declared namespace-scope overloads shouldn't be considered +// when instantiating f<int>. +bool operator!=(A, int) = delete; +bool operator<(A, int) = delete; +bool operator<=(A, int) = delete; +bool operator>(A, int) = delete; +bool operator>=(A, int) = delete; + +template void f<int>(); diff --git a/libcc1/libcp1plugin.cc b/libcc1/libcp1plugin.cc index ea6ee55..fccdce6 100644 --- a/libcc1/libcp1plugin.cc +++ b/libcc1/libcp1plugin.cc @@ -2669,7 +2669,7 @@ plugin_build_unary_expr (cc1_plugin::connection *self, break; default: - result = build_x_unary_op (/*loc=*/0, opcode, op0, tf_error); + result = build_x_unary_op (/*loc=*/0, opcode, op0, NULL_TREE, tf_error); break; } @@ -2794,7 +2794,7 @@ plugin_build_binary_expr (cc1_plugin::connection *self, default: result = build_x_binary_op (/*loc=*/0, opcode, op0, ERROR_MARK, - op1, ERROR_MARK, NULL, tf_error); + op1, ERROR_MARK, NULL_TREE, NULL, tf_error); break; } |