From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 8AF8F3858D28 for ; Wed, 15 Dec 2021 20:59:36 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 8AF8F3858D28 Received: from mail-qk1-f197.google.com (mail-qk1-f197.google.com [209.85.222.197]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-9-2KgHCMDGO2GxtBFX2uBuhw-1; Wed, 15 Dec 2021 15:59:33 -0500 X-MC-Unique: 2KgHCMDGO2GxtBFX2uBuhw-1 Received: by mail-qk1-f197.google.com with SMTP id s8-20020a05620a254800b0046d6993d174so20032028qko.3 for ; Wed, 15 Dec 2021 12:59:33 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:date:mime-version:user-agent:subject :content-language:to:references:from:in-reply-to :content-transfer-encoding; bh=HdXcFopZmtBKcQaI+FtKSIpv99E/PVZ8AJmtJh6hB5c=; b=7zxOPlYd1Bs0dvAmgWznSZUthAlblMat18cqB7jD7RaJtF66k3y3mnieVhr/xdoAZu 8m/FZHwgBVziaY0JFA6ISiCVZ7QHIAJbFIsvVrcio29mXlpDHeI/B2cACvGzqT91+o7F QT02MkiVwbmhlVYru2D0QrDikRZ0iG3JjQPXqYZXkoDjSekmDNsJyqMJjCFAWQxlfl3Z ucAgMSMAYLUaRzIzuW+wlzx7+JKa4t3qZMRBxyMfUD39REhPlyd7Wdk9JWTB4PgyIka4 6UoDXZwSg/86/gOllcw3QArr8PAH5sH5SABv10RmKnPX0kOd/RJ2YSnmpwCCBbVJLC2F ldIg== X-Gm-Message-State: AOAM5323BTyisShCeCx7hfB3f4tAO5iEytgTyn8BOoMH05kK4nVqsRwg dNfKy7FkoLUeaKINIDVPJ2kNw059F1njDwP8oYVQ8NajJnvzIEBb+HG9Fn8oYtfdr8/Vq6szn19 Vk7Xtmm4JjAOWHk3xFw== X-Received: by 2002:a05:620a:4092:: with SMTP id f18mr9940128qko.629.1639601971301; Wed, 15 Dec 2021 12:59:31 -0800 (PST) X-Google-Smtp-Source: ABdhPJzkkp6nsCF4yD6t8t1lpqW/YrickuSSNmDP2B9Y03p74TAMd3mA/rUkGPPZydiSWLBAbeiD9Q== X-Received: by 2002:a05:620a:4092:: with SMTP id f18mr9940080qko.629.1639601969844; Wed, 15 Dec 2021 12:59:29 -0800 (PST) Received: from [192.168.1.149] (130-44-159-43.s15913.c3-0.arl-cbr1.sbo-arl.ma.cable.rcncustomer.com. [130.44.159.43]) by smtp.gmail.com with ESMTPSA id 9sm1655346qkm.5.2021.12.15.12.59.28 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Wed, 15 Dec 2021 12:59:29 -0800 (PST) Message-ID: <11c7cfa4-a2a3-d012-5638-687194ea8737@redhat.com> Date: Wed, 15 Dec 2021 15:59:27 -0500 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.4.0 Subject: Re: [PATCH] c++: two-stage name lookup for overloaded operators [PR51577] To: Patrick Palka , gcc-patches@gcc.gnu.org References: <20211210145333.2868378-1-ppalka@redhat.com> From: Jason Merrill In-Reply-To: <20211210145333.2868378-1-ppalka@redhat.com> X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Language: en-US Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-Spam-Status: No, score=-14.0 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, NICE_REPLY_A, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 15 Dec 2021 20:59:41 -0000 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) : Pass > DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS to build_x_*. > (tsubst_copy_and_build) : Likewise. > : Likewise. > : Likewise. > : Likewise. > : 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 *arglist, > + vec *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 *arglist; > @@ -6554,7 +6567,7 @@ build_new_op (const op_location_t &loc, enum tree_code code, int flags, > p = conversion_obstack_alloc (0); > > result = add_operator_candidates (&candidates, code, code2, arglist, > - flags, complain); > + lookups, flags, complain); > if (result == error_mark_node) > goto user_defined_result_ready; > > @@ -6610,7 +6623,7 @@ build_new_op (const op_location_t &loc, enum tree_code code, int flags, > else > code = PREDECREMENT_EXPR; > result = build_new_op (loc, code, flags, arg1, NULL_TREE, > - NULL_TREE, overload, complain); > + NULL_TREE, lookups, overload, complain); > break; > > /* The caller will deal with these. */ > @@ -6767,7 +6780,7 @@ build_new_op (const op_location_t &loc, enum tree_code code, int flags, > warning_sentinel ws (warn_zero_as_null_pointer_constant); > result = build_new_op (loc, code, > LOOKUP_NORMAL|LOOKUP_REWRITTEN, > - lhs, rhs, NULL_TREE, > + lhs, rhs, NULL_TREE, lookups, > NULL, complain); > } > break; > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc > index 2896efdd7f2..c235a657a77 100644 > --- a/gcc/cp/constraint.cc > +++ b/gcc/cp/constraint.cc > @@ -202,15 +202,8 @@ finish_constraint_binary_op (location_t loc, > return error_mark_node; > if (!check_constraint_operands (loc, lhs, rhs)) > return error_mark_node; > - tree overload; > - cp_expr expr = build_x_binary_op (loc, code, > - lhs, TREE_CODE (lhs), > - rhs, TREE_CODE (rhs), > - &overload, tf_none); > - /* When either operand is dependent, the overload set may be non-empty. */ > - if (expr == error_mark_node) > - return error_mark_node; > - expr.set_location (loc); > + cp_expr expr > + = build_min_nt_loc (loc, code, lhs.get_value (), rhs.get_value ()); > expr.set_range (lhs.get_start (), rhs.get_finish ()); > return expr; > } > diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc > index 9017902e6fb..c00672eeb6e 100644 > --- a/gcc/cp/coroutines.cc > +++ b/gcc/cp/coroutines.cc > @@ -912,7 +912,7 @@ build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind) > if (MAYBE_CLASS_TYPE_P (TREE_TYPE (a))) > { > o = build_new_op (loc, CO_AWAIT_EXPR, LOOKUP_NORMAL, a, NULL_TREE, > - NULL_TREE, NULL, tf_warning_or_error); > + NULL_TREE, NULL_TREE, NULL, tf_warning_or_error); > /* If no viable functions are found, o is a. */ > if (!o || o == error_mark_node) > o = a; > diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c > index 38eae881f0c..36e04cdee5e 100644 > --- a/gcc/cp/cp-objcp-common.c > +++ b/gcc/cp/cp-objcp-common.c > @@ -484,6 +484,7 @@ cp_common_init_ts (void) > /* New Types. */ > MARK_TS_TYPE_COMMON (UNBOUND_CLASS_TEMPLATE); > MARK_TS_TYPE_COMMON (TYPE_ARGUMENT_PACK); > + MARK_TS_TYPE_COMMON (DEPENDENT_OPERATOR_TYPE); > > MARK_TS_TYPE_NON_COMMON (DECLTYPE_TYPE); > MARK_TS_TYPE_NON_COMMON (TYPENAME_TYPE); > diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def > index 725139bb457..6fb838cc850 100644 > --- a/gcc/cp/cp-tree.def > +++ b/gcc/cp/cp-tree.def > @@ -476,6 +476,11 @@ DEFTREECODE (UNDERLYING_TYPE, "underlying_type", tcc_type, 0) > BASES_TYPE is the type in question. */ > DEFTREECODE (BASES, "bases", tcc_type, 0) > > +/* Dependent operator expressions are given this type rather than a NULL_TREE > + type so that we have somewhere to stash the result of phase 1 name lookup > + (namely into DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS). */ > +DEFTREECODE (DEPENDENT_OPERATOR_TYPE, "dependent_operator_type", tcc_type, 0) > + > /* Used to represent the template information stored by template > specializations. > The accessors are: > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > index e4330fb1f8b..8b98c061eea 100644 > --- a/gcc/cp/cp-tree.h > +++ b/gcc/cp/cp-tree.h > @@ -2183,7 +2183,8 @@ enum languages { lang_c, lang_cplusplus }; > || TREE_CODE (T) == TYPENAME_TYPE \ > || TREE_CODE (T) == TYPEOF_TYPE \ > || TREE_CODE (T) == BOUND_TEMPLATE_TEMPLATE_PARM \ > - || TREE_CODE (T) == DECLTYPE_TYPE) > + || TREE_CODE (T) == DECLTYPE_TYPE \ > + || TREE_CODE (T) == DEPENDENT_OPERATOR_TYPE) > > /* Nonzero if T is a class (or struct or union) type. Also nonzero > for template type parameters, typename types, and instantiated > @@ -3976,9 +3977,13 @@ struct GTY(()) lang_decl { > TREE_LANG_FLAG_0 (FOLD_EXPR_CHECK (NODE)) > > /* An INTEGER_CST containing the tree code of the folded operator. */ > -#define FOLD_EXPR_OP(NODE) \ > +#define FOLD_EXPR_OP_RAW(NODE) \ > TREE_OPERAND (FOLD_EXPR_CHECK (NODE), 0) > > +/* The tree code of the folded operator. */ > +#define FOLD_EXPR_OP(NODE) \ > + ((enum tree_code) TREE_INT_CST_LOW (FOLD_EXPR_OP_RAW (NODE))) > + > /* The expression containing an unexpanded parameter pack. */ > #define FOLD_EXPR_PACK(NODE) \ > TREE_OPERAND (FOLD_EXPR_CHECK (NODE), 1) > @@ -4033,6 +4038,20 @@ struct GTY(()) lang_decl { > #define CALL_EXPR_OPERATOR_SYNTAX(NODE) \ > TREE_LANG_FLAG_6 (CALL_OR_AGGR_INIT_CHECK (NODE)) > > +/* A TREE_LIST containing the result of phase 1 name lookup of the operator > + overloads that are pertinent to the dependent operator expression whose > + type is NODE. Each TREE_PURPOSE is an IDENTIFIER_NODE and TREE_VALUE is > + the corresponding (possibly empty) lookup result. */ > +#define DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS(NODE) \ > + TYPE_VALUES_RAW (DEPENDENT_OPERATOR_TYPE_CHECK (NODE)) > + > +/* Helper for the above accessor macro that takes a dependent operator > + expression instead of the type thereof. */ > +#define DEPENDENT_OPERATOR_SAVED_LOOKUPS(NODE) \ > + (TREE_TYPE (NODE) && TREE_CODE (TREE_TYPE (NODE)) == DEPENDENT_OPERATOR_TYPE \ > + ? DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS (TREE_TYPE (NODE)) \ > + : NULL_TREE) Let's make this one an inline function; I'd prefer in general to avoid adding new macros with rvalue results, or that use their argument more than once. > /* Indicates whether a string literal has been parenthesized. Such > usages are disallowed in certain circumstances. */ > > @@ -6462,14 +6481,15 @@ extern tree build_special_member_call (tree, tree, > tree, int, tsubst_flags_t); > extern tree build_new_op (const op_location_t &, > enum tree_code, > - int, tree, tree, tree, tree *, > - tsubst_flags_t); > + int, tree, tree, tree, tree, > + tree *, tsubst_flags_t); > /* Wrapper that leaves out the usually-null op3 and overload parms. */ > inline tree build_new_op (const op_location_t &loc, enum tree_code code, > int flags, tree arg1, tree arg2, > tsubst_flags_t complain) > { > - return build_new_op (loc, code, flags, arg1, arg2, NULL_TREE, NULL, complain); > + return build_new_op (loc, code, flags, arg1, arg2, NULL_TREE, NULL_TREE, > + NULL, complain); > } > extern tree build_op_call (tree, vec **, > 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 **, > 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 *, > 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 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 *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 *for_block, > if (auto_node) > { > tree t = build_x_indirect_ref (input_location, begin, RO_UNARY_STAR, > - tf_none); > + NULL_TREE, tf_none); > if (!error_operand_p (t)) > TREE_TYPE (orig_decl) = do_auto_deduction (TREE_TYPE (orig_decl), > t, auto_node); > @@ -42060,7 +42062,7 @@ cp_finish_omp_range_for (tree orig, tree begin) > /* The declaration is initialized with *__begin inside the loop body. */ > cp_finish_decl (decl, > build_x_indirect_ref (input_location, begin, RO_UNARY_STAR, > - tf_warning_or_error), > + NULL_TREE, tf_warning_or_error), > /*is_constant_init*/false, NULL_TREE, > LOOKUP_ONLYCONVERTING); > if (VAR_P (decl) && DECL_DECOMPOSITION_P (decl)) > diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c > index cbdb4b566aa..2340139b238 100644 > --- a/gcc/cp/pt.c > +++ b/gcc/cp/pt.c > @@ -12657,23 +12657,26 @@ expand_empty_fold (tree t, tsubst_flags_t complain) > static tree > fold_expression (tree t, tree left, tree right, tsubst_flags_t complain) > { > - tree op = FOLD_EXPR_OP (t); > - tree_code code = (tree_code)TREE_INT_CST_LOW (op); > + tree_code code = FOLD_EXPR_OP (t); > + > + tree lookups = DEPENDENT_OPERATOR_SAVED_LOOKUPS (t); > > // Handle compound assignment operators. > if (FOLD_EXPR_MODIFY_P (t)) > - return build_x_modify_expr (input_location, left, code, right, complain); > + return build_x_modify_expr (input_location, left, code, right, > + lookups, complain); > > warning_sentinel s(warn_parentheses); > switch (code) > { > case COMPOUND_EXPR: > - return build_x_compound_expr (input_location, left, right, complain); > + return build_x_compound_expr (input_location, left, right, > + lookups, complain); > default: > return build_x_binary_op (input_location, code, > left, TREE_CODE (left), > right, TREE_CODE (right), > - /*overload=*/NULL, > + lookups, /*overload=*/NULL, > complain); > } > } > @@ -17908,7 +17911,7 @@ tsubst_omp_for_iterator (tree t, int i, tree declv, tree &orig_declv, > tree lhs = RECUR (TREE_OPERAND (incr, 0)); > tree rhs = RECUR (TREE_OPERAND (incr, 1)); > incr = build_x_modify_expr (EXPR_LOCATION (incr), lhs, > - NOP_EXPR, rhs, complain); > + NOP_EXPR, rhs, NULL_TREE, complain); > } > else > incr = RECUR (incr); > @@ -19221,6 +19224,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl, > RETURN (RECUR (TREE_OPERAND (t, 1))); > RETURN (build_x_compound_expr (EXPR_LOCATION (t), tmp, > RECUR (TREE_OPERAND (t, 1)), > + DEPENDENT_OPERATOR_SAVED_LOOKUPS (t), > complain)); > > case ANNOTATE_EXPR: > @@ -19872,6 +19876,7 @@ tsubst_copy_and_build (tree t, > } > else > r = build_x_indirect_ref (input_location, r, RO_UNARY_STAR, > + DEPENDENT_OPERATOR_SAVED_LOOKUPS (t), > complain|decltype_flag); > > if (REF_PARENTHESIZED_P (t)) > @@ -19982,6 +19987,7 @@ tsubst_copy_and_build (tree t, > op1 = tsubst_non_call_postfix_expression (TREE_OPERAND (t, 0), > args, complain, in_decl); > RETURN (build_x_unary_op (input_location, TREE_CODE (t), op1, > + DEPENDENT_OPERATOR_SAVED_LOOKUPS (t), > complain|decltype_flag)); > > case PREDECREMENT_EXPR: > @@ -19995,6 +20001,7 @@ tsubst_copy_and_build (tree t, > case IMAGPART_EXPR: > RETURN (build_x_unary_op (input_location, TREE_CODE (t), > RECUR (TREE_OPERAND (t, 0)), > + DEPENDENT_OPERATOR_SAVED_LOOKUPS (t), > complain|decltype_flag)); > > case FIX_TRUNC_EXPR: > @@ -20013,6 +20020,7 @@ tsubst_copy_and_build (tree t, > op1 = tsubst_non_call_postfix_expression (op1, args, complain, > in_decl); > RETURN (build_x_unary_op (input_location, ADDR_EXPR, op1, > + DEPENDENT_OPERATOR_SAVED_LOOKUPS (t), > complain|decltype_flag)); > > case PLUS_EXPR: > @@ -20077,6 +20085,7 @@ tsubst_copy_and_build (tree t, > (warning_suppressed_p (TREE_OPERAND (t, 1)) > ? ERROR_MARK > : TREE_CODE (TREE_OPERAND (t, 1))), > + DEPENDENT_OPERATOR_SAVED_LOOKUPS (t), > /*overload=*/NULL, > complain|decltype_flag); > if (EXPR_P (r)) > @@ -20229,8 +20238,10 @@ tsubst_copy_and_build (tree t, > warning_sentinel s(warn_div_by_zero); > tree lhs = RECUR (TREE_OPERAND (t, 0)); > tree rhs = RECUR (TREE_OPERAND (t, 2)); > + > tree r = build_x_modify_expr > (EXPR_LOCATION (t), lhs, TREE_CODE (TREE_OPERAND (t, 1)), rhs, > + DEPENDENT_OPERATOR_SAVED_LOOKUPS (t), > complain|decltype_flag); > /* TREE_NO_WARNING must be set if either the expression was > parenthesized or it uses an operator such as >>= rather > @@ -20331,6 +20342,7 @@ tsubst_copy_and_build (tree t, > RETURN (build_x_compound_expr (EXPR_LOCATION (t), > op0, > RECUR (TREE_OPERAND (t, 1)), > + DEPENDENT_OPERATOR_SAVED_LOOKUPS (t), > complain|decltype_flag)); > } > > @@ -27011,6 +27023,9 @@ dependent_type_p_r (tree type) > if (TREE_CODE (type) == TYPE_PACK_EXPANSION) > return true; > > + if (TREE_CODE (type) == DEPENDENT_OPERATOR_TYPE) > + return true; > + > if (any_dependent_type_attributes_p (TYPE_ATTRIBUTES (type))) > return true; > > diff --git a/gcc/cp/ptree.c b/gcc/cp/ptree.c > index d514aa2cad2..f7ddae77679 100644 > --- a/gcc/cp/ptree.c > +++ b/gcc/cp/ptree.c > @@ -151,6 +151,12 @@ cxx_print_type (FILE *file, tree node, int indent) > print_node (file, "expr", DECLTYPE_TYPE_EXPR (node), indent + 4); > return; > > + case DEPENDENT_OPERATOR_TYPE: > + print_node (file, "saved_lookups", > + DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS (node), > + indent + 4); > + return; > + > case TYPENAME_TYPE: > print_node (file, "fullname", TYPENAME_TYPE_FULLNAME (node), > indent + 4); > diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c > index cdf63c15e21..7078af03d3c 100644 > --- a/gcc/cp/semantics.c > +++ b/gcc/cp/semantics.c > @@ -2920,7 +2920,7 @@ finish_increment_expr (cp_expr expr, enum tree_code code) > expr.get_start (), > get_finish (input_location)); > cp_expr result = build_x_unary_op (combined_loc, code, expr, > - tf_warning_or_error); > + NULL_TREE, tf_warning_or_error); > /* TODO: build_x_unary_op doesn't honor the location, so set it here. */ > result.set_location (combined_loc); > return result; > @@ -3031,7 +3031,8 @@ finish_unary_op_expr (location_t op_loc, enum tree_code code, cp_expr expr, > of the operator token to the end of EXPR. */ > location_t combined_loc = make_location (op_loc, > op_loc, expr.get_finish ()); > - cp_expr result = build_x_unary_op (combined_loc, code, expr, complain); > + cp_expr result = build_x_unary_op (combined_loc, code, expr, > + NULL_TREE, complain); > /* TODO: build_x_unary_op doesn't always honor the location. */ > result.set_location (combined_loc); > > @@ -9881,7 +9882,7 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, > TREE_CODE (cond), > iter, ERROR_MARK, > TREE_OPERAND (cond, 1), ERROR_MARK, > - NULL, tf_warning_or_error); > + NULL_TREE, NULL, tf_warning_or_error); > if (error_operand_p (tem)) > return true; > } > @@ -9895,9 +9896,10 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, > error_at (elocus, "invalid controlling predicate"); > return true; > } > - diff = build_x_binary_op (elocus, MINUS_EXPR, TREE_OPERAND (cond, 1), > - ERROR_MARK, iter, ERROR_MARK, NULL, > - tf_warning_or_error); > + diff = build_x_binary_op (elocus, MINUS_EXPR, > + TREE_OPERAND (cond, 1), ERROR_MARK, > + iter, ERROR_MARK, > + NULL_TREE, NULL, tf_warning_or_error); > diff = cp_fully_fold (diff); > if (error_operand_p (diff)) > return true; > @@ -9925,7 +9927,7 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, > } > iter_incr = build_x_unary_op (EXPR_LOCATION (incr), > TREE_CODE (incr), iter, > - tf_warning_or_error); > + NULL_TREE, tf_warning_or_error); > if (error_operand_p (iter_incr)) > return true; > else if (TREE_CODE (incr) == PREINCREMENT_EXPR > @@ -9951,6 +9953,7 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, > iter_incr = build_x_modify_expr (EXPR_LOCATION (rhs), > iter, TREE_CODE (rhs), > TREE_OPERAND (rhs, 1), > + NULL_TREE, > tf_warning_or_error); > if (error_operand_p (iter_incr)) > return true; > @@ -9980,13 +9983,13 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, > PLUS_EXPR, > TREE_OPERAND (rhs, 0), > ERROR_MARK, iter, > - ERROR_MARK, NULL, > + ERROR_MARK, NULL_TREE, NULL, > tf_warning_or_error); > if (error_operand_p (iter_incr)) > return true; > iter_incr = build_x_modify_expr (EXPR_LOCATION (rhs), > iter, NOP_EXPR, > - iter_incr, > + iter_incr, NULL_TREE, > tf_warning_or_error); > if (error_operand_p (iter_incr)) > return true; > @@ -10097,7 +10100,7 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, > if (init != NULL) > finish_expr_stmt (build_x_modify_expr (elocus, > iter, NOP_EXPR, init, > - tf_warning_or_error)); > + NULL_TREE, tf_warning_or_error)); > init = build_int_cst (TREE_TYPE (diff), 0); > if (c && iter_incr == NULL > && (!ordered || (i < collapse && collapse > 1))) > @@ -10106,23 +10109,24 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, > { > finish_expr_stmt (build_x_modify_expr (elocus, > incr_var, NOP_EXPR, > - incr, tf_warning_or_error)); > + incr, NULL_TREE, > + tf_warning_or_error)); > incr = incr_var; > } > iter_incr = build_x_modify_expr (elocus, > iter, PLUS_EXPR, incr, > - tf_warning_or_error); > + NULL_TREE, tf_warning_or_error); > } > if (c && ordered && i < collapse && collapse > 1) > iter_incr = incr; > finish_expr_stmt (build_x_modify_expr (elocus, > last, NOP_EXPR, init, > - tf_warning_or_error)); > + NULL_TREE, tf_warning_or_error)); > if (diffvar) > { > finish_expr_stmt (build_x_modify_expr (elocus, > diffvar, NOP_EXPR, > - diff, tf_warning_or_error)); > + diff, NULL_TREE, tf_warning_or_error)); > diff = diffvar; > } > *pre_body = pop_stmt_list (*pre_body); > @@ -10138,13 +10142,13 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, > iter_init = build2 (MINUS_EXPR, TREE_TYPE (diff), decl, last); > iter_init = build_x_modify_expr (elocus, > iter, PLUS_EXPR, iter_init, > - tf_warning_or_error); > + NULL_TREE, tf_warning_or_error); > if (iter_init != error_mark_node) > iter_init = build1 (NOP_EXPR, void_type_node, iter_init); > finish_expr_stmt (iter_init); > finish_expr_stmt (build_x_modify_expr (elocus, > last, NOP_EXPR, decl, > - tf_warning_or_error)); > + NULL_TREE, tf_warning_or_error)); > add_stmt (orig_body); > *body = pop_stmt_list (*body); > > @@ -10162,7 +10166,7 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, > iter_init = build2 (MINUS_EXPR, TREE_TYPE (diff), iter_init, last); > iter_init = build_x_modify_expr (elocus, > iter, PLUS_EXPR, iter_init, > - tf_warning_or_error); > + NULL_TREE, tf_warning_or_error); > if (iter_init != error_mark_node) > iter_init = build1 (NOP_EXPR, void_type_node, iter_init); > finish_expr_stmt (iter_init); > @@ -10873,7 +10877,7 @@ finish_omp_cancel (tree clauses) > ifc = build_x_binary_op (OMP_CLAUSE_LOCATION (ifc), NE_EXPR, > OMP_CLAUSE_IF_EXPR (ifc), ERROR_MARK, > integer_zero_node, ERROR_MARK, > - NULL, tf_warning_or_error); > + NULL_TREE, NULL, tf_warning_or_error); > } > else > ifc = boolean_true_node; > @@ -12125,6 +12129,9 @@ finish_unary_fold_expr (tree expr, int op, tree_code dir) > tree code = build_int_cstu (integer_type_node, abs (op)); > tree fold = build_min_nt_loc (UNKNOWN_LOCATION, dir, code, pack); > FOLD_EXPR_MODIFY_P (fold) = (op < 0); > + TREE_TYPE (fold) = build_dependent_operator_type (NULL_TREE, > + FOLD_EXPR_OP (fold), > + FOLD_EXPR_MODIFY_P (fold)); > return fold; > } > > @@ -12151,6 +12158,9 @@ finish_binary_fold_expr (tree pack, tree init, int op, tree_code dir) > tree code = build_int_cstu (integer_type_node, abs (op)); > tree fold = build_min_nt_loc (UNKNOWN_LOCATION, dir, code, pack, init); > FOLD_EXPR_MODIFY_P (fold) = (op < 0); > + TREE_TYPE (fold) = build_dependent_operator_type (NULL_TREE, > + FOLD_EXPR_OP (fold), > + FOLD_EXPR_MODIFY_P (fold)); > return fold; > } > > diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c > index f6f7927f293..f319dbf3526 100644 > --- a/gcc/cp/tree.c > +++ b/gcc/cp/tree.c > @@ -5974,8 +5974,6 @@ cp_free_lang_data (tree t) > DECL_EXTERNAL (t) = 1; > TREE_STATIC (t) = 0; > } > - if (TREE_CODE (t) == FUNCTION_DECL) > - discard_operator_bindings (t); > if (TREE_CODE (t) == NAMESPACE_DECL) > /* We do not need the leftover chaining of namespaces from the > binding level. */ > diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c > index 4e60db40c76..88dca343315 100644 > --- a/gcc/cp/typeck.c > +++ b/gcc/cp/typeck.c > @@ -2602,6 +2602,7 @@ rationalize_conditional_expr (enum tree_code code, tree t, > ? LE_EXPR : GE_EXPR), > op0, TREE_CODE (op0), > op1, TREE_CODE (op1), > + NULL_TREE, > /*overload=*/NULL, > complain), > cp_build_unary_op (code, op0, false, complain), > @@ -3487,6 +3488,64 @@ build_ptrmemfunc_access_expr (tree ptrmem, tree member_name) > return build_simple_component_ref (ptrmem, member); > } > > +/* Return a TREE_LIST of namespace-scope overloads for the given operator, > + and for any other relevant operator. */ > + > +static tree > +op_unqualified_lookup (tree_code code, bool is_assign) > +{ > + tree lookups = NULL_TREE; > + > + if (cxx_dialect >= cxx20 && !is_assign) > + { > + if (code == NE_EXPR) > + { > + /* != can get rewritten in terms of ==. */ > + tree fnname = ovl_op_identifier (false, EQ_EXPR); > + if (tree fns = lookup_name (fnname, LOOK_where::BLOCK_NAMESPACE)) > + lookups = tree_cons (fnname, fns, lookups); > + } > + else if (code == GT_EXPR || code == LE_EXPR > + || code == LT_EXPR || code == GE_EXPR) > + { > + /* These can get rewritten in terms of <=>. */ > + tree fnname = ovl_op_identifier (false, SPACESHIP_EXPR); > + if (tree fns = lookup_name (fnname, LOOK_where::BLOCK_NAMESPACE)) > + lookups = tree_cons (fnname, fns, lookups); > + } > + } > + > + tree fnname = ovl_op_identifier (is_assign, code); > + if (tree fns = lookup_name (fnname, LOOK_where::BLOCK_NAMESPACE)) > + lookups = tree_cons (fnname, fns, lookups); > + > + if (lookups) > + return lookups; > + else > + return build_tree_list (NULL_TREE, NULL_TREE); > +} > + > +/* Create a DEPENDENT_OPERATOR_TYPE for a dependent operator expression of > + the given operator. LOOKUPS, if non-NULL, is the result of phase 1 > + name lookup for the given operator. */ > + > +tree > +build_dependent_operator_type (tree lookups, tree_code code, bool is_assign) > +{ > + if (lookups) > + /* We're partially instantiating a dependent operator expression, and > + LOOKUPS contains the result of phase 1 name lookup that we performed > + earlier at template definition time, so just carry it over. */; If we're going to keep using the same set of lookups, can we also reuse the same DEPENDENT_OPERATOR_TYPE? It seems like you could pass the type to build_x_* instead of pulling the lookups out as early. > + else > + /* We're processing a dependent operator expression at template definition > + time, so perform phase 1 name lookup now. */ > + lookups = op_unqualified_lookup (code, is_assign); > + > + tree type = cxx_make_type (DEPENDENT_OPERATOR_TYPE); > + DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS (type) = lookups; > + return type; > +} > + > /* Given an expression PTR for a pointer, return an expression > for the value pointed to. > ERRORSTRING is the name of the operator to appear in error messages. > @@ -3496,7 +3555,7 @@ build_ptrmemfunc_access_expr (tree ptrmem, tree member_name) > > tree > build_x_indirect_ref (location_t loc, tree expr, ref_operator errorstring, > - tsubst_flags_t complain) > + tree lookups, tsubst_flags_t complain) > { > tree orig_expr = expr; > tree rval; > @@ -3516,12 +3575,18 @@ build_x_indirect_ref (location_t loc, tree expr, ref_operator errorstring, > return build_min (INDIRECT_REF, TREE_TYPE (TREE_TYPE (expr)), expr); > } > if (type_dependent_expression_p (expr)) > - return build_min_nt_loc (loc, INDIRECT_REF, expr); > + { > + expr = build_min_nt_loc (loc, INDIRECT_REF, expr); > + TREE_TYPE (expr) > + = build_dependent_operator_type (lookups, INDIRECT_REF, false); > + return expr; > + } > expr = build_non_dependent_expr (expr); > } > > rval = build_new_op (loc, INDIRECT_REF, LOOKUP_NORMAL, expr, > - NULL_TREE, NULL_TREE, &overload, complain); > + NULL_TREE, NULL_TREE, lookups, > + &overload, complain); > if (!rval) > rval = cp_build_indirect_ref (loc, expr, errorstring, complain); > > @@ -4458,8 +4523,8 @@ convert_arguments (tree typelist, vec **values, tree fndecl, > tree > build_x_binary_op (const op_location_t &loc, enum tree_code code, tree arg1, > enum tree_code arg1_code, tree arg2, > - enum tree_code arg2_code, tree *overload_p, > - tsubst_flags_t complain) > + enum tree_code arg2_code, tree lookups, > + tree *overload_p, tsubst_flags_t complain) > { > tree orig_arg1; > tree orig_arg2; > @@ -4475,7 +4540,8 @@ build_x_binary_op (const op_location_t &loc, enum tree_code code, tree arg1, > || type_dependent_expression_p (arg2)) > { > expr = build_min_nt_loc (loc, code, arg1, arg2); > - maybe_save_operator_binding (expr); > + TREE_TYPE (expr) > + = build_dependent_operator_type (lookups, code, false); > return expr; > } > arg1 = build_non_dependent_expr (arg1); > @@ -4486,7 +4552,7 @@ build_x_binary_op (const op_location_t &loc, enum tree_code code, tree arg1, > expr = build_m_component_ref (arg1, arg2, complain); > else > expr = build_new_op (loc, code, LOOKUP_NORMAL, arg1, arg2, NULL_TREE, > - &overload, complain); > + lookups, &overload, complain); > > if (overload_p != NULL) > *overload_p = overload; > @@ -4538,7 +4604,7 @@ build_x_array_ref (location_t loc, tree arg1, tree arg2, > } > > expr = build_new_op (loc, ARRAY_REF, LOOKUP_NORMAL, arg1, arg2, > - NULL_TREE, &overload, complain); > + NULL_TREE, NULL_TREE, &overload, complain); > > if (processing_template_decl && expr != error_mark_node) > { > @@ -6402,7 +6468,7 @@ pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype, > > tree > build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg, > - tsubst_flags_t complain) > + tree lookups, tsubst_flags_t complain) > { > tree orig_expr = xarg; > tree exp; > @@ -6414,7 +6480,7 @@ build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg, > if (type_dependent_expression_p (xarg)) > { > tree e = build_min_nt_loc (loc, code, xarg.get_value (), NULL_TREE); > - maybe_save_operator_binding (e); > + TREE_TYPE (e) = build_dependent_operator_type (lookups, code, false); > return e; > } > > @@ -6439,7 +6505,7 @@ build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg, > /* Don't look for a function. */; > else > exp = build_new_op (loc, code, LOOKUP_NORMAL, xarg, NULL_TREE, > - NULL_TREE, &overload, complain); > + NULL_TREE, lookups, &overload, complain); > > if (!exp && code == ADDR_EXPR) > { > @@ -7508,7 +7574,8 @@ build_x_compound_expr_from_list (tree list, expr_list_kind exp, > > for (list = TREE_CHAIN (list); list; list = TREE_CHAIN (list)) > expr = build_x_compound_expr (EXPR_LOCATION (TREE_VALUE (list)), > - expr, TREE_VALUE (list), complain); > + expr, TREE_VALUE (list), NULL_TREE, > + complain); > } > > return expr; > @@ -7543,7 +7610,7 @@ build_x_compound_expr_from_vec (vec *vec, const char *msg, > expr = (*vec)[0]; > for (ix = 1; vec->iterate (ix, &t); ++ix) > expr = build_x_compound_expr (EXPR_LOCATION (t), expr, > - t, complain); > + t, NULL_TREE, complain); > > return expr; > } > @@ -7553,7 +7620,7 @@ build_x_compound_expr_from_vec (vec *vec, const char *msg, > > tree > build_x_compound_expr (location_t loc, tree op1, tree op2, > - tsubst_flags_t complain) > + tree lookups, tsubst_flags_t complain) > { > tree result; > tree orig_op1 = op1; > @@ -7566,7 +7633,8 @@ build_x_compound_expr (location_t loc, tree op1, tree op2, > || type_dependent_expression_p (op2)) > { > result = build_min_nt_loc (loc, COMPOUND_EXPR, op1, op2); > - maybe_save_operator_binding (result); > + TREE_TYPE (result) > + = build_dependent_operator_type (lookups, COMPOUND_EXPR, false); > return result; > } > op1 = build_non_dependent_expr (op1); > @@ -7574,7 +7642,7 @@ build_x_compound_expr (location_t loc, tree op1, tree op2, > } > > result = build_new_op (loc, COMPOUND_EXPR, LOOKUP_NORMAL, op1, op2, > - NULL_TREE, &overload, complain); > + NULL_TREE, lookups, &overload, complain); > if (!result) > result = cp_build_compound_expr (op1, op2, complain); > > @@ -9017,8 +9085,8 @@ cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, > { > result = build_new_op (input_location, MODIFY_EXPR, > LOOKUP_NORMAL, lhs, rhs, > - make_node (NOP_EXPR), /*overload=*/NULL, > - complain); > + make_node (NOP_EXPR), NULL_TREE, > + /*overload=*/NULL, complain); > if (result == NULL_TREE) > return error_mark_node; > goto ret; > @@ -9233,7 +9301,7 @@ cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, > > cp_expr > build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, > - tree rhs, tsubst_flags_t complain) > + tree rhs, tree lookups, tsubst_flags_t complain) > { > tree orig_lhs = lhs; > tree orig_rhs = rhs; > @@ -9250,7 +9318,9 @@ build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, > { > tree op = build_min_nt_loc (loc, modifycode, NULL_TREE, NULL_TREE); > tree rval = build_min_nt_loc (loc, MODOP_EXPR, lhs, op, rhs); > - maybe_save_operator_binding (rval); > + if (modifycode != NOP_EXPR) > + TREE_TYPE (rval) > + = build_dependent_operator_type (lookups, modifycode, true); > return rval; > } > > @@ -9262,7 +9332,7 @@ build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, > { > tree op = build_nt (modifycode, NULL_TREE, NULL_TREE); > tree rval = build_new_op (loc, MODIFY_EXPR, LOOKUP_NORMAL, > - lhs, rhs, op, &overload, complain); > + lhs, rhs, op, lookups, &overload, complain); > if (rval) > { > if (rval == error_mark_node) > diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c > index 3fb651a02ba..724684c0457 100644 > --- a/gcc/cp/typeck2.c > +++ b/gcc/cp/typeck2.c > @@ -1956,7 +1956,7 @@ build_x_arrow (location_t loc, tree expr, tsubst_flags_t complain) > > while ((expr = build_new_op (loc, COMPONENT_REF, > LOOKUP_NORMAL, expr, NULL_TREE, NULL_TREE, > - &fn, complain))) > + NULL_TREE, &fn, complain))) > { > if (expr == error_mark_node) > return error_mark_node; > diff --git a/gcc/testsuite/g++.dg/lookup/operator-3-ops.h b/gcc/testsuite/g++.dg/lookup/operator-3-ops.h > new file mode 100644 > index 00000000000..fbd242a4e66 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/lookup/operator-3-ops.h > @@ -0,0 +1,53 @@ > +void operator+(N::A); > +void operator-(N::A); > +void operator*(N::A); > +void operator~(N::A); > +#if __cplusplus >= 201103L > +void operator&(N::A) = delete; > +#else > +void operator&(N::A); > +#endif > +void operator!(N::A); > +void operator++(N::A); > +void operator--(N::A); > +void operator++(N::A, int); > +void operator--(N::A, int); > + > +void operator->*(N::A, N::A); > +void operator/(N::A, N::A); > +void operator*(N::A, N::A); > +void operator+(N::A, N::A); > +void operator-(N::A, N::A); > +void operator%(N::A, N::A); > +void operator&(N::A, N::A); > +void operator|(N::A, N::A); > +void operator^(N::A, N::A); > +void operator<<(N::A, N::A); > +void operator>>(N::A, N::A); > +void operator&&(N::A, N::A); > +void operator||(N::A, N::A); > +#if __cplusplus >= 201103L > +void operator,(N::A, N::A) = delete; > +#else > +void operator,(N::A, N::A); > +#endif > + > +void operator==(N::A, N::A); > +void operator!=(N::A, N::A); > +void operator<(N::A, N::A); > +void operator>(N::A, N::A); > +void operator<=(N::A, N::A); > +void operator>=(N::A, N::A); > +#if __cplusplus > 201703L > +void operator<=>(N::A, N::A); > +#endif > + > +void operator+=(N::A, N::A); > +void operator-=(N::A, N::A); > +void operator*=(N::A, N::A); > +void operator/=(N::A, N::A); > +void operator%=(N::A, N::A); > +void operator|=(N::A, N::A); > +void operator^=(N::A, N::A); > +void operator<<=(N::A, N::A); > +void operator>>=(N::A, N::A); > diff --git a/gcc/testsuite/g++.dg/lookup/operator-3.C b/gcc/testsuite/g++.dg/lookup/operator-3.C > index bc5eb3d6693..98011efd543 100644 > --- a/gcc/testsuite/g++.dg/lookup/operator-3.C > +++ b/gcc/testsuite/g++.dg/lookup/operator-3.C > @@ -1,4 +1,6 @@ > // PR c++/51577 > +// Verify we don't consider later-declared namespace-scope operator overloads > +// when instantiating a dependent operator expression at block scope. > > template void f (T x) { > +x; // { dg-error "no match" } > @@ -50,59 +52,7 @@ template 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 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 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 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 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 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 struct S { > + static constexpr bool is_primary = true; > +}; > + > +template struct S { }; > +template struct S { }; > +template struct S { }; > +template struct S { }; > +template struct S { }; > +template struct S { }; > +template struct S { }; > +template struct S { }; > +template struct S { }; > +template struct S { }; > + > +template struct S*T())> { }; > +template struct S { }; > +template struct S { }; > +template struct S { }; > +template struct S { }; > +template struct S { }; > +template struct S { }; > +template struct S { }; > +template struct S { }; > +template struct S { }; > +template struct S> T())> { }; > +template struct S { }; > +template struct S { }; > +template struct S { }; > + > +template struct S { }; > +template struct S { }; > +template struct S { }; > +template struct S T())> { }; > +template struct S { }; > +template struct S= T())> { }; > +#if __cplusplus > 201703L > +template struct S T())> { }; > +#endif > + > +template struct S { }; > +template struct S { }; > +template struct S { }; > +template struct S { }; > +template struct S { }; > +template struct S { }; > +template struct S { }; > +template struct S { }; > +template struct S>= T())> { }; > + > +namespace N { struct A { }; } > + > +#include "operator-3-ops.h" > + > +static_assert(S::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 > + void operator+=(T); > + }; > + > + struct A { > + void operator+=(char); // #1 > + > + template > + void f() { > + string s; > + s += T(); > + } > + > + void g() { > + f(); > + } > + }; > +} // namespace N > + > +template > +void operator+=(N::string, T); > diff --git a/gcc/testsuite/g++.dg/lookup/operator-8.C b/gcc/testsuite/g++.dg/lookup/operator-8.C > new file mode 100644 > index 00000000000..30d02b806d8 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/lookup/operator-8.C > @@ -0,0 +1,34 @@ > +// Verify phase 1 lookup works properly for rewritten non-dependent conditional > +// operator expressions. > + > +// This test currently fails due to build_min_non_dep_op_overload not knowing > +// how to handle rewritten operator expressions; see the FIXME in build_new_op. > + > +// { dg-do compile { target c++20 } } > + > +#include > + > +struct A { > + bool operator==(int); > + std::strong_ordering operator<=>(int); > +}; > + > +template > +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. > +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(); > 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; > } >