From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 5FBE4385B800 for ; Tue, 22 Aug 2023 01:51:45 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 5FBE4385B800 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1692669105; 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=dmrcMWdLRnu/xWl/gpfN9QHN6WI69r7i3ubIV/BUUd4=; b=cXwsRKttNYHGVZBlgxLMYOErTWlewHmuUIprBVO0SZz6csy6QeULhOmaHe60Q+qosNIqOZ 7K+I2fX8JVrHGxsm5IB2LkUUS28lDppYGk4FlPlveJ1TiBs/yrVaS1K6eLp3fD6ya6ITBR Zu7qZTkMzvhbG2/PmTolPex0jGYJDNI= Received: from mail-qk1-f197.google.com (mail-qk1-f197.google.com [209.85.222.197]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-57-TMuhclkuP9qI7gJDREUcdg-1; Mon, 21 Aug 2023 21:51:43 -0400 X-MC-Unique: TMuhclkuP9qI7gJDREUcdg-1 Received: by mail-qk1-f197.google.com with SMTP id af79cd13be357-76d8cc15605so411908885a.3 for ; Mon, 21 Aug 2023 18:51:42 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1692669102; x=1693273902; 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=dmrcMWdLRnu/xWl/gpfN9QHN6WI69r7i3ubIV/BUUd4=; b=FvM0HOMdG0sxF/2AoTF45/bb3/BKXm7GpWfmu1i+jIarcBSG0GBWCzaE/sQIfeKNPS ewUKhPBb6S4KNutw+RkW3WT2Vu8+PnMsZFIjk6mCwdCbCjBKO4T16g0vZ07p/TqkOv3M AyFzN8ybuRy8tgPciPnu6MNCaVtwsoz1HPYs0PcI5Jpj+5emeZmt8Ap6Fp3O/X8AZ83M jHB3PSFuPIFLB52F4nGx40WHI8nAb8QelpGWeD+joYxucvQQtcdzzcKg5hTG9nKW6ZVn zhGa5ZYefqNVy7KOpseXFbhB1IDAmqPNT8SbeYYLwPwX+zkplXiHuEwnmZen6Hg3h8zv G58w== X-Gm-Message-State: AOJu0Yz+vL8HV+s8CqyEqt1T7kfV1zQ2ppdDNN/oBblI6kjU8gtnKmTI 6ybekY4Ow6bzcw5cSrxzUj7S5C8yBHEStj6D5+8MmM7zk98ZIEvKtK2epGppa56I2RD0AAj5ZcL Xib/gajfIeL1cASqtoOw9/yGe5Zvi2zkhZoj8VC8+v+xTW0miZPMgcTOfj6PJ5PaZpud6mfadHU o= X-Received: by 2002:a05:620a:20de:b0:76d:a010:6e4f with SMTP id f30-20020a05620a20de00b0076da0106e4fmr6205657qka.44.1692669102037; Mon, 21 Aug 2023 18:51:42 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGVwwXIStZP5wdnNnUFuujCHslbFhUq/i94FQ2lAEMZfwV9XZpLpvvw3kyi/iLaslYQeXKcwg== X-Received: by 2002:a05:620a:20de:b0:76d:a010:6e4f with SMTP id f30-20020a05620a20de00b0076da0106e4fmr6205641qka.44.1692669101619; Mon, 21 Aug 2023 18:51:41 -0700 (PDT) Received: from localhost.localdomain (ool-457670bb.dyn.optonline.net. [69.118.112.187]) by smtp.gmail.com with ESMTPSA id d19-20020a05620a141300b00767660afed5sm715681qkj.99.2023.08.21.18.51.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Aug 2023 18:51:41 -0700 (PDT) From: Patrick Palka To: gcc-patches@gcc.gnu.org Cc: jason@redhat.com, Patrick Palka Subject: [PATCH] c++: refine CWG 2369 satisfaction vs non-dep convs [PR99599] Date: Mon, 21 Aug 2023 21:51:38 -0400 Message-ID: <20230822015139.1920183-1-ppalka@redhat.com> X-Mailer: git-send-email 2.42.0.rc2 MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-13.6 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H4,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,SPF_NONE,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look like a reasonable approach? I didn't observe any compile time/memory impact of this change. -- >8 -- As described in detail in the PR, CWG 2369 has the surprising consequence of introducing constraint recursion in seemingly valid and innocent code. This patch attempts to fix this surpising behavior for the majority of problematic use cases. Rather than checking satisfaction before _all_ non-dependent conversions, as specified by the CWG issue, this patch makes us first check "safe" non-dependent conversions, then satisfaction, then followed by "unsafe" non-dependent conversions. In this case, a conversion is "safe" if computing it is guaranteed to not induce template instantiation. This patch heuristically determines "safety" by checking for a constructor template or conversion function template in the (class) parm or arg types respectively. If neither type has such a member, then computing the conversion should not induce instantiation (modulo satisfaction checking of non-template constructor and conversion functions I suppose). PR c++/99599 gcc/cp/ChangeLog: * config-lang.in (gtfiles): Add search.cc. * pt.cc (check_non_deducible_conversions): Add bool parameter passed down to check_non_deducible_conversion. (fn_type_unification): Call check_non_deducible_conversions an extra time before satisfaction with noninst_only_p=true. (check_non_deducible_conversion): Add bool parameter controlling whether to compute only conversions that are guaranteed to not induce template instantiation. * search.cc (conversions_cache): Define. (lookup_conversions): Use it to cache the lookup. Improve cache rate by considering TYPE_MAIN_VARIANT of the type. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/concepts-nondep4.C: New test. * g++.dg/cpp2a/concepts-nondep4a.C: New test. --- gcc/cp/config-lang.in | 1 + gcc/cp/pt.cc | 66 +++++++++++++++++-- gcc/cp/search.cc | 14 +++- gcc/testsuite/g++.dg/cpp2a/concepts-nondep4.C | 21 ++++++ .../g++.dg/cpp2a/concepts-nondep4a.C | 30 +++++++++ 5 files changed, 125 insertions(+), 7 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-nondep4.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-nondep4a.C diff --git a/gcc/cp/config-lang.in b/gcc/cp/config-lang.in index a6c7883cc24..e34c392d208 100644 --- a/gcc/cp/config-lang.in +++ b/gcc/cp/config-lang.in @@ -52,6 +52,7 @@ gtfiles="\ \$(srcdir)/cp/name-lookup.cc \ \$(srcdir)/cp/parser.cc \$(srcdir)/cp/pt.cc \ \$(srcdir)/cp/rtti.cc \ +\$(srcdir)/cp/search.cc \ \$(srcdir)/cp/semantics.cc \ \$(srcdir)/cp/tree.cc \$(srcdir)/cp/typeck2.cc \ \$(srcdir)/cp/vtable-class-hierarchy.cc \ diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index a4809f034dc..c761b73b771 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -151,7 +151,7 @@ static tree get_partial_spec_bindings (tree, tree, tree); static void tsubst_enum (tree, tree, tree); static bool check_instantiated_args (tree, tree, tsubst_flags_t); static int check_non_deducible_conversion (tree, tree, unification_kind_t, int, - struct conversion **, bool); + struct conversion **, bool, bool); static int maybe_adjust_types_for_deduction (tree, unification_kind_t, tree*, tree*, tree); static int type_unification_real (tree, tree, tree, const tree *, @@ -22315,7 +22315,8 @@ pack_deducible_p (tree parm, tree fn) static int check_non_deducible_conversions (tree parms, const tree *args, unsigned nargs, tree fn, unification_kind_t strict, int flags, - struct conversion **convs, bool explain_p) + struct conversion **convs, bool explain_p, + bool noninst_only_p) { /* Non-constructor methods need to leave a conversion for 'this', which isn't included in nargs here. */ @@ -22351,7 +22352,7 @@ check_non_deducible_conversions (tree parms, const tree *args, unsigned nargs, int lflags = conv_flags (ia, nargs, fn, arg, flags); if (check_non_deducible_conversion (parm, arg, strict, lflags, - conv_p, explain_p)) + conv_p, explain_p, noninst_only_p)) return 1; } @@ -22651,6 +22652,16 @@ fn_type_unification (tree fn, deduced: + /* As a refinement of CWG2369, check first and foremost non-dependent + conversions that we know are not going to induce template instantiation + (PR99599). */ + if (strict == DEDUCE_CALL + && incomplete + && check_non_deducible_conversions (parms, args, nargs, fn, strict, flags, + convs, explain_p, + /*noninst_only_p=*/true)) + goto fail; + /* CWG2369: Check satisfaction before non-deducible conversions. */ if (!constraints_satisfied_p (fn, targs)) { @@ -22664,7 +22675,8 @@ fn_type_unification (tree fn, as the standard says that we substitute explicit args immediately. */ if (incomplete && check_non_deducible_conversions (parms, args, nargs, fn, strict, flags, - convs, explain_p)) + convs, explain_p, + /*noninst_only_p=*/false)) goto fail; /* All is well so far. Now, check: @@ -22899,7 +22911,7 @@ maybe_adjust_types_for_deduction (tree tparms, static int check_non_deducible_conversion (tree parm, tree arg, unification_kind_t strict, int flags, struct conversion **conv_p, - bool explain_p) + bool explain_p, bool noninst_only_p) { tree type; @@ -22921,6 +22933,50 @@ check_non_deducible_conversion (tree parm, tree arg, unification_kind_t strict, { bool ok = false; tree conv_arg = TYPE_P (arg) ? NULL_TREE : arg; + if (conv_p && *conv_p) + { + /* This conversion was already computed earlier (when + computing only non-instantiating conversions). */ + gcc_checking_assert (!noninst_only_p); + return unify_success (explain_p); + } + if (noninst_only_p) + { + /* We're checking only non-instantiating conversions. + A conversion may instantiate only if it's to/from a + class type that has a constructor template/conversion + function template. */ + tree parm_nonref = non_reference (parm); + tree type_nonref = non_reference (type); + + if (CLASS_TYPE_P (parm_nonref)) + { + if (!COMPLETE_TYPE_P (parm_nonref) + && CLASSTYPE_TEMPLATE_INSTANTIATION (parm_nonref)) + return unify_success (explain_p); + + tree ctors = get_class_binding (parm_nonref, + complete_ctor_identifier); + for (tree ctor : lkp_range (ctors)) + if (TREE_CODE (ctor) == TEMPLATE_DECL) + return unify_success (explain_p); + } + + if (CLASS_TYPE_P (type_nonref)) + { + if (!COMPLETE_TYPE_P (type_nonref) + && CLASSTYPE_TEMPLATE_INSTANTIATION (type_nonref)) + return unify_success (explain_p); + + tree convs = lookup_conversions (type_nonref); + for (; convs; convs = TREE_CHAIN (convs)) + if (TREE_CODE (TREE_VALUE (convs)) == TEMPLATE_DECL) + return unify_success (explain_p); + } + + /* Checking this conversion definitely won't induce a template + instantiation. */ + } if (conv_p) /* Avoid recalculating this in add_function_candidate. */ ok = (*conv_p diff --git a/gcc/cp/search.cc b/gcc/cp/search.cc index cd80f285ac9..9986eec4856 100644 --- a/gcc/cp/search.cc +++ b/gcc/cp/search.cc @@ -2582,6 +2582,10 @@ lookup_conversions_r (tree binfo, int virtual_depth, int virtualness, return my_virtualness; } +/* A cache of the result of lookup_conversions. */ + +static GTY((cache)) type_tree_cache_map *conversions_cache; + /* Return a TREE_LIST containing all the non-hidden user-defined conversion functions for TYPE (and its base-classes). The TREE_VALUE of each node is the FUNCTION_DECL of the conversion @@ -2594,12 +2598,15 @@ lookup_conversions_r (tree binfo, int virtual_depth, int virtualness, tree lookup_conversions (tree type) { - tree convs; - + type = TYPE_MAIN_VARIANT (type); complete_type (type); if (!CLASS_TYPE_P (type) || !TYPE_BINFO (type)) return NULL_TREE; + if (tree *c = hash_map_safe_get (conversions_cache, type)) + return *c; + + tree convs; lookup_conversions_r (TYPE_BINFO (type), 0, 0, NULL_TREE, NULL_TREE, &convs); tree list = NULL_TREE; @@ -2618,6 +2625,7 @@ lookup_conversions (tree type) } } + hash_map_safe_put (conversions_cache, type, list); return list; } @@ -2798,3 +2806,5 @@ any_dependent_bases_p (tree type) return false; } + +#include "gt-cp-search.h" diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-nondep4.C b/gcc/testsuite/g++.dg/cpp2a/concepts-nondep4.C new file mode 100644 index 00000000000..d26783524f7 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-nondep4.C @@ -0,0 +1,21 @@ +// PR c++/99599 +// { dg-do compile { target c++20 } } + +struct foo_tag { }; +struct bar_tag { }; + +template +concept fooable = requires(T it) { + invoke_tag(foo_tag{}, it); +}; + +template +void invoke_tag(foo_tag, T in); + +template +void invoke_tag(bar_tag, T it); + +int main() { + invoke_tag(foo_tag{}, 2); + invoke_tag(bar_tag{}, 2); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-nondep4a.C b/gcc/testsuite/g++.dg/cpp2a/concepts-nondep4a.C new file mode 100644 index 00000000000..5ee214aa495 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-nondep4a.C @@ -0,0 +1,30 @@ +// PR c++/99599 +// { dg-do compile { target c++20 } } + +struct foo_tag { + foo_tag() = default; + foo_tag(int); + operator int(); +}; + +struct bar_tag { + bar_tag() = default; + bar_tag(int); + operator int(); +}; + +template +concept fooable = requires(T it) { + invoke_tag(foo_tag{}, it); +}; + +template +void invoke_tag(foo_tag, T in); + +template +void invoke_tag(bar_tag, T it); + +int main() { + invoke_tag(foo_tag{}, 2); + invoke_tag(bar_tag{}, 2); +} -- 2.42.0.rc2