[PATCH] c++: two-stage name lookup for overloaded operators [PR51577]
Jason Merrill
jason@redhat.com
Thu Dec 16 03:30:37 GMT 2021
On 12/15/21 17:53, Patrick Palka wrote:
>
>
> On Wed, 15 Dec 2021, Jason Merrill wrote:
>
>> On 12/10/21 09:53, Patrick Palka wrote:
>>> 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 result 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, say a DEPENDENT_OPERATOR_EXPR with enough operands to store
>>> the lookup results along and everything else, but that'd require a lot
>>> of careful work to make sure we handle this new tree code properly
>>> across the frontend.
>>>
>>> However, currently type-dependent operator (and call) expressions are
>>> given an empty TREE_TYPE, so this space is effectively unused except to
>>> signal that the expression is type-dependent. It'd be convenient if we
>>> could use this space to store the lookup results 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, and since the frontend doesn't seem to care
>>> much about the particular 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_binary_op & friends 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. 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.
>>>
>>> Bootstrapped and regtested on x86-64-pc-linux-gnu, does this look OK for
>>> trunk?
>>>
>>> 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.
>>> (DEPENDENT_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
>>> DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS to build_x_*.
>>> (tsubst_omp_for_iterator): Adjust call to build_x_modify_expr.
>>> (tsubst_expr) <case COMPOUND_EXPR>: Pass
>>> DEPENDENT_OPERATOR_TYPE_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 parmaeter 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.
>>> ---
>>> gcc/cp/call.c | 33 +++--
>>> gcc/cp/constraint.cc | 11 +-
>>> gcc/cp/coroutines.cc | 2 +-
>>> gcc/cp/cp-objcp-common.c | 1 +
>>> gcc/cp/cp-tree.def | 5 +
>>> gcc/cp/cp-tree.h | 45 +++++--
>>> gcc/cp/cxx-pretty-print.c | 4 +-
>>> gcc/cp/decl.c | 2 -
>>> gcc/cp/decl2.c | 5 +-
>>> gcc/cp/method.c | 12 +-
>>> gcc/cp/module.cc | 2 +
>>> gcc/cp/name-lookup.c | 133 +------------------
>>> gcc/cp/name-lookup.h | 3 -
>>> gcc/cp/parser.c | 40 +++---
>>> gcc/cp/pt.c | 27 +++-
>>> gcc/cp/ptree.c | 6 +
>>> gcc/cp/semantics.c | 46 ++++---
>>> gcc/cp/tree.c | 2 -
>>> gcc/cp/typeck.c | 112 +++++++++++++---
>>> gcc/cp/typeck2.c | 2 +-
>>> gcc/testsuite/g++.dg/lookup/operator-3-ops.h | 53 ++++++++
>>> gcc/testsuite/g++.dg/lookup/operator-3.C | 56 +-------
>>> gcc/testsuite/g++.dg/lookup/operator-3a.C | 61 +++++++++
>>> gcc/testsuite/g++.dg/lookup/operator-4.C | 74 +++++++++++
>>> gcc/testsuite/g++.dg/lookup/operator-4a.C | 76 +++++++++++
>>> gcc/testsuite/g++.dg/lookup/operator-5.C | 74 +++++++++++
>>> gcc/testsuite/g++.dg/lookup/operator-5a.C | 76 +++++++++++
>>> gcc/testsuite/g++.dg/lookup/operator-6.C | 59 ++++++++
>>> gcc/testsuite/g++.dg/lookup/operator-7.C | 27 ++++
>>> gcc/testsuite/g++.dg/lookup/operator-8.C | 34 +++++
>>> libcc1/libcp1plugin.cc | 4 +-
>>> 31 files changed, 787 insertions(+), 300 deletions(-)
>>> create mode 100644 gcc/testsuite/g++.dg/lookup/operator-3-ops.h
>>> create mode 100644 gcc/testsuite/g++.dg/lookup/operator-3a.C
>>> create mode 100644 gcc/testsuite/g++.dg/lookup/operator-4.C
>>> create mode 100644 gcc/testsuite/g++.dg/lookup/operator-4a.C
>>> create mode 100644 gcc/testsuite/g++.dg/lookup/operator-5.C
>>> create mode 100644 gcc/testsuite/g++.dg/lookup/operator-5a.C
>>> create mode 100644 gcc/testsuite/g++.dg/lookup/operator-6.C
>>> create mode 100644 gcc/testsuite/g++.dg/lookup/operator-7.C
>>> create mode 100644 gcc/testsuite/g++.dg/lookup/operator-8.C
>>>
>>> diff --git a/gcc/cp/call.c b/gcc/cp/call.c
>>> index 28bd8e0c260..53a391cbc6b 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 contains 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;
>>> vec<tree, va_gc> *arglist;
>>> @@ -6554,7 +6567,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;
>>> @@ -6610,7 +6623,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. */
>>> @@ -6767,7 +6780,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 2896efdd7f2..c235a657a77 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 9017902e6fb..c00672eeb6e 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 38eae881f0c..36e04cdee5e 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 725139bb457..6fb838cc850 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 e4330fb1f8b..8b98c061eea 100644
>>> --- a/gcc/cp/cp-tree.h
>>> +++ b/gcc/cp/cp-tree.h
>>> @@ -2183,7 +2183,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
>>> @@ -3976,9 +3977,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)
>>> @@ -4033,6 +4038,20 @@ 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. */
>>> +#define DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS(NODE) \
>>> + TYPE_VALUES_RAW (DEPENDENT_OPERATOR_TYPE_CHECK (NODE))
>>> +
>>> +/* Helper for the above accessor macro that takes a dependent operator
>>> + expression instead of the type thereof. */
>>> +#define DEPENDENT_OPERATOR_SAVED_LOOKUPS(NODE) \
>>> + (TREE_TYPE (NODE) && TREE_CODE (TREE_TYPE (NODE)) ==
>>> DEPENDENT_OPERATOR_TYPE \
>>> + ? DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS (TREE_TYPE (NODE)) \
>>> + : NULL_TREE)
>>
>> Let's make this one an inline function; I'd prefer in general to avoid adding
>> new macros with rvalue results, or that use their argument more than once.
>
> Sounds good. I also renamed it to the more apt TEMPLATED_OPERATOR_SAVED_LOOKUPS
> since we use it on dependent as well as non-dependent operator expressions.
Since it's now a function, it should also be lower case. OK with that
change.
>>
>>> /* Indicates whether a string literal has been parenthesized. Such
>>> usages are disallowed in certain circumstances. */
>>> @@ -6462,14 +6481,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);
>>> @@ -7873,8 +7893,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,
>>> @@ -7892,20 +7913,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);
>>> @@ -7920,7 +7941,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,
>>> @@ -7936,7 +7957,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 3ea357deb80..6af009c6890 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 56f80775ca0..0b8b33a097c 100644
>>> --- a/gcc/cp/decl.c
>>> +++ b/gcc/cp/decl.c
>>> @@ -17098,8 +17098,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 99f5dc784b7..062c175430b 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 935946f5eef..44439bae4ec 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 71d0fab411f..28283264da6 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 080692899a8..5ae7d870cc0 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 f63c4f5b8bb..db705d20c68 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 6f273bfe21f..1baa90ef8fd 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
>>> @@ -18914,7 +18914,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;
>>> @@ -41551,7 +41551,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. */
>>> @@ -41628,11 +41628,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);
>>> @@ -41860,7 +41862,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;
>>> @@ -41982,7 +41984,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))
>>> @@ -42020,7 +42022,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);
>>> @@ -42060,7 +42062,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 cbdb4b566aa..2340139b238 100644
>>> --- a/gcc/cp/pt.c
>>> +++ b/gcc/cp/pt.c
>>> @@ -12657,23 +12657,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 = DEPENDENT_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);
>>> }
>>> }
>>> @@ -17908,7 +17911,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);
>>> @@ -19221,6 +19224,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)),
>>> + DEPENDENT_OPERATOR_SAVED_LOOKUPS (t),
>>> complain));
>>> case ANNOTATE_EXPR:
>>> @@ -19872,6 +19876,7 @@ tsubst_copy_and_build (tree t,
>>> }
>>> else
>>> r = build_x_indirect_ref (input_location, r, RO_UNARY_STAR,
>>> + DEPENDENT_OPERATOR_SAVED_LOOKUPS (t),
>>> complain|decltype_flag);
>>> if (REF_PARENTHESIZED_P (t))
>>> @@ -19982,6 +19987,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,
>>> + DEPENDENT_OPERATOR_SAVED_LOOKUPS (t),
>>> complain|decltype_flag));
>>> case PREDECREMENT_EXPR:
>>> @@ -19995,6 +20001,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)),
>>> + DEPENDENT_OPERATOR_SAVED_LOOKUPS (t),
>>> complain|decltype_flag));
>>> case FIX_TRUNC_EXPR:
>>> @@ -20013,6 +20020,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,
>>> + DEPENDENT_OPERATOR_SAVED_LOOKUPS (t),
>>> complain|decltype_flag));
>>> case PLUS_EXPR:
>>> @@ -20077,6 +20085,7 @@ tsubst_copy_and_build (tree t,
>>> (warning_suppressed_p (TREE_OPERAND (t, 1))
>>> ? ERROR_MARK
>>> : TREE_CODE (TREE_OPERAND (t, 1))),
>>> + DEPENDENT_OPERATOR_SAVED_LOOKUPS (t),
>>> /*overload=*/NULL,
>>> complain|decltype_flag);
>>> if (EXPR_P (r))
>>> @@ -20229,8 +20238,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,
>>> + DEPENDENT_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
>>> @@ -20331,6 +20342,7 @@ tsubst_copy_and_build (tree t,
>>> RETURN (build_x_compound_expr (EXPR_LOCATION (t),
>>> op0,
>>> RECUR (TREE_OPERAND (t, 1)),
>>> + DEPENDENT_OPERATOR_SAVED_LOOKUPS (t),
>>> complain|decltype_flag));
>>> }
>>> @@ -27011,6 +27023,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 d514aa2cad2..f7ddae77679 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 cdf63c15e21..7078af03d3c 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);
>>> @@ -9881,7 +9882,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;
>>> }
>>> @@ -9895,9 +9896,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;
>>> @@ -9925,7 +9927,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
>>> @@ -9951,6 +9953,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;
>>> @@ -9980,13 +9983,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;
>>> @@ -10097,7 +10100,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)))
>>> @@ -10106,23 +10109,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);
>>> @@ -10138,13 +10142,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);
>>> @@ -10162,7 +10166,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);
>>> @@ -10873,7 +10877,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;
>>> @@ -12125,6 +12129,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;
>>> }
>>> @@ -12151,6 +12158,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 f6f7927f293..f319dbf3526 100644
>>> --- a/gcc/cp/tree.c
>>> +++ b/gcc/cp/tree.c
>>> @@ -5974,8 +5974,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 4e60db40c76..88dca343315 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,64 @@ 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 contains the result of phase 1 name lookup that we performed
>>> + earlier at template definition time, so just carry it over. */;
>>
>> If we're going to keep using the same set of lookups, can we also reuse the
>> same DEPENDENT_OPERATOR_TYPE? It seems like you could pass the type to
>> build_x_* instead of pulling the lookups out as early.
>
> That sounds like it'd work well. But what if we instead just make
> DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS point back to the corresponding
> DEPENDENT_OPERATOR_TYPE via TREE_TYPE? As in the below...
>
> v2: Rename DEPENDENT_OPERATOR_SAVED_LOOKUPS to
> TEMPLATED_OPERATOR_SAVED_LOOKUPS and turn into an inline function.
>
> Make build_dependent_operator_type set/inspect the TREE_TYPE of the
> lookup result in order to reuse the DEPENDENT_OPERATOR_TYPE during
> partial instantiations. Document this in the comment for
> DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS.
>
> Bootstrap and regtest in progress.
>
> -- >8 --
>
> 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 parmaeter 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.
> ---
> gcc/cp/call.c | 33 +++--
> gcc/cp/constraint.cc | 11 +-
> gcc/cp/coroutines.cc | 2 +-
> gcc/cp/cp-objcp-common.c | 1 +
> gcc/cp/cp-tree.def | 5 +
> gcc/cp/cp-tree.h | 49 +++++--
> gcc/cp/cxx-pretty-print.c | 4 +-
> gcc/cp/decl.c | 2 -
> gcc/cp/decl2.c | 5 +-
> gcc/cp/method.c | 12 +-
> gcc/cp/module.cc | 2 +
> gcc/cp/name-lookup.c | 133 +------------------
> gcc/cp/name-lookup.h | 3 -
> gcc/cp/parser.c | 40 +++---
> gcc/cp/pt.c | 27 +++-
> gcc/cp/ptree.c | 6 +
> gcc/cp/semantics.c | 46 ++++---
> gcc/cp/tree.c | 2 -
> gcc/cp/typeck.c | 115 +++++++++++++---
> gcc/cp/typeck2.c | 2 +-
> gcc/testsuite/g++.dg/lookup/operator-3-ops.h | 53 ++++++++
> gcc/testsuite/g++.dg/lookup/operator-3.C | 56 +-------
> gcc/testsuite/g++.dg/lookup/operator-3a.C | 61 +++++++++
> gcc/testsuite/g++.dg/lookup/operator-4.C | 74 +++++++++++
> gcc/testsuite/g++.dg/lookup/operator-4a.C | 76 +++++++++++
> gcc/testsuite/g++.dg/lookup/operator-5.C | 74 +++++++++++
> gcc/testsuite/g++.dg/lookup/operator-5a.C | 76 +++++++++++
> gcc/testsuite/g++.dg/lookup/operator-6.C | 59 ++++++++
> gcc/testsuite/g++.dg/lookup/operator-7.C | 27 ++++
> gcc/testsuite/g++.dg/lookup/operator-8.C | 34 +++++
> libcc1/libcp1plugin.cc | 4 +-
> 31 files changed, 794 insertions(+), 300 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/lookup/operator-3-ops.h
> create mode 100644 gcc/testsuite/g++.dg/lookup/operator-3a.C
> create mode 100644 gcc/testsuite/g++.dg/lookup/operator-4.C
> create mode 100644 gcc/testsuite/g++.dg/lookup/operator-4a.C
> create mode 100644 gcc/testsuite/g++.dg/lookup/operator-5.C
> create mode 100644 gcc/testsuite/g++.dg/lookup/operator-5a.C
> create mode 100644 gcc/testsuite/g++.dg/lookup/operator-6.C
> create mode 100644 gcc/testsuite/g++.dg/lookup/operator-7.C
> create mode 100644 gcc/testsuite/g++.dg/lookup/operator-8.C
>
> diff --git a/gcc/cp/call.c b/gcc/cp/call.c
> index 347df5da35d..31c2308dc28 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 contains 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 566f4e38fac..8e25ae23670 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 9017902e6fb..c00672eeb6e 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 38eae881f0c..36e04cdee5e 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 725139bb457..6fb838cc850 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 7f32cf56383..57a0da4e0ef 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,24 @@ 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)
> +{
> + return TREE_TYPE (EXPR_CHECK (t)) && TREE_CODE (TREE_TYPE (t)) == DEPENDENT_OPERATOR_TYPE
> + ? DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS (TREE_TYPE (t))
> + : NULL_TREE;
> +}
> +
> /* Indicates whether a string literal has been parenthesized. Such
> usages are disallowed in certain circumstances. */
>
> @@ -6464,14 +6487,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 +7899,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 +7919,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 +7947,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 +7963,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 3ea357deb80..6af009c6890 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 7c2048c6acb..24dd6701663 100644
> --- a/gcc/cp/decl.c
> +++ b/gcc/cp/decl.c
> @@ -17098,8 +17098,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 99f5dc784b7..062c175430b 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 935946f5eef..44439bae4ec 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 9266055cd92..f3e7af22699 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 6b5e4349595..3bd7b206abb 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 f63c4f5b8bb..db705d20c68 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 c2564e51e41..5d72201f87c 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 42133a30c97..60cc23ba987 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 d514aa2cad2..f7ddae77679 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 356fb83200c..6603066c620 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 284fb5f4b2a..29f3c171606 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 4e60db40c76..d73c7fb6e33 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 contains 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 3fb651a02ba..724684c0457 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 00000000000..fbd242a4e66
> --- /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 bc5eb3d6693..98011efd543 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 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 00000000000..62ae5c36dc2
> --- /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 00000000000..e0b80a1c3b3
> --- /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 00000000000..b4a3f947b05
> --- /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 00000000000..2bbb2c41618
> --- /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 00000000000..6f9ecd65a50
> --- /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 00000000000..b59c137226a
> --- /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 00000000000..546fcb0a526
> --- /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 00000000000..01adff963dc
> --- /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 functions shouldn't be considered
> +// during instantiation of 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 ea6ee553401..fccdce6ad47 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;
> }
>
More information about the Gcc-patches
mailing list