c++: two-stage name lookup for overloaded operators [PR51577]

Message ID 20211210145333.2868378-1-ppalka@redhat.com
State New
Headers
Series c++: two-stage name lookup for overloaded operators [PR51577] |

Commit Message

Patrick Palka Dec. 10, 2021, 2:53 p.m. UTC
  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
  

Comments

Jason Merrill Dec. 15, 2021, 8:59 p.m. UTC | #1
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.

>   /* 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.

> +  else
> +    /* 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;
> +  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 +3555,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 +3575,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 +4523,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 +4540,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 +4552,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 +4604,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 +6468,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 +6480,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 +6505,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 +7574,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 +7610,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 +7620,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 +7633,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 +7642,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 +9085,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 +9301,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 +9318,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 +9332,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..30d02b806d8
> --- /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;
>       }
>
  
Patrick Palka Dec. 15, 2021, 10:53 p.m. UTC | #2
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.

> 
> >   /* 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;
     }
  
Jason Merrill Dec. 16, 2021, 3:30 a.m. UTC | #3
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;
>       }
>
  

Patch

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)
+
 /* 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.  */;
+  else
+    /* 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;
+  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 +3555,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 +3575,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 +4523,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 +4540,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 +4552,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 +4604,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 +6468,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 +6480,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 +6505,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 +7574,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 +7610,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 +7620,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 +7633,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 +7642,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 +9085,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 +9301,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 +9318,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 +9332,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..30d02b806d8
--- /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;
     }