From patchwork Thu Jan 11 22:01:48 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jason Merrill X-Patchwork-Id: 1885799 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=A2qdDl5H; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4T9zDL2bfTz1yPf for ; Fri, 12 Jan 2024 09:02:26 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 21CD8385DC06 for ; Thu, 11 Jan 2024 22:02:24 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 3D1213858D33 for ; Thu, 11 Jan 2024 22:01:57 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 3D1213858D33 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 3D1213858D33 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1705010520; cv=none; b=EvAZnvf/rv0bjlQNulfMnzhkD1neLoZKAE2j3sD/Uokkisfsie1q31C6Hyl+cO0mD5nXCKl8IlzMTPXYWX8ixacXYtMRLM/8h8qKE39at/XQQsfGEQ55IsdjDWIrSe2Dcy9u1Kk+3KzhDLYX1FOQ1UZ1IMMgYciWvH81rHhvgfg= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1705010520; c=relaxed/simple; bh=2n0coOkrn31ylYwUgZEgTthr4q6nFFKa83alRu8/L3E=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=LDQlCl6NI1kRVArlTph4ta/WRSwXeaS0O+080auFxIXSVWzvOkBwVGUz1RxfN8YDaJ1ujXjjJDPzNpOgu1Ht0FUQYg8Z9vHlr/+BW4y3FItTiQ0yZPdsg40Vyvv42PCcRsSyl1O5DI+77/RRKvh6HdL8nLkrWfXom3PF76Vu//s= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1705010516; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=5di7KcqonEyGIB+xfJyp98vRn/iVkwnmqEhtPEuEhnA=; b=A2qdDl5Hg6bLn/JwF7NcJ+MQQOhSIWGS3JUsGaEkZfmjTFg18/3mSdM3q81nmFQvsdqdka 5MVSSeLijfPdicKHgtpj1Uh/wI7uH07qe5Ub0enPg8WMFkd2QjcUCW1bRxICPe+VlZVXJI Pe9FrgXfDjJUDPy/bKIY4Mk7bIlqw28= Received: from mail-oi1-f197.google.com (mail-oi1-f197.google.com [209.85.167.197]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-205-3krZZSCpNCimjq37EI0egA-1; Thu, 11 Jan 2024 17:01:55 -0500 X-MC-Unique: 3krZZSCpNCimjq37EI0egA-1 Received: by mail-oi1-f197.google.com with SMTP id 5614622812f47-3bbbfb0f038so7111785b6e.3 for ; Thu, 11 Jan 2024 14:01:55 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1705010514; x=1705615314; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=VSPUFRpfVY0TNEWKa+ULO+FL+AK847vxjlKMEiUDWbg=; b=KlgClgLrBxanjgrOi6TazUEd0w6f7bpWC130wCDNtnMyi7W0vEwahw7ttr8gK4nwUT jhgeEzRDhteB2LzHeaipj+T5EC4I+nh+Z8zyuKZNMlN/LBtRZNB55xjf9G7CdJeSRywK UtJq9CKBNyD1GRKSTQiHmsHJ3U3Nuqsm+Dt40JmoCxyV4cXtD3/Xa+IzKxdgAuuHROzg ULy2NtnFAxEHJSHAnD6d2sRj08dzMEWLzBNEU5bX2YsZIlzbTqvsHkIBTzRmgiMhz4aC bR2Cldk6wp6YTk8JxfVvGm2Bt3i5+DX4Is06J5UuS/fnoYGj8jeR9WAmTjO7PwkyXndb d4SA== X-Gm-Message-State: AOJu0YzAGMHbUeAL/Uu18DHR0uPp0fHhw1Kg5mYn8Z0edkMOYV/5SKio m6LH9ZiJoUgvz4Glb7HzvxXPgbagkUGfa/5m61vIgcSBfXZ5+T7EiBu4d2caKpnJFQC8nQ/rbs5 30ZDDFY0OL0TvHG6n4VJOIWpIMlAebUHJxKUTFdrz3dGRES8+Uu2Ua/M7BbDUnaBCxKtnSMmmJi PBGGx/1w== X-Received: by 2002:a05:6808:614b:b0:3bd:3b3b:15fe with SMTP id dl11-20020a056808614b00b003bd3b3b15femr297895oib.119.1705010514022; Thu, 11 Jan 2024 14:01:54 -0800 (PST) X-Google-Smtp-Source: AGHT+IH2qEnyAHuSeUZ3oHyp8TdSj9Vyd5y5z2KeDgQc4N9ZfKvZt+0D8bHeBgWh6+GruQVG9TxccA== X-Received: by 2002:a05:6808:614b:b0:3bd:3b3b:15fe with SMTP id dl11-20020a056808614b00b003bd3b3b15femr297871oib.119.1705010513226; Thu, 11 Jan 2024 14:01:53 -0800 (PST) Received: from jason.com (130-44-146-16.s12558.c3-0.arl-cbr1.sbo-arl.ma.cable.rcncustomer.com. [130.44.146.16]) by smtp.gmail.com with ESMTPSA id x12-20020ad440cc000000b00680613267d5sm592546qvp.115.2024.01.11.14.01.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 11 Jan 2024 14:01:52 -0800 (PST) From: Jason Merrill To: gcc-patches@gcc.gnu.org Cc: waffl3x Subject: [pushed] c++: corresponding object parms [PR113191] Date: Thu, 11 Jan 2024 17:01:48 -0500 Message-Id: <20240111220148.1575375-1-jason@redhat.com> X-Mailer: git-send-email 2.39.3 MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-22.8 required=5.0 tests=BAYES_00, DKIM_INVALID, DKIM_SIGNED, GIT_PATCH_0, KAM_DMARC_NONE, KAM_DMARC_STATUS, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, RCVD_IN_SORBS_WEB, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE, URIBL_BLACK autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org Tested x86_64-pc-linux-gnu, applying to trunk. -- 8< -- As discussed, our handling of corresponding object parameters needed to handle the using-declaration case better. And I took the opportunity to share code between the add_method and cand_parms_match uses. This patch specifically doesn't compare reversed parameters, but a follow-up patch will. PR c++/113191 gcc/cp/ChangeLog: * class.cc (xobj_iobj_parameters_correspond): Add context parm. (object_parms_correspond): Factor out of... (add_method): ...here. * method.cc (defaulted_late_check): Use it. * call.cc (class_of_implicit_object): New. (object_parms_correspond): Overload taking two candidates. (cand_parms_match): Use it. (joust): Check reversed before comparing constraints. * cp-tree.h (object_parms_correspond): Declare. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/concepts-memfun4.C: New test. --- gcc/cp/cp-tree.h | 1 + gcc/cp/call.cc | 84 ++++++++---- gcc/cp/class.cc | 121 ++++++++++-------- gcc/cp/method.cc | 16 +-- gcc/testsuite/g++.dg/cpp2a/concepts-memfun4.C | 97 ++++++++++++++ 5 files changed, 228 insertions(+), 91 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-memfun4.C base-commit: 9bac1d7839f129f93f159c27adaf472ee3ab23a2 diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index f3f265a3db8..83009fc837c 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6853,6 +6853,7 @@ extern bool is_empty_base_ref (tree); extern tree build_vtbl_ref (tree, tree); extern tree build_vfn_ref (tree, tree); extern tree get_vtable_decl (tree, int); +extern bool object_parms_correspond (tree, tree, tree); extern bool add_method (tree, tree, bool); extern tree declared_access (tree); extern bool maybe_push_used_methods (tree); diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 191664ee227..6f024b8abc3 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -12685,6 +12685,51 @@ joust_maybe_elide_copy (z_candidate *cand) return false; } +/* Return the class that CAND's implicit object parameter refers to. */ + +static tree +class_of_implicit_object (z_candidate *cand) +{ + if (!DECL_IOBJ_MEMBER_FUNCTION_P (cand->fn)) + return NULL_TREE; + + /* "For conversion functions that are implicit object member functions, + the function is considered to be a member of the class of the implied + object argument for the purpose of defining the type of the implicit + object parameter." */ + if (DECL_CONV_FN_P (cand->fn)) + return TYPE_MAIN_VARIANT (TREE_TYPE (cand->first_arg)); + + /* "For non-conversion functions that are implicit object member + functions nominated by a using-declaration in a derived class, the + function is considered to be a member of the derived class for the + purpose of defining the type of the implicit object parameter." + + That derived class is reflected in the conversion_path binfo. */ + return BINFO_TYPE (cand->conversion_path); +} + +/* True if candidates C1 and C2 have corresponding object parameters per + [basic.scope.scope]. */ + +static bool +object_parms_correspond (z_candidate *c1, z_candidate *c2) +{ + tree context = class_of_implicit_object (c1); + tree ctx2 = class_of_implicit_object (c2); + if (!ctx2) + /* Leave context as is. */; + else if (!context) + context = ctx2; + else if (context != ctx2) + /* This can't happen for normal function calls, since it means finding + functions in multiple bases which would fail with an ambiguous lookup, + but it can occur with reversed operators. */ + return false; + + return object_parms_correspond (c1->fn, c2->fn, context); +} + /* True if the defining declarations of the two candidates have equivalent parameters. */ @@ -12712,35 +12757,25 @@ cand_parms_match (z_candidate *c1, z_candidate *c2) } tree parms1 = TYPE_ARG_TYPES (TREE_TYPE (fn1)); tree parms2 = TYPE_ARG_TYPES (TREE_TYPE (fn2)); - auto skip_parms = [](tree fn, tree parms){ - if (DECL_XOBJ_MEMBER_FUNCTION_P (fn)) - return TREE_CHAIN (parms); - else - return skip_artificial_parms_for (fn, parms); - }; if (!(DECL_FUNCTION_MEMBER_P (fn1) && DECL_FUNCTION_MEMBER_P (fn2))) /* Early escape. */; - else if ((DECL_STATIC_FUNCTION_P (fn1) - != DECL_STATIC_FUNCTION_P (fn2))) + + /* CWG2789 is not adequate, it should specify corresponding object + parameters, not same typed object parameters. */ + else if (!object_parms_correspond (c1, c2)) + return false; + else { - /* Ignore 'this' when comparing the parameters of a static member - function with those of a non-static one. */ - parms1 = skip_parms (fn1, parms1); - parms2 = skip_parms (fn2, parms2); - } - else if ((DECL_XOBJ_MEMBER_FUNCTION_P (fn1) - || DECL_XOBJ_MEMBER_FUNCTION_P (fn2)) - && (DECL_IOBJ_MEMBER_FUNCTION_P (fn1) - || DECL_IOBJ_MEMBER_FUNCTION_P (fn2))) - { - bool xobj_iobj_parameters_correspond (tree, tree); - /* CWG2789 is not adequate, it should specify corresponding object - parameters, not same typed object parameters. */ - if (!xobj_iobj_parameters_correspond (fn1, fn2)) - return false; /* We just compared the object parameters, if they don't correspond - we already return false. */ + we already returned false. */ + auto skip_parms = [] (tree fn, tree parms) + { + if (DECL_XOBJ_MEMBER_FUNCTION_P (fn)) + return TREE_CHAIN (parms); + else + return skip_artificial_parms_for (fn, parms); + }; parms1 = skip_parms (fn1, parms1); parms2 = skip_parms (fn2, parms2); } @@ -13104,6 +13139,7 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn, if (flag_concepts && DECL_P (cand1->fn) && DECL_P (cand2->fn) && !cand1->template_decl && !cand2->template_decl + && cand1->reversed () == cand2->reversed () && cand_parms_match (cand1, cand2)) { winner = more_constrained (cand1->fn, cand2->fn); diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc index e5e609badf3..3374756fb9a 100644 --- a/gcc/cp/class.cc +++ b/gcc/cp/class.cc @@ -1020,15 +1020,11 @@ modify_vtable_entry (tree t, /* Check if the object parameters of an xobj and iobj member function - correspond. This function assumes that the iobj parameter has been - correctly adjusted when the function is introduced by a using declaration - per [over.match.funcs.general.4]. + correspond. CONTEXT is the class that an implicit object parameter + refers to. */ - ??? But it isn't, that's only considered at overload resolution time. - cand_parms_match will probably need to check cand->conversion_path. */ - -bool -xobj_iobj_parameters_correspond (tree fn1, tree fn2) +static bool +xobj_iobj_parameters_correspond (tree fn1, tree fn2, tree context) { gcc_assert (DECL_IOBJ_MEMBER_FUNCTION_P (fn1) || DECL_IOBJ_MEMBER_FUNCTION_P (fn2)); @@ -1042,11 +1038,6 @@ xobj_iobj_parameters_correspond (tree fn1, tree fn2) tree iobj_fn = DECL_IOBJ_MEMBER_FUNCTION_P (fn1) ? fn1 : fn2; tree iobj_fn_type = TREE_TYPE (iobj_fn); - /* Will work for a pointer or reference param type. So this will continue - to work even if we change how the object parameter of an iobj member - function is represented. */ - tree iobj_param_type - = TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (iobj_fn_type))); /* If the iobj member function was introduced with a using declaration, the type of its object parameter is considered to be that of the class it was @@ -1116,7 +1107,7 @@ xobj_iobj_parameters_correspond (tree fn1, tree fn2) check for that case. */ if (!same_type_ignoring_top_level_qualifiers_p - (iobj_param_type, non_reference (xobj_param))) + (context, non_reference (xobj_param))) return false; /* We don't get to bail yet even if we have a by-value xobj parameter, @@ -1214,6 +1205,63 @@ xobj_iobj_parameters_correspond (tree fn1, tree fn2) return true; } +/* True if FN and METHOD have corresponding object parms per + [basic.scope.scope], or if one of them is a static member function (which + are considered to have an object parm that corresponds to any other). + CONTEXT is the class that an implicit object member function is considered + to be a member of for the purpose of this comparison, per + [over.match.funcs]. */ + +bool +object_parms_correspond (tree fn, tree method, tree context) +{ + tree fn_type = TREE_TYPE (fn); + tree method_type = TREE_TYPE (method); + + /* Compare the quals on the 'this' parm. Don't compare + the whole types, as used functions are treated as + coming from the using class in overload resolution. */ + if (DECL_IOBJ_MEMBER_FUNCTION_P (fn) + && DECL_IOBJ_MEMBER_FUNCTION_P (method)) + { + /* Either both or neither need to be ref-qualified for + differing quals to allow overloading. */ + if ((FUNCTION_REF_QUALIFIED (fn_type) + == FUNCTION_REF_QUALIFIED (method_type)) + && (type_memfn_quals (fn_type) != type_memfn_quals (method_type) + || type_memfn_rqual (fn_type) != type_memfn_rqual (method_type))) + return false; + return true; + } + /* Treat a static member function as corresponding to any object parm. */ + else if (DECL_STATIC_FUNCTION_P (fn) || DECL_STATIC_FUNCTION_P (method)) + return true; + /* Handle special correspondence rules for xobj vs xobj and xobj vs iobj + member function declarations. + We don't worry about static member functions here. */ + else if (DECL_XOBJ_MEMBER_FUNCTION_P (fn) + && DECL_XOBJ_MEMBER_FUNCTION_P (method)) + { + auto get_object_param = [] (tree fn) + { + return TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (fn))); + }; + /* We skip the object parameter below, check it here instead of + making changes to that code. */ + tree fn_param = get_object_param (fn); + tree method_param = get_object_param (method); + if (!same_type_p (fn_param, method_param)) + return false; + } + else if (DECL_XOBJ_MEMBER_FUNCTION_P (fn) + || DECL_XOBJ_MEMBER_FUNCTION_P (method)) + return xobj_iobj_parameters_correspond (fn, method, context); + else + gcc_unreachable (); + + return true; +} + /* Add method METHOD to class TYPE. If VIA_USING indicates whether METHOD is being injected via a using_decl. Returns true if the method could be added to the method vec. */ @@ -1268,51 +1316,12 @@ add_method (tree type, tree method, bool via_using) functions in the derived class override and/or hide member functions with the same name and parameter types in a base class (rather than conflicting). */ + if (!object_parms_correspond (fn, method, type)) + continue; + tree fn_type = TREE_TYPE (fn); tree method_type = TREE_TYPE (method); - /* Compare the quals on the 'this' parm. Don't compare - the whole types, as used functions are treated as - coming from the using class in overload resolution. */ - if (DECL_IOBJ_MEMBER_FUNCTION_P (fn) - && DECL_IOBJ_MEMBER_FUNCTION_P (method) - /* Either both or neither need to be ref-qualified for - differing quals to allow overloading. */ - && (FUNCTION_REF_QUALIFIED (fn_type) - == FUNCTION_REF_QUALIFIED (method_type)) - && (type_memfn_quals (fn_type) != type_memfn_quals (method_type) - || type_memfn_rqual (fn_type) != type_memfn_rqual (method_type))) - continue; - - /* Handle special correspondence rules for xobj vs xobj and xobj vs iobj - member function declarations. - We don't worry about static member functions here. */ - if ((!DECL_XOBJ_MEMBER_FUNCTION_P (fn) - && !DECL_XOBJ_MEMBER_FUNCTION_P (method)) - || DECL_STATIC_FUNCTION_P (fn) || DECL_STATIC_FUNCTION_P (method)) - /* Early escape. */; - else if (DECL_XOBJ_MEMBER_FUNCTION_P (fn) - && DECL_XOBJ_MEMBER_FUNCTION_P (method)) - { - auto get_object_param = [](tree fn){ - return TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (fn))); - }; - /* We skip the object parameter below, check it here instead of - making changes to that code. */ - tree fn_param = get_object_param (fn); - tree method_param = get_object_param (method); - if (!same_type_p (fn_param, method_param)) - continue; - } - else if (DECL_XOBJ_MEMBER_FUNCTION_P (fn) - || DECL_XOBJ_MEMBER_FUNCTION_P (method)) - { - if (!xobj_iobj_parameters_correspond (fn, method)) - continue; - } - else - gcc_unreachable (); - tree real_fn = fn; tree real_method = method; diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc index 6a9f03eb8f3..d49e5a565e8 100644 --- a/gcc/cp/method.cc +++ b/gcc/cp/method.cc @@ -3395,24 +3395,18 @@ defaulted_late_check (tree fn) { tree fn_obj_ref_type = TREE_VALUE (fn_parms); /* We can't default xobj operators with an xobj parameter that is not - an lvalue reference. */ + an lvalue reference, even if it would correspond. */ if (!TYPE_REF_P (fn_obj_ref_type) - || TYPE_REF_IS_RVALUE (fn_obj_ref_type)) - return false; - /* If implicit_fn's object parameter is not a pointer, something is not - right. */ - gcc_assert (TYPE_PTR_P (TREE_VALUE (implicit_fn_parms))); - /* Strip the reference/pointer off each object parameter before - comparing them. */ - if (!same_type_p (TREE_TYPE (fn_obj_ref_type), - TREE_TYPE (TREE_VALUE (implicit_fn_parms)))) + || TYPE_REF_IS_RVALUE (fn_obj_ref_type) + || !object_parms_correspond (fn, implicit_fn, + DECL_CONTEXT (implicit_fn))) return false; /* We just compared the object parameters, skip over them before passing to compparms. */ fn_parms = TREE_CHAIN (fn_parms); implicit_fn_parms = TREE_CHAIN (implicit_fn_parms); } - return compparms(fn_parms, implicit_fn_parms); + return compparms (fn_parms, implicit_fn_parms); }; if (!same_type_p (TREE_TYPE (TREE_TYPE (fn)), diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-memfun4.C b/gcc/testsuite/g++.dg/cpp2a/concepts-memfun4.C new file mode 100644 index 00000000000..2fa661dbe96 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-memfun4.C @@ -0,0 +1,97 @@ +// PR c++/113191 +// { dg-do compile { target c++23 } } + +template struct S; + +template +struct B { + constexpr int f() const requires true { return 5; } + constexpr operator int () const requires true { return 5; } + constexpr int g(this S&&) requires true { return 5; } + constexpr int h() requires true { return 5; } +}; + +template +struct S : B<> { + using B::f; + using B::g; + using B::h; + constexpr int f() const { return 10; } + constexpr operator int () const { return 10; } + constexpr int g() { return 10; } + constexpr int h(this S&&) { return 10; } +}; + +// implicit object parms match, B::f is more constrained +static_assert(S<>{}.f() == 5); +static_assert(S<>{}.g() == 5); +static_assert(S<>{}.h() == 5); + +template +struct C { + constexpr int f() const { return 15; } + constexpr operator int () const { return 15; } +}; + +template +struct S2: B<>, C<> { }; + +// implicit object parms for conversion functions are all considered to be from +// the class of the object argument +static_assert(S2<>{} == 5); + +// ambiguous lookup, so we never actually compare the candidates +// if we did, implicit object parms don't match due to different classes +// so constraints aren't considered and it would still be ambiguous +static_assert(S2<>{}.f() == 5); // { dg-error "ambiguous" } + +template +struct S3 : B<> { + using B::f; + constexpr int f() volatile { return 10; } +}; + +// implicit object parms don't match due to different cv-quals +static_assert(S3<>{}.f() == 5); // { dg-error "ambiguous" } + +template +struct S4 : B<> { + using B::f; + constexpr int f() const & { return 10; } +}; + +// no ref-qual matches any ref-qual +static_assert(S4<>{}.f() == 5); + +template +struct C2 { + constexpr operator int () volatile { return 15; } +}; + +template +struct S5: B<>, C2<> { }; + +// implicit object parms don't match due to different cv-quals +static_assert(S5<>{} == 5); // { dg-error "ambiguous" } + +namespace N1 { + template struct B; + + template + struct A { + constexpr bool operator==(B<>&) { return true; } + }; + + template + struct B { + constexpr bool operator==(A<>&) requires true { return false; } + }; + + A<> a; + B<> b; + // when comparing the A op== to the reversed B op==, we don't compare + // constraints and so fall through to the tiebreaker that chooses the + // non-reversed candidate. + // ??? shouldn't we compare constraints? + static_assert (a == b); +}