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.133.124]) by sourceware.org (Postfix) with ESMTPS id 08DEE3858D3C for ; Thu, 16 Dec 2021 03:30:49 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 08DEE3858D3C Received: from mail-qt1-f200.google.com (mail-qt1-f200.google.com [209.85.160.200]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-36-L7tSp9KAM7aYTgCcrP7ffg-1; Wed, 15 Dec 2021 22:30:43 -0500 X-MC-Unique: L7tSp9KAM7aYTgCcrP7ffg-1 Received: by mail-qt1-f200.google.com with SMTP id h8-20020a05622a170800b002acc8656e05so32144724qtk.7 for ; Wed, 15 Dec 2021 19:30:43 -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:cc:references:from:in-reply-to :content-transfer-encoding; bh=9KogJLOn6exd3t021x2Bmo6qefQAisgRFIfDXbCe4lw=; b=6bb7XDeevi2QYEqorqWf8LGCI7cUPm2QxmPv+borL/5ewFt2X7neM4mnIyxopcTlsZ SKQMQbNh9F+ZKCZcV+T5rm8W4JOWtR4AS4v7FsDs5aeEJxKYeSVyqGIpvfnqcLbkKFWK U5JqcjVEJAnjm0bt2prh9qWb0i8v/q1RpGehZpt34DmGZOqHtFNzT9gB2knxz+wJGmGx PayKVb85CUyAV419eg+JXcU3He97dY7pQlRweHEBH4n3WEvINUntnVqB3CRSJ1OILeoi jjlQDikLF+Lvugql1twrFXMVRYpZRJB7dY0fxXnnvI5iinAMaEjKxk54jOMy8btQh2Xy 6tkQ== X-Gm-Message-State: AOAM532/ajGFZa9rD+xGVBJ2vLwJ2YBqA5/obG8HW/fpwE0/2cmI0WT9 LbBz1pnfAdrj2H6ZtJnKqqpkLMcTL4I/5iL0/uCovpT+wW9lCrBeEJrp90HU2N78/3HipxSAMAL P9BVHsrnkqUQvqCOz6g== X-Received: by 2002:a05:622a:4d4:: with SMTP id q20mr15461263qtx.606.1639625441111; Wed, 15 Dec 2021 19:30:41 -0800 (PST) X-Google-Smtp-Source: ABdhPJwIJwZwexg64nJ6uAKLUUGx8QYOoW6iyi0VG9yZ4q3qo6Og5bRt/a7u64QEkyljJQLK4vQOQQ== X-Received: by 2002:a05:622a:4d4:: with SMTP id q20mr15461193qtx.606.1639625439090; Wed, 15 Dec 2021 19:30:39 -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 e7sm3240328qtx.72.2021.12.15.19.30.37 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Wed, 15 Dec 2021 19:30:38 -0800 (PST) Message-ID: Date: Wed, 15 Dec 2021 22:30:37 -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 Cc: gcc-patches@gcc.gnu.org References: <20211210145333.2868378-1-ppalka@redhat.com> <11c7cfa4-a2a3-d012-5638-687194ea8737@redhat.com> <318cb467-0e62-2694-d838-125d2f49b11a@idea> From: Jason Merrill In-Reply-To: <318cb467-0e62-2694-d838-125d2f49b11a@idea> 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_H2, 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: Thu, 16 Dec 2021 03:30:54 -0000 On 12/15/21 17:53, Patrick Palka wrote: > > > On Wed, 15 Dec 2021, Jason Merrill wrote: > >> On 12/10/21 09:53, Patrick Palka wrote: >>> In order to properly implement two-stage name lookup for dependent >>> operator expressions, we need to remember the result of unqualified >>> lookup of the operator at template definition time, and reuse that >>> result rather than performing another unqualified lookup at >>> instantiation time. >>> >>> Ideally we could just store the lookup result in the expression directly, >>> but as pointed out in r9-6405 this isn't really possible since we use >>> the standard tree codes to represent most dependent operator expressions. >>> >>> We could perhaps create a new tree code to represent dependent operator >>> expressions, say a DEPENDENT_OPERATOR_EXPR with enough operands to store >>> the lookup results along and everything else, but that'd require a lot >>> of careful work to make sure we handle this new tree code properly >>> across the frontend. >>> >>> However, currently type-dependent operator (and call) expressions are >>> given an empty TREE_TYPE, so this space is effectively unused except to >>> signal that the expression is type-dependent. It'd be convenient if we >>> could use this space to store the lookup results while preserving the >>> dependent-ness of the expression. >>> >>> To that end, this patch creates a new kind of type, called >>> DEPENDENT_OPERATOR_TYPE, which we give to dependent operator expressions >>> and into which we can store the result of operator lookup at template >>> definition time (DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS). Since this >>> type is always dependent, and since the frontend doesn't seem to care >>> much about the particular type of a type-dependent expression, using >>> this type in place of a NULL_TREE type seems to just work; only >>> dependent_type_p and WILDCARD_TYPE_P need to be adjusted to return true >>> for this new type. >>> >>> The rest of the patch mostly consists of adding the necessary plumbing >>> to pass DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS to add_operator_candidates, >>> adjusting all callers of build_x_binary_op & friends appropriately, and >>> removing the now unnecessary push_operator_bindings mechanism. >>> >>> In passing, this patch simplifies finish_constraint_binary_op to avoid >>> using build_x_binary_op for building a binary constraint-expr; we don't >>> need to consider operator||/&& overloads here. This patch also makes >>> FOLD_EXPR_OP yield a tree_code instead of a raw INTEGER_CST. >>> >>> Finally, this patch adds the XFAILed test operator-8.C which is about >>> broken two-stage name lookup for rewritten non-dependent operator >>> expressions, an existing bug that's otherwise only documented in >>> build_new_op. >>> >>> Bootstrapped and regtested on x86-64-pc-linux-gnu, does this look OK for >>> trunk? >>> >>> PR c++/51577 >>> PR c++/83035 >>> PR c++/100465 >>> >>> gcc/cp/ChangeLog: >>> >>> * call.c (add_operator_candidates): Add lookups parameter. >>> Use it to avoid performing a second unqualified lookup when >>> instantiating a dependent operator expression. >>> (build_new_op): Add lookups parameter and pass it appropriately. >>> * constraint.cc (finish_constraint_binary_op): Use >>> build_min_nt_loc instead of build_x_binary_op. >>> * coroutines.cc (build_co_await): Adjust call to build_new_op. >>> * cp-objcp-common.c (cp_common_init_ts): Mark >>> DEPENDENT_OPERATOR_TYPE appropriately. >>> * cp-tree.def (DEPENDENT_OPERATOR_TYPE): Define. >>> * cp-tree.h (WILDCARD_TYPE_P): Accept DEPENDENT_OPERATOR_TYPE. >>> (FOLD_EXPR_OP_RAW): New, renamed from ... >>> (FOLD_EXPR_OP): ... this. Change this to return the tree_code >>> directly. >>> (DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS): Define. >>> (DEPENDENT_OPERATOR_SAVED_LOOKUPS): Define. >>> (build_new_op): Add lookups parameter. >>> (build_dependent_operator_type): Declare. >>> (build_x_indirect_ref): Add lookups parameter. >>> (build_x_binary_op): Likewise. >>> (build_x_unary_op): Likewise. >>> (build_x_compound_expr): Likewise. >>> (build_x_modify_expr): Likewise. >>> * cxx-pretty-print.c (get_fold_operator): Adjust after >>> FOLD_EXPR_OP change. >>> * decl.c (start_preparsed_function): Don't call >>> push_operator_bindings. >>> * decl2.c (grok_array_decl): Adjust calls to build_new_op. >>> * method.c (do_one_comp): Likewise. >>> (build_comparison_op): Likewise. >>> * module.cc (trees_out::type_node): Handle DEPENDENT_OPERATOR_TYPE. >>> (trees_in::tree_node): Likewise. >>> * name-lookup.c (lookup_name): Revert r11-2876 change. >>> (op_unqualified_lookup): Remove. >>> (maybe_save_operator_binding): Remove. >>> (discard_operator_bindings): Remove. >>> (push_operator_bindings): Remove. >>> * name-lookup.h (maybe_save_operator_binding): Remove. >>> (push_operator_bindings): Remove. >>> (discard_operator_bindings): Remove. >>> * parser.c (cp_parser_unary_expression): Adjust calls to build_x_*. >>> (cp_parser_binary_expression): Likewise. >>> (cp_parser_assignment_expression): Likewise. >>> (cp_parser_expression): Likewise. >>> (do_range_for_auto_deduction): Likewise. >>> (cp_convert_range_for): Likewise. >>> (cp_parser_perform_range_for_lookup): Likewise. >>> (cp_parser_template_argument): Likewise. >>> (cp_parser_omp_for_cond): Likewise. >>> (cp_parser_omp_for_incr): Likewise. >>> (cp_parser_omp_for_loop_init): Likewise. >>> (cp_convert_omp_range_for): Likewise. >>> (cp_finish_omp_range_for): Likewise. >>> * pt.c (fold_expression): Adjust after FOLD_EXPR_OP change. Pass >>> DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS to build_x_*. >>> (tsubst_omp_for_iterator): Adjust call to build_x_modify_expr. >>> (tsubst_expr) : 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. > > Sounds good. I also renamed it to the more apt TEMPLATED_OPERATOR_SAVED_LOOKUPS > since we use it on dependent as well as non-dependent operator expressions. Since it's now a function, it should also be lower case. OK with that change. >> >>> /* Indicates whether a string literal has been parenthesized. Such >>> usages are disallowed in certain circumstances. */ >>> @@ -6462,14 +6481,15 @@ extern tree build_special_member_call >>> (tree, tree, >>> tree, int, tsubst_flags_t); >>> extern tree build_new_op (const op_location_t &, >>> enum tree_code, >>> - int, tree, tree, tree, tree >>> *, >>> - tsubst_flags_t); >>> + int, tree, tree, tree, tree, >>> + tree *, tsubst_flags_t); >>> /* Wrapper that leaves out the usually-null op3 and overload parms. */ >>> inline tree build_new_op (const op_location_t &loc, enum tree_code code, >>> int flags, tree arg1, tree arg2, >>> tsubst_flags_t complain) >>> { >>> - return build_new_op (loc, code, flags, arg1, arg2, NULL_TREE, NULL, >>> complain); >>> + return build_new_op (loc, code, flags, arg1, arg2, NULL_TREE, NULL_TREE, >>> + NULL, complain); >>> } >>> extern tree build_op_call (tree, vec **, >>> 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. > > That sounds like it'd work well. But what if we instead just make > DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS point back to the corresponding > DEPENDENT_OPERATOR_TYPE via TREE_TYPE? As in the below... > > v2: Rename DEPENDENT_OPERATOR_SAVED_LOOKUPS to > TEMPLATED_OPERATOR_SAVED_LOOKUPS and turn into an inline function. > > Make build_dependent_operator_type set/inspect the TREE_TYPE of the > lookup result in order to reuse the DEPENDENT_OPERATOR_TYPE during > partial instantiations. Document this in the comment for > DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS. > > Bootstrap and regtest in progress. > > -- >8 -- > > PR c++/51577 > PR c++/83035 > PR c++/100465 > > gcc/cp/ChangeLog: > > * call.c (add_operator_candidates): Add lookups parameter. > Use it to avoid performing a second unqualified lookup when > instantiating a dependent operator expression. > (build_new_op): Add lookups parameter and pass it appropriately. > * constraint.cc (finish_constraint_binary_op): Use > build_min_nt_loc instead of build_x_binary_op. > * coroutines.cc (build_co_await): Adjust call to build_new_op. > * cp-objcp-common.c (cp_common_init_ts): Mark > DEPENDENT_OPERATOR_TYPE appropriately. > * cp-tree.def (DEPENDENT_OPERATOR_TYPE): Define. > * cp-tree.h (WILDCARD_TYPE_P): Accept DEPENDENT_OPERATOR_TYPE. > (FOLD_EXPR_OP_RAW): New, renamed from ... > (FOLD_EXPR_OP): ... this. Change this to return the tree_code directly. > (DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS): Define. > (TEMPLATED_OPERATOR_SAVED_LOOKUPS): Define. > (build_new_op): Add lookups parameter. > (build_dependent_operator_type): Declare. > (build_x_indirect_ref): Add lookups parameter. > (build_x_binary_op): Likewise. > (build_x_unary_op): Likewise. > (build_x_compound_expr): Likewise. > (build_x_modify_expr): Likewise. > * cxx-pretty-print.c (get_fold_operator): Adjust after > FOLD_EXPR_OP change. > * decl.c (start_preparsed_function): Don't call > push_operator_bindings. > * decl2.c (grok_array_decl): Adjust calls to build_new_op. > * method.c (do_one_comp): Likewise. > (build_comparison_op): Likewise. > * module.cc (trees_out::type_node): Handle DEPENDENT_OPERATOR_TYPE. > (trees_in::tree_node): Likewise. > * name-lookup.c (lookup_name): Revert r11-2876 change. > (op_unqualified_lookup): Remove. > (maybe_save_operator_binding): Remove. > (discard_operator_bindings): Remove. > (push_operator_bindings): Remove. > * name-lookup.h (maybe_save_operator_binding): Remove. > (push_operator_bindings): Remove. > (discard_operator_bindings): Remove. > * parser.c (cp_parser_unary_expression): Adjust calls to build_x_*. > (cp_parser_binary_expression): Likewise. > (cp_parser_assignment_expression): Likewise. > (cp_parser_expression): Likewise. > (do_range_for_auto_deduction): Likewise. > (cp_convert_range_for): Likewise. > (cp_parser_perform_range_for_lookup): Likewise. > (cp_parser_template_argument): Likewise. > (cp_parser_omp_for_cond): Likewise. > (cp_parser_omp_for_incr): Likewise. > (cp_parser_omp_for_loop_init): Likewise. > (cp_convert_omp_range_for): Likewise. > (cp_finish_omp_range_for): Likewise. > * pt.c (fold_expression): Adjust after FOLD_EXPR_OP change. Pass > TEMPLATED_OPERATOR_SAVED_LOOKUPS to build_x_*. > (tsubst_omp_for_iterator): Adjust call to build_x_modify_expr. > (tsubst_expr) : Pass > TEMPLATED_OPERATOR_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 | 49 +++++-- > gcc/cp/cxx-pretty-print.c | 4 +- > gcc/cp/decl.c | 2 - > gcc/cp/decl2.c | 5 +- > gcc/cp/method.c | 12 +- > gcc/cp/module.cc | 2 + > gcc/cp/name-lookup.c | 133 +------------------ > gcc/cp/name-lookup.h | 3 - > gcc/cp/parser.c | 40 +++--- > gcc/cp/pt.c | 27 +++- > gcc/cp/ptree.c | 6 + > gcc/cp/semantics.c | 46 ++++--- > gcc/cp/tree.c | 2 - > gcc/cp/typeck.c | 115 +++++++++++++--- > gcc/cp/typeck2.c | 2 +- > gcc/testsuite/g++.dg/lookup/operator-3-ops.h | 53 ++++++++ > gcc/testsuite/g++.dg/lookup/operator-3.C | 56 +------- > gcc/testsuite/g++.dg/lookup/operator-3a.C | 61 +++++++++ > gcc/testsuite/g++.dg/lookup/operator-4.C | 74 +++++++++++ > gcc/testsuite/g++.dg/lookup/operator-4a.C | 76 +++++++++++ > gcc/testsuite/g++.dg/lookup/operator-5.C | 74 +++++++++++ > gcc/testsuite/g++.dg/lookup/operator-5a.C | 76 +++++++++++ > gcc/testsuite/g++.dg/lookup/operator-6.C | 59 ++++++++ > gcc/testsuite/g++.dg/lookup/operator-7.C | 27 ++++ > gcc/testsuite/g++.dg/lookup/operator-8.C | 34 +++++ > libcc1/libcp1plugin.cc | 4 +- > 31 files changed, 794 insertions(+), 300 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/lookup/operator-3-ops.h > create mode 100644 gcc/testsuite/g++.dg/lookup/operator-3a.C > create mode 100644 gcc/testsuite/g++.dg/lookup/operator-4.C > create mode 100644 gcc/testsuite/g++.dg/lookup/operator-4a.C > create mode 100644 gcc/testsuite/g++.dg/lookup/operator-5.C > create mode 100644 gcc/testsuite/g++.dg/lookup/operator-5a.C > create mode 100644 gcc/testsuite/g++.dg/lookup/operator-6.C > create mode 100644 gcc/testsuite/g++.dg/lookup/operator-7.C > create mode 100644 gcc/testsuite/g++.dg/lookup/operator-8.C > > diff --git a/gcc/cp/call.c b/gcc/cp/call.c > index 347df5da35d..31c2308dc28 100644 > --- a/gcc/cp/call.c > +++ b/gcc/cp/call.c > @@ -6285,12 +6285,17 @@ op_is_ordered (tree_code code) > > /* Subroutine of build_new_op: Add to CANDIDATES all candidates for the > operator indicated by CODE/CODE2. This function calls itself recursively to > - handle C++20 rewritten comparison operator candidates. */ > + handle C++20 rewritten comparison operator candidates. > + > + LOOKUPS, if non-NULL, is the set of pertinent namespace-scope operator > + overloads to consider. This parameter is used when instantiating a > + dependent operator expression and has the same structure as > + DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS. */ > > static tree > add_operator_candidates (z_candidate **candidates, > tree_code code, tree_code code2, > - vec *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; > releasing_vec arglist; > @@ -6552,7 +6565,7 @@ build_new_op (const op_location_t &loc, enum tree_code code, int flags, > p = conversion_obstack_alloc (0); > > result = add_operator_candidates (&candidates, code, code2, arglist, > - flags, complain); > + lookups, flags, complain); > if (result == error_mark_node) > goto user_defined_result_ready; > > @@ -6608,7 +6621,7 @@ build_new_op (const op_location_t &loc, enum tree_code code, int flags, > else > code = PREDECREMENT_EXPR; > result = build_new_op (loc, code, flags, arg1, NULL_TREE, > - NULL_TREE, overload, complain); > + NULL_TREE, lookups, overload, complain); > break; > > /* The caller will deal with these. */ > @@ -6765,7 +6778,7 @@ build_new_op (const op_location_t &loc, enum tree_code code, int flags, > warning_sentinel ws (warn_zero_as_null_pointer_constant); > result = build_new_op (loc, code, > LOOKUP_NORMAL|LOOKUP_REWRITTEN, > - lhs, rhs, NULL_TREE, > + lhs, rhs, NULL_TREE, lookups, > NULL, complain); > } > break; > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc > index 566f4e38fac..8e25ae23670 100644 > --- a/gcc/cp/constraint.cc > +++ b/gcc/cp/constraint.cc > @@ -202,15 +202,8 @@ finish_constraint_binary_op (location_t loc, > return error_mark_node; > if (!check_constraint_operands (loc, lhs, rhs)) > return error_mark_node; > - tree overload; > - cp_expr expr = build_x_binary_op (loc, code, > - lhs, TREE_CODE (lhs), > - rhs, TREE_CODE (rhs), > - &overload, tf_none); > - /* When either operand is dependent, the overload set may be non-empty. */ > - if (expr == error_mark_node) > - return error_mark_node; > - expr.set_location (loc); > + cp_expr expr > + = build_min_nt_loc (loc, code, lhs.get_value (), rhs.get_value ()); > expr.set_range (lhs.get_start (), rhs.get_finish ()); > return expr; > } > diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc > index 9017902e6fb..c00672eeb6e 100644 > --- a/gcc/cp/coroutines.cc > +++ b/gcc/cp/coroutines.cc > @@ -912,7 +912,7 @@ build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind) > if (MAYBE_CLASS_TYPE_P (TREE_TYPE (a))) > { > o = build_new_op (loc, CO_AWAIT_EXPR, LOOKUP_NORMAL, a, NULL_TREE, > - NULL_TREE, NULL, tf_warning_or_error); > + NULL_TREE, NULL_TREE, NULL, tf_warning_or_error); > /* If no viable functions are found, o is a. */ > if (!o || o == error_mark_node) > o = a; > diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c > index 38eae881f0c..36e04cdee5e 100644 > --- a/gcc/cp/cp-objcp-common.c > +++ b/gcc/cp/cp-objcp-common.c > @@ -484,6 +484,7 @@ cp_common_init_ts (void) > /* New Types. */ > MARK_TS_TYPE_COMMON (UNBOUND_CLASS_TEMPLATE); > MARK_TS_TYPE_COMMON (TYPE_ARGUMENT_PACK); > + MARK_TS_TYPE_COMMON (DEPENDENT_OPERATOR_TYPE); > > MARK_TS_TYPE_NON_COMMON (DECLTYPE_TYPE); > MARK_TS_TYPE_NON_COMMON (TYPENAME_TYPE); > diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def > index 725139bb457..6fb838cc850 100644 > --- a/gcc/cp/cp-tree.def > +++ b/gcc/cp/cp-tree.def > @@ -476,6 +476,11 @@ DEFTREECODE (UNDERLYING_TYPE, "underlying_type", tcc_type, 0) > BASES_TYPE is the type in question. */ > DEFTREECODE (BASES, "bases", tcc_type, 0) > > +/* Dependent operator expressions are given this type rather than a NULL_TREE > + type so that we have somewhere to stash the result of phase 1 name lookup > + (namely into DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS). */ > +DEFTREECODE (DEPENDENT_OPERATOR_TYPE, "dependent_operator_type", tcc_type, 0) > + > /* Used to represent the template information stored by template > specializations. > The accessors are: > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > index 7f32cf56383..57a0da4e0ef 100644 > --- a/gcc/cp/cp-tree.h > +++ b/gcc/cp/cp-tree.h > @@ -2185,7 +2185,8 @@ enum languages { lang_c, lang_cplusplus }; > || TREE_CODE (T) == TYPENAME_TYPE \ > || TREE_CODE (T) == TYPEOF_TYPE \ > || TREE_CODE (T) == BOUND_TEMPLATE_TEMPLATE_PARM \ > - || TREE_CODE (T) == DECLTYPE_TYPE) > + || TREE_CODE (T) == DECLTYPE_TYPE \ > + || TREE_CODE (T) == DEPENDENT_OPERATOR_TYPE) > > /* Nonzero if T is a class (or struct or union) type. Also nonzero > for template type parameters, typename types, and instantiated > @@ -3978,9 +3979,13 @@ struct GTY(()) lang_decl { > TREE_LANG_FLAG_0 (FOLD_EXPR_CHECK (NODE)) > > /* An INTEGER_CST containing the tree code of the folded operator. */ > -#define FOLD_EXPR_OP(NODE) \ > +#define FOLD_EXPR_OP_RAW(NODE) \ > TREE_OPERAND (FOLD_EXPR_CHECK (NODE), 0) > > +/* The tree code of the folded operator. */ > +#define FOLD_EXPR_OP(NODE) \ > + ((enum tree_code) TREE_INT_CST_LOW (FOLD_EXPR_OP_RAW (NODE))) > + > /* The expression containing an unexpanded parameter pack. */ > #define FOLD_EXPR_PACK(NODE) \ > TREE_OPERAND (FOLD_EXPR_CHECK (NODE), 1) > @@ -4035,6 +4040,24 @@ struct GTY(()) lang_decl { > #define CALL_EXPR_OPERATOR_SYNTAX(NODE) \ > TREE_LANG_FLAG_6 (CALL_OR_AGGR_INIT_CHECK (NODE)) > > +/* A TREE_LIST containing the result of phase 1 name lookup of the operator > + overloads that are pertinent to the dependent operator expression whose > + type is NODE. Each TREE_PURPOSE is an IDENTIFIER_NODE and TREE_VALUE is > + the corresponding (possibly empty) lookup result. The TREE_TYPE of the > + first TREE_LIST node points back to NODE. */ > +#define DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS(NODE) \ > + TYPE_VALUES_RAW (DEPENDENT_OPERATOR_TYPE_CHECK (NODE)) > + > +/* Guarded helper for the above accessor macro that takes a (templated) > + operator expression instead of the type thereof. */ > +inline tree > +TEMPLATED_OPERATOR_SAVED_LOOKUPS (tree t) > +{ > + return TREE_TYPE (EXPR_CHECK (t)) && TREE_CODE (TREE_TYPE (t)) == DEPENDENT_OPERATOR_TYPE > + ? DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS (TREE_TYPE (t)) > + : NULL_TREE; > +} > + > /* Indicates whether a string literal has been parenthesized. Such > usages are disallowed in certain circumstances. */ > > @@ -6464,14 +6487,15 @@ extern tree build_special_member_call (tree, tree, > tree, int, tsubst_flags_t); > extern tree build_new_op (const op_location_t &, > enum tree_code, > - int, tree, tree, tree, tree *, > - tsubst_flags_t); > + int, tree, tree, tree, tree, > + tree *, tsubst_flags_t); > /* Wrapper that leaves out the usually-null op3 and overload parms. */ > inline tree build_new_op (const op_location_t &loc, enum tree_code code, > int flags, tree arg1, tree arg2, > tsubst_flags_t complain) > { > - return build_new_op (loc, code, flags, arg1, arg2, NULL_TREE, NULL, complain); > + return build_new_op (loc, code, flags, arg1, arg2, NULL_TREE, NULL_TREE, > + NULL, complain); > } > extern tree build_op_call (tree, vec **, > tsubst_flags_t); > @@ -7875,8 +7899,9 @@ extern tree build_class_member_access_expr (cp_expr, tree, tree, bool, > extern tree finish_class_member_access_expr (cp_expr, tree, bool, > tsubst_flags_t); > extern tree lookup_destructor (tree, tree, tree, tsubst_flags_t); > +extern tree build_dependent_operator_type (tree, enum tree_code, bool); > extern tree build_x_indirect_ref (location_t, tree, > - ref_operator, > + ref_operator, tree, > tsubst_flags_t); > extern tree cp_build_indirect_ref (location_t, tree, > ref_operator, > @@ -7894,20 +7919,20 @@ extern tree cp_build_function_call_vec (tree, vec **, > extern tree build_x_binary_op (const op_location_t &, > enum tree_code, tree, > enum tree_code, tree, > - enum tree_code, tree *, > - tsubst_flags_t); > + enum tree_code, tree, > + tree *, tsubst_flags_t); > inline tree build_x_binary_op (const op_location_t &loc, > enum tree_code code, tree arg1, tree arg2, > tsubst_flags_t complain) > { > return build_x_binary_op (loc, code, arg1, TREE_CODE (arg1), arg2, > - TREE_CODE (arg2), NULL, complain); > + TREE_CODE (arg2), NULL_TREE, NULL, complain); > } > extern tree build_x_array_ref (location_t, tree, tree, > tsubst_flags_t); > extern tree build_x_unary_op (location_t, > enum tree_code, cp_expr, > - tsubst_flags_t); > + tree, tsubst_flags_t); > extern tree cp_build_addressof (location_t, tree, > tsubst_flags_t); > extern tree cp_build_addr_expr (tree, tsubst_flags_t); > @@ -7922,7 +7947,7 @@ extern tree build_x_compound_expr_from_list (tree, expr_list_kind, > extern tree build_x_compound_expr_from_vec (vec *, > const char *, tsubst_flags_t); > extern tree build_x_compound_expr (location_t, tree, tree, > - tsubst_flags_t); > + tree, tsubst_flags_t); > extern tree build_compound_expr (location_t, tree, tree); > extern tree cp_build_compound_expr (tree, tree, tsubst_flags_t); > extern tree build_static_cast (location_t, tree, tree, > @@ -7938,7 +7963,7 @@ extern tree cp_build_c_cast (location_t, tree, tree, > tsubst_flags_t); > extern cp_expr build_x_modify_expr (location_t, tree, > enum tree_code, tree, > - tsubst_flags_t); > + tree, tsubst_flags_t); > extern tree cp_build_modify_expr (location_t, tree, > enum tree_code, tree, > tsubst_flags_t); > diff --git a/gcc/cp/cxx-pretty-print.c b/gcc/cp/cxx-pretty-print.c > index 3ea357deb80..6af009c6890 100644 > --- a/gcc/cp/cxx-pretty-print.c > +++ b/gcc/cp/cxx-pretty-print.c > @@ -2541,8 +2541,8 @@ pp_cxx_addressof_expression (cxx_pretty_printer *pp, tree t) > static char const* > get_fold_operator (tree t) > { > - int op = int_cst_value (FOLD_EXPR_OP (t)); > - ovl_op_info_t *info = OVL_OP_INFO (FOLD_EXPR_MODIFY_P (t), op); > + ovl_op_info_t *info = OVL_OP_INFO (FOLD_EXPR_MODIFY_P (t), > + FOLD_EXPR_OP (t)); > return info->name; > } > > diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c > index 7c2048c6acb..24dd6701663 100644 > --- a/gcc/cp/decl.c > +++ b/gcc/cp/decl.c > @@ -17098,8 +17098,6 @@ start_preparsed_function (tree decl1, tree attrs, int flags) > > store_parm_decls (current_function_parms); > > - push_operator_bindings (); > - > if (!processing_template_decl > && (flag_lifetime_dse > 1) > && DECL_CONSTRUCTOR_P (decl1) > diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c > index 99f5dc784b7..062c175430b 100644 > --- a/gcc/cp/decl2.c > +++ b/gcc/cp/decl2.c > @@ -417,7 +417,8 @@ grok_array_decl (location_t loc, tree array_expr, tree index_exp, > { > if (index_exp) > expr = build_new_op (loc, ARRAY_REF, LOOKUP_NORMAL, array_expr, > - index_exp, NULL_TREE, &overload, complain); > + index_exp, NULL_TREE, NULL_TREE, > + &overload, complain); > else if ((*index_exp_list)->is_empty ()) > expr = build_op_subscript (loc, array_expr, index_exp_list, &overload, > complain); > @@ -431,7 +432,7 @@ grok_array_decl (location_t loc, tree array_expr, tree index_exp, > tf_none); > if (idx != error_mark_node) > expr = build_new_op (loc, ARRAY_REF, LOOKUP_NORMAL, array_expr, > - idx, NULL_TREE, &overload, > + idx, NULL_TREE, NULL_TREE, &overload, > complain & tf_decltype); > if (expr == error_mark_node) > { > diff --git a/gcc/cp/method.c b/gcc/cp/method.c > index 935946f5eef..44439bae4ec 100644 > --- a/gcc/cp/method.c > +++ b/gcc/cp/method.c > @@ -1372,7 +1372,7 @@ do_one_comp (location_t loc, const comp_info &info, tree sub, tree lhs, tree rhs > to 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 *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 *for_block, > if (auto_node) > { > tree t = build_x_indirect_ref (input_location, begin, RO_UNARY_STAR, > - tf_none); > + NULL_TREE, tf_none); > if (!error_operand_p (t)) > TREE_TYPE (orig_decl) = do_auto_deduction (TREE_TYPE (orig_decl), > t, auto_node); > @@ -42073,7 +42075,7 @@ cp_finish_omp_range_for (tree orig, tree begin) > /* The declaration is initialized with *__begin inside the loop body. */ > cp_finish_decl (decl, > build_x_indirect_ref (input_location, begin, RO_UNARY_STAR, > - tf_warning_or_error), > + NULL_TREE, tf_warning_or_error), > /*is_constant_init*/false, NULL_TREE, > LOOKUP_ONLYCONVERTING); > if (VAR_P (decl) && DECL_DECOMPOSITION_P (decl)) > diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c > index 42133a30c97..60cc23ba987 100644 > --- a/gcc/cp/pt.c > +++ b/gcc/cp/pt.c > @@ -12640,23 +12640,26 @@ expand_empty_fold (tree t, tsubst_flags_t complain) > static tree > fold_expression (tree t, tree left, tree right, tsubst_flags_t complain) > { > - tree op = FOLD_EXPR_OP (t); > - tree_code code = (tree_code)TREE_INT_CST_LOW (op); > + tree_code code = FOLD_EXPR_OP (t); > + > + tree lookups = TEMPLATED_OPERATOR_SAVED_LOOKUPS (t); > > // Handle compound assignment operators. > if (FOLD_EXPR_MODIFY_P (t)) > - return build_x_modify_expr (input_location, left, code, right, complain); > + return build_x_modify_expr (input_location, left, code, right, > + lookups, complain); > > warning_sentinel s(warn_parentheses); > switch (code) > { > case COMPOUND_EXPR: > - return build_x_compound_expr (input_location, left, right, complain); > + return build_x_compound_expr (input_location, left, right, > + lookups, complain); > default: > return build_x_binary_op (input_location, code, > left, TREE_CODE (left), > right, TREE_CODE (right), > - /*overload=*/NULL, > + lookups, /*overload=*/NULL, > complain); > } > } > @@ -17891,7 +17894,7 @@ tsubst_omp_for_iterator (tree t, int i, tree declv, tree &orig_declv, > tree lhs = RECUR (TREE_OPERAND (incr, 0)); > tree rhs = RECUR (TREE_OPERAND (incr, 1)); > incr = build_x_modify_expr (EXPR_LOCATION (incr), lhs, > - NOP_EXPR, rhs, complain); > + NOP_EXPR, rhs, NULL_TREE, complain); > } > else > incr = RECUR (incr); > @@ -19204,6 +19207,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl, > RETURN (RECUR (TREE_OPERAND (t, 1))); > RETURN (build_x_compound_expr (EXPR_LOCATION (t), tmp, > RECUR (TREE_OPERAND (t, 1)), > + TEMPLATED_OPERATOR_SAVED_LOOKUPS (t), > complain)); > > case ANNOTATE_EXPR: > @@ -19855,6 +19859,7 @@ tsubst_copy_and_build (tree t, > } > else > r = build_x_indirect_ref (input_location, r, RO_UNARY_STAR, > + TEMPLATED_OPERATOR_SAVED_LOOKUPS (t), > complain|decltype_flag); > > if (REF_PARENTHESIZED_P (t)) > @@ -19965,6 +19970,7 @@ tsubst_copy_and_build (tree t, > op1 = tsubst_non_call_postfix_expression (TREE_OPERAND (t, 0), > args, complain, in_decl); > RETURN (build_x_unary_op (input_location, TREE_CODE (t), op1, > + TEMPLATED_OPERATOR_SAVED_LOOKUPS (t), > complain|decltype_flag)); > > case PREDECREMENT_EXPR: > @@ -19978,6 +19984,7 @@ tsubst_copy_and_build (tree t, > case IMAGPART_EXPR: > RETURN (build_x_unary_op (input_location, TREE_CODE (t), > RECUR (TREE_OPERAND (t, 0)), > + TEMPLATED_OPERATOR_SAVED_LOOKUPS (t), > complain|decltype_flag)); > > case FIX_TRUNC_EXPR: > @@ -19996,6 +20003,7 @@ tsubst_copy_and_build (tree t, > op1 = tsubst_non_call_postfix_expression (op1, args, complain, > in_decl); > RETURN (build_x_unary_op (input_location, ADDR_EXPR, op1, > + TEMPLATED_OPERATOR_SAVED_LOOKUPS (t), > complain|decltype_flag)); > > case PLUS_EXPR: > @@ -20060,6 +20068,7 @@ tsubst_copy_and_build (tree t, > (warning_suppressed_p (TREE_OPERAND (t, 1)) > ? ERROR_MARK > : TREE_CODE (TREE_OPERAND (t, 1))), > + TEMPLATED_OPERATOR_SAVED_LOOKUPS (t), > /*overload=*/NULL, > complain|decltype_flag); > if (EXPR_P (r)) > @@ -20212,8 +20221,10 @@ tsubst_copy_and_build (tree t, > warning_sentinel s(warn_div_by_zero); > tree lhs = RECUR (TREE_OPERAND (t, 0)); > tree rhs = RECUR (TREE_OPERAND (t, 2)); > + > tree r = build_x_modify_expr > (EXPR_LOCATION (t), lhs, TREE_CODE (TREE_OPERAND (t, 1)), rhs, > + TEMPLATED_OPERATOR_SAVED_LOOKUPS (t), > complain|decltype_flag); > /* TREE_NO_WARNING must be set if either the expression was > parenthesized or it uses an operator such as >>= rather > @@ -20314,6 +20325,7 @@ tsubst_copy_and_build (tree t, > RETURN (build_x_compound_expr (EXPR_LOCATION (t), > op0, > RECUR (TREE_OPERAND (t, 1)), > + TEMPLATED_OPERATOR_SAVED_LOOKUPS (t), > complain|decltype_flag)); > } > > @@ -26994,6 +27006,9 @@ dependent_type_p_r (tree type) > if (TREE_CODE (type) == TYPE_PACK_EXPANSION) > return true; > > + if (TREE_CODE (type) == DEPENDENT_OPERATOR_TYPE) > + return true; > + > if (any_dependent_type_attributes_p (TYPE_ATTRIBUTES (type))) > return true; > > diff --git a/gcc/cp/ptree.c b/gcc/cp/ptree.c > index d514aa2cad2..f7ddae77679 100644 > --- a/gcc/cp/ptree.c > +++ b/gcc/cp/ptree.c > @@ -151,6 +151,12 @@ cxx_print_type (FILE *file, tree node, int indent) > print_node (file, "expr", DECLTYPE_TYPE_EXPR (node), indent + 4); > return; > > + case DEPENDENT_OPERATOR_TYPE: > + print_node (file, "saved_lookups", > + DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS (node), > + indent + 4); > + return; > + > case TYPENAME_TYPE: > print_node (file, "fullname", TYPENAME_TYPE_FULLNAME (node), > indent + 4); > diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c > index 356fb83200c..6603066c620 100644 > --- a/gcc/cp/semantics.c > +++ b/gcc/cp/semantics.c > @@ -2920,7 +2920,7 @@ finish_increment_expr (cp_expr expr, enum tree_code code) > expr.get_start (), > get_finish (input_location)); > cp_expr result = build_x_unary_op (combined_loc, code, expr, > - tf_warning_or_error); > + NULL_TREE, tf_warning_or_error); > /* TODO: build_x_unary_op doesn't honor the location, so set it here. */ > result.set_location (combined_loc); > return result; > @@ -3031,7 +3031,8 @@ finish_unary_op_expr (location_t op_loc, enum tree_code code, cp_expr expr, > of the operator token to the end of EXPR. */ > location_t combined_loc = make_location (op_loc, > op_loc, expr.get_finish ()); > - cp_expr result = build_x_unary_op (combined_loc, code, expr, complain); > + cp_expr result = build_x_unary_op (combined_loc, code, expr, > + NULL_TREE, complain); > /* TODO: build_x_unary_op doesn't always honor the location. */ > result.set_location (combined_loc); > > @@ -9884,7 +9885,7 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, > TREE_CODE (cond), > iter, ERROR_MARK, > TREE_OPERAND (cond, 1), ERROR_MARK, > - NULL, tf_warning_or_error); > + NULL_TREE, NULL, tf_warning_or_error); > if (error_operand_p (tem)) > return true; > } > @@ -9898,9 +9899,10 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, > error_at (elocus, "invalid controlling predicate"); > return true; > } > - diff = build_x_binary_op (elocus, MINUS_EXPR, TREE_OPERAND (cond, 1), > - ERROR_MARK, iter, ERROR_MARK, NULL, > - tf_warning_or_error); > + diff = build_x_binary_op (elocus, MINUS_EXPR, > + TREE_OPERAND (cond, 1), ERROR_MARK, > + iter, ERROR_MARK, > + NULL_TREE, NULL, tf_warning_or_error); > diff = cp_fully_fold (diff); > if (error_operand_p (diff)) > return true; > @@ -9928,7 +9930,7 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, > } > iter_incr = build_x_unary_op (EXPR_LOCATION (incr), > TREE_CODE (incr), iter, > - tf_warning_or_error); > + NULL_TREE, tf_warning_or_error); > if (error_operand_p (iter_incr)) > return true; > else if (TREE_CODE (incr) == PREINCREMENT_EXPR > @@ -9954,6 +9956,7 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, > iter_incr = build_x_modify_expr (EXPR_LOCATION (rhs), > iter, TREE_CODE (rhs), > TREE_OPERAND (rhs, 1), > + NULL_TREE, > tf_warning_or_error); > if (error_operand_p (iter_incr)) > return true; > @@ -9983,13 +9986,13 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, > PLUS_EXPR, > TREE_OPERAND (rhs, 0), > ERROR_MARK, iter, > - ERROR_MARK, NULL, > + ERROR_MARK, NULL_TREE, NULL, > tf_warning_or_error); > if (error_operand_p (iter_incr)) > return true; > iter_incr = build_x_modify_expr (EXPR_LOCATION (rhs), > iter, NOP_EXPR, > - iter_incr, > + iter_incr, NULL_TREE, > tf_warning_or_error); > if (error_operand_p (iter_incr)) > return true; > @@ -10100,7 +10103,7 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, > if (init != NULL) > finish_expr_stmt (build_x_modify_expr (elocus, > iter, NOP_EXPR, init, > - tf_warning_or_error)); > + NULL_TREE, tf_warning_or_error)); > init = build_int_cst (TREE_TYPE (diff), 0); > if (c && iter_incr == NULL > && (!ordered || (i < collapse && collapse > 1))) > @@ -10109,23 +10112,24 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, > { > finish_expr_stmt (build_x_modify_expr (elocus, > incr_var, NOP_EXPR, > - incr, tf_warning_or_error)); > + incr, NULL_TREE, > + tf_warning_or_error)); > incr = incr_var; > } > iter_incr = build_x_modify_expr (elocus, > iter, PLUS_EXPR, incr, > - tf_warning_or_error); > + NULL_TREE, tf_warning_or_error); > } > if (c && ordered && i < collapse && collapse > 1) > iter_incr = incr; > finish_expr_stmt (build_x_modify_expr (elocus, > last, NOP_EXPR, init, > - tf_warning_or_error)); > + NULL_TREE, tf_warning_or_error)); > if (diffvar) > { > finish_expr_stmt (build_x_modify_expr (elocus, > diffvar, NOP_EXPR, > - diff, tf_warning_or_error)); > + diff, NULL_TREE, tf_warning_or_error)); > diff = diffvar; > } > *pre_body = pop_stmt_list (*pre_body); > @@ -10141,13 +10145,13 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, > iter_init = build2 (MINUS_EXPR, TREE_TYPE (diff), decl, last); > iter_init = build_x_modify_expr (elocus, > iter, PLUS_EXPR, iter_init, > - tf_warning_or_error); > + NULL_TREE, tf_warning_or_error); > if (iter_init != error_mark_node) > iter_init = build1 (NOP_EXPR, void_type_node, iter_init); > finish_expr_stmt (iter_init); > finish_expr_stmt (build_x_modify_expr (elocus, > last, NOP_EXPR, decl, > - tf_warning_or_error)); > + NULL_TREE, tf_warning_or_error)); > add_stmt (orig_body); > *body = pop_stmt_list (*body); > > @@ -10165,7 +10169,7 @@ handle_omp_for_class_iterator (int i, location_t locus, enum tree_code code, > iter_init = build2 (MINUS_EXPR, TREE_TYPE (diff), iter_init, last); > iter_init = build_x_modify_expr (elocus, > iter, PLUS_EXPR, iter_init, > - tf_warning_or_error); > + NULL_TREE, tf_warning_or_error); > if (iter_init != error_mark_node) > iter_init = build1 (NOP_EXPR, void_type_node, iter_init); > finish_expr_stmt (iter_init); > @@ -10876,7 +10880,7 @@ finish_omp_cancel (tree clauses) > ifc = build_x_binary_op (OMP_CLAUSE_LOCATION (ifc), NE_EXPR, > OMP_CLAUSE_IF_EXPR (ifc), ERROR_MARK, > integer_zero_node, ERROR_MARK, > - NULL, tf_warning_or_error); > + NULL_TREE, NULL, tf_warning_or_error); > } > else > ifc = boolean_true_node; > @@ -12128,6 +12132,9 @@ finish_unary_fold_expr (tree expr, int op, tree_code dir) > tree code = build_int_cstu (integer_type_node, abs (op)); > tree fold = build_min_nt_loc (UNKNOWN_LOCATION, dir, code, pack); > FOLD_EXPR_MODIFY_P (fold) = (op < 0); > + TREE_TYPE (fold) = build_dependent_operator_type (NULL_TREE, > + FOLD_EXPR_OP (fold), > + FOLD_EXPR_MODIFY_P (fold)); > return fold; > } > > @@ -12154,6 +12161,9 @@ finish_binary_fold_expr (tree pack, tree init, int op, tree_code dir) > tree code = build_int_cstu (integer_type_node, abs (op)); > tree fold = build_min_nt_loc (UNKNOWN_LOCATION, dir, code, pack, init); > FOLD_EXPR_MODIFY_P (fold) = (op < 0); > + TREE_TYPE (fold) = build_dependent_operator_type (NULL_TREE, > + FOLD_EXPR_OP (fold), > + FOLD_EXPR_MODIFY_P (fold)); > return fold; > } > > diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c > index 284fb5f4b2a..29f3c171606 100644 > --- a/gcc/cp/tree.c > +++ b/gcc/cp/tree.c > @@ -5975,8 +5975,6 @@ cp_free_lang_data (tree t) > DECL_EXTERNAL (t) = 1; > TREE_STATIC (t) = 0; > } > - if (TREE_CODE (t) == FUNCTION_DECL) > - discard_operator_bindings (t); > if (TREE_CODE (t) == NAMESPACE_DECL) > /* We do not need the leftover chaining of namespaces from the > binding level. */ > diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c > index 4e60db40c76..d73c7fb6e33 100644 > --- a/gcc/cp/typeck.c > +++ b/gcc/cp/typeck.c > @@ -2602,6 +2602,7 @@ rationalize_conditional_expr (enum tree_code code, tree t, > ? LE_EXPR : GE_EXPR), > op0, TREE_CODE (op0), > op1, TREE_CODE (op1), > + NULL_TREE, > /*overload=*/NULL, > complain), > cp_build_unary_op (code, op0, false, complain), > @@ -3487,6 +3488,67 @@ build_ptrmemfunc_access_expr (tree ptrmem, tree member_name) > return build_simple_component_ref (ptrmem, member); > } > > +/* Return a TREE_LIST of namespace-scope overloads for the given operator, > + and for any other relevant operator. */ > + > +static tree > +op_unqualified_lookup (tree_code code, bool is_assign) > +{ > + tree lookups = NULL_TREE; > + > + if (cxx_dialect >= cxx20 && !is_assign) > + { > + if (code == NE_EXPR) > + { > + /* != can get rewritten in terms of ==. */ > + tree fnname = ovl_op_identifier (false, EQ_EXPR); > + if (tree fns = lookup_name (fnname, LOOK_where::BLOCK_NAMESPACE)) > + lookups = tree_cons (fnname, fns, lookups); > + } > + else if (code == GT_EXPR || code == LE_EXPR > + || code == LT_EXPR || code == GE_EXPR) > + { > + /* These can get rewritten in terms of <=>. */ > + tree fnname = ovl_op_identifier (false, SPACESHIP_EXPR); > + if (tree fns = lookup_name (fnname, LOOK_where::BLOCK_NAMESPACE)) > + lookups = tree_cons (fnname, fns, lookups); > + } > + } > + > + tree fnname = ovl_op_identifier (is_assign, code); > + if (tree fns = lookup_name (fnname, LOOK_where::BLOCK_NAMESPACE)) > + lookups = tree_cons (fnname, fns, lookups); > + > + if (lookups) > + return lookups; > + else > + return build_tree_list (NULL_TREE, NULL_TREE); > +} > + > +/* Create a DEPENDENT_OPERATOR_TYPE for a dependent operator expression of > + the given operator. LOOKUPS, if non-NULL, is the result of phase 1 > + name lookup for the given operator. */ > + > +tree > +build_dependent_operator_type (tree lookups, tree_code code, bool is_assign) > +{ > + if (lookups) > + /* We're partially instantiating a dependent operator expression, and > + LOOKUPS contains the result of phase 1 name lookup that we performed > + earlier at template definition time, so just reuse the corresponding > + DEPENDENT_OPERATOR_TYPE. */ > + return TREE_TYPE (lookups); > + > + /* Otherwise we're processing a dependent operator expression at template > + definition time, so perform phase 1 name lookup now. */ > + lookups = op_unqualified_lookup (code, is_assign); > + > + tree type = cxx_make_type (DEPENDENT_OPERATOR_TYPE); > + DEPENDENT_OPERATOR_TYPE_SAVED_LOOKUPS (type) = lookups; > + TREE_TYPE (lookups) = type; > + return type; > +} > + > /* Given an expression PTR for a pointer, return an expression > for the value pointed to. > ERRORSTRING is the name of the operator to appear in error messages. > @@ -3496,7 +3558,7 @@ build_ptrmemfunc_access_expr (tree ptrmem, tree member_name) > > tree > build_x_indirect_ref (location_t loc, tree expr, ref_operator errorstring, > - tsubst_flags_t complain) > + tree lookups, tsubst_flags_t complain) > { > tree orig_expr = expr; > tree rval; > @@ -3516,12 +3578,18 @@ build_x_indirect_ref (location_t loc, tree expr, ref_operator errorstring, > return build_min (INDIRECT_REF, TREE_TYPE (TREE_TYPE (expr)), expr); > } > if (type_dependent_expression_p (expr)) > - return build_min_nt_loc (loc, INDIRECT_REF, expr); > + { > + expr = build_min_nt_loc (loc, INDIRECT_REF, expr); > + TREE_TYPE (expr) > + = build_dependent_operator_type (lookups, INDIRECT_REF, false); > + return expr; > + } > expr = build_non_dependent_expr (expr); > } > > rval = build_new_op (loc, INDIRECT_REF, LOOKUP_NORMAL, expr, > - NULL_TREE, NULL_TREE, &overload, complain); > + NULL_TREE, NULL_TREE, lookups, > + &overload, complain); > if (!rval) > rval = cp_build_indirect_ref (loc, expr, errorstring, complain); > > @@ -4458,8 +4526,8 @@ convert_arguments (tree typelist, vec **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 *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 *vec, const char *msg, > > tree > build_x_compound_expr (location_t loc, tree op1, tree op2, > - tsubst_flags_t complain) > + tree lookups, tsubst_flags_t complain) > { > tree result; > tree orig_op1 = op1; > @@ -7566,7 +7636,8 @@ build_x_compound_expr (location_t loc, tree op1, tree op2, > || type_dependent_expression_p (op2)) > { > result = build_min_nt_loc (loc, COMPOUND_EXPR, op1, op2); > - maybe_save_operator_binding (result); > + TREE_TYPE (result) > + = build_dependent_operator_type (lookups, COMPOUND_EXPR, false); > return result; > } > op1 = build_non_dependent_expr (op1); > @@ -7574,7 +7645,7 @@ build_x_compound_expr (location_t loc, tree op1, tree op2, > } > > result = build_new_op (loc, COMPOUND_EXPR, LOOKUP_NORMAL, op1, op2, > - NULL_TREE, &overload, complain); > + NULL_TREE, lookups, &overload, complain); > if (!result) > result = cp_build_compound_expr (op1, op2, complain); > > @@ -9017,8 +9088,8 @@ cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, > { > result = build_new_op (input_location, MODIFY_EXPR, > LOOKUP_NORMAL, lhs, rhs, > - make_node (NOP_EXPR), /*overload=*/NULL, > - complain); > + make_node (NOP_EXPR), NULL_TREE, > + /*overload=*/NULL, complain); > if (result == NULL_TREE) > return error_mark_node; > goto ret; > @@ -9233,7 +9304,7 @@ cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, > > cp_expr > build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, > - tree rhs, tsubst_flags_t complain) > + tree rhs, tree lookups, tsubst_flags_t complain) > { > tree orig_lhs = lhs; > tree orig_rhs = rhs; > @@ -9250,7 +9321,9 @@ build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, > { > tree op = build_min_nt_loc (loc, modifycode, NULL_TREE, NULL_TREE); > tree rval = build_min_nt_loc (loc, MODOP_EXPR, lhs, op, rhs); > - maybe_save_operator_binding (rval); > + if (modifycode != NOP_EXPR) > + TREE_TYPE (rval) > + = build_dependent_operator_type (lookups, modifycode, true); > return rval; > } > > @@ -9262,7 +9335,7 @@ build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, > { > tree op = build_nt (modifycode, NULL_TREE, NULL_TREE); > tree rval = build_new_op (loc, MODIFY_EXPR, LOOKUP_NORMAL, > - lhs, rhs, op, &overload, complain); > + lhs, rhs, op, lookups, &overload, complain); > if (rval) > { > if (rval == error_mark_node) > diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c > index 3fb651a02ba..724684c0457 100644 > --- a/gcc/cp/typeck2.c > +++ b/gcc/cp/typeck2.c > @@ -1956,7 +1956,7 @@ build_x_arrow (location_t loc, tree expr, tsubst_flags_t complain) > > while ((expr = build_new_op (loc, COMPONENT_REF, > LOOKUP_NORMAL, expr, NULL_TREE, NULL_TREE, > - &fn, complain))) > + NULL_TREE, &fn, complain))) > { > if (expr == error_mark_node) > return error_mark_node; > diff --git a/gcc/testsuite/g++.dg/lookup/operator-3-ops.h b/gcc/testsuite/g++.dg/lookup/operator-3-ops.h > new file mode 100644 > index 00000000000..fbd242a4e66 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/lookup/operator-3-ops.h > @@ -0,0 +1,53 @@ > +void operator+(N::A); > +void operator-(N::A); > +void operator*(N::A); > +void operator~(N::A); > +#if __cplusplus >= 201103L > +void operator&(N::A) = delete; > +#else > +void operator&(N::A); > +#endif > +void operator!(N::A); > +void operator++(N::A); > +void operator--(N::A); > +void operator++(N::A, int); > +void operator--(N::A, int); > + > +void operator->*(N::A, N::A); > +void operator/(N::A, N::A); > +void operator*(N::A, N::A); > +void operator+(N::A, N::A); > +void operator-(N::A, N::A); > +void operator%(N::A, N::A); > +void operator&(N::A, N::A); > +void operator|(N::A, N::A); > +void operator^(N::A, N::A); > +void operator<<(N::A, N::A); > +void operator>>(N::A, N::A); > +void operator&&(N::A, N::A); > +void operator||(N::A, N::A); > +#if __cplusplus >= 201103L > +void operator,(N::A, N::A) = delete; > +#else > +void operator,(N::A, N::A); > +#endif > + > +void operator==(N::A, N::A); > +void operator!=(N::A, N::A); > +void operator<(N::A, N::A); > +void operator>(N::A, N::A); > +void operator<=(N::A, N::A); > +void operator>=(N::A, N::A); > +#if __cplusplus > 201703L > +void operator<=>(N::A, N::A); > +#endif > + > +void operator+=(N::A, N::A); > +void operator-=(N::A, N::A); > +void operator*=(N::A, N::A); > +void operator/=(N::A, N::A); > +void operator%=(N::A, N::A); > +void operator|=(N::A, N::A); > +void operator^=(N::A, N::A); > +void operator<<=(N::A, N::A); > +void operator>>=(N::A, N::A); > diff --git a/gcc/testsuite/g++.dg/lookup/operator-3.C b/gcc/testsuite/g++.dg/lookup/operator-3.C > index bc5eb3d6693..98011efd543 100644 > --- a/gcc/testsuite/g++.dg/lookup/operator-3.C > +++ b/gcc/testsuite/g++.dg/lookup/operator-3.C > @@ -1,4 +1,6 @@ > // PR c++/51577 > +// Verify we don't consider later-declared namespace-scope operator overloads > +// when instantiating a dependent operator expression at block scope. > > template 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..01adff963dc > --- /dev/null > +++ b/gcc/testsuite/g++.dg/lookup/operator-8.C > @@ -0,0 +1,34 @@ > +// Verify phase 1 lookup works properly for rewritten non-dependent conditional > +// operator expressions. > + > +// This test currently fails due to build_min_non_dep_op_overload not knowing > +// how to handle rewritten operator expressions; see the FIXME in build_new_op. > + > +// { dg-do compile { target c++20 } } > + > +#include > + > +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; > } >