On Wed, 15 Dec 2021, Jason Merrill wrote:

> On 12/10/21 09:53, Patrick Palka wrote:
> > In order to properly implement two-stage name lookup for dependent
> > operator expressions, we need to remember the result of unqualified
> > lookup of the operator at template definition time, and reuse that
> > result rather than performing another unqualified lookup at
> > instantiation time.
> > 
> > Ideally we could just store the lookup result in the expression directly,
> > but as pointed out in r9-6405 this isn't really possible since we use
> > the standard tree codes to represent most dependent operator expressions.
> > 
> > We could perhaps create a new tree code to represent dependent operator
> > expressions, say a DEPENDENT_OPERATOR_EXPR with enough operands to store
> > the lookup results along and everything else, but that'd require a lot
> > of careful work to make sure we handle this new tree code properly
> > across the frontend.
> > 
> > However, currently type-dependent operator (and call) expressions are
> > given an empty TREE_TYPE, so this space is effectively unused except to
> > signal that the expression is type-dependent.  It'd be convenient if we
> > could use this space to store the lookup results while preserving the
> > dependent-ness of the expression.
> > 
> > To that end, this patch creates a new kind of type, called
> > DEPENDENT_OPERATOR_TYPE, which we give to dependent operator expressions
> > and into which we can store the result of operator lookup at template
> > definition time (DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS).  Since this
> > type is always dependent, and since the frontend doesn't seem to care
> > much about the particular type of a type-dependent expression, using
> > this type in place of a NULL_TREE type seems to just work; only
> > dependent_type_p and WILDCARD_TYPE_P need to be adjusted to return true
> > for this new type.
> > 
> > The rest of the patch mostly consists of adding the necessary plumbing
> > to pass DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS to add_operator_candidates,
> > adjusting all callers of build_x_binary_op & friends appropriately, and
> > removing the now unnecessary push_operator_bindings mechanism.
> > 
> > In passing, this patch simplifies finish_constraint_binary_op to avoid
> > using build_x_binary_op for building a binary constraint-expr; we don't
> > need to consider operator||/&& overloads here.  This patch also makes
> > FOLD_EXPR_OP yield a tree_code instead of a raw INTEGER_CST.
> > 
> > Finally, this patch adds the XFAILed test operator-8.C which is about
> > broken two-stage name lookup for rewritten non-dependent operator
> > expressions, an existing bug that's otherwise only documented in
> > build_new_op.
> > 
> > Bootstrapped and regtested on x86-64-pc-linux-gnu, does this look OK for
> > trunk?
> > 
> >     PR c++/51577
> >     PR c++/83035
> >     PR c++/100465
> > 
> > gcc/cp/ChangeLog:
> > 
> >     * call.c (add_operator_candidates): Add lookups parameter.
> >     Use it to avoid performing a second unqualified lookup when
> >     instantiating a dependent operator expression.
> >     (build_new_op): Add lookups parameter and pass it appropriately.
> >     * constraint.cc (finish_constraint_binary_op): Use
> >     build_min_nt_loc instead of build_x_binary_op.
> >     * coroutines.cc (build_co_await): Adjust call to build_new_op.
> >     * cp-objcp-common.c (cp_common_init_ts): Mark
> >     DEPENDENT_OPERATOR_TYPE appropriately.
> >     * cp-tree.def (DEPENDENT_OPERATOR_TYPE): Define.
> >     * cp-tree.h (WILDCARD_TYPE_P): Accept DEPENDENT_OPERATOR_TYPE.
> >     (FOLD_EXPR_OP_RAW): New, renamed from ...
> >     (FOLD_EXPR_OP): ... this.  Change this to return the tree_code
> > directly.
> >     (DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS): Define.
> >     (DEPENDENT_OPERATOR_SAVED_LOOKUPS): Define.
> >     (build_new_op): Add lookups parameter.
> >     (build_dependent_operator_type): Declare.
> >     (build_x_indirect_ref): Add lookups parameter.
> >     (build_x_binary_op): Likewise.
> >     (build_x_unary_op): Likewise.
> >     (build_x_compound_expr): Likewise.
> >     (build_x_modify_expr): Likewise.
> >     * cxx-pretty-print.c (get_fold_operator): Adjust after
> >     FOLD_EXPR_OP change.
> >     * decl.c (start_preparsed_function): Don't call
> >     push_operator_bindings.
> >     * decl2.c (grok_array_decl): Adjust calls to build_new_op.
> >     * method.c (do_one_comp): Likewise.
> >     (build_comparison_op): Likewise.
> >     * module.cc (trees_out::type_node): Handle DEPENDENT_OPERATOR_TYPE.
> >     (trees_in::tree_node): Likewise.
> >     * name-lookup.c (lookup_name): Revert r11-2876 change.
> >     (op_unqualified_lookup): Remove.
> >     (maybe_save_operator_binding): Remove.
> >     (discard_operator_bindings): Remove.
> >     (push_operator_bindings): Remove.
> >     * name-lookup.h (maybe_save_operator_binding): Remove.
> >     (push_operator_bindings): Remove.
> >     (discard_operator_bindings): Remove.
> >     * parser.c (cp_parser_unary_expression): Adjust calls to build_x_*.
> >     (cp_parser_binary_expression): Likewise.
> >     (cp_parser_assignment_expression): Likewise.
> >     (cp_parser_expression): Likewise.
> >     (do_range_for_auto_deduction): Likewise.
> >     (cp_convert_range_for): Likewise.
> >     (cp_parser_perform_range_for_lookup): Likewise.
> >     (cp_parser_template_argument): Likewise.
> >     (cp_parser_omp_for_cond): Likewise.
> >     (cp_parser_omp_for_incr): Likewise.
> >     (cp_parser_omp_for_loop_init): Likewise.
> >     (cp_convert_omp_range_for): Likewise.
> >     (cp_finish_omp_range_for): Likewise.
> >     * pt.c (fold_expression): Adjust after FOLD_EXPR_OP change. Pass
> >     DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS to build_x_*.
> >     (tsubst_omp_for_iterator): Adjust call to build_x_modify_expr.
> >     (tsubst_expr) <case COMPOUND_EXPR>: Pass
> >     DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS to build_x_*.
> >     (tsubst_copy_and_build) <case INDIRECT_REF>: Likewise.
> >     <case tcc_unary>: Likewise.
> >     <case tcc_binary>: Likewise.
> >     <case MODOP_EXPR>: Likewise.
> >     <case COMPOUND_EXPR>: Likewise.
> >     (dependent_type_p_r): Return true for DEPENDENT_OPERATOR_TYPE.
> >     * ptree.c (cxx_print_type): Handle DEPENDENT_OPERATOR_TYPE.
> >     * semantics.c (finish_increment_expr): Adjust call to
> >     build_x_unary_op.
> >     (finish_unary_op_expr): Likewise.
> >     (handle_omp_for_class_iterator): Adjust calls to build_x_*.
> >     (finish_omp_cancel): Likewise.
> >     (finish_unary_fold_expr): Use build_dependent_operator_type.
> >     (finish_binary_fold_expr): Likewise.
> >     * tree.c (cp_free_lang_data): Don't call discard_operator_bindings.
> >     * typeck.c (rationalize_conditional_expr): Adjust call to
> >     build_x_binary_op.
> >     (op_unqualified_lookup): Define.
> >     (build_dependent_operator_type): Define.
> >     (build_x_indirect_ref): Add lookups parmaeter and use
> >     build_dependent_operator_type.
> >     (build_x_binary_op): Likewise.
> >     (build_x_array_ref): Likewise.
> >     (build_x_unary_op): Likewise.
> >     (build_x_compound_expr_from_list): Adjust call to
> >     build_x_compound_expr.
> >     (build_x_compound_expr_from_vec): Likewise.
> >     (build_x_compound_expr): Add lookups parameter and use
> >     build_dependent_operator_type.
> >     (cp_build_modify_expr): Adjust call to build_new_op.
> >     (build_x_modify_expr): Add lookups parameter and use
> >     build_dependent_operator_type.
> >     * typeck2.c (build_x_arrow): Adjust call to build_new_op.
> > 
> > libcc1/ChangeLog:
> > 
> >     * libcp1plugin.cc (plugin_build_unary_expr): Adjust call to
> >     build_x_unary_op.
> >     (plugin_build_binary_expr): Adjust call to build_x_binary_op.
> > 
> > gcc/testsuite/ChangeLog:
> > 
> >     * g++.dg/lookup/operator-3.C: Split out operator overload
> >     declarations into ...
> >     * g++.dg/lookup/operator-3-ops.h: ... here.
> >     * g++.dg/lookup/operator-3a.C: New test.
> >     * g++.dg/lookup/operator-4.C: New test.
> >     * g++.dg/lookup/operator-4a.C: New test.
> >     * g++.dg/lookup/operator-5.C: New test.
> >     * g++.dg/lookup/operator-5a.C: New test.
> >     * g++.dg/lookup/operator-6.C: New test.
> >     * g++.dg/lookup/operator-7.C: New test.
> >     * g++.dg/lookup/operator-8.C: New test.
> > ---
> >   gcc/cp/call.c                                |  33 +++--
> >   gcc/cp/constraint.cc                         |  11 +-
> >   gcc/cp/coroutines.cc                         |   2 +-
> >   gcc/cp/cp-objcp-common.c                     |   1 +
> >   gcc/cp/cp-tree.def                           |   5 +
> >   gcc/cp/cp-tree.h                             |  45 +++++--
> >   gcc/cp/cxx-pretty-print.c                    |   4 +-
> >   gcc/cp/decl.c                                |   2 -
> >   gcc/cp/decl2.c                               |   5 +-
> >   gcc/cp/method.c                              |  12 +-
> >   gcc/cp/module.cc                             |   2 +
> >   gcc/cp/name-lookup.c                         | 133 +------------------
> >   gcc/cp/name-lookup.h                         |   3 -
> >   gcc/cp/parser.c                              |  40 +++---
> >   gcc/cp/pt.c                                  |  27 +++-
> >   gcc/cp/ptree.c                               |   6 +
> >   gcc/cp/semantics.c                           |  46 ++++---
> >   gcc/cp/tree.c                                |   2 -
> >   gcc/cp/typeck.c                              | 112 +++++++++++++---
> >   gcc/cp/typeck2.c                             |   2 +-
> >   gcc/testsuite/g++.dg/lookup/operator-3-ops.h |  53 ++++++++
> >   gcc/testsuite/g++.dg/lookup/operator-3.C     |  56 +-------
> >   gcc/testsuite/g++.dg/lookup/operator-3a.C    |  61 +++++++++
> >   gcc/testsuite/g++.dg/lookup/operator-4.C     |  74 +++++++++++
> >   gcc/testsuite/g++.dg/lookup/operator-4a.C    |  76 +++++++++++
> >   gcc/testsuite/g++.dg/lookup/operator-5.C     |  74 +++++++++++
> >   gcc/testsuite/g++.dg/lookup/operator-5a.C    |  76 +++++++++++
> >   gcc/testsuite/g++.dg/lookup/operator-6.C     |  59 ++++++++
> >   gcc/testsuite/g++.dg/lookup/operator-7.C     |  27 ++++
> >   gcc/testsuite/g++.dg/lookup/operator-8.C     |  34 +++++
> >   libcc1/libcp1plugin.cc                       |   4 +-
> >   31 files changed, 787 insertions(+), 300 deletions(-)
> >   create mode 100644 gcc/testsuite/g++.dg/lookup/operator-3-ops.h
> >   create mode 100644 gcc/testsuite/g++.dg/lookup/operator-3a.C
> >   create mode 100644 gcc/testsuite/g++.dg/lookup/operator-4.C
> >   create mode 100644 gcc/testsuite/g++.dg/lookup/operator-4a.C
> >   create mode 100644 gcc/testsuite/g++.dg/lookup/operator-5.C
> >   create mode 100644 gcc/testsuite/g++.dg/lookup/operator-5a.C
> >   create mode 100644 gcc/testsuite/g++.dg/lookup/operator-6.C
> >   create mode 100644 gcc/testsuite/g++.dg/lookup/operator-7.C
> >   create mode 100644 gcc/testsuite/g++.dg/lookup/operator-8.C
> > 
> > diff --git a/gcc/cp/call.c b/gcc/cp/call.c
> > index 28bd8e0c260..53a391cbc6b 100644
> > --- a/gcc/cp/call.c
> > +++ b/gcc/cp/call.c
> > @@ -6285,12 +6285,17 @@ op_is_ordered (tree_code code)
> >     /* Subroutine of build_new_op: Add to CANDIDATES all candidates for the
> >      operator indicated by CODE/CODE2.  This function calls itself
> > recursively to
> > -   handle C++20 rewritten comparison operator candidates.  */
> > +   handle C++20 rewritten comparison operator candidates.
> > +
> > +   LOOKUPS, if non-NULL, is the set of pertinent namespace-scope operator
> > +   overloads to consider.  This parameter is used when instantiating a
> > +   dependent operator expression and has the same structure as
> > +   DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS.  */
> >     static tree
> >   add_operator_candidates (z_candidate **candidates,
> >                      tree_code code, tree_code code2,
> > -                    vec<tree, va_gc> *arglist,
> > +                    vec<tree, va_gc> *arglist, tree lookups,
> >                      int flags, tsubst_flags_t complain)
> >   {
> >     z_candidate *start_candidates = *candidates;
> > @@ -6326,7 +6331,15 @@ add_operator_candidates (z_candidate **candidates,
> >        consider.  */
> >     if (!memonly)
> >       {
> > -      tree fns = lookup_name (fnname, LOOK_where::BLOCK_NAMESPACE);
> > +      tree fns;
> > +      if (!lookups)
> > +   fns = lookup_name (fnname, LOOK_where::BLOCK_NAMESPACE);
> > +      /* If LOOKUPS is non-NULL, then we're instantiating a dependent
> > operator
> > +    expression, and LOOKUPS contains the result of stage 1 name lookup.
> > */
> > +      else if (tree found = purpose_member (fnname, lookups))
> > +   fns = TREE_VALUE (found);
> > +      else
> > +   fns = NULL_TREE;
> >         fns = lookup_arg_dependent (fnname, fns, arglist);
> >         add_candidates (fns, NULL_TREE, arglist, NULL_TREE,
> >                   NULL_TREE, false, NULL_TREE, NULL_TREE,
> > @@ -6429,7 +6442,7 @@ add_operator_candidates (z_candidate **candidates,
> >       if (rewrite_code != code)
> >         /* Add rewritten candidates in same order.  */
> >         add_operator_candidates (candidates, rewrite_code, ERROR_MARK,
> > -                                arglist, flags, complain);
> > +                                arglist, lookups, flags, complain);
> >               z_candidate *save_cand = *candidates;
> >   @@ -6439,7 +6452,7 @@ add_operator_candidates (z_candidate **candidates,
> >       revlist->quick_push ((*arglist)[1]);
> >       revlist->quick_push ((*arglist)[0]);
> >       add_operator_candidates (candidates, rewrite_code, ERROR_MARK,
> > -                              revlist, flags, complain);
> > +                              revlist, lookups, flags, complain);
> >               /* Release the vec if we didn't add a candidate that uses it. 
> >  */
> >       for (z_candidate *c = *candidates; c != save_cand; c = c->next)
> > @@ -6457,8 +6470,8 @@ add_operator_candidates (z_candidate **candidates,
> >     tree
> >   build_new_op (const op_location_t &loc, enum tree_code code, int flags,
> > -         tree arg1, tree arg2, tree arg3, tree *overload,
> > -         tsubst_flags_t complain)
> > +         tree arg1, tree arg2, tree arg3, tree lookups,
> > +         tree *overload, tsubst_flags_t complain)
> >   {
> >     struct z_candidate *candidates = 0, *cand;
> >     vec<tree, va_gc> *arglist;
> > @@ -6554,7 +6567,7 @@ build_new_op (const op_location_t &loc, enum tree_code
> > code, int flags,
> >     p = conversion_obstack_alloc (0);
> >       result = add_operator_candidates (&candidates, code, code2, arglist,
> > -                               flags, complain);
> > +                               lookups, flags, complain);
> >     if (result == error_mark_node)
> >       goto user_defined_result_ready;
> >   @@ -6610,7 +6623,7 @@ build_new_op (const op_location_t &loc, enum
> > tree_code code, int flags,
> >       else
> >         code = PREDECREMENT_EXPR;
> >       result = build_new_op (loc, code, flags, arg1, NULL_TREE,
> > -                            NULL_TREE, overload, complain);
> > +                            NULL_TREE, lookups, overload, complain);
> >       break;
> >               /* The caller will deal with these.  */
> > @@ -6767,7 +6780,7 @@ build_new_op (const op_location_t &loc, enum tree_code
> > code, int flags,
> >                 warning_sentinel ws (warn_zero_as_null_pointer_constant);
> >                 result = build_new_op (loc, code,
> >                                        LOOKUP_NORMAL|LOOKUP_REWRITTEN,
> > -                                      lhs, rhs, NULL_TREE,
> > +                                      lhs, rhs, NULL_TREE, lookups,
> >                                        NULL, complain);
> >               }
> >               break;
> > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> > index 2896efdd7f2..c235a657a77 100644
> > --- a/gcc/cp/constraint.cc
> > +++ b/gcc/cp/constraint.cc
> > @@ -202,15 +202,8 @@ finish_constraint_binary_op (location_t loc,
> >       return error_mark_node;
> >     if (!check_constraint_operands (loc, lhs, rhs))
> >       return error_mark_node;
> > -  tree overload;
> > -  cp_expr expr = build_x_binary_op (loc, code,
> > -                               lhs, TREE_CODE (lhs),
> > -                               rhs, TREE_CODE (rhs),
> > -                               &overload, tf_none);
> > -  /* When either operand is dependent, the overload set may be non-empty.
> > */
> > -  if (expr == error_mark_node)
> > -    return error_mark_node;
> > -  expr.set_location (loc);
> > +  cp_expr expr
> > +    = build_min_nt_loc (loc, code, lhs.get_value (), rhs.get_value ());
> >     expr.set_range (lhs.get_start (), rhs.get_finish ());
> >     return expr;
> >   }
> > diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
> > index 9017902e6fb..c00672eeb6e 100644
> > --- a/gcc/cp/coroutines.cc
> > +++ b/gcc/cp/coroutines.cc
> > @@ -912,7 +912,7 @@ build_co_await (location_t loc, tree a,
> > suspend_point_kind suspend_kind)
> >     if (MAYBE_CLASS_TYPE_P (TREE_TYPE (a)))
> >       {
> >         o = build_new_op (loc, CO_AWAIT_EXPR, LOOKUP_NORMAL, a, NULL_TREE,
> > -                   NULL_TREE, NULL, tf_warning_or_error);
> > +                   NULL_TREE, NULL_TREE, NULL, tf_warning_or_error);
> >         /* If no viable functions are found, o is a.  */
> >         if (!o || o == error_mark_node)
> >     o = a;
> > diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c
> > index 38eae881f0c..36e04cdee5e 100644
> > --- a/gcc/cp/cp-objcp-common.c
> > +++ b/gcc/cp/cp-objcp-common.c
> > @@ -484,6 +484,7 @@ cp_common_init_ts (void)
> >     /* New Types.  */
> >     MARK_TS_TYPE_COMMON (UNBOUND_CLASS_TEMPLATE);
> >     MARK_TS_TYPE_COMMON (TYPE_ARGUMENT_PACK);
> > +  MARK_TS_TYPE_COMMON (DEPENDENT_OPERATOR_TYPE);
> >       MARK_TS_TYPE_NON_COMMON (DECLTYPE_TYPE);
> >     MARK_TS_TYPE_NON_COMMON (TYPENAME_TYPE);
> > diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def
> > index 725139bb457..6fb838cc850 100644
> > --- a/gcc/cp/cp-tree.def
> > +++ b/gcc/cp/cp-tree.def
> > @@ -476,6 +476,11 @@ DEFTREECODE (UNDERLYING_TYPE, "underlying_type",
> > tcc_type, 0)
> >      BASES_TYPE is the type in question.  */
> >   DEFTREECODE (BASES, "bases", tcc_type, 0)
> >   +/* Dependent operator expressions are given this type rather than a
> > NULL_TREE
> > +   type so that we have somewhere to stash the result of phase 1 name
> > lookup
> > +   (namely into DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS).  */
> > +DEFTREECODE (DEPENDENT_OPERATOR_TYPE, "dependent_operator_type", tcc_type,
> > 0)
> > +
> >   /* Used to represent the template information stored by template
> >      specializations.
> >      The accessors are:
> > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > index e4330fb1f8b..8b98c061eea 100644
> > --- a/gcc/cp/cp-tree.h
> > +++ b/gcc/cp/cp-tree.h
> > @@ -2183,7 +2183,8 @@ enum languages { lang_c, lang_cplusplus };
> >      || TREE_CODE (T) == TYPENAME_TYPE                      \
> >      || TREE_CODE (T) == TYPEOF_TYPE                        \
> >      || TREE_CODE (T) == BOUND_TEMPLATE_TEMPLATE_PARM       \
> > -   || TREE_CODE (T) == DECLTYPE_TYPE)
> > +   || TREE_CODE (T) == DECLTYPE_TYPE                       \
> > +   || TREE_CODE (T) == DEPENDENT_OPERATOR_TYPE)
> >     /* Nonzero if T is a class (or struct or union) type.  Also nonzero
> >      for template type parameters, typename types, and instantiated
> > @@ -3976,9 +3977,13 @@ struct GTY(()) lang_decl {
> >     TREE_LANG_FLAG_0 (FOLD_EXPR_CHECK (NODE))
> >     /* An INTEGER_CST containing the tree code of the folded operator. */
> > -#define FOLD_EXPR_OP(NODE) \
> > +#define FOLD_EXPR_OP_RAW(NODE) \
> >     TREE_OPERAND (FOLD_EXPR_CHECK (NODE), 0)
> >   +/* The tree code of the folded operator.  */
> > +#define FOLD_EXPR_OP(NODE) \
> > +  ((enum tree_code) TREE_INT_CST_LOW (FOLD_EXPR_OP_RAW (NODE)))
> > +
> >   /* The expression containing an unexpanded parameter pack. */
> >   #define FOLD_EXPR_PACK(NODE) \
> >     TREE_OPERAND (FOLD_EXPR_CHECK (NODE), 1)
> > @@ -4033,6 +4038,20 @@ struct GTY(()) lang_decl {
> >   #define CALL_EXPR_OPERATOR_SYNTAX(NODE) \
> >     TREE_LANG_FLAG_6 (CALL_OR_AGGR_INIT_CHECK (NODE))
> >   +/* A TREE_LIST containing the result of phase 1 name lookup of the
> > operator
> > +   overloads that are pertinent to the dependent operator expression whose
> > +   type is NODE.  Each TREE_PURPOSE is an IDENTIFIER_NODE and TREE_VALUE is
> > +   the corresponding (possibly empty) lookup result.  */
> > +#define DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS(NODE) \
> > +  TYPE_VALUES_RAW (DEPENDENT_OPERATOR_TYPE_CHECK (NODE))
> > +
> > +/* Helper for the above accessor macro that takes a dependent operator
> > +   expression instead of the type thereof.  */
> > +#define DEPENDENT_OPERATOR_SAVED_LOOKUPS(NODE) \
> > +  (TREE_TYPE (NODE) && TREE_CODE (TREE_TYPE (NODE)) ==
> > DEPENDENT_OPERATOR_TYPE \
> > +   ? DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS (TREE_TYPE (NODE)) \
> > +   : NULL_TREE)
> 
> Let's make this one an inline function; I'd prefer in general to avoid adding
> new macros with rvalue results, or that use their argument more than once.

Sounds good.  I also renamed it to the more apt TEMPLATED_OPERATOR_SAVED_LOOKUPS
since we use it on dependent as well as non-dependent operator expressions.

> 
> >   /* Indicates whether a string literal has been parenthesized. Such
> >      usages are disallowed in certain circumstances.  */
> >   @@ -6462,14 +6481,15 @@ extern tree build_special_member_call
> > (tree, tree,
> >                                              tree, int, tsubst_flags_t);
> >   extern tree build_new_op                  (const op_location_t &,
> >                                              enum tree_code,
> > -                                            int, tree, tree, tree, tree
> > *,
> > -                                            tsubst_flags_t);
> > +                                            int, tree, tree, tree, tree,
> > +                                            tree *, tsubst_flags_t);
> >   /* Wrapper that leaves out the usually-null op3 and overload parms.  */
> >   inline tree build_new_op (const op_location_t &loc, enum tree_code code,
> >                       int flags, tree arg1, tree arg2,
> >                       tsubst_flags_t complain)
> >   {
> > -  return build_new_op (loc, code, flags, arg1, arg2, NULL_TREE, NULL,
> > complain);
> > +  return build_new_op (loc, code, flags, arg1, arg2, NULL_TREE, NULL_TREE,
> > +                  NULL, complain);
> >   }
> >   extern tree build_op_call                 (tree, vec<tree, va_gc> **,
> >                                              tsubst_flags_t);
> > @@ -7873,8 +7893,9 @@ extern tree build_class_member_access_expr
> > (cp_expr, tree, tree, bool,
> >   extern tree finish_class_member_access_expr     (cp_expr, tree, bool,
> >                                              tsubst_flags_t);
> >   extern tree lookup_destructor                     (tree, tree, tree,
> > tsubst_flags_t);
> > +extern tree build_dependent_operator_type  (tree, enum tree_code, bool);
> >   extern tree build_x_indirect_ref          (location_t, tree,
> > -                                            ref_operator,
> > +                                            ref_operator, tree,
> >                                              tsubst_flags_t);
> >   extern tree cp_build_indirect_ref         (location_t, tree,
> >                                              ref_operator,
> > @@ -7892,20 +7913,20 @@ extern tree cp_build_function_call_vec
> > (tree, vec<tree, va_gc> **,
> >   extern tree build_x_binary_op                     (const op_location_t
> > &,
> >                                              enum tree_code, tree,
> >                                              enum tree_code, tree,
> > -                                            enum tree_code, tree *,
> > -                                            tsubst_flags_t);
> > +                                            enum tree_code, tree,
> > +                                            tree *, tsubst_flags_t);
> >   inline tree build_x_binary_op (const op_location_t &loc,
> >                            enum tree_code code, tree arg1, tree arg2,
> >                            tsubst_flags_t complain)
> >   {
> >     return build_x_binary_op (loc, code, arg1, TREE_CODE (arg1), arg2,
> > -                       TREE_CODE (arg2), NULL, complain);
> > +                       TREE_CODE (arg2), NULL_TREE, NULL, complain);
> >   }
> >   extern tree build_x_array_ref                     (location_t, tree,
> > tree,
> >                                              tsubst_flags_t);
> >   extern tree build_x_unary_op                      (location_t,
> >                                              enum tree_code, cp_expr,
> > -                                                 tsubst_flags_t);
> > +                                            tree, tsubst_flags_t);
> >   extern tree cp_build_addressof                    (location_t, tree,
> >                                              tsubst_flags_t);
> >   extern tree cp_build_addr_expr                    (tree,
> > tsubst_flags_t);
> > @@ -7920,7 +7941,7 @@ extern tree build_x_compound_expr_from_list   (tree,
> > expr_list_kind,
> >   extern tree build_x_compound_expr_from_vec        (vec<tree, va_gc> *,
> >                                              const char *,
> > tsubst_flags_t);
> >   extern tree build_x_compound_expr         (location_t, tree, tree,
> > -                                            tsubst_flags_t);
> > +                                            tree, tsubst_flags_t);
> >   extern tree build_compound_expr                 (location_t, tree, tree);
> >   extern tree cp_build_compound_expr                (tree, tree, 
> > tsubst_flags_t);
> >   extern tree build_static_cast                     (location_t, tree,
> > tree,
> > @@ -7936,7 +7957,7 @@ extern tree cp_build_c_cast
> > (location_t, tree, tree,
> >                                              tsubst_flags_t);
> >   extern cp_expr build_x_modify_expr                (location_t, tree,
> >                                              enum tree_code, tree,
> > -                                            tsubst_flags_t);
> > +                                            tree, tsubst_flags_t);
> >   extern tree cp_build_modify_expr          (location_t, tree,
> >                                              enum tree_code, tree,
> >                                              tsubst_flags_t);
> > diff --git a/gcc/cp/cxx-pretty-print.c b/gcc/cp/cxx-pretty-print.c
> > index 3ea357deb80..6af009c6890 100644
> > --- a/gcc/cp/cxx-pretty-print.c
> > +++ b/gcc/cp/cxx-pretty-print.c
> > @@ -2541,8 +2541,8 @@ pp_cxx_addressof_expression (cxx_pretty_printer *pp,
> > tree t)
> >   static char const*
> >   get_fold_operator (tree t)
> >   {
> > -  int op = int_cst_value (FOLD_EXPR_OP (t));
> > -  ovl_op_info_t *info = OVL_OP_INFO (FOLD_EXPR_MODIFY_P (t), op);
> > +  ovl_op_info_t *info = OVL_OP_INFO (FOLD_EXPR_MODIFY_P (t),
> > +                                FOLD_EXPR_OP (t));
> >     return info->name;
> >   }
> >   diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
> > index 56f80775ca0..0b8b33a097c 100644
> > --- a/gcc/cp/decl.c
> > +++ b/gcc/cp/decl.c
> > @@ -17098,8 +17098,6 @@ start_preparsed_function (tree decl1, tree attrs,
> > int flags)
> >       store_parm_decls (current_function_parms);
> >   -  push_operator_bindings ();
> > -
> >     if (!processing_template_decl
> >         && (flag_lifetime_dse > 1)
> >         && DECL_CONSTRUCTOR_P (decl1)
> > diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
> > index 99f5dc784b7..062c175430b 100644
> > --- a/gcc/cp/decl2.c
> > +++ b/gcc/cp/decl2.c
> > @@ -417,7 +417,8 @@ grok_array_decl (location_t loc, tree array_expr, tree
> > index_exp,
> >       {
> >         if (index_exp)
> >     expr = build_new_op (loc, ARRAY_REF, LOOKUP_NORMAL, array_expr,
> > -                        index_exp, NULL_TREE, &overload, complain);
> > +                        index_exp, NULL_TREE, NULL_TREE,
> > +                        &overload, complain);
> >         else if ((*index_exp_list)->is_empty ())
> >     expr = build_op_subscript (loc, array_expr, index_exp_list, &overload,
> >                                complain);
> > @@ -431,7 +432,7 @@ grok_array_decl (location_t loc, tree array_expr, tree
> > index_exp,
> >                                                      tf_none);
> >           if (idx != error_mark_node)
> >             expr = build_new_op (loc, ARRAY_REF, LOOKUP_NORMAL,
> > array_expr,
> > -                                idx, NULL_TREE, &overload,
> > +                                idx, NULL_TREE, NULL_TREE, &overload,
> >                                  complain & tf_decltype);
> >           if (expr == error_mark_node)
> >             {
> > diff --git a/gcc/cp/method.c b/gcc/cp/method.c
> > index 935946f5eef..44439bae4ec 100644
> > --- a/gcc/cp/method.c
> > +++ b/gcc/cp/method.c
> > @@ -1372,7 +1372,7 @@ do_one_comp (location_t loc, const comp_info &info,
> > tree sub, tree lhs, tree rhs
> >        to </=, so don't give an error yet if <=> lookup fails.  */
> >     bool tentative = retcat != cc_last;
> >     tree comp = build_new_op (loc, code, flags, lhs, rhs,
> > -                       NULL_TREE, &overload,
> > +                       NULL_TREE, NULL_TREE, &overload,
> >                         tentative ? tf_none : complain);
> >       if (code != SPACESHIP_EXPR)
> > @@ -1684,8 +1684,8 @@ build_comparison_op (tree fndecl, bool defining,
> > tsubst_flags_t complain)
> >               comp = retval = var;
> >             }
> >           eq = build_new_op (info.loc, EQ_EXPR, flags, comp,
> > -                            integer_zero_node, NULL_TREE, NULL,
> > -                            complain);
> > +                            integer_zero_node, NULL_TREE, NULL_TREE,
> > +                            NULL, complain);
> >         }
> >       tree ceq = contextual_conv_bool (eq, complain);
> >       info.check (ceq);
> > @@ -1720,7 +1720,7 @@ build_comparison_op (tree fndecl, bool defining,
> > tsubst_flags_t complain)
> >     else if (code == NE_EXPR)
> >       {
> >         tree comp = build_new_op (info.loc, EQ_EXPR, flags, lhs, rhs,
> > -                           NULL_TREE, NULL, complain);
> > +                           NULL_TREE, NULL_TREE, NULL, complain);
> >         comp = contextual_conv_bool (comp, complain);
> >         info.check (comp);
> >         if (defining)
> > @@ -1732,9 +1732,9 @@ build_comparison_op (tree fndecl, bool defining,
> > tsubst_flags_t complain)
> >     else
> >       {
> >         tree comp = build_new_op (info.loc, SPACESHIP_EXPR, flags, lhs, rhs,
> > -                           NULL_TREE, NULL, complain);
> > +                           NULL_TREE, NULL_TREE, NULL, complain);
> >         tree comp2 = build_new_op (info.loc, code, flags, comp,
> > integer_zero_node,
> > -                            NULL_TREE, NULL, complain);
> > +                            NULL_TREE, NULL_TREE, NULL, complain);
> >         info.check (comp2);
> >         if (defining)
> >     finish_return_stmt (comp2);
> > diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
> > index 71d0fab411f..28283264da6 100644
> > --- a/gcc/cp/module.cc
> > +++ b/gcc/cp/module.cc
> > @@ -8789,6 +8789,7 @@ trees_out::type_node (tree type)
> >       case DECLTYPE_TYPE:
> >       case TYPEOF_TYPE:
> >       case UNDERLYING_TYPE:
> > +    case DEPENDENT_OPERATOR_TYPE:
> >         tree_node (TYPE_VALUES_RAW (type));
> >         if (TREE_CODE (type) == DECLTYPE_TYPE)
> >     /* We stash a whole bunch of things into decltype's
> > @@ -9311,6 +9312,7 @@ trees_in::tree_node (bool is_use)
> >       case DECLTYPE_TYPE:
> >       case TYPEOF_TYPE:
> >       case UNDERLYING_TYPE:
> > +     case DEPENDENT_OPERATOR_TYPE:
> >         {
> >           tree expr = tree_node ();
> >           if (!get_overrun ())
> > diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
> > index 080692899a8..5ae7d870cc0 100644
> > --- a/gcc/cp/name-lookup.c
> > +++ b/gcc/cp/name-lookup.c
> > @@ -7725,20 +7725,14 @@ lookup_name (tree name, LOOK_where where, LOOK_want
> > want)
> >                 if (binding)
> >           {
> > -           /* The saved lookups for an operator record 'nothing
> > -              found' as error_mark_node.  We need to stop the search
> > -              here, but not return the error mark node.  */
> > -           if (binding == error_mark_node)
> > -             binding = NULL_TREE;
> > -
> >             val = binding;
> > -           goto found;
> > +           break;
> >           }
> >       }
> >         }
> >       /* Now lookup in namespace scopes.  */
> > -  if (bool (where & LOOK_where::NAMESPACE))
> > +  if (!val && bool (where & LOOK_where::NAMESPACE))
> >       {
> >         name_lookup lookup (name, want);
> >         if (lookup.search_unqualified
> > @@ -7746,8 +7740,6 @@ lookup_name (tree name, LOOK_where where, LOOK_want
> > want)
> >     val = lookup.value;
> >       }
> >   - found:;
> > -
> >     /* If we have a known type overload, pull it out.  This can happen
> >        for both using decls and unhidden functions.  */
> >     if (val && TREE_CODE (val) == OVERLOAD && TREE_TYPE (val) !=
> > unknown_type_node)
> > @@ -8949,125 +8941,4 @@ cp_emit_debug_info_for_using (tree t, tree context)
> >       }
> >   }
> >   -/* Return the result of unqualified lookup for the overloaded operator
> > -   designated by CODE, if we are in a template and the binding we find is
> > -   not.  */
> > -
> > -static tree
> > -op_unqualified_lookup (tree fnname)
> > -{
> > -  if (cxx_binding *binding = IDENTIFIER_BINDING (fnname))
> > -    {
> > -      cp_binding_level *l = binding->scope;
> > -      while (l && !l->this_entity)
> > -   l = l->level_chain;
> > -
> > -      if (l && uses_template_parms (l->this_entity))
> > -   /* Don't preserve decls from an uninstantiated template,
> > -      wait until that template is instantiated.  */
> > -   return NULL_TREE;
> > -    }
> > -
> > -  tree fns = lookup_name (fnname);
> > -  if (!fns)
> > -    /* Remember we found nothing!  */
> > -    return error_mark_node;
> > -
> > -  tree d = fns;
> > -  if (TREE_CODE (d) == TREE_LIST)
> > -    d = TREE_VALUE (d);
> > -  if (is_overloaded_fn (d))
> > -    d = get_first_fn (d);
> > -  if (DECL_CLASS_SCOPE_P (d))
> > -    /* We don't need to remember class-scope functions or declarations,
> > -       normal unqualified lookup will find them again.  */
> > -    return NULL_TREE;
> > -
> > -  return fns;
> > -}
> > -
> > -/* E is an expression representing an operation with dependent type, so we
> > -   don't know yet whether it will use the built-in meaning of the operator
> > or a
> > -   function.  Remember declarations of that operator in scope.
> > -
> > -   We then inject a fake binding of that lookup into the
> > -   instantiation's parameter scope.  This approach fails if the user
> > -   has different using declarations or directives in different local
> > -   binding of the current function from whence we need to do lookups
> > -   (we'll cache what we see on the first lookup).  */
> > -
> > -static const char *const op_bind_attrname = "operator bindings";
> > -
> > -void
> > -maybe_save_operator_binding (tree e)
> > -{
> > -  /* This is only useful in a template.  */
> > -  if (!processing_template_decl)
> > -    return;
> > -
> > -  tree cfn = current_function_decl;
> > -  if (!cfn)
> > -    return;
> > -
> > -  tree fnname;
> > -  if(TREE_CODE (e) == MODOP_EXPR)
> > -    fnname = ovl_op_identifier (true, TREE_CODE (TREE_OPERAND (e, 1)));
> > -  else
> > -    fnname = ovl_op_identifier (false, TREE_CODE (e));
> > -  if (!fnname || fnname == assign_op_identifier)
> > -    return;
> > -
> > -  tree attributes = DECL_ATTRIBUTES (cfn);
> > -  tree op_attr = lookup_attribute (op_bind_attrname, attributes);
> > -  if (!op_attr)
> > -    {
> > -      tree *ap = &DECL_ATTRIBUTES (cfn);
> > -      while (*ap && ATTR_IS_DEPENDENT (*ap))
> > -   ap = &TREE_CHAIN (*ap);
> > -      op_attr = tree_cons (get_identifier (op_bind_attrname),
> > -                      NULL_TREE, *ap);
> > -      *ap = op_attr;
> > -    }
> > -
> > -  tree op_bind = purpose_member (fnname, TREE_VALUE (op_attr));
> > -  if (!op_bind)
> > -    {
> > -      tree fns = op_unqualified_lookup (fnname);
> > -
> > -      /* Always record, so we don't keep looking for this
> > -    operator.  */
> > -      TREE_VALUE (op_attr) = tree_cons (fnname, fns, TREE_VALUE (op_attr));
> > -    }
> > -}
> > -
> > -/* Called from cp_free_lang_data so we don't put this into LTO.  */
> > -
> > -void
> > -discard_operator_bindings (tree decl)
> > -{
> > -  DECL_ATTRIBUTES (decl) = remove_attribute (op_bind_attrname,
> > -                                        DECL_ATTRIBUTES (decl));
> > -}
> > -
> > -/* Subroutine of start_preparsed_function: push the bindings we saved away
> > in
> > -   maybe_save_op_lookup into the function parameter binding level.  */
> > -
> > -void
> > -push_operator_bindings ()
> > -{
> > -  tree decl1 = current_function_decl;
> > -  if (tree attr = lookup_attribute (op_bind_attrname,
> > -                               DECL_ATTRIBUTES (decl1)))
> > -    for (tree binds = TREE_VALUE (attr); binds; binds = TREE_CHAIN (binds))
> > -      if (tree val = TREE_VALUE (binds))
> > -   {
> > -     tree name = TREE_PURPOSE (binds);
> > -     if (TREE_CODE (val) == TREE_LIST)
> > -       for (tree v = val; v; v = TREE_CHAIN (v))
> > -         push_local_binding (name, TREE_VALUE (v), /*using*/true);
> > -     else
> > -       push_local_binding (name, val, /*using*/true);
> > -   }
> > -}
> > -
> >   #include "gt-cp-name-lookup.h"
> > diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h
> > index f63c4f5b8bb..db705d20c68 100644
> > --- a/gcc/cp/name-lookup.h
> > +++ b/gcc/cp/name-lookup.h
> > @@ -465,10 +465,7 @@ extern void push_nested_namespace (tree);
> >   extern void pop_nested_namespace (tree);
> >   extern void push_to_top_level (void);
> >   extern void pop_from_top_level (void);
> > -extern void maybe_save_operator_binding (tree);
> > -extern void push_operator_bindings (void);
> >   extern void push_using_decl_bindings (tree, tree);
> > -extern void discard_operator_bindings (tree);
> >     /* Lower level interface for modules. */
> >   extern tree *mergeable_namespace_slots (tree ns, tree name, bool
> > is_global,
> > diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> > index 6f273bfe21f..1baa90ef8fd 100644
> > --- a/gcc/cp/parser.c
> > +++ b/gcc/cp/parser.c
> > @@ -8731,7 +8731,7 @@ cp_parser_unary_expression (cp_parser *parser,
> > cp_id_kind * pidk,
> >         return build_x_unary_op (token->location,
> >                                  (keyword == RID_REALPART
> >                                   ? REALPART_EXPR : IMAGPART_EXPR),
> > -                                expression,
> > +                                expression, NULL_TREE,
> >                                        tf_warning_or_error);
> >       }
> >       break;
> > @@ -8908,7 +8908,7 @@ cp_parser_unary_expression (cp_parser *parser,
> > cp_id_kind * pidk,
> >     case INDIRECT_REF:
> >       non_constant_p = NIC_STAR;
> >       expression = build_x_indirect_ref (loc, cast_expression,
> > -                                        RO_UNARY_STAR,
> > +                                        RO_UNARY_STAR, NULL_TREE,
> >                                                complain);
> >             /* TODO: build_x_indirect_ref does not always honor the
> >                location, so ensure it is set.  */
> > @@ -8921,7 +8921,7 @@ cp_parser_unary_expression (cp_parser *parser,
> > cp_id_kind * pidk,
> >     case BIT_NOT_EXPR:
> >       expression = build_x_unary_op (loc, unary_operator,
> >                                      cast_expression,
> > -                                         complain);
> > +                                    NULL_TREE, complain);
> >             /* TODO: build_x_unary_op does not always honor the location,
> >                so ensure it is set.  */
> >             expression.set_location (loc);
> > @@ -10149,7 +10149,7 @@ cp_parser_binary_expression (cp_parser* parser, bool
> > cast_p,
> >       op_location_t op_loc (current.loc, combined_loc);
> >       current.lhs = build_x_binary_op (op_loc, current.tree_type,
> >                                              current.lhs, current.lhs_type,
> > -                                           rhs, rhs_type, &overload,
> > +                                      rhs, rhs_type, NULL_TREE,
> > &overload,
> >                                              complain_flags (decltype_p));
> >             /* TODO: build_x_binary_op doesn't always honor the location.
> > */
> >             current.lhs.set_location (combined_loc);
> > @@ -10328,7 +10328,7 @@ cp_parser_assignment_expression (cp_parser* parser,
> > cp_id_kind * pidk,
> >                                rhs.get_finish ());
> >           expr = build_x_modify_expr (loc, expr,
> >                                       assignment_operator,
> > -                                     rhs,
> > +                                     rhs, NULL_TREE,
> >                                       complain_flags (decltype_p));
> >                 /* TODO: build_x_modify_expr doesn't honor the location,
> >                    so we must set it here.  */
> > @@ -10480,7 +10480,7 @@ cp_parser_expression (cp_parser* parser, cp_id_kind
> > * pidk,
> >                            expression.get_start (),
> >                            assignment_expression.get_finish ());
> >       expression = build_x_compound_expr (loc, expression,
> > -                                         assignment_expression,
> > +                                         assignment_expression,
> > NULL_TREE,
> >                                           complain_flags (decltype_p));
> >       expression.set_location (loc);
> >     }
> > @@ -13617,7 +13617,7 @@ do_range_for_auto_deduction (tree decl, tree
> > range_expr)
> >       iter_decl = build_decl (input_location, VAR_DECL, NULL_TREE,
> >                               iter_type);
> >       iter_decl = build_x_indirect_ref (input_location, iter_decl,
> > -                                       RO_UNARY_STAR,
> > +                                       RO_UNARY_STAR, NULL_TREE,
> >                                         tf_warning_or_error);
> >       TREE_TYPE (decl) = do_auto_deduction (TREE_TYPE (decl),
> >                                             iter_decl, auto_node,
> > @@ -13804,7 +13804,7 @@ cp_convert_range_for (tree statement, tree
> > range_decl, tree range_expr,
> >     condition = build_x_binary_op (input_location, NE_EXPR,
> >                              begin, ERROR_MARK,
> >                              end, ERROR_MARK,
> > -                            NULL, tf_warning_or_error);
> > +                            NULL_TREE, NULL, tf_warning_or_error);
> >     finish_for_cond (condition, statement, ivdep, unroll);
> >       /* The new increment expression.  */
> > @@ -13818,7 +13818,7 @@ cp_convert_range_for (tree statement, tree
> > range_decl, tree range_expr,
> >       /* The declaration is initialized with *__begin inside the loop body.
> > */
> >     tree deref_begin = build_x_indirect_ref (input_location, begin,
> > RO_UNARY_STAR,
> > -                                      tf_warning_or_error);
> > +                                      NULL_TREE, tf_warning_or_error);
> >     cp_finish_decl (range_decl, deref_begin,
> >               /*is_constant_init*/false, NULL_TREE,
> >               LOOKUP_ONLYCONVERTING);
> > @@ -13924,7 +13924,7 @@ cp_parser_perform_range_for_lookup (tree range, tree
> > *begin, tree *end)
> >               && (build_x_binary_op (input_location, NE_EXPR,
> >                                      *begin, ERROR_MARK,
> >                                      *end, ERROR_MARK,
> > -                                    NULL, tf_none)
> > +                                    NULL_TREE, NULL, tf_none)
> >                   != error_mark_node))
> >             /* P0184R0 allows __begin and __end to have different types,
> >                but make sure they are comparable so we can give a better
> > @@ -18914,7 +18914,7 @@ cp_parser_template_argument (cp_parser* parser)
> >         {
> >           if (address_p)
> >             argument = build_x_unary_op (loc, ADDR_EXPR, argument,
> > -                                        tf_warning_or_error);
> > +                                        NULL_TREE, tf_warning_or_error);
> >           else
> >             argument = convert_from_reference (argument);
> >           return argument;
> > @@ -41551,7 +41551,7 @@ cp_parser_omp_for_cond (cp_parser *parser, tree
> > decl, enum tree_code code)
> >                         TREE_CODE (cond),
> >                         TREE_OPERAND (cond, 0), ERROR_MARK,
> >                         TREE_OPERAND (cond, 1), ERROR_MARK,
> > -                       /*overload=*/NULL, tf_warning_or_error);
> > +                       NULL_TREE, /*overload=*/NULL,
> > tf_warning_or_error);
> >   }
> >     /* Helper function, to parse omp for increment expression.  */
> > @@ -41628,11 +41628,13 @@ cp_parser_omp_for_incr (cp_parser *parser, tree
> > decl)
> >             lhs = rhs;
> >           else
> >             lhs = build_x_unary_op (input_location, NEGATE_EXPR, rhs,
> > -                                   tf_warning_or_error);
> > +                                   NULL_TREE, tf_warning_or_error);
> >         }
> >       else
> > -       lhs = build_x_binary_op (input_location, op, lhs, ERROR_MARK, rhs,
> > -                                ERROR_MARK, NULL, tf_warning_or_error);
> > +       lhs = build_x_binary_op (input_location, op,
> > +                                lhs, ERROR_MARK,
> > +                                rhs, ERROR_MARK,
> > +                                NULL_TREE, NULL, tf_warning_or_error);
> >     }
> >       }
> >     while (token->type == CPP_PLUS || token->type == CPP_MINUS);
> > @@ -41860,7 +41862,7 @@ cp_parser_omp_for_loop_init (cp_parser *parser,
> >       orig_init = rhs;
> >       finish_expr_stmt (build_x_modify_expr (EXPR_LOCATION (rhs),
> >                                              decl, NOP_EXPR,
> > -                                            rhs,
> > +                                            rhs, NULL_TREE,
> >                                              tf_warning_or_error));
> >       if (!add_private_clause)
> >         add_private_clause = decl;
> > @@ -41982,7 +41984,7 @@ cp_convert_omp_range_for (tree &this_pre_body,
> > vec<tree, va_gc> *for_block,
> >       cond = build_x_binary_op (input_location, NE_EXPR,
> >                           begin, ERROR_MARK,
> >                           end, ERROR_MARK,
> > -                         NULL, tf_warning_or_error);
> > +                         NULL_TREE, NULL, tf_warning_or_error);
> >       /* The new increment expression.  */
> >     if (CLASS_TYPE_P (iter_type))
> > @@ -42020,7 +42022,7 @@ cp_convert_omp_range_for (tree &this_pre_body,
> > vec<tree, va_gc> *for_block,
> >     if (auto_node)
> >       {
> >         tree t = build_x_indirect_ref (input_location, begin, RO_UNARY_STAR,
> > -                                tf_none);
> > +                                NULL_TREE, tf_none);
> >         if (!error_operand_p (t))
> >     TREE_TYPE (orig_decl) = do_auto_deduction (TREE_TYPE (orig_decl),
> >                                                t, auto_node);
> > @@ -42060,7 +42062,7 @@ cp_finish_omp_range_for (tree orig, tree begin)
> >     /* The declaration is initialized with *__begin inside the loop body.
> > */
> >     cp_finish_decl (decl,
> >               build_x_indirect_ref (input_location, begin, RO_UNARY_STAR,
> > -                                   tf_warning_or_error),
> > +                                   NULL_TREE, tf_warning_or_error),
> >               /*is_constant_init*/false, NULL_TREE,
> >               LOOKUP_ONLYCONVERTING);
> >     if (VAR_P (decl) && DECL_DECOMPOSITION_P (decl))
> > diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
> > index cbdb4b566aa..2340139b238 100644
> > --- a/gcc/cp/pt.c
> > +++ b/gcc/cp/pt.c
> > @@ -12657,23 +12657,26 @@ expand_empty_fold (tree t, tsubst_flags_t
> > complain)
> >   static tree
> >   fold_expression (tree t, tree left, tree right, tsubst_flags_t complain)
> >   {
> > -  tree op = FOLD_EXPR_OP (t);
> > -  tree_code code = (tree_code)TREE_INT_CST_LOW (op);
> > +  tree_code code = FOLD_EXPR_OP (t);
> > +
> > +  tree lookups = DEPENDENT_OPERATOR_SAVED_LOOKUPS (t);
> >       // Handle compound assignment operators.
> >     if (FOLD_EXPR_MODIFY_P (t))
> > -    return build_x_modify_expr (input_location, left, code, right,
> > complain);
> > +    return build_x_modify_expr (input_location, left, code, right,
> > +                           lookups, complain);
> >       warning_sentinel s(warn_parentheses);
> >     switch (code)
> >       {
> >       case COMPOUND_EXPR:
> > -      return build_x_compound_expr (input_location, left, right, complain);
> > +      return build_x_compound_expr (input_location, left, right,
> > +                               lookups, complain);
> >       default:
> >         return build_x_binary_op (input_location, code,
> >                                   left, TREE_CODE (left),
> >                                   right, TREE_CODE (right),
> > -                                /*overload=*/NULL,
> > +                           lookups, /*overload=*/NULL,
> >                                   complain);
> >       }
> >   }
> > @@ -17908,7 +17911,7 @@ tsubst_omp_for_iterator (tree t, int i, tree declv,
> > tree &orig_declv,
> >           tree lhs = RECUR (TREE_OPERAND (incr, 0));
> >           tree rhs = RECUR (TREE_OPERAND (incr, 1));
> >           incr = build_x_modify_expr (EXPR_LOCATION (incr), lhs,
> > -                                     NOP_EXPR, rhs, complain);
> > +                                     NOP_EXPR, rhs, NULL_TREE, complain);
> >         }
> >       else
> >         incr = RECUR (incr);
> > @@ -19221,6 +19224,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t
> > complain, tree in_decl,
> >     RETURN (RECUR (TREE_OPERAND (t, 1)));
> >         RETURN (build_x_compound_expr (EXPR_LOCATION (t), tmp,
> >                                 RECUR (TREE_OPERAND (t, 1)),
> > +                               DEPENDENT_OPERATOR_SAVED_LOOKUPS (t),
> >                                 complain));
> >         case ANNOTATE_EXPR:
> > @@ -19872,6 +19876,7 @@ tsubst_copy_and_build (tree t,
> >       }
> >     else
> >       r = build_x_indirect_ref (input_location, r, RO_UNARY_STAR,
> > +                               DEPENDENT_OPERATOR_SAVED_LOOKUPS (t),
> >                                 complain|decltype_flag);
> >             if (REF_PARENTHESIZED_P (t))
> > @@ -19982,6 +19987,7 @@ tsubst_copy_and_build (tree t,
> >         op1 = tsubst_non_call_postfix_expression (TREE_OPERAND (t, 0),
> >                                             args, complain, in_decl);
> >         RETURN (build_x_unary_op (input_location, TREE_CODE (t), op1,
> > +                           DEPENDENT_OPERATOR_SAVED_LOOKUPS (t),
> >                             complain|decltype_flag));
> >         case PREDECREMENT_EXPR:
> > @@ -19995,6 +20001,7 @@ tsubst_copy_and_build (tree t,
> >       case IMAGPART_EXPR:
> >         RETURN (build_x_unary_op (input_location, TREE_CODE (t),
> >                             RECUR (TREE_OPERAND (t, 0)),
> > +                           DEPENDENT_OPERATOR_SAVED_LOOKUPS (t),
> >                             complain|decltype_flag));
> >         case FIX_TRUNC_EXPR:
> > @@ -20013,6 +20020,7 @@ tsubst_copy_and_build (tree t,
> >     op1 = tsubst_non_call_postfix_expression (op1, args, complain,
> >                                               in_decl);
> >         RETURN (build_x_unary_op (input_location, ADDR_EXPR, op1,
> > +                           DEPENDENT_OPERATOR_SAVED_LOOKUPS (t),
> >                             complain|decltype_flag));
> >         case PLUS_EXPR:
> > @@ -20077,6 +20085,7 @@ tsubst_copy_and_build (tree t,
> >        (warning_suppressed_p (TREE_OPERAND (t, 1))
> >         ? ERROR_MARK
> >         : TREE_CODE (TREE_OPERAND (t, 1))),
> > +      DEPENDENT_OPERATOR_SAVED_LOOKUPS (t),
> >        /*overload=*/NULL,
> >        complain|decltype_flag);
> >     if (EXPR_P (r))
> > @@ -20229,8 +20238,10 @@ tsubst_copy_and_build (tree t,
> >     warning_sentinel s(warn_div_by_zero);
> >     tree lhs = RECUR (TREE_OPERAND (t, 0));
> >     tree rhs = RECUR (TREE_OPERAND (t, 2));
> > +
> >     tree r = build_x_modify_expr
> >       (EXPR_LOCATION (t), lhs, TREE_CODE (TREE_OPERAND (t, 1)), rhs,
> > +      DEPENDENT_OPERATOR_SAVED_LOOKUPS (t),
> >        complain|decltype_flag);
> >     /* TREE_NO_WARNING must be set if either the expression was
> >        parenthesized or it uses an operator such as >>= rather
> > @@ -20331,6 +20342,7 @@ tsubst_copy_and_build (tree t,
> >     RETURN (build_x_compound_expr (EXPR_LOCATION (t),
> >                                    op0,
> >                                    RECUR (TREE_OPERAND (t, 1)),
> > +                                  DEPENDENT_OPERATOR_SAVED_LOOKUPS (t),
> >                                    complain|decltype_flag));
> >         }
> >   @@ -27011,6 +27023,9 @@ dependent_type_p_r (tree type)
> >     if (TREE_CODE (type) == TYPE_PACK_EXPANSION)
> >       return true;
> >   +  if (TREE_CODE (type) == DEPENDENT_OPERATOR_TYPE)
> > +    return true;
> > +
> >     if (any_dependent_type_attributes_p (TYPE_ATTRIBUTES (type)))
> >       return true;
> >   diff --git a/gcc/cp/ptree.c b/gcc/cp/ptree.c
> > index d514aa2cad2..f7ddae77679 100644
> > --- a/gcc/cp/ptree.c
> > +++ b/gcc/cp/ptree.c
> > @@ -151,6 +151,12 @@ cxx_print_type (FILE *file, tree node, int indent)
> >         print_node (file, "expr", DECLTYPE_TYPE_EXPR (node), indent + 4);
> >         return;
> >   +    case DEPENDENT_OPERATOR_TYPE:
> > +      print_node (file, "saved_lookups",
> > +             DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS (node),
> > +             indent + 4);
> > +      return;
> > +
> >       case TYPENAME_TYPE:
> >         print_node (file, "fullname", TYPENAME_TYPE_FULLNAME (node),
> >               indent + 4);
> > diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
> > index cdf63c15e21..7078af03d3c 100644
> > --- a/gcc/cp/semantics.c
> > +++ b/gcc/cp/semantics.c
> > @@ -2920,7 +2920,7 @@ finish_increment_expr (cp_expr expr, enum tree_code
> > code)
> >                                        expr.get_start (),
> >                                        get_finish (input_location));
> >     cp_expr result = build_x_unary_op (combined_loc, code, expr,
> > -                                tf_warning_or_error);
> > +                                NULL_TREE, tf_warning_or_error);
> >     /* TODO: build_x_unary_op doesn't honor the location, so set it here.
> > */
> >     result.set_location (combined_loc);
> >     return result;
> > @@ -3031,7 +3031,8 @@ finish_unary_op_expr (location_t op_loc, enum
> > tree_code code, cp_expr expr,
> >        of the operator token to the end of EXPR.  */
> >     location_t combined_loc = make_location (op_loc,
> >                                        op_loc, expr.get_finish ());
> > -  cp_expr result = build_x_unary_op (combined_loc, code, expr, complain);
> > +  cp_expr result = build_x_unary_op (combined_loc, code, expr,
> > +                                NULL_TREE, complain);
> >     /* TODO: build_x_unary_op doesn't always honor the location.  */
> >     result.set_location (combined_loc);
> >   @@ -9881,7 +9882,7 @@ handle_omp_for_class_iterator (int i, location_t
> > locus, enum tree_code code,
> >                                     TREE_CODE (cond),
> >                                     iter, ERROR_MARK,
> >                                     TREE_OPERAND (cond, 1), ERROR_MARK,
> > -                                   NULL, tf_warning_or_error);
> > +                                   NULL_TREE, NULL, tf_warning_or_error);
> >       if (error_operand_p (tem))
> >         return true;
> >     }
> > @@ -9895,9 +9896,10 @@ handle_omp_for_class_iterator (int i, location_t
> > locus, enum tree_code code,
> >         error_at (elocus, "invalid controlling predicate");
> >         return true;
> >       }
> > -  diff = build_x_binary_op (elocus, MINUS_EXPR, TREE_OPERAND (cond, 1),
> > -                       ERROR_MARK, iter, ERROR_MARK, NULL,
> > -                       tf_warning_or_error);
> > +  diff = build_x_binary_op (elocus, MINUS_EXPR,
> > +                       TREE_OPERAND (cond, 1), ERROR_MARK,
> > +                       iter, ERROR_MARK,
> > +                       NULL_TREE, NULL, tf_warning_or_error);
> >     diff = cp_fully_fold (diff);
> >     if (error_operand_p (diff))
> >       return true;
> > @@ -9925,7 +9927,7 @@ handle_omp_for_class_iterator (int i, location_t
> > locus, enum tree_code code,
> >     }
> >         iter_incr = build_x_unary_op (EXPR_LOCATION (incr),
> >                                 TREE_CODE (incr), iter,
> > -                               tf_warning_or_error);
> > +                               NULL_TREE, tf_warning_or_error);
> >         if (error_operand_p (iter_incr))
> >     return true;
> >         else if (TREE_CODE (incr) == PREINCREMENT_EXPR
> > @@ -9951,6 +9953,7 @@ handle_omp_for_class_iterator (int i, location_t
> > locus, enum tree_code code,
> >               iter_incr = build_x_modify_expr (EXPR_LOCATION (rhs),
> >                                                iter, TREE_CODE (rhs),
> >                                                TREE_OPERAND (rhs, 1),
> > +                                              NULL_TREE,
> >                                                tf_warning_or_error);
> >               if (error_operand_p (iter_incr))
> >                 return true;
> > @@ -9980,13 +9983,13 @@ handle_omp_for_class_iterator (int i, location_t
> > locus, enum tree_code code,
> >                                              PLUS_EXPR,
> >                                              TREE_OPERAND (rhs, 0),
> >                                              ERROR_MARK, iter,
> > -                                            ERROR_MARK, NULL,
> > +                                            ERROR_MARK, NULL_TREE, NULL,
> >                                              tf_warning_or_error);
> >               if (error_operand_p (iter_incr))
> >                 return true;
> >               iter_incr = build_x_modify_expr (EXPR_LOCATION (rhs),
> >                                                iter, NOP_EXPR,
> > -                                              iter_incr,
> > +                                              iter_incr, NULL_TREE,
> >                                                tf_warning_or_error);
> >               if (error_operand_p (iter_incr))
> >                 return true;
> > @@ -10097,7 +10100,7 @@ handle_omp_for_class_iterator (int i, location_t
> > locus, enum tree_code code,
> >     if (init != NULL)
> >       finish_expr_stmt (build_x_modify_expr (elocus,
> >                                        iter, NOP_EXPR, init,
> > -                                      tf_warning_or_error));
> > +                                      NULL_TREE, tf_warning_or_error));
> >     init = build_int_cst (TREE_TYPE (diff), 0);
> >     if (c && iter_incr == NULL
> >         && (!ordered || (i < collapse && collapse > 1)))
> > @@ -10106,23 +10109,24 @@ handle_omp_for_class_iterator (int i, location_t
> > locus, enum tree_code code,
> >     {
> >       finish_expr_stmt (build_x_modify_expr (elocus,
> >                                              incr_var, NOP_EXPR,
> > -                                            incr, tf_warning_or_error));
> > +                                            incr, NULL_TREE,
> > +                                            tf_warning_or_error));
> >       incr = incr_var;
> >     }
> >         iter_incr = build_x_modify_expr (elocus,
> >                                    iter, PLUS_EXPR, incr,
> > -                                  tf_warning_or_error);
> > +                                  NULL_TREE, tf_warning_or_error);
> >       }
> >     if (c && ordered && i < collapse && collapse > 1)
> >       iter_incr = incr;
> >     finish_expr_stmt (build_x_modify_expr (elocus,
> >                                      last, NOP_EXPR, init,
> > -                                    tf_warning_or_error));
> > +                                    NULL_TREE, tf_warning_or_error));
> >     if (diffvar)
> >       {
> >         finish_expr_stmt (build_x_modify_expr (elocus,
> >                                          diffvar, NOP_EXPR,
> > -                                        diff, tf_warning_or_error));
> > +                                        diff, NULL_TREE,
> > tf_warning_or_error));
> >         diff = diffvar;
> >       }
> >     *pre_body = pop_stmt_list (*pre_body);
> > @@ -10138,13 +10142,13 @@ handle_omp_for_class_iterator (int i, location_t
> > locus, enum tree_code code,
> >     iter_init = build2 (MINUS_EXPR, TREE_TYPE (diff), decl, last);
> >     iter_init = build_x_modify_expr (elocus,
> >                                iter, PLUS_EXPR, iter_init,
> > -                              tf_warning_or_error);
> > +                              NULL_TREE, tf_warning_or_error);
> >     if (iter_init != error_mark_node)
> >       iter_init = build1 (NOP_EXPR, void_type_node, iter_init);
> >     finish_expr_stmt (iter_init);
> >     finish_expr_stmt (build_x_modify_expr (elocus,
> >                                      last, NOP_EXPR, decl,
> > -                                    tf_warning_or_error));
> > +                                    NULL_TREE, tf_warning_or_error));
> >     add_stmt (orig_body);
> >     *body = pop_stmt_list (*body);
> >   @@ -10162,7 +10166,7 @@ handle_omp_for_class_iterator (int i, location_t
> > locus, enum tree_code code,
> >       iter_init = build2 (MINUS_EXPR, TREE_TYPE (diff), iter_init, last);
> >       iter_init = build_x_modify_expr (elocus,
> >                                        iter, PLUS_EXPR, iter_init,
> > -                                      tf_warning_or_error);
> > +                                      NULL_TREE, tf_warning_or_error);
> >       if (iter_init != error_mark_node)
> >         iter_init = build1 (NOP_EXPR, void_type_node, iter_init);
> >       finish_expr_stmt (iter_init);
> > @@ -10873,7 +10877,7 @@ finish_omp_cancel (tree clauses)
> >     ifc = build_x_binary_op (OMP_CLAUSE_LOCATION (ifc), NE_EXPR,
> >                              OMP_CLAUSE_IF_EXPR (ifc), ERROR_MARK,
> >                              integer_zero_node, ERROR_MARK,
> > -                            NULL, tf_warning_or_error);
> > +                            NULL_TREE, NULL, tf_warning_or_error);
> >       }
> >     else
> >       ifc = boolean_true_node;
> > @@ -12125,6 +12129,9 @@ finish_unary_fold_expr (tree expr, int op, tree_code
> > dir)
> >     tree code = build_int_cstu (integer_type_node, abs (op));
> >     tree fold = build_min_nt_loc (UNKNOWN_LOCATION, dir, code, pack);
> >     FOLD_EXPR_MODIFY_P (fold) = (op < 0);
> > +  TREE_TYPE (fold) = build_dependent_operator_type (NULL_TREE,
> > +                                               FOLD_EXPR_OP (fold),
> > +                                               FOLD_EXPR_MODIFY_P
> > (fold));
> >     return fold;
> >   }
> >   @@ -12151,6 +12158,9 @@ finish_binary_fold_expr (tree pack, tree init, int
> > op, tree_code dir)
> >     tree code = build_int_cstu (integer_type_node, abs (op));
> >     tree fold = build_min_nt_loc (UNKNOWN_LOCATION, dir, code, pack, init);
> >     FOLD_EXPR_MODIFY_P (fold) = (op < 0);
> > +  TREE_TYPE (fold) = build_dependent_operator_type (NULL_TREE,
> > +                                               FOLD_EXPR_OP (fold),
> > +                                               FOLD_EXPR_MODIFY_P
> > (fold));
> >     return fold;
> >   }
> >   diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
> > index f6f7927f293..f319dbf3526 100644
> > --- a/gcc/cp/tree.c
> > +++ b/gcc/cp/tree.c
> > @@ -5974,8 +5974,6 @@ cp_free_lang_data (tree t)
> >         DECL_EXTERNAL (t) = 1;
> >         TREE_STATIC (t) = 0;
> >       }
> > -  if (TREE_CODE (t) == FUNCTION_DECL)
> > -    discard_operator_bindings (t);
> >     if (TREE_CODE (t) == NAMESPACE_DECL)
> >       /* We do not need the leftover chaining of namespaces from the
> >          binding level.  */
> > diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
> > index 4e60db40c76..88dca343315 100644
> > --- a/gcc/cp/typeck.c
> > +++ b/gcc/cp/typeck.c
> > @@ -2602,6 +2602,7 @@ rationalize_conditional_expr (enum tree_code code,
> > tree t,
> >                                                 ? LE_EXPR : GE_EXPR),
> >                                                op0, TREE_CODE (op0),
> >                                                op1, TREE_CODE (op1),
> > +                                              NULL_TREE,
> >                                                /*overload=*/NULL,
> >                                                complain),
> >                                   cp_build_unary_op (code, op0, false,
> > complain),
> > @@ -3487,6 +3488,64 @@ build_ptrmemfunc_access_expr (tree ptrmem, tree
> > member_name)
> >     return build_simple_component_ref (ptrmem, member);
> >   }
> >   +/* Return a TREE_LIST of namespace-scope overloads for the given
> > operator,
> > +   and for any other relevant operator.  */
> > +
> > +static tree
> > +op_unqualified_lookup (tree_code code, bool is_assign)
> > +{
> > +  tree lookups = NULL_TREE;
> > +
> > +  if (cxx_dialect >= cxx20 && !is_assign)
> > +    {
> > +      if (code == NE_EXPR)
> > +   {
> > +     /* != can get rewritten in terms of ==.  */
> > +     tree fnname = ovl_op_identifier (false, EQ_EXPR);
> > +     if (tree fns = lookup_name (fnname, LOOK_where::BLOCK_NAMESPACE))
> > +       lookups = tree_cons (fnname, fns, lookups);
> > +   }
> > +      else if (code == GT_EXPR || code == LE_EXPR
> > +          || code == LT_EXPR || code == GE_EXPR)
> > +   {
> > +     /* These can get rewritten in terms of <=>.  */
> > +     tree fnname = ovl_op_identifier (false, SPACESHIP_EXPR);
> > +     if (tree fns = lookup_name (fnname, LOOK_where::BLOCK_NAMESPACE))
> > +       lookups = tree_cons (fnname, fns, lookups);
> > +   }
> > +    }
> > +
> > +  tree fnname = ovl_op_identifier (is_assign, code);
> > +  if (tree fns = lookup_name (fnname, LOOK_where::BLOCK_NAMESPACE))
> > +    lookups = tree_cons (fnname, fns, lookups);
> > +
> > +  if (lookups)
> > +    return lookups;
> > +  else
> > +    return build_tree_list (NULL_TREE, NULL_TREE);
> > +}
> > +
> > +/* Create a DEPENDENT_OPERATOR_TYPE for a dependent operator expression of
> > +   the given operator.  LOOKUPS, if non-NULL, is the result of phase 1
> > +   name lookup for the given operator.  */
> > +
> > +tree
> > +build_dependent_operator_type (tree lookups, tree_code code, bool
> > is_assign)
> > +{
> > +  if (lookups)
> > +    /* We're partially instantiating a dependent operator expression, and
> > +       LOOKUPS contains the result of phase 1 name lookup that we performed
> > +       earlier at template definition time, so just carry it over.  */;
> 
> If we're going to keep using the same set of lookups, can we also reuse the
> same DEPENDENT_OPERATOR_TYPE?  It seems like you could pass the type to
> build_x_* instead of pulling the lookups out as early.

That sounds like it'd work well.  But what if we instead just make
DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS point back to the corresponding
DEPENDENT_OPERATOR_TYPE via TREE_TYPE?  As in the below...

v2: Rename DEPENDENT_OPERATOR_SAVED_LOOKUPS to
    TEMPLATED_OPERATOR_SAVED_LOOKUPS and turn into an inline function.

    Make build_dependent_operator_type set/inspect the TREE_TYPE of the
    lookup result in order to reuse the DEPENDENT_OPERATOR_TYPE during
    partial instantiations.  Document this in the comment for
    DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS.

Bootstrap and regtest in progress.

-- >8 --

        PR c++/51577
        PR c++/83035
        PR c++/100465

gcc/cp/ChangeLog:

        * call.c (add_operator_candidates): Add lookups parameter.
        Use it to avoid performing a second unqualified lookup when
        instantiating a dependent operator expression.
        (build_new_op): Add lookups parameter and pass it appropriately.
        * constraint.cc (finish_constraint_binary_op): Use
        build_min_nt_loc instead of build_x_binary_op.
        * coroutines.cc (build_co_await): Adjust call to build_new_op.
        * cp-objcp-common.c (cp_common_init_ts): Mark
        DEPENDENT_OPERATOR_TYPE appropriately.
        * cp-tree.def (DEPENDENT_OPERATOR_TYPE): Define.
        * cp-tree.h (WILDCARD_TYPE_P): Accept DEPENDENT_OPERATOR_TYPE.
        (FOLD_EXPR_OP_RAW): New, renamed from ...
        (FOLD_EXPR_OP): ... this.  Change this to return the tree_code directly.
        (DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS): Define.
        (TEMPLATED_OPERATOR_SAVED_LOOKUPS): Define.
        (build_new_op): Add lookups parameter.
        (build_dependent_operator_type): Declare.
        (build_x_indirect_ref): Add lookups parameter.
        (build_x_binary_op): Likewise.
        (build_x_unary_op): Likewise.
        (build_x_compound_expr): Likewise.
        (build_x_modify_expr): Likewise.
        * cxx-pretty-print.c (get_fold_operator): Adjust after
        FOLD_EXPR_OP change.
        * decl.c (start_preparsed_function): Don't call
        push_operator_bindings.
        * decl2.c (grok_array_decl): Adjust calls to build_new_op.
        * method.c (do_one_comp): Likewise.
        (build_comparison_op): Likewise.
        * module.cc (trees_out::type_node): Handle DEPENDENT_OPERATOR_TYPE.
        (trees_in::tree_node): Likewise.
        * name-lookup.c (lookup_name): Revert r11-2876 change.
        (op_unqualified_lookup): Remove.
        (maybe_save_operator_binding): Remove.
        (discard_operator_bindings): Remove.
        (push_operator_bindings): Remove.
        * name-lookup.h (maybe_save_operator_binding): Remove.
        (push_operator_bindings): Remove.
        (discard_operator_bindings): Remove.
        * parser.c (cp_parser_unary_expression): Adjust calls to build_x_*.
        (cp_parser_binary_expression): Likewise.
        (cp_parser_assignment_expression): Likewise.
        (cp_parser_expression): Likewise.
        (do_range_for_auto_deduction): Likewise.
        (cp_convert_range_for): Likewise.
        (cp_parser_perform_range_for_lookup): Likewise.
        (cp_parser_template_argument): Likewise.
        (cp_parser_omp_for_cond): Likewise.
        (cp_parser_omp_for_incr): Likewise.
        (cp_parser_omp_for_loop_init): Likewise.
        (cp_convert_omp_range_for): Likewise.
        (cp_finish_omp_range_for): Likewise.
        * pt.c (fold_expression): Adjust after FOLD_EXPR_OP change. Pass
        TEMPLATED_OPERATOR_SAVED_LOOKUPS to build_x_*.
        (tsubst_omp_for_iterator): Adjust call to build_x_modify_expr.
        (tsubst_expr) <case COMPOUND_EXPR>: Pass
        TEMPLATED_OPERATOR_SAVED_LOOKUPS to build_x_*.
        (tsubst_copy_and_build) <case INDIRECT_REF>: Likewise.
        <case tcc_unary>: Likewise.
        <case tcc_binary>: Likewise.
        <case MODOP_EXPR>: Likewise.
        <case COMPOUND_EXPR>: Likewise.
        (dependent_type_p_r): Return true for DEPENDENT_OPERATOR_TYPE.
        * ptree.c (cxx_print_type): Handle DEPENDENT_OPERATOR_TYPE.
        * semantics.c (finish_increment_expr): Adjust call to
        build_x_unary_op.
        (finish_unary_op_expr): Likewise.
        (handle_omp_for_class_iterator): Adjust calls to build_x_*.
        (finish_omp_cancel): Likewise.
        (finish_unary_fold_expr): Use build_dependent_operator_type.
        (finish_binary_fold_expr): Likewise.
        * tree.c (cp_free_lang_data): Don't call discard_operator_bindings.
        * typeck.c (rationalize_conditional_expr): Adjust call to
        build_x_binary_op.
        (op_unqualified_lookup): Define.
        (build_dependent_operator_type): Define.
        (build_x_indirect_ref): Add lookups parmaeter and use
        build_dependent_operator_type.
        (build_x_binary_op): Likewise.
        (build_x_array_ref): Likewise.
        (build_x_unary_op): Likewise.
        (build_x_compound_expr_from_list): Adjust call to
        build_x_compound_expr.
        (build_x_compound_expr_from_vec): Likewise.
        (build_x_compound_expr): Add lookups parameter and use
        build_dependent_operator_type.
        (cp_build_modify_expr): Adjust call to build_new_op.
        (build_x_modify_expr): Add lookups parameter and use
        build_dependent_operator_type.
        * typeck2.c (build_x_arrow): Adjust call to build_new_op.

libcc1/ChangeLog:

        * libcp1plugin.cc (plugin_build_unary_expr): Adjust call to
        build_x_unary_op.
        (plugin_build_binary_expr): Adjust call to build_x_binary_op.

gcc/testsuite/ChangeLog:

        * g++.dg/lookup/operator-3.C: Split out operator overload
        declarations into ...
        * g++.dg/lookup/operator-3-ops.h: ... here.
        * g++.dg/lookup/operator-3a.C: New test.
        * g++.dg/lookup/operator-4.C: New test.
        * g++.dg/lookup/operator-4a.C: New test.
        * g++.dg/lookup/operator-5.C: New test.
        * g++.dg/lookup/operator-5a.C: New test.
        * g++.dg/lookup/operator-6.C: New test.
        * g++.dg/lookup/operator-7.C: New test.
        * g++.dg/lookup/operator-8.C: New test.
---
 gcc/cp/call.c                                |  33 +++--
 gcc/cp/constraint.cc                         |  11 +-
 gcc/cp/coroutines.cc                         |   2 +-
 gcc/cp/cp-objcp-common.c                     |   1 +
 gcc/cp/cp-tree.def                           |   5 +
 gcc/cp/cp-tree.h                             |  49 +++++--
 gcc/cp/cxx-pretty-print.c                    |   4 +-
 gcc/cp/decl.c                                |   2 -
 gcc/cp/decl2.c                               |   5 +-
 gcc/cp/method.c                              |  12 +-
 gcc/cp/module.cc                             |   2 +
 gcc/cp/name-lookup.c                         | 133 +------------------
 gcc/cp/name-lookup.h                         |   3 -
 gcc/cp/parser.c                              |  40 +++---
 gcc/cp/pt.c                                  |  27 +++-
 gcc/cp/ptree.c                               |   6 +
 gcc/cp/semantics.c                           |  46 ++++---
 gcc/cp/tree.c                                |   2 -
 gcc/cp/typeck.c                              | 115 +++++++++++++---
 gcc/cp/typeck2.c                             |   2 +-
 gcc/testsuite/g++.dg/lookup/operator-3-ops.h |  53 ++++++++
 gcc/testsuite/g++.dg/lookup/operator-3.C     |  56 +-------
 gcc/testsuite/g++.dg/lookup/operator-3a.C    |  61 +++++++++
 gcc/testsuite/g++.dg/lookup/operator-4.C     |  74 +++++++++++
 gcc/testsuite/g++.dg/lookup/operator-4a.C    |  76 +++++++++++
 gcc/testsuite/g++.dg/lookup/operator-5.C     |  74 +++++++++++
 gcc/testsuite/g++.dg/lookup/operator-5a.C    |  76 +++++++++++
 gcc/testsuite/g++.dg/lookup/operator-6.C     |  59 ++++++++
 gcc/testsuite/g++.dg/lookup/operator-7.C     |  27 ++++
 gcc/testsuite/g++.dg/lookup/operator-8.C     |  34 +++++
 libcc1/libcp1plugin.cc                       |   4 +-
 31 files changed, 794 insertions(+), 300 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/lookup/operator-3-ops.h
 create mode 100644 gcc/testsuite/g++.dg/lookup/operator-3a.C
 create mode 100644 gcc/testsuite/g++.dg/lookup/operator-4.C
 create mode 100644 gcc/testsuite/g++.dg/lookup/operator-4a.C
 create mode 100644 gcc/testsuite/g++.dg/lookup/operator-5.C
 create mode 100644 gcc/testsuite/g++.dg/lookup/operator-5a.C
 create mode 100644 gcc/testsuite/g++.dg/lookup/operator-6.C
 create mode 100644 gcc/testsuite/g++.dg/lookup/operator-7.C
 create mode 100644 gcc/testsuite/g++.dg/lookup/operator-8.C

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

Reply via email to