public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r12-6022] c++: two-stage name lookup for overloaded operators [PR51577]
@ 2021-12-16 18:41 Patrick Palka
  0 siblings, 0 replies; only message in thread
From: Patrick Palka @ 2021-12-16 18:41 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:bb2a7f80a98de3febefbb32b1e4898062bdb6af8

commit r12-6022-gbb2a7f80a98de3febefbb32b1e4898062bdb6af8
Author: Patrick Palka <ppalka@redhat.com>
Date:   Thu Dec 16 13:40:42 2021 -0500

    c++: two-stage name lookup for overloaded operators [PR51577]
    
    In order to properly implement two-stage name lookup for dependent
    operator expressions, we need to remember the result of unqualified
    lookup of the operator at template definition time, and reuse that
    result rather than performing another unqualified lookup at
    instantiation time.
    
    Ideally we could just store the lookup in the expression directly, but
    as pointed out in r9-6405 this isn't really possible since we use the
    standard tree codes to represent most dependent operator expressions.
    
    We could perhaps create a new tree code to represent dependent operator
    expressions, with enough operands to store the lookup along with
    everything else, but that'd require a lot of careful work to make sure
    we handle this new tree code properly across the frontend.
    
    But currently type-dependent operator (and call) expressions are given
    an empty TREE_TYPE, which dependent_type_p treats as dependent, so this
    field is effectively unused except to signal that the expression is
    type-dependent.  It'd be convenient if we could store the lookup there
    while preserving the dependent-ness of the expression.
    
    To that end, this patch creates a new kind of type, called
    DEPENDENT_OPERATOR_TYPE, which we give to dependent operator expressions
    and into which we can store the result of operator lookup at template
    definition time (DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS).  Since this
    type is always dependent (by definition), and since the frontend doesn't
    seem to care much about the exact type of a type-dependent expression,
    using this type in place of a NULL_TREE type seems to "just work"; only
    dependent_type_p and WILDCARD_TYPE_P need to be adjusted to return true
    for this new type.
    
    The rest of the patch mostly consists of adding the necessary plumbing
    to pass DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS to add_operator_candidates,
    adjusting all callers of build_x_* appropriately, and removing the now
    unnecessary push_operator_bindings mechanism.
    
    In passing, this patch simplifies finish_constraint_binary_op to avoid
    using build_x_binary_op for building a binary constraint-expr; we don't
    need to consider operator overloads here, as the &&/|| inside a
    constraint effectively always has the built-in meaning (since atomic
    constraints must have bool type).
    
    This patch also makes FOLD_EXPR_OP yield a tree_code instead of a raw
    INTEGER_CST.
    
    Finally, this patch adds the XFAILed test operator-8.C which is about
    broken two-stage name lookup for rewritten non-dependent operator
    expressions, an existing bug that's otherwise only documented in
    build_new_op.
    
            PR c++/51577
            PR c++/83035
            PR c++/100465
    
    gcc/cp/ChangeLog:
    
            * call.c (add_operator_candidates): Add lookups parameter.
            Use it to avoid performing a second unqualified lookup when
            instantiating a dependent operator expression.
            (build_new_op): Add lookups parameter and pass it appropriately.
            * constraint.cc (finish_constraint_binary_op): Use
            build_min_nt_loc instead of build_x_binary_op.
            * coroutines.cc (build_co_await): Adjust call to build_new_op.
            * cp-objcp-common.c (cp_common_init_ts): Mark
            DEPENDENT_OPERATOR_TYPE appropriately.
            * cp-tree.def (DEPENDENT_OPERATOR_TYPE): Define.
            * cp-tree.h (WILDCARD_TYPE_P): Accept DEPENDENT_OPERATOR_TYPE.
            (FOLD_EXPR_OP_RAW): New, renamed from ...
            (FOLD_EXPR_OP): ... this.  Change this to return the tree_code directly.
            (DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS): Define.
            (templated_operator_saved_lookups): Define.
            (build_new_op): Add lookups parameter.
            (build_dependent_operator_type): Declare.
            (build_x_indirect_ref): Add lookups parameter.
            (build_x_binary_op): Likewise.
            (build_x_unary_op): Likewise.
            (build_x_compound_expr): Likewise.
            (build_x_modify_expr): Likewise.
            * cxx-pretty-print.c (get_fold_operator): Adjust after
            FOLD_EXPR_OP change.
            * decl.c (start_preparsed_function): Don't call
            push_operator_bindings.
            * decl2.c (grok_array_decl): Adjust calls to build_new_op.
            * method.c (do_one_comp): Likewise.
            (build_comparison_op): Likewise.
            * module.cc (trees_out::type_node): Handle DEPENDENT_OPERATOR_TYPE.
            (trees_in::tree_node): Likewise.
            * name-lookup.c (lookup_name): Revert r11-2876 change.
            (op_unqualified_lookup): Remove.
            (maybe_save_operator_binding): Remove.
            (discard_operator_bindings): Remove.
            (push_operator_bindings): Remove.
            * name-lookup.h (maybe_save_operator_binding): Remove.
            (push_operator_bindings): Remove.
            (discard_operator_bindings): Remove.
            * parser.c (cp_parser_unary_expression): Adjust calls to build_x_*.
            (cp_parser_binary_expression): Likewise.
            (cp_parser_assignment_expression): Likewise.
            (cp_parser_expression): Likewise.
            (do_range_for_auto_deduction): Likewise.
            (cp_convert_range_for): Likewise.
            (cp_parser_perform_range_for_lookup): Likewise.
            (cp_parser_template_argument): Likewise.
            (cp_parser_omp_for_cond): Likewise.
            (cp_parser_omp_for_incr): Likewise.
            (cp_parser_omp_for_loop_init): Likewise.
            (cp_convert_omp_range_for): Likewise.
            (cp_finish_omp_range_for): Likewise.
            * pt.c (fold_expression): Adjust after FOLD_EXPR_OP change. Pass
            templated_operator_saved_lookups to build_x_*.
            (tsubst_omp_for_iterator): Adjust call to build_x_modify_expr.
            (tsubst_expr) <case COMPOUND_EXPR>: Pass
            templated_operator_saved_lookups to build_x_*.
            (tsubst_copy_and_build) <case INDIRECT_REF>: Likewise.
            <case tcc_unary>: Likewise.
            <case tcc_binary>: Likewise.
            <case MODOP_EXPR>: Likewise.
            <case COMPOUND_EXPR>: Likewise.
            (dependent_type_p_r): Return true for DEPENDENT_OPERATOR_TYPE.
            * ptree.c (cxx_print_type): Handle DEPENDENT_OPERATOR_TYPE.
            * semantics.c (finish_increment_expr): Adjust call to
            build_x_unary_op.
            (finish_unary_op_expr): Likewise.
            (handle_omp_for_class_iterator): Adjust calls to build_x_*.
            (finish_omp_cancel): Likewise.
            (finish_unary_fold_expr): Use build_dependent_operator_type.
            (finish_binary_fold_expr): Likewise.
            * tree.c (cp_free_lang_data): Don't call discard_operator_bindings.
            * typeck.c (rationalize_conditional_expr): Adjust call to
            build_x_binary_op.
            (op_unqualified_lookup): Define.
            (build_dependent_operator_type): Define.
            (build_x_indirect_ref): Add lookups parameter and use
            build_dependent_operator_type.
            (build_x_binary_op): Likewise.
            (build_x_array_ref): Likewise.
            (build_x_unary_op): Likewise.
            (build_x_compound_expr_from_list): Adjust call to
            build_x_compound_expr.
            (build_x_compound_expr_from_vec): Likewise.
            (build_x_compound_expr): Add lookups parameter and use
            build_dependent_operator_type.
            (cp_build_modify_expr): Adjust call to build_new_op.
            (build_x_modify_expr): Add lookups parameter and use
            build_dependent_operator_type.
            * typeck2.c (build_x_arrow): Adjust call to build_new_op.
    
    libcc1/ChangeLog:
    
            * libcp1plugin.cc (plugin_build_unary_expr): Adjust call to
            build_x_unary_op.
            (plugin_build_binary_expr): Adjust call to build_x_binary_op.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/lookup/operator-3.C: Split out operator overload
            declarations into ...
            * g++.dg/lookup/operator-3-ops.h: ... here.
            * g++.dg/lookup/operator-3a.C: New test.
            * g++.dg/lookup/operator-4.C: New test.
            * g++.dg/lookup/operator-4a.C: New test.
            * g++.dg/lookup/operator-5.C: New test.
            * g++.dg/lookup/operator-5a.C: New test.
            * g++.dg/lookup/operator-6.C: New test.
            * g++.dg/lookup/operator-7.C: New test.
            * g++.dg/lookup/operator-8.C: New test.

Diff:
---
 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                             |  51 +++++++---
 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, 796 insertions(+), 300 deletions(-)

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 347df5da35d..495dcdd77b3 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -6285,12 +6285,17 @@ op_is_ordered (tree_code code)
 
 /* Subroutine of build_new_op: Add to CANDIDATES all candidates for the
    operator indicated by CODE/CODE2.  This function calls itself recursively to
-   handle C++20 rewritten comparison operator candidates.  */
+   handle C++20 rewritten comparison operator candidates.
+
+   LOOKUPS, if non-NULL, is the set of pertinent namespace-scope operator
+   overloads to consider.  This parameter is used when instantiating a
+   dependent operator expression and has the same structure as
+   DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS.  */
 
 static tree
 add_operator_candidates (z_candidate **candidates,
 			 tree_code code, tree_code code2,
-			 vec<tree, va_gc> *arglist,
+			 vec<tree, va_gc> *arglist, tree lookups,
 			 int flags, tsubst_flags_t complain)
 {
   z_candidate *start_candidates = *candidates;
@@ -6326,7 +6331,15 @@ add_operator_candidates (z_candidate **candidates,
      consider.  */
   if (!memonly)
     {
-      tree fns = lookup_name (fnname, LOOK_where::BLOCK_NAMESPACE);
+      tree fns;
+      if (!lookups)
+	fns = lookup_name (fnname, LOOK_where::BLOCK_NAMESPACE);
+      /* If LOOKUPS is non-NULL, then we're instantiating a dependent operator
+	 expression, and LOOKUPS is the result of stage 1 name lookup.  */
+      else if (tree found = purpose_member (fnname, lookups))
+	fns = TREE_VALUE (found);
+      else
+	fns = NULL_TREE;
       fns = lookup_arg_dependent (fnname, fns, arglist);
       add_candidates (fns, NULL_TREE, arglist, NULL_TREE,
 		      NULL_TREE, false, NULL_TREE, NULL_TREE,
@@ -6429,7 +6442,7 @@ add_operator_candidates (z_candidate **candidates,
 	  if (rewrite_code != code)
 	    /* Add rewritten candidates in same order.  */
 	    add_operator_candidates (candidates, rewrite_code, ERROR_MARK,
-				     arglist, flags, complain);
+				     arglist, lookups, flags, complain);
 
 	  z_candidate *save_cand = *candidates;
 
@@ -6439,7 +6452,7 @@ add_operator_candidates (z_candidate **candidates,
 	  revlist->quick_push ((*arglist)[1]);
 	  revlist->quick_push ((*arglist)[0]);
 	  add_operator_candidates (candidates, rewrite_code, ERROR_MARK,
-				   revlist, flags, complain);
+				   revlist, lookups, flags, complain);
 
 	  /* Release the vec if we didn't add a candidate that uses it.  */
 	  for (z_candidate *c = *candidates; c != save_cand; c = c->next)
@@ -6457,8 +6470,8 @@ add_operator_candidates (z_candidate **candidates,
 
 tree
 build_new_op (const op_location_t &loc, enum tree_code code, int flags,
-	      tree arg1, tree arg2, tree arg3, tree *overload,
-	      tsubst_flags_t complain)
+	      tree arg1, tree arg2, tree arg3, tree lookups,
+	      tree *overload, tsubst_flags_t complain)
 {
   struct z_candidate *candidates = 0, *cand;
   releasing_vec arglist;
@@ -6552,7 +6565,7 @@ build_new_op (const op_location_t &loc, enum tree_code code, int flags,
   p = conversion_obstack_alloc (0);
 
   result = add_operator_candidates (&candidates, code, code2, arglist,
-				    flags, complain);
+				    lookups, flags, complain);
   if (result == error_mark_node)
     goto user_defined_result_ready;
 
@@ -6608,7 +6621,7 @@ build_new_op (const op_location_t &loc, enum tree_code code, int flags,
 	  else
 	    code = PREDECREMENT_EXPR;
 	  result = build_new_op (loc, code, flags, arg1, NULL_TREE,
-				 NULL_TREE, overload, complain);
+				 NULL_TREE, lookups, overload, complain);
 	  break;
 
 	  /* The caller will deal with these.  */
@@ -6765,7 +6778,7 @@ build_new_op (const op_location_t &loc, enum tree_code code, int flags,
 		    warning_sentinel ws (warn_zero_as_null_pointer_constant);
 		    result = build_new_op (loc, code,
 					   LOOKUP_NORMAL|LOOKUP_REWRITTEN,
-					   lhs, rhs, NULL_TREE,
+					   lhs, rhs, NULL_TREE, lookups,
 					   NULL, complain);
 		  }
 		  break;
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 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..9eff4349181 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -2185,7 +2185,8 @@ enum languages { lang_c, lang_cplusplus };
    || TREE_CODE (T) == TYPENAME_TYPE			\
    || TREE_CODE (T) == TYPEOF_TYPE			\
    || TREE_CODE (T) == BOUND_TEMPLATE_TEMPLATE_PARM	\
-   || TREE_CODE (T) == DECLTYPE_TYPE)
+   || TREE_CODE (T) == DECLTYPE_TYPE			\
+   || TREE_CODE (T) == DEPENDENT_OPERATOR_TYPE)
 
 /* Nonzero if T is a class (or struct or union) type.  Also nonzero
    for template type parameters, typename types, and instantiated
@@ -3978,9 +3979,13 @@ struct GTY(()) lang_decl {
   TREE_LANG_FLAG_0 (FOLD_EXPR_CHECK (NODE))
 
 /* An INTEGER_CST containing the tree code of the folded operator. */
-#define FOLD_EXPR_OP(NODE) \
+#define FOLD_EXPR_OP_RAW(NODE) \
   TREE_OPERAND (FOLD_EXPR_CHECK (NODE), 0)
 
+/* The tree code of the folded operator.  */
+#define FOLD_EXPR_OP(NODE) \
+  ((enum tree_code) TREE_INT_CST_LOW (FOLD_EXPR_OP_RAW (NODE)))
+
 /* The expression containing an unexpanded parameter pack. */
 #define FOLD_EXPR_PACK(NODE) \
   TREE_OPERAND (FOLD_EXPR_CHECK (NODE), 1)
@@ -4035,6 +4040,26 @@ struct GTY(()) lang_decl {
 #define CALL_EXPR_OPERATOR_SYNTAX(NODE) \
   TREE_LANG_FLAG_6 (CALL_OR_AGGR_INIT_CHECK (NODE))
 
+/* A TREE_LIST containing the result of phase 1 name lookup of the operator
+   overloads that are pertinent to the dependent operator expression whose
+   type is NODE.  Each TREE_PURPOSE is an IDENTIFIER_NODE and TREE_VALUE is
+   the corresponding (possibly empty) lookup result.  The TREE_TYPE of the
+   first TREE_LIST node points back to NODE.  */
+#define DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS(NODE) \
+  TYPE_VALUES_RAW (DEPENDENT_OPERATOR_TYPE_CHECK (NODE))
+
+/* Guarded helper for the above accessor macro that takes a (templated)
+   operator expression instead of the type thereof.  */
+inline tree
+templated_operator_saved_lookups (tree t)
+{
+  tree type = TREE_TYPE (EXPR_CHECK (t));
+  if (type && TREE_CODE (type) == DEPENDENT_OPERATOR_TYPE)
+    return DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS (type);
+  else
+    return NULL_TREE;
+}
+
 /* Indicates whether a string literal has been parenthesized. Such
    usages are disallowed in certain circumstances.  */
 
@@ -6464,14 +6489,15 @@ extern tree build_special_member_call		(tree, tree,
 						 tree, int, tsubst_flags_t);
 extern tree build_new_op			(const op_location_t &,
 						 enum tree_code,
-						 int, tree, tree, tree, tree *,
-						 tsubst_flags_t);
+						 int, tree, tree, tree, tree,
+						 tree *, tsubst_flags_t);
 /* Wrapper that leaves out the usually-null op3 and overload parms.  */
 inline tree build_new_op (const op_location_t &loc, enum tree_code code,
 			  int flags, tree arg1, tree arg2,
 			  tsubst_flags_t complain)
 {
-  return build_new_op (loc, code, flags, arg1, arg2, NULL_TREE, NULL, complain);
+  return build_new_op (loc, code, flags, arg1, arg2, NULL_TREE, NULL_TREE,
+		       NULL, complain);
 }
 extern tree build_op_call			(tree, vec<tree, va_gc> **,
 						 tsubst_flags_t);
@@ -7875,8 +7901,9 @@ extern tree build_class_member_access_expr      (cp_expr, tree, tree, bool,
 extern tree finish_class_member_access_expr     (cp_expr, tree, bool,
 						 tsubst_flags_t);
 extern tree lookup_destructor			(tree, tree, tree, tsubst_flags_t);
+extern tree build_dependent_operator_type	(tree, enum tree_code, bool);
 extern tree build_x_indirect_ref		(location_t, tree,
-						 ref_operator,
+						 ref_operator, tree,
 						 tsubst_flags_t);
 extern tree cp_build_indirect_ref		(location_t, tree,
 						 ref_operator,
@@ -7894,20 +7921,20 @@ extern tree cp_build_function_call_vec		(tree, vec<tree, va_gc> **,
 extern tree build_x_binary_op			(const op_location_t &,
 						 enum tree_code, tree,
 						 enum tree_code, tree,
-						 enum tree_code, tree *,
-						 tsubst_flags_t);
+						 enum tree_code, tree,
+						 tree *, tsubst_flags_t);
 inline tree build_x_binary_op (const op_location_t &loc,
 			       enum tree_code code, tree arg1, tree arg2,
 			       tsubst_flags_t complain)
 {
   return build_x_binary_op (loc, code, arg1, TREE_CODE (arg1), arg2,
-			    TREE_CODE (arg2), NULL, complain);
+			    TREE_CODE (arg2), NULL_TREE, NULL, complain);
 }
 extern tree build_x_array_ref			(location_t, tree, tree,
 						 tsubst_flags_t);
 extern tree build_x_unary_op			(location_t,
 						 enum tree_code, cp_expr,
-                                                 tsubst_flags_t);
+						 tree, tsubst_flags_t);
 extern tree cp_build_addressof			(location_t, tree,
 						 tsubst_flags_t);
 extern tree cp_build_addr_expr			(tree, tsubst_flags_t);
@@ -7922,7 +7949,7 @@ extern tree build_x_compound_expr_from_list	(tree, expr_list_kind,
 extern tree build_x_compound_expr_from_vec	(vec<tree, va_gc> *,
 						 const char *, tsubst_flags_t);
 extern tree build_x_compound_expr		(location_t, tree, tree,
-						 tsubst_flags_t);
+						 tree, tsubst_flags_t);
 extern tree build_compound_expr                 (location_t, tree, tree);
 extern tree cp_build_compound_expr		(tree, tree, tsubst_flags_t);
 extern tree build_static_cast			(location_t, tree, tree,
@@ -7938,7 +7965,7 @@ extern tree cp_build_c_cast			(location_t, tree, tree,
 						 tsubst_flags_t);
 extern cp_expr build_x_modify_expr		(location_t, tree,
 						 enum tree_code, tree,
-						 tsubst_flags_t);
+						 tree, tsubst_flags_t);
 extern tree cp_build_modify_expr		(location_t, tree,
 						 enum tree_code, tree,
 						 tsubst_flags_t);
diff --git a/gcc/cp/cxx-pretty-print.c b/gcc/cp/cxx-pretty-print.c
index 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 2e0339858c3..7ca8770bd02 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -17118,8 +17118,6 @@ start_preparsed_function (tree decl1, tree attrs, int flags)
 
   store_parm_decls (current_function_parms);
 
-  push_operator_bindings ();
-
   if (!processing_template_decl
       && (flag_lifetime_dse > 1)
       && DECL_CONSTRUCTOR_P (decl1)
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 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 62a058d583f..18c6f118ae6 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..5184b02d3e4 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -2602,6 +2602,7 @@ rationalize_conditional_expr (enum tree_code code, tree t,
 						    ? LE_EXPR : GE_EXPR),
 						   op0, TREE_CODE (op0),
 						   op1, TREE_CODE (op1),
+						   NULL_TREE,
 						   /*overload=*/NULL,
 						   complain),
                                 cp_build_unary_op (code, op0, false, complain),
@@ -3487,6 +3488,67 @@ build_ptrmemfunc_access_expr (tree ptrmem, tree member_name)
   return build_simple_component_ref (ptrmem, member);
 }
 
+/* Return a TREE_LIST of namespace-scope overloads for the given operator,
+   and for any other relevant operator.  */
+
+static tree
+op_unqualified_lookup (tree_code code, bool is_assign)
+{
+  tree lookups = NULL_TREE;
+
+  if (cxx_dialect >= cxx20 && !is_assign)
+    {
+      if (code == NE_EXPR)
+	{
+	  /* != can get rewritten in terms of ==.  */
+	  tree fnname = ovl_op_identifier (false, EQ_EXPR);
+	  if (tree fns = lookup_name (fnname, LOOK_where::BLOCK_NAMESPACE))
+	    lookups = tree_cons (fnname, fns, lookups);
+	}
+      else if (code == GT_EXPR || code == LE_EXPR
+	       || code == LT_EXPR || code == GE_EXPR)
+	{
+	  /* These can get rewritten in terms of <=>.  */
+	  tree fnname = ovl_op_identifier (false, SPACESHIP_EXPR);
+	  if (tree fns = lookup_name (fnname, LOOK_where::BLOCK_NAMESPACE))
+	    lookups = tree_cons (fnname, fns, lookups);
+	}
+    }
+
+  tree fnname = ovl_op_identifier (is_assign, code);
+  if (tree fns = lookup_name (fnname, LOOK_where::BLOCK_NAMESPACE))
+    lookups = tree_cons (fnname, fns, lookups);
+
+  if (lookups)
+    return lookups;
+  else
+    return build_tree_list (NULL_TREE, NULL_TREE);
+}
+
+/* Create a DEPENDENT_OPERATOR_TYPE for a dependent operator expression of
+   the given operator.  LOOKUPS, if non-NULL, is the result of phase 1
+   name lookup for the given operator.  */
+
+tree
+build_dependent_operator_type (tree lookups, tree_code code, bool is_assign)
+{
+  if (lookups)
+    /* We're partially instantiating a dependent operator expression, and
+       LOOKUPS is the result of phase 1 name lookup that we performed
+       earlier at template definition time, so just reuse the corresponding
+       DEPENDENT_OPERATOR_TYPE.  */
+    return TREE_TYPE (lookups);
+
+  /* Otherwise we're processing a dependent operator expression at template
+     definition time, so perform phase 1 name lookup now.  */
+  lookups = op_unqualified_lookup (code, is_assign);
+
+  tree type = cxx_make_type (DEPENDENT_OPERATOR_TYPE);
+  DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS (type) = lookups;
+  TREE_TYPE (lookups) = type;
+  return type;
+}
+
 /* Given an expression PTR for a pointer, return an expression
    for the value pointed to.
    ERRORSTRING is the name of the operator to appear in error messages.
@@ -3496,7 +3558,7 @@ build_ptrmemfunc_access_expr (tree ptrmem, tree member_name)
 
 tree
 build_x_indirect_ref (location_t loc, tree expr, ref_operator errorstring, 
-                      tsubst_flags_t complain)
+		      tree lookups, tsubst_flags_t complain)
 {
   tree orig_expr = expr;
   tree rval;
@@ -3516,12 +3578,18 @@ build_x_indirect_ref (location_t loc, tree expr, ref_operator errorstring,
 	  return build_min (INDIRECT_REF, TREE_TYPE (TREE_TYPE (expr)), expr);
 	}
       if (type_dependent_expression_p (expr))
-	return build_min_nt_loc (loc, INDIRECT_REF, expr);
+	{
+	  expr = build_min_nt_loc (loc, INDIRECT_REF, expr);
+	  TREE_TYPE (expr)
+	    = build_dependent_operator_type (lookups, INDIRECT_REF, false);
+	  return expr;
+	}
       expr = build_non_dependent_expr (expr);
     }
 
   rval = build_new_op (loc, INDIRECT_REF, LOOKUP_NORMAL, expr,
-		       NULL_TREE, NULL_TREE, &overload, complain);
+		       NULL_TREE, NULL_TREE, lookups,
+		       &overload, complain);
   if (!rval)
     rval = cp_build_indirect_ref (loc, expr, errorstring, complain);
 
@@ -4458,8 +4526,8 @@ convert_arguments (tree typelist, vec<tree, va_gc> **values, tree fndecl,
 tree
 build_x_binary_op (const op_location_t &loc, enum tree_code code, tree arg1,
 		   enum tree_code arg1_code, tree arg2,
-		   enum tree_code arg2_code, tree *overload_p,
-		   tsubst_flags_t complain)
+		   enum tree_code arg2_code, tree lookups,
+		   tree *overload_p, tsubst_flags_t complain)
 {
   tree orig_arg1;
   tree orig_arg2;
@@ -4475,7 +4543,8 @@ build_x_binary_op (const op_location_t &loc, enum tree_code code, tree arg1,
 	  || type_dependent_expression_p (arg2))
 	{
 	  expr = build_min_nt_loc (loc, code, arg1, arg2);
-	  maybe_save_operator_binding (expr);
+	  TREE_TYPE (expr)
+	    = build_dependent_operator_type (lookups, code, false);
 	  return expr;
 	}
       arg1 = build_non_dependent_expr (arg1);
@@ -4486,7 +4555,7 @@ build_x_binary_op (const op_location_t &loc, enum tree_code code, tree arg1,
     expr = build_m_component_ref (arg1, arg2, complain);
   else
     expr = build_new_op (loc, code, LOOKUP_NORMAL, arg1, arg2, NULL_TREE,
-			 &overload, complain);
+			 lookups, &overload, complain);
 
   if (overload_p != NULL)
     *overload_p = overload;
@@ -4538,7 +4607,7 @@ build_x_array_ref (location_t loc, tree arg1, tree arg2,
     }
 
   expr = build_new_op (loc, ARRAY_REF, LOOKUP_NORMAL, arg1, arg2,
-		       NULL_TREE, &overload, complain);
+		       NULL_TREE, NULL_TREE, &overload, complain);
 
   if (processing_template_decl && expr != error_mark_node)
     {
@@ -6402,7 +6471,7 @@ pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype,
 
 tree
 build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg,
-		  tsubst_flags_t complain)
+		  tree lookups, tsubst_flags_t complain)
 {
   tree orig_expr = xarg;
   tree exp;
@@ -6414,7 +6483,7 @@ build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg,
       if (type_dependent_expression_p (xarg))
 	{
 	  tree e = build_min_nt_loc (loc, code, xarg.get_value (), NULL_TREE);
-	  maybe_save_operator_binding (e);
+	  TREE_TYPE (e) = build_dependent_operator_type (lookups, code, false);
 	  return e;
 	}
 
@@ -6439,7 +6508,7 @@ build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg,
     /* Don't look for a function.  */;
   else
     exp = build_new_op (loc, code, LOOKUP_NORMAL, xarg, NULL_TREE,
-			NULL_TREE, &overload, complain);
+			NULL_TREE, lookups, &overload, complain);
 
   if (!exp && code == ADDR_EXPR)
     {
@@ -7508,7 +7577,8 @@ build_x_compound_expr_from_list (tree list, expr_list_kind exp,
 
       for (list = TREE_CHAIN (list); list; list = TREE_CHAIN (list))
 	expr = build_x_compound_expr (EXPR_LOCATION (TREE_VALUE (list)),
-				      expr, TREE_VALUE (list), complain);
+				      expr, TREE_VALUE (list), NULL_TREE,
+				      complain);
     }
 
   return expr;
@@ -7543,7 +7613,7 @@ build_x_compound_expr_from_vec (vec<tree, va_gc> *vec, const char *msg,
       expr = (*vec)[0];
       for (ix = 1; vec->iterate (ix, &t); ++ix)
 	expr = build_x_compound_expr (EXPR_LOCATION (t), expr,
-				      t, complain);
+				      t, NULL_TREE, complain);
 
       return expr;
     }
@@ -7553,7 +7623,7 @@ build_x_compound_expr_from_vec (vec<tree, va_gc> *vec, const char *msg,
 
 tree
 build_x_compound_expr (location_t loc, tree op1, tree op2,
-		       tsubst_flags_t complain)
+		       tree lookups, tsubst_flags_t complain)
 {
   tree result;
   tree orig_op1 = op1;
@@ -7566,7 +7636,8 @@ build_x_compound_expr (location_t loc, tree op1, tree op2,
 	  || type_dependent_expression_p (op2))
 	{
 	  result = build_min_nt_loc (loc, COMPOUND_EXPR, op1, op2);
-	  maybe_save_operator_binding (result);
+	  TREE_TYPE (result)
+	    = build_dependent_operator_type (lookups, COMPOUND_EXPR, false);
 	  return result;
 	}
       op1 = build_non_dependent_expr (op1);
@@ -7574,7 +7645,7 @@ build_x_compound_expr (location_t loc, tree op1, tree op2,
     }
 
   result = build_new_op (loc, COMPOUND_EXPR, LOOKUP_NORMAL, op1, op2,
-			 NULL_TREE, &overload, complain);
+			 NULL_TREE, lookups, &overload, complain);
   if (!result)
     result = cp_build_compound_expr (op1, op2, complain);
 
@@ -9017,8 +9088,8 @@ cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
 	    {
 	      result = build_new_op (input_location, MODIFY_EXPR,
 				     LOOKUP_NORMAL, lhs, rhs,
-				     make_node (NOP_EXPR), /*overload=*/NULL,
-				     complain);
+				     make_node (NOP_EXPR), NULL_TREE,
+				     /*overload=*/NULL, complain);
 	      if (result == NULL_TREE)
 		return error_mark_node;
 	      goto ret;
@@ -9233,7 +9304,7 @@ cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
 
 cp_expr
 build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
-		     tree rhs, tsubst_flags_t complain)
+		     tree rhs, tree lookups, tsubst_flags_t complain)
 {
   tree orig_lhs = lhs;
   tree orig_rhs = rhs;
@@ -9250,7 +9321,9 @@ build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
 	{
 	  tree op = build_min_nt_loc (loc, modifycode, NULL_TREE, NULL_TREE);
 	  tree rval = build_min_nt_loc (loc, MODOP_EXPR, lhs, op, rhs);
-	  maybe_save_operator_binding (rval);
+	  if (modifycode != NOP_EXPR)
+	    TREE_TYPE (rval)
+	      = build_dependent_operator_type (lookups, modifycode, true);
 	  return rval;
 	}
 
@@ -9262,7 +9335,7 @@ build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
     {
       tree op = build_nt (modifycode, NULL_TREE, NULL_TREE);
       tree rval = build_new_op (loc, MODIFY_EXPR, LOOKUP_NORMAL,
-				lhs, rhs, op, &overload, complain);
+				lhs, rhs, op, lookups, &overload, complain);
       if (rval)
 	{
 	  if (rval == error_mark_node)
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 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..ab0257a66aa 100644
--- a/gcc/testsuite/g++.dg/lookup/operator-3.C
+++ b/gcc/testsuite/g++.dg/lookup/operator-3.C
@@ -1,4 +1,6 @@
 // PR c++/51577
+// Verify we don't consider later-declared namespace-scope operator overloads
+// when instantiating a dependent operator expression that occurs at block scope.
 
 template <class T> void f (T x) {
   +x; // { dg-error "no match" }
@@ -50,59 +52,7 @@ template <class T> void f (T x) {
 
 namespace N { struct A { }; }
 
-void operator+(N::A);
-void operator-(N::A);
-void operator*(N::A);
-void operator~(N::A);
-#if __cplusplus >= 201103L
-void operator&(N::A) = delete;
-#else
-void operator&(N::A);
-#endif
-void operator!(N::A);
-void operator++(N::A);
-void operator--(N::A);
-void operator++(N::A, int);
-void operator--(N::A, int);
-
-void operator->*(N::A, N::A);
-void operator/(N::A, N::A);
-void operator*(N::A, N::A);
-void operator+(N::A, N::A);
-void operator-(N::A, N::A);
-void operator%(N::A, N::A);
-void operator&(N::A, N::A);
-void operator|(N::A, N::A);
-void operator^(N::A, N::A);
-void operator<<(N::A, N::A);
-void operator>>(N::A, N::A);
-void operator&&(N::A, N::A);
-void operator||(N::A, N::A);
-#if __cplusplus >= 201103L
-void operator,(N::A, N::A) = delete;
-#else
-void operator,(N::A, N::A);
-#endif
-
-void operator==(N::A, N::A);
-void operator!=(N::A, N::A);
-void operator<(N::A, N::A);
-void operator>(N::A, N::A);
-void operator<=(N::A, N::A);
-void operator>=(N::A, N::A);
-#if __cplusplus > 201703L
-void operator<=>(N::A, N::A);
-#endif
-
-void operator+=(N::A, N::A);
-void operator-=(N::A, N::A);
-void operator*=(N::A, N::A);
-void operator/=(N::A, N::A);
-void operator%=(N::A, N::A);
-void operator|=(N::A, N::A);
-void operator^=(N::A, N::A);
-void operator<<=(N::A, N::A);
-void operator>>=(N::A, N::A);
+#include "operator-3-ops.h"
 
 int main() {
   f(N::A());
diff --git a/gcc/testsuite/g++.dg/lookup/operator-3a.C b/gcc/testsuite/g++.dg/lookup/operator-3a.C
new file mode 100644
index 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..64d8a97cdd0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/operator-8.C
@@ -0,0 +1,34 @@
+// Verify phase 1 lookup works properly for rewritten non-dependent conditional
+// operator expressions.
+
+// This test currently fails due to build_min_non_dep_op_overload not knowing
+// how to handle rewritten operator expressions; see the FIXME in build_new_op.
+
+// { dg-do compile { target c++20 } }
+
+#include <compare>
+
+struct A {
+  bool operator==(int);
+  std::strong_ordering operator<=>(int);
+};
+
+template<class T>
+void f() {
+  A a;
+  (void)(a != 0, 0 != a); // { dg-bogus "deleted" "" { xfail *-*-* } }
+  (void)(a < 0, 0 < a);   // { dg-bogus "deleted" "" { xfail *-*-* } }
+  (void)(a <= 0, 0 <= a); // { dg-bogus "deleted" "" { xfail *-*-* } }
+  (void)(a > 0, 0 > a);   // { dg-bogus "deleted" "" { xfail *-*-* } }
+  (void)(a >= 0, 0 >= a); // { dg-bogus "deleted" "" { xfail *-*-* } }
+}
+
+// These later-declared namespace-scope overloads shouldn't be considered
+// when instantiating f<int>.
+bool operator!=(A, int) = delete;
+bool operator<(A, int) = delete;
+bool operator<=(A, int) = delete;
+bool operator>(A, int) = delete;
+bool operator>=(A, int) = delete;
+
+template void f<int>();
diff --git a/libcc1/libcp1plugin.cc b/libcc1/libcp1plugin.cc
index 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;
     }


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2021-12-16 18:41 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-16 18:41 [gcc r12-6022] c++: two-stage name lookup for overloaded operators [PR51577] Patrick Palka

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).