public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Jason Merrill <jason@redhat.com>
To: Patrick Palka <ppalka@redhat.com>, gcc-patches@gcc.gnu.org
Subject: Re: [PATCH] c++: two-stage name lookup for overloaded operators [PR51577]
Date: Wed, 15 Dec 2021 15:59:27 -0500	[thread overview]
Message-ID: <11c7cfa4-a2a3-d012-5638-687194ea8737@redhat.com> (raw)
In-Reply-To: <20211210145333.2868378-1-ppalka@redhat.com>

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;
>       }
>   


  reply	other threads:[~2021-12-15 20:59 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-12-10 14:53 Patrick Palka
2021-12-15 20:59 ` Jason Merrill [this message]
2021-12-15 22:53   ` Patrick Palka
2021-12-16  3:30     ` Jason Merrill

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=11c7cfa4-a2a3-d012-5638-687194ea8737@redhat.com \
    --to=jason@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=ppalka@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).