Message ID | 20211215174905.3848806-1-ppalka@redhat.com |
---|---|
State | New |
Headers |
Return-Path: <gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org> X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A60163858418 for <patchwork@sourceware.org>; Wed, 15 Dec 2021 17:49:41 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org A60163858418 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1639590581; bh=qb56P8aG8CaLOGsVBdx0sXn2i/8i9W1zEdBgs7SMv4E=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=ogoJvjWiovSrGAidrhbHIiXIZgejsjYuibu4VvCYqAXNhMEHvZDpdmoXBdZdPVghI 5NHw3Iw1pmtcoZDlrh1h9LaGrE/DPE2sjqYuZ8YLgCisL41mY4vO6fbdVY7Tl8nWr9 hhYoGphINZQhAwT4Ur/FEY2m9YclgS+d4ul4bbbw= 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.133.124]) by sourceware.org (Postfix) with ESMTPS id EAA6C3858D28 for <gcc-patches@gcc.gnu.org>; Wed, 15 Dec 2021 17:49:10 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org EAA6C3858D28 Received: from mail-qk1-f198.google.com (mail-qk1-f198.google.com [209.85.222.198]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-530-lSw5isgEPOSZBargUNgMBg-1; Wed, 15 Dec 2021 12:49:09 -0500 X-MC-Unique: lSw5isgEPOSZBargUNgMBg-1 Received: by mail-qk1-f198.google.com with SMTP id bm33-20020a05620a19a100b0046f1b6f3526so19486388qkb.21 for <gcc-patches@gcc.gnu.org>; Wed, 15 Dec 2021 09:49:09 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=qb56P8aG8CaLOGsVBdx0sXn2i/8i9W1zEdBgs7SMv4E=; b=nwUTAsIPDpfDbqIjVeHUFc0z7lKPcvHpbA5ah6FniQLnRXNDD+2yp8AMTMKy0dRHUZ AB5XE8xIBWQPZ+zTdxM6qtAhQUooGL+PfTWWWBtGpBTUS4AsJmVYnnMBa8vQe/oW7Di3 HPdp71DwbdroTivpmciKAYLnRyA8BvgHylx3JY+8OdMbysrnLFcvMuZngIhh8kwq5MJT raW3kWBt4W0fGW/EiptD5YmJhWz2flMyL0OnJmjp4eg8Q+JhYzOmnFQwzhXPywNMz4hR +497LMSReYYKJ2t+ca8lYf8dsFxsAHBIFIqIGniBwCwL3upsIuNMiQhxVZ0n+0hVWvIy aTJg== X-Gm-Message-State: AOAM532BTECMOJWqOaqnfI3K6zacdWokUP0vbZv+rWqe1bgH7F6uXK3I cWCTgsyx5LBnweBUE62ilZzW7SUpEuV/oTH/mYy/Z8/dM+tu05ZWq4QhZCyimey8qut8wv72Y1v oTsdahzFWHrOjgBwJrY3HdFWI8aXUI6aopbsy6LL2AxVK6+nhjUTbpjJIEOJjWAtftdo= X-Received: by 2002:ac8:4e93:: with SMTP id 19mr8286868qtp.559.1639590548316; Wed, 15 Dec 2021 09:49:08 -0800 (PST) X-Google-Smtp-Source: ABdhPJwFyvTzEvY570d9EfY0W46aGjE0XD1MhS+bbJEajvNuenb2jEh6xsIlkQD2bNqrJYKCUh0DxA== X-Received: by 2002:ac8:4e93:: with SMTP id 19mr8286825qtp.559.1639590547811; Wed, 15 Dec 2021 09:49:07 -0800 (PST) Received: from localhost.localdomain (ool-457d493a.dyn.optonline.net. [69.125.73.58]) by smtp.gmail.com with ESMTPSA id z8sm1982242qta.50.2021.12.15.09.49.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Dec 2021 09:49:07 -0800 (PST) To: gcc-patches@gcc.gnu.org Subject: [PATCH] c++: ahead-of-time overload set pruning for non-dep calls Date: Wed, 15 Dec 2021 12:49:05 -0500 Message-Id: <20211215174905.3848806-1-ppalka@redhat.com> X-Mailer: git-send-email 2.34.1.182.ge773545c7f MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-16.0 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_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 <gcc-patches.gcc.gnu.org> List-Unsubscribe: <https://gcc.gnu.org/mailman/options/gcc-patches>, <mailto:gcc-patches-request@gcc.gnu.org?subject=unsubscribe> List-Archive: <https://gcc.gnu.org/pipermail/gcc-patches/> List-Post: <mailto:gcc-patches@gcc.gnu.org> List-Help: <mailto:gcc-patches-request@gcc.gnu.org?subject=help> List-Subscribe: <https://gcc.gnu.org/mailman/listinfo/gcc-patches>, <mailto:gcc-patches-request@gcc.gnu.org?subject=subscribe> From: Patrick Palka via Gcc-patches <gcc-patches@gcc.gnu.org> Reply-To: Patrick Palka <ppalka@redhat.com> Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" <gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org> |
Series |
c++: ahead-of-time overload set pruning for non-dep calls
|
|
Commit Message
Patrick Palka
Dec. 15, 2021, 5:49 p.m. UTC
This patch makes us remember the function selected by overload resolution during ahead of time processing of a non-dependent call expression, so that we avoid repeating most of the work of overload resolution at instantiation time. This mirrors what we already do for non-dependent operator expressions via build_min_non_dep_op_overload. Some caveats: * When processing ahead of time a non-dependent call to a member function template inside a class template (as in g++.dg/template/deduce4.C), we end up generating an "inverted" partial instantiation such as S<T>::foo<int, int>(), the kinds of which we're apparently not prepared to fully instantiate (e.g. tsubst_baselink mishandles it). So this patch disables this optimization for such functions and adds a FIXME. * WHen trying to make the instantiation machinery handle these partial instantiations, I made a couple of changes in register_specialization and tsubst_function_decl that get us closer to handling such partial instantiations and that seem like improvements on their own, so this patch includes these changes. * This change triggered a latent FUNCTION_DECL pretty printing issue in cpp0x/error2.C -- since we now resolve the call to foo<0> ahead of time, the error now looks like: error: expansion pattern ‘foo()()=0’ contains no parameter pack where the FUNCTION_DECL foo is clearly misprinted. But this pretty-printing issue could be reproduced without this patch if we replace foo with an ordinary function. Since this testcase was added to verify pretty printing of TEMPLATE_ID_EXPR, I work around this test failure by making the call to foo type-dependent and thus immune to this ahead of time pruning. * We now reject parts of cpp0x/fntmp-equiv1.C because we notice that the call d(f, b) in template <unsigned long f, unsigned b, typename> e<d(f, b)> d(); isn't constexpr because the (resolved) d isn't. I tried fixing this by making d constexpr, but then the call to d from main becomes ambiguous. So I settled with removing this part of the testcase. Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for trunk? Also tested on cmcstl2 and range-v3. gcc/cp/ChangeLog: * call.c (build_new_method_call): For a non-dependent call expression inside a template, returning a templated tree whose overload set contains just the selected function. * pt.c (register_specialization): Check only the innermost template args for dependence in the early exit test. (tsubst_function_decl): Simplify obtaining the template arguments for a partial instantiation. * semantics.c (finish_call_expr): As with build_new_method_call. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/error2.C: Make the call to foo type-dependent in order to avoid latent pretty-printing issue for FUNCTION_DECL inside MODOP_EXPR. * g++.dg/cpp0x/fntmp-equiv1.C: Remove ill-formed parts of testcase that we now diagnose. * g++.dg/template/non-dependent16.C: New test. * g++.dg/template/non-dependent16a.C: New test. --- gcc/cp/call.c | 17 +++++++++ gcc/cp/pt.c | 18 ++------- gcc/cp/semantics.c | 15 ++++++++ gcc/testsuite/g++.dg/cpp0x/error2.C | 4 +- gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C | 4 -- .../g++.dg/template/non-dependent16.C | 37 +++++++++++++++++++ .../g++.dg/template/non-dependent16a.C | 36 ++++++++++++++++++ 7 files changed, 111 insertions(+), 20 deletions(-) create mode 100644 gcc/testsuite/g++.dg/template/non-dependent16.C create mode 100644 gcc/testsuite/g++.dg/template/non-dependent16a.C
Comments
On 12/15/21 12:49, Patrick Palka wrote: > This patch makes us remember the function selected by overload > resolution during ahead of time processing of a non-dependent call > expression, so that we avoid repeating most of the work of overload > resolution at instantiation time. This mirrors what we already do for > non-dependent operator expressions via build_min_non_dep_op_overload. > > Some caveats: > > * When processing ahead of time a non-dependent call to a member > function template inside a class template (as in > g++.dg/template/deduce4.C), we end up generating an "inverted" partial > instantiation such as S<T>::foo<int, int>(), the kinds of which we're > apparently not prepared to fully instantiate (e.g. tsubst_baselink > mishandles it). So this patch disables this optimization for such > functions and adds a FIXME. I wonder if it would be worthwhile to build a TEMPLATE_ID_EXPR to remember the deduced template args, even if we are failing to remember the actual function? > * WHen trying to make the instantiation machinery handle these partial > instantiations, I made a couple of changes in register_specialization > and tsubst_function_decl that get us closer to handling such partial > instantiations and that seem like improvements on their own, so this > patch includes these changes. The tsubst_function_decl change makes me nervous; surely there was some reason that function wasn't that way in the first place. Let's hold these changes for stage 1 if they aren't actually fixing anything. > * This change triggered a latent FUNCTION_DECL pretty printing issue > in cpp0x/error2.C -- since we now resolve the call to foo<0> ahead > of time, the error now looks like: > > error: expansion pattern ‘foo()()=0’ contains no parameter pack > > where the FUNCTION_DECL foo is clearly misprinted. But this > pretty-printing issue could be reproduced without this patch if > we replace foo with an ordinary function. Since this testcase was > added to verify pretty printing of TEMPLATE_ID_EXPR, I work around > this test failure by making the call to foo type-dependent and thus > immune to this ahead of time pruning. > > * We now reject parts of cpp0x/fntmp-equiv1.C because we notice that > the call d(f, b) in > > template <unsigned long f, unsigned b, typename> e<d(f, b)> d(); > > isn't constexpr because the (resolved) d isn't. I tried fixing this > by making d constexpr, but then the call to d from main becomes > ambiguous. So I settled with removing this part of the testcase. > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for > trunk? Also tested on cmcstl2 and range-v3. > > gcc/cp/ChangeLog: > > * call.c (build_new_method_call): For a non-dependent call > expression inside a template, returning a templated tree > whose overload set contains just the selected function. > * pt.c (register_specialization): Check only the innermost > template args for dependence in the early exit test. > (tsubst_function_decl): Simplify obtaining the template arguments > for a partial instantiation. > * semantics.c (finish_call_expr): As with build_new_method_call. > > gcc/testsuite/ChangeLog: > > * g++.dg/cpp0x/error2.C: Make the call to foo type-dependent in > order to avoid latent pretty-printing issue for FUNCTION_DECL > inside MODOP_EXPR. > * g++.dg/cpp0x/fntmp-equiv1.C: Remove ill-formed parts of > testcase that we now diagnose. > * g++.dg/template/non-dependent16.C: New test. > * g++.dg/template/non-dependent16a.C: New test. > --- > gcc/cp/call.c | 17 +++++++++ > gcc/cp/pt.c | 18 ++------- > gcc/cp/semantics.c | 15 ++++++++ > gcc/testsuite/g++.dg/cpp0x/error2.C | 4 +- > gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C | 4 -- > .../g++.dg/template/non-dependent16.C | 37 +++++++++++++++++++ > .../g++.dg/template/non-dependent16a.C | 36 ++++++++++++++++++ > 7 files changed, 111 insertions(+), 20 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/template/non-dependent16.C > create mode 100644 gcc/testsuite/g++.dg/template/non-dependent16a.C > > diff --git a/gcc/cp/call.c b/gcc/cp/call.c > index 53a391cbc6b..92d96c19f5c 100644 > --- a/gcc/cp/call.c > +++ b/gcc/cp/call.c > @@ -11165,6 +11165,23 @@ build_new_method_call (tree instance, tree fns, vec<tree, va_gc> **args, > } > if (INDIRECT_REF_P (call)) > call = TREE_OPERAND (call, 0); > + > + /* Prune all but the selected function from the original overload > + set so that we can avoid some duplicate work at instantiation time. */ > + if (really_overloaded_fn (fns)) > + { > + if (DECL_TEMPLATE_INFO (fn) > + && DECL_MEMBER_TEMPLATE_P (DECL_TI_TEMPLATE (fn)) > + && dependent_type_p (DECL_CONTEXT (fn))) > + /* FIXME: We're not prepared to fully instantiate "inverted" > + partial instantiations such as A<T>::f<int>(). */; > + else > + { > + orig_fns = copy_node (orig_fns); > + BASELINK_FUNCTIONS (orig_fns) = fn; > + } > + } > + > call = (build_min_non_dep_call_vec > (call, > build_min (COMPONENT_REF, TREE_TYPE (CALL_EXPR_FN (call)), > diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c > index 2340139b238..b114114e617 100644 > --- a/gcc/cp/pt.c > +++ b/gcc/cp/pt.c > @@ -1566,18 +1566,10 @@ register_specialization (tree spec, tree tmpl, tree args, bool is_friend, > && TREE_CODE (spec) == NONTYPE_ARGUMENT_PACK)); > > if (TREE_CODE (spec) == FUNCTION_DECL > - && uses_template_parms (DECL_TI_ARGS (spec))) > + && uses_template_parms (INNERMOST_TEMPLATE_ARGS (DECL_TI_ARGS (spec)))) > /* This is the FUNCTION_DECL for a partial instantiation. Don't > - register it; we want the corresponding TEMPLATE_DECL instead. > - We use `uses_template_parms (DECL_TI_ARGS (spec))' rather than > - the more obvious `uses_template_parms (spec)' to avoid problems > - with default function arguments. In particular, given > - something like this: > - > - template <class T> void f(T t1, T t = T()) > - > - the default argument expression is not substituted for in an > - instantiation unless and until it is actually needed. */ > + register it; we want to register the corresponding TEMPLATE_DECL > + instead. */ > return spec; > > if (optimize_specialization_lookup_p (tmpl)) > @@ -13960,9 +13952,7 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain, > > /* Calculate the complete set of arguments used to > specialize R. */ > - argvec = tsubst_template_args (DECL_TI_ARGS > - (DECL_TEMPLATE_RESULT > - (DECL_TI_TEMPLATE (t))), > + argvec = tsubst_template_args (DECL_TI_ARGS (t), > args, complain, in_decl); > if (argvec == error_mark_node) > return error_mark_node; > diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c > index 7078af03d3c..57f689042b9 100644 > --- a/gcc/cp/semantics.c > +++ b/gcc/cp/semantics.c > @@ -2893,6 +2893,21 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual, > { > if (INDIRECT_REF_P (result)) > result = TREE_OPERAND (result, 0); > + > + /* Prune all but the selected function from the original overload > + set so that we can avoid some duplicate work at instantiation time. */ > + if (TREE_CODE (result) == CALL_EXPR > + && really_overloaded_fn (orig_fn)) > + { > + orig_fn = CALL_EXPR_FN (result); > + if (TREE_CODE (orig_fn) == COMPONENT_REF) > + { > + /* The result of build_new_method_call. */ > + orig_fn = TREE_OPERAND (orig_fn, 1); > + gcc_assert (BASELINK_P (orig_fn)); > + } > + } > + > result = build_call_vec (TREE_TYPE (result), orig_fn, orig_args); > SET_EXPR_LOCATION (result, input_location); > KOENIG_LOOKUP_P (result) = koenig_p; > diff --git a/gcc/testsuite/g++.dg/cpp0x/error2.C b/gcc/testsuite/g++.dg/cpp0x/error2.C > index e6af294c180..eb966362ccb 100644 > --- a/gcc/testsuite/g++.dg/cpp0x/error2.C > +++ b/gcc/testsuite/g++.dg/cpp0x/error2.C > @@ -3,7 +3,7 @@ > > template<int> int foo(); > > -template<typename F> void bar(F f) > +template<typename F, int N> void bar(F f) > { > - f((foo<0>()=0)...); // { dg-error "pattern '\\(foo\\<0\\>\\)\\(\\)=0'" } > + f((foo<N>()=0)...); // { dg-error "pattern '\\(foo\\<N\\>\\)\\(\\)=0'" } > } > diff --git a/gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C b/gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C > index 833ae6fc85c..60ebad8d1d3 100644 > --- a/gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C > +++ b/gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C > @@ -1,10 +1,7 @@ > // PR c++/86946, DR 1321 > // { dg-do compile { target c++11 } } > > -int d(int, int); > template <long> class e {}; > -template <unsigned long f, unsigned b, typename> e<sizeof(d(f, b))> d(); > -template <unsigned long f, unsigned b, typename> e<d(f, b)> d(); > > template <class T, class U> constexpr T d2(T, U) { return 42; } > template <unsigned long f, unsigned b, typename> e<d2(f, b)> d2(); > @@ -17,7 +14,6 @@ template <unsigned long f, unsigned b, typename> e<sizeof(d3(f, b))> d3(); > > int main() > { > - d<1,2,int>(); > d2<1,2,int>(); > d3<1,2,int>(); > } > diff --git a/gcc/testsuite/g++.dg/template/non-dependent16.C b/gcc/testsuite/g++.dg/template/non-dependent16.C > new file mode 100644 > index 00000000000..ee8ef902529 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/template/non-dependent16.C > @@ -0,0 +1,37 @@ > +// This test verifies that after resolving a non-dependent call expression > +// ahead of time, we prune all but the selected candidate from the overload > +// set. Without this optimization, overload resolution for the final call to > +// f<void>() would be exponential in the size of the overload set. > + > +// { dg-do compile { target c++11 } } > + > +template<class T> void f(); > +template<class T> auto f() -> decltype(f<void>(), 1, *T()); > +template<class T> auto f() -> decltype(f<void>(), 2, *T()); > +template<class T> auto f() -> decltype(f<void>(), 3, *T()); > +template<class T> auto f() -> decltype(f<void>(), 4, *T()); > +template<class T> auto f() -> decltype(f<void>(), 5, *T()); > +template<class T> auto f() -> decltype(f<void>(), 6, *T()); > +template<class T> auto f() -> decltype(f<void>(), 7, *T()); > +template<class T> auto f() -> decltype(f<void>(), 8, *T()); > +template<class T> auto f() -> decltype(f<void>(), 9, *T()); > +template<class T> auto f() -> decltype(f<void>(), 10, *T()); > +template<class T> auto f() -> decltype(f<void>(), 11, *T()); > +template<class T> auto f() -> decltype(f<void>(), 12, *T()); > +template<class T> auto f() -> decltype(f<void>(), 13, *T()); > +template<class T> auto f() -> decltype(f<void>(), 14, *T()); > +template<class T> auto f() -> decltype(f<void>(), 15, *T()); > +template<class T> auto f() -> decltype(f<void>(), 16, *T()); > +template<class T> auto f() -> decltype(f<void>(), 17, *T()); > +template<class T> auto f() -> decltype(f<void>(), 18, *T()); > +template<class T> auto f() -> decltype(f<void>(), 19, *T()); > +template<class T> auto f() -> decltype(f<void>(), 20, *T()); > +template<class T> auto f() -> decltype(f<void>(), 21, *T()); > +template<class T> auto f() -> decltype(f<void>(), 22, *T()); > +template<class T> auto f() -> decltype(f<void>(), 23, *T()); > +template<class T> auto f() -> decltype(f<void>(), 24, *T()); > +template<class T> auto f() -> decltype(f<void>(), 25, *T()); > + > +int main() { > + f<void>(); > +} > diff --git a/gcc/testsuite/g++.dg/template/non-dependent16a.C b/gcc/testsuite/g++.dg/template/non-dependent16a.C > new file mode 100644 > index 00000000000..0e04d646c0b > --- /dev/null > +++ b/gcc/testsuite/g++.dg/template/non-dependent16a.C > @@ -0,0 +1,36 @@ > +// Like non-dependent16.C, but using member functions. > + > +// { dg-do compile { target c++11 } } > + > +struct A { > + template<class T> static void f(); > + template<class T> static auto f() -> decltype(f<void>(), 1, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 2, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 3, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 4, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 5, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 6, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 7, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 8, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 9, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 10, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 11, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 12, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 13, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 14, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 15, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 16, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 17, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 18, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 19, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 20, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 21, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 22, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 23, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 24, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 25, *T()); > +}; > + > +int main() { > + A::f<void>(); > +}
On Wed, 15 Dec 2021, Jason Merrill wrote: > On 12/15/21 12:49, Patrick Palka wrote: > > This patch makes us remember the function selected by overload > > resolution during ahead of time processing of a non-dependent call > > expression, so that we avoid repeating most of the work of overload > > resolution at instantiation time. This mirrors what we already do for > > non-dependent operator expressions via build_min_non_dep_op_overload. > > > > Some caveats: > > > > * When processing ahead of time a non-dependent call to a member > > function template inside a class template (as in > > g++.dg/template/deduce4.C), we end up generating an "inverted" partial > > instantiation such as S<T>::foo<int, int>(), the kinds of which we're > > apparently not prepared to fully instantiate (e.g. tsubst_baselink > > mishandles it). So this patch disables this optimization for such > > functions and adds a FIXME. > > I wonder if it would be worthwhile to build a TEMPLATE_ID_EXPR to remember the > deduced template args, even if we are failing to remember the actual function? Hmm, that transformation could have observable effects, since overload resolution for f<int>(0) might end up instantiating more things than for f(0) due to the explicit-args substitution step: template<class T> struct A { using type = typename T::type; }; template<class T> void f(T); template<class T, class U = typename T::type> typename A<T>::type f(T); Here overload resolution for f(0) succeeds and selects the first overload but for f<int>(0) induces a hard error. Also I worry that such a transformation might affect declaration matching in weird ways due to conflating f(0) with f<int>(0). We could at least though prune the overload set to the corresponding selected function template rather than the "inside-out" specialization; I'll try to implement that. > > > * WHen trying to make the instantiation machinery handle these partial > > instantiations, I made a couple of changes in register_specialization > > and tsubst_function_decl that get us closer to handling such partial > > instantiations and that seem like improvements on their own, so this > > patch includes these changes. > > The tsubst_function_decl change makes me nervous; surely there was some reason > that function wasn't that way in the first place. Let's hold these changes > for stage 1 if they aren't actually fixing anything. Will do. > > > * This change triggered a latent FUNCTION_DECL pretty printing issue > > in cpp0x/error2.C -- since we now resolve the call to foo<0> ahead > > of time, the error now looks like: > > > > error: expansion pattern ‘foo()()=0’ contains no parameter pack > > > > where the FUNCTION_DECL foo is clearly misprinted. But this > > pretty-printing issue could be reproduced without this patch if > > we replace foo with an ordinary function. Since this testcase was > > added to verify pretty printing of TEMPLATE_ID_EXPR, I work around > > this test failure by making the call to foo type-dependent and thus > > immune to this ahead of time pruning. > > > > * We now reject parts of cpp0x/fntmp-equiv1.C because we notice that > > the call d(f, b) in > > > > template <unsigned long f, unsigned b, typename> e<d(f, b)> d(); > > > > isn't constexpr because the (resolved) d isn't. I tried fixing this > > by making d constexpr, but then the call to d from main becomes > > ambiguous. So I settled with removing this part of the testcase. > > > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for > > trunk? Also tested on cmcstl2 and range-v3. > > > > gcc/cp/ChangeLog: > > > > * call.c (build_new_method_call): For a non-dependent call > > expression inside a template, returning a templated tree > > whose overload set contains just the selected function. > > * pt.c (register_specialization): Check only the innermost > > template args for dependence in the early exit test. > > (tsubst_function_decl): Simplify obtaining the template arguments > > for a partial instantiation. > > * semantics.c (finish_call_expr): As with build_new_method_call. > > > > gcc/testsuite/ChangeLog: > > > > * g++.dg/cpp0x/error2.C: Make the call to foo type-dependent in > > order to avoid latent pretty-printing issue for FUNCTION_DECL > > inside MODOP_EXPR. > > * g++.dg/cpp0x/fntmp-equiv1.C: Remove ill-formed parts of > > testcase that we now diagnose. > > * g++.dg/template/non-dependent16.C: New test. > > * g++.dg/template/non-dependent16a.C: New test. > > --- > > gcc/cp/call.c | 17 +++++++++ > > gcc/cp/pt.c | 18 ++------- > > gcc/cp/semantics.c | 15 ++++++++ > > gcc/testsuite/g++.dg/cpp0x/error2.C | 4 +- > > gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C | 4 -- > > .../g++.dg/template/non-dependent16.C | 37 +++++++++++++++++++ > > .../g++.dg/template/non-dependent16a.C | 36 ++++++++++++++++++ > > 7 files changed, 111 insertions(+), 20 deletions(-) > > create mode 100644 gcc/testsuite/g++.dg/template/non-dependent16.C > > create mode 100644 gcc/testsuite/g++.dg/template/non-dependent16a.C > > > > diff --git a/gcc/cp/call.c b/gcc/cp/call.c > > index 53a391cbc6b..92d96c19f5c 100644 > > --- a/gcc/cp/call.c > > +++ b/gcc/cp/call.c > > @@ -11165,6 +11165,23 @@ build_new_method_call (tree instance, tree fns, > > vec<tree, va_gc> **args, > > } > > if (INDIRECT_REF_P (call)) > > call = TREE_OPERAND (call, 0); > > + > > + /* Prune all but the selected function from the original overload > > + set so that we can avoid some duplicate work at instantiation time. > > */ > > + if (really_overloaded_fn (fns)) > > + { > > + if (DECL_TEMPLATE_INFO (fn) > > + && DECL_MEMBER_TEMPLATE_P (DECL_TI_TEMPLATE (fn)) > > + && dependent_type_p (DECL_CONTEXT (fn))) > > + /* FIXME: We're not prepared to fully instantiate "inverted" > > + partial instantiations such as A<T>::f<int>(). */; > > + else > > + { > > + orig_fns = copy_node (orig_fns); > > + BASELINK_FUNCTIONS (orig_fns) = fn; > > + } > > + } > > + > > call = (build_min_non_dep_call_vec > > (call, > > build_min (COMPONENT_REF, TREE_TYPE (CALL_EXPR_FN (call)), > > diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c > > index 2340139b238..b114114e617 100644 > > --- a/gcc/cp/pt.c > > +++ b/gcc/cp/pt.c > > @@ -1566,18 +1566,10 @@ register_specialization (tree spec, tree tmpl, tree > > args, bool is_friend, > > && TREE_CODE (spec) == NONTYPE_ARGUMENT_PACK)); > > if (TREE_CODE (spec) == FUNCTION_DECL > > - && uses_template_parms (DECL_TI_ARGS (spec))) > > + && uses_template_parms (INNERMOST_TEMPLATE_ARGS (DECL_TI_ARGS > > (spec)))) > > /* This is the FUNCTION_DECL for a partial instantiation. Don't > > - register it; we want the corresponding TEMPLATE_DECL instead. > > - We use `uses_template_parms (DECL_TI_ARGS (spec))' rather than > > - the more obvious `uses_template_parms (spec)' to avoid problems > > - with default function arguments. In particular, given > > - something like this: > > - > > - template <class T> void f(T t1, T t = T()) > > - > > - the default argument expression is not substituted for in an > > - instantiation unless and until it is actually needed. */ > > + register it; we want to register the corresponding TEMPLATE_DECL > > + instead. */ > > return spec; > > if (optimize_specialization_lookup_p (tmpl)) > > @@ -13960,9 +13952,7 @@ tsubst_function_decl (tree t, tree args, > > tsubst_flags_t complain, > > /* Calculate the complete set of arguments used to > > specialize R. */ > > - argvec = tsubst_template_args (DECL_TI_ARGS > > - (DECL_TEMPLATE_RESULT > > - (DECL_TI_TEMPLATE (t))), > > + argvec = tsubst_template_args (DECL_TI_ARGS (t), > > args, complain, in_decl); > > if (argvec == error_mark_node) > > return error_mark_node; > > diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c > > index 7078af03d3c..57f689042b9 100644 > > --- a/gcc/cp/semantics.c > > +++ b/gcc/cp/semantics.c > > @@ -2893,6 +2893,21 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, > > bool disallow_virtual, > > { > > if (INDIRECT_REF_P (result)) > > result = TREE_OPERAND (result, 0); > > + > > + /* Prune all but the selected function from the original overload > > + set so that we can avoid some duplicate work at instantiation time. > > */ > > + if (TREE_CODE (result) == CALL_EXPR > > + && really_overloaded_fn (orig_fn)) > > + { > > + orig_fn = CALL_EXPR_FN (result); > > + if (TREE_CODE (orig_fn) == COMPONENT_REF) > > + { > > + /* The result of build_new_method_call. */ > > + orig_fn = TREE_OPERAND (orig_fn, 1); > > + gcc_assert (BASELINK_P (orig_fn)); > > + } > > + } > > + > > result = build_call_vec (TREE_TYPE (result), orig_fn, orig_args); > > SET_EXPR_LOCATION (result, input_location); > > KOENIG_LOOKUP_P (result) = koenig_p; > > diff --git a/gcc/testsuite/g++.dg/cpp0x/error2.C > > b/gcc/testsuite/g++.dg/cpp0x/error2.C > > index e6af294c180..eb966362ccb 100644 > > --- a/gcc/testsuite/g++.dg/cpp0x/error2.C > > +++ b/gcc/testsuite/g++.dg/cpp0x/error2.C > > @@ -3,7 +3,7 @@ > > template<int> int foo(); > > -template<typename F> void bar(F f) > > +template<typename F, int N> void bar(F f) > > { > > - f((foo<0>()=0)...); // { dg-error "pattern '\\(foo\\<0\\>\\)\\(\\)=0'" } > > + f((foo<N>()=0)...); // { dg-error "pattern '\\(foo\\<N\\>\\)\\(\\)=0'" } > > } > > diff --git a/gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C > > b/gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C > > index 833ae6fc85c..60ebad8d1d3 100644 > > --- a/gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C > > +++ b/gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C > > @@ -1,10 +1,7 @@ > > // PR c++/86946, DR 1321 > > // { dg-do compile { target c++11 } } > > -int d(int, int); > > template <long> class e {}; > > -template <unsigned long f, unsigned b, typename> e<sizeof(d(f, b))> d(); > > -template <unsigned long f, unsigned b, typename> e<d(f, b)> d(); > > template <class T, class U> constexpr T d2(T, U) { return 42; } > > template <unsigned long f, unsigned b, typename> e<d2(f, b)> d2(); > > @@ -17,7 +14,6 @@ template <unsigned long f, unsigned b, typename> > > e<sizeof(d3(f, b))> d3(); > > int main() > > { > > - d<1,2,int>(); > > d2<1,2,int>(); > > d3<1,2,int>(); > > } > > diff --git a/gcc/testsuite/g++.dg/template/non-dependent16.C > > b/gcc/testsuite/g++.dg/template/non-dependent16.C > > new file mode 100644 > > index 00000000000..ee8ef902529 > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/template/non-dependent16.C > > @@ -0,0 +1,37 @@ > > +// This test verifies that after resolving a non-dependent call expression > > +// ahead of time, we prune all but the selected candidate from the overload > > +// set. Without this optimization, overload resolution for the final call > > to > > +// f<void>() would be exponential in the size of the overload set. > > + > > +// { dg-do compile { target c++11 } } > > + > > +template<class T> void f(); > > +template<class T> auto f() -> decltype(f<void>(), 1, *T()); > > +template<class T> auto f() -> decltype(f<void>(), 2, *T()); > > +template<class T> auto f() -> decltype(f<void>(), 3, *T()); > > +template<class T> auto f() -> decltype(f<void>(), 4, *T()); > > +template<class T> auto f() -> decltype(f<void>(), 5, *T()); > > +template<class T> auto f() -> decltype(f<void>(), 6, *T()); > > +template<class T> auto f() -> decltype(f<void>(), 7, *T()); > > +template<class T> auto f() -> decltype(f<void>(), 8, *T()); > > +template<class T> auto f() -> decltype(f<void>(), 9, *T()); > > +template<class T> auto f() -> decltype(f<void>(), 10, *T()); > > +template<class T> auto f() -> decltype(f<void>(), 11, *T()); > > +template<class T> auto f() -> decltype(f<void>(), 12, *T()); > > +template<class T> auto f() -> decltype(f<void>(), 13, *T()); > > +template<class T> auto f() -> decltype(f<void>(), 14, *T()); > > +template<class T> auto f() -> decltype(f<void>(), 15, *T()); > > +template<class T> auto f() -> decltype(f<void>(), 16, *T()); > > +template<class T> auto f() -> decltype(f<void>(), 17, *T()); > > +template<class T> auto f() -> decltype(f<void>(), 18, *T()); > > +template<class T> auto f() -> decltype(f<void>(), 19, *T()); > > +template<class T> auto f() -> decltype(f<void>(), 20, *T()); > > +template<class T> auto f() -> decltype(f<void>(), 21, *T()); > > +template<class T> auto f() -> decltype(f<void>(), 22, *T()); > > +template<class T> auto f() -> decltype(f<void>(), 23, *T()); > > +template<class T> auto f() -> decltype(f<void>(), 24, *T()); > > +template<class T> auto f() -> decltype(f<void>(), 25, *T()); > > + > > +int main() { > > + f<void>(); > > +} > > diff --git a/gcc/testsuite/g++.dg/template/non-dependent16a.C > > b/gcc/testsuite/g++.dg/template/non-dependent16a.C > > new file mode 100644 > > index 00000000000..0e04d646c0b > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/template/non-dependent16a.C > > @@ -0,0 +1,36 @@ > > +// Like non-dependent16.C, but using member functions. > > + > > +// { dg-do compile { target c++11 } } > > + > > +struct A { > > + template<class T> static void f(); > > + template<class T> static auto f() -> decltype(f<void>(), 1, *T()); > > + template<class T> static auto f() -> decltype(f<void>(), 2, *T()); > > + template<class T> static auto f() -> decltype(f<void>(), 3, *T()); > > + template<class T> static auto f() -> decltype(f<void>(), 4, *T()); > > + template<class T> static auto f() -> decltype(f<void>(), 5, *T()); > > + template<class T> static auto f() -> decltype(f<void>(), 6, *T()); > > + template<class T> static auto f() -> decltype(f<void>(), 7, *T()); > > + template<class T> static auto f() -> decltype(f<void>(), 8, *T()); > > + template<class T> static auto f() -> decltype(f<void>(), 9, *T()); > > + template<class T> static auto f() -> decltype(f<void>(), 10, *T()); > > + template<class T> static auto f() -> decltype(f<void>(), 11, *T()); > > + template<class T> static auto f() -> decltype(f<void>(), 12, *T()); > > + template<class T> static auto f() -> decltype(f<void>(), 13, *T()); > > + template<class T> static auto f() -> decltype(f<void>(), 14, *T()); > > + template<class T> static auto f() -> decltype(f<void>(), 15, *T()); > > + template<class T> static auto f() -> decltype(f<void>(), 16, *T()); > > + template<class T> static auto f() -> decltype(f<void>(), 17, *T()); > > + template<class T> static auto f() -> decltype(f<void>(), 18, *T()); > > + template<class T> static auto f() -> decltype(f<void>(), 19, *T()); > > + template<class T> static auto f() -> decltype(f<void>(), 20, *T()); > > + template<class T> static auto f() -> decltype(f<void>(), 21, *T()); > > + template<class T> static auto f() -> decltype(f<void>(), 22, *T()); > > + template<class T> static auto f() -> decltype(f<void>(), 23, *T()); > > + template<class T> static auto f() -> decltype(f<void>(), 24, *T()); > > + template<class T> static auto f() -> decltype(f<void>(), 25, *T()); > > +}; > > + > > +int main() { > > + A::f<void>(); > > +} > >
On Thu, 16 Dec 2021, Patrick Palka wrote: > On Wed, 15 Dec 2021, Jason Merrill wrote: > > > On 12/15/21 12:49, Patrick Palka wrote: > > > This patch makes us remember the function selected by overload > > > resolution during ahead of time processing of a non-dependent call > > > expression, so that we avoid repeating most of the work of overload > > > resolution at instantiation time. This mirrors what we already do for > > > non-dependent operator expressions via build_min_non_dep_op_overload. > > > > > > Some caveats: > > > > > > * When processing ahead of time a non-dependent call to a member > > > function template inside a class template (as in > > > g++.dg/template/deduce4.C), we end up generating an "inverted" partial > > > instantiation such as S<T>::foo<int, int>(), the kinds of which we're > > > apparently not prepared to fully instantiate (e.g. tsubst_baselink > > > mishandles it). So this patch disables this optimization for such > > > functions and adds a FIXME. > > > > I wonder if it would be worthwhile to build a TEMPLATE_ID_EXPR to remember the > > deduced template args, even if we are failing to remember the actual function? > > Hmm, that transformation could have observable effects, since overload > resolution for f<int>(0) might end up instantiating more things than for > f(0) due to the explicit-args substitution step: > > template<class T> struct A { using type = typename T::type; }; > > template<class T> void f(T); > template<class T, class U = typename T::type> typename A<T>::type f(T); > > Here overload resolution for f(0) succeeds and selects the first > overload but for f<int>(0) induces a hard error. Also I worry that such > a transformation might affect declaration matching in weird ways due > to conflating f(0) with f<int>(0). > > We could at least though prune the overload set to the corresponding > selected function template rather than the "inside-out" specialization; > I'll try to implement that. Here's a bootstrapped and regtested patch which implements that: -- >8 -- This patch makes us remember the function selected by overload resolution during ahead of time processing of a non-dependent call expression, so that we avoid repeating most of the work of overload resolution for the call at instantiation time. Note that we already do this for non-dependent operator expressions via build_min_non_dep_op_overload. Some caveats: * When processing ahead of time a non-dependent call to a member function template inside a common class template (as in g++.dg/template/deduce4.C), we end up generating an "inverted" partial instantiation such as S<T>::foo<int, int>(), the likes of which we're apparently not prepared to fully instantiate (e.g. tsubst_baselink mishandles it). So this patch makes us prune to the selected template instead of the specialization in this case. * This change triggered a latent FUNCTION_DECL pretty printing issue in cpp0x/error2.C -- since we now resolve the call to foo<0> ahead of time, the error now looks like: error: expansion pattern ‘foo()()=0’ contains no parameter pack where the FUNCTION_DECL for foo<0> is clearly misprinted. But this pretty-printing issue could be reproduced without this patch if we define foo as a non-template function. Since this testcase was added to verify pretty printing of TEMPLATE_ID_EXPR, I work around this test failure by making the call to foo type-dependent and thus immune to this ahead of time pruning. * We now reject parts of cpp0x/fntmp-equiv1.C because we notice that the call d(f, b) in template <unsigned long f, unsigned b, typename> e<d(f, b)> d(); is always non-constexpr because the selected d isn't. I tried fixing this by making it constexpr, but then the call to d from main becomes ambiguous. So I settled with removing this part of the testcase. gcc/cp/ChangeLog: * call.c (build_new_method_call): For a non-dependent call expression inside a template, returning a templated tree whose overload set contains just the selected function. * semantics.c (finish_call_expr): Likewise. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/error2.C: Make the call to foo type-dependent in order to avoid latent pretty-printing issue for FUNCTION_DECL inside MODOP_EXPR. * g++.dg/cpp0x/fntmp-equiv1.C: Remove ill-formed parts of testcase that we now diagnose. * g++.dg/template/non-dependent16.C: New test. * g++.dg/template/non-dependent16a.C: New test. * g++.dg/template/non-dependent17.C: New test. --- gcc/cp/call.c | 27 ++++++++++++++ gcc/cp/semantics.c | 15 ++++++++ gcc/testsuite/g++.dg/cpp0x/error2.C | 4 +- gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C | 4 -- .../g++.dg/template/non-dependent16.C | 37 +++++++++++++++++++ .../g++.dg/template/non-dependent16a.C | 36 ++++++++++++++++++ .../g++.dg/template/non-dependent17.C | 21 +++++++++++ 7 files changed, 138 insertions(+), 6 deletions(-) create mode 100644 gcc/testsuite/g++.dg/template/non-dependent16.C create mode 100644 gcc/testsuite/g++.dg/template/non-dependent16a.C create mode 100644 gcc/testsuite/g++.dg/template/non-dependent17.C diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 495dcdd77b3..1fbfc580a1e 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -11163,6 +11163,33 @@ build_new_method_call (tree instance, tree fns, vec<tree, va_gc> **args, } if (INDIRECT_REF_P (call)) call = TREE_OPERAND (call, 0); + + /* Prune all but the selected function from the original overload + set so that we can avoid some duplicate work at instantiation time. */ + if (really_overloaded_fn (fns)) + { + if (DECL_TEMPLATE_INFO (fn) + && DECL_MEMBER_TEMPLATE_P (DECL_TI_TEMPLATE (fn)) + && dependent_type_p (DECL_CONTEXT (fn))) + { + /* FIXME: We're not prepared to fully instantiate "inside-out" + partial instantiations such as A<T>::f<int>(). So instead + use the selected template, not the specialization. */ + + if (OVL_SINGLE_P (fns)) + /* If the original overload set consists of a single function + template, this isn't beneficial. */ + goto skip_prune; + + fn = ovl_make (DECL_TI_TEMPLATE (fn)); + if (template_only) + fn = lookup_template_function (fn, explicit_targs); + } + orig_fns = copy_node (orig_fns); + BASELINK_FUNCTIONS (orig_fns) = fn; + } + +skip_prune: call = (build_min_non_dep_call_vec (call, build_min (COMPONENT_REF, TREE_TYPE (CALL_EXPR_FN (call)), diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 6603066c620..6ffd82cb0ec 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -2893,6 +2893,21 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual, { if (INDIRECT_REF_P (result)) result = TREE_OPERAND (result, 0); + + /* Prune all but the selected function from the original overload + set so that we can avoid some duplicate work at instantiation time. */ + if (TREE_CODE (result) == CALL_EXPR + && really_overloaded_fn (orig_fn)) + { + orig_fn = CALL_EXPR_FN (result); + if (TREE_CODE (orig_fn) == COMPONENT_REF) + { + /* The non-dependent result of build_new_method_call. */ + orig_fn = TREE_OPERAND (orig_fn, 1); + gcc_assert (BASELINK_P (orig_fn)); + } + } + result = build_call_vec (TREE_TYPE (result), orig_fn, orig_args); SET_EXPR_LOCATION (result, input_location); KOENIG_LOOKUP_P (result) = koenig_p; diff --git a/gcc/testsuite/g++.dg/cpp0x/error2.C b/gcc/testsuite/g++.dg/cpp0x/error2.C index e6af294c180..eb966362ccb 100644 --- a/gcc/testsuite/g++.dg/cpp0x/error2.C +++ b/gcc/testsuite/g++.dg/cpp0x/error2.C @@ -3,7 +3,7 @@ template<int> int foo(); -template<typename F> void bar(F f) +template<typename F, int N> void bar(F f) { - f((foo<0>()=0)...); // { dg-error "pattern '\\(foo\\<0\\>\\)\\(\\)=0'" } + f((foo<N>()=0)...); // { dg-error "pattern '\\(foo\\<N\\>\\)\\(\\)=0'" } } diff --git a/gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C b/gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C index 833ae6fc85c..60ebad8d1d3 100644 --- a/gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C +++ b/gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C @@ -1,10 +1,7 @@ // PR c++/86946, DR 1321 // { dg-do compile { target c++11 } } -int d(int, int); template <long> class e {}; -template <unsigned long f, unsigned b, typename> e<sizeof(d(f, b))> d(); -template <unsigned long f, unsigned b, typename> e<d(f, b)> d(); template <class T, class U> constexpr T d2(T, U) { return 42; } template <unsigned long f, unsigned b, typename> e<d2(f, b)> d2(); @@ -17,7 +14,6 @@ template <unsigned long f, unsigned b, typename> e<sizeof(d3(f, b))> d3(); int main() { - d<1,2,int>(); d2<1,2,int>(); d3<1,2,int>(); } diff --git a/gcc/testsuite/g++.dg/template/non-dependent16.C b/gcc/testsuite/g++.dg/template/non-dependent16.C new file mode 100644 index 00000000000..ee8ef902529 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/non-dependent16.C @@ -0,0 +1,37 @@ +// This test verifies that after resolving a non-dependent call expression +// ahead of time, we prune all but the selected candidate from the overload +// set. Without this optimization, overload resolution for the final call to +// f<void>() would be exponential in the size of the overload set. + +// { dg-do compile { target c++11 } } + +template<class T> void f(); +template<class T> auto f() -> decltype(f<void>(), 1, *T()); +template<class T> auto f() -> decltype(f<void>(), 2, *T()); +template<class T> auto f() -> decltype(f<void>(), 3, *T()); +template<class T> auto f() -> decltype(f<void>(), 4, *T()); +template<class T> auto f() -> decltype(f<void>(), 5, *T()); +template<class T> auto f() -> decltype(f<void>(), 6, *T()); +template<class T> auto f() -> decltype(f<void>(), 7, *T()); +template<class T> auto f() -> decltype(f<void>(), 8, *T()); +template<class T> auto f() -> decltype(f<void>(), 9, *T()); +template<class T> auto f() -> decltype(f<void>(), 10, *T()); +template<class T> auto f() -> decltype(f<void>(), 11, *T()); +template<class T> auto f() -> decltype(f<void>(), 12, *T()); +template<class T> auto f() -> decltype(f<void>(), 13, *T()); +template<class T> auto f() -> decltype(f<void>(), 14, *T()); +template<class T> auto f() -> decltype(f<void>(), 15, *T()); +template<class T> auto f() -> decltype(f<void>(), 16, *T()); +template<class T> auto f() -> decltype(f<void>(), 17, *T()); +template<class T> auto f() -> decltype(f<void>(), 18, *T()); +template<class T> auto f() -> decltype(f<void>(), 19, *T()); +template<class T> auto f() -> decltype(f<void>(), 20, *T()); +template<class T> auto f() -> decltype(f<void>(), 21, *T()); +template<class T> auto f() -> decltype(f<void>(), 22, *T()); +template<class T> auto f() -> decltype(f<void>(), 23, *T()); +template<class T> auto f() -> decltype(f<void>(), 24, *T()); +template<class T> auto f() -> decltype(f<void>(), 25, *T()); + +int main() { + f<void>(); +} diff --git a/gcc/testsuite/g++.dg/template/non-dependent16a.C b/gcc/testsuite/g++.dg/template/non-dependent16a.C new file mode 100644 index 00000000000..0e04d646c0b --- /dev/null +++ b/gcc/testsuite/g++.dg/template/non-dependent16a.C @@ -0,0 +1,36 @@ +// Like non-dependent16.C, but using member functions. + +// { dg-do compile { target c++11 } } + +struct A { + template<class T> static void f(); + template<class T> static auto f() -> decltype(f<void>(), 1, *T()); + template<class T> static auto f() -> decltype(f<void>(), 2, *T()); + template<class T> static auto f() -> decltype(f<void>(), 3, *T()); + template<class T> static auto f() -> decltype(f<void>(), 4, *T()); + template<class T> static auto f() -> decltype(f<void>(), 5, *T()); + template<class T> static auto f() -> decltype(f<void>(), 6, *T()); + template<class T> static auto f() -> decltype(f<void>(), 7, *T()); + template<class T> static auto f() -> decltype(f<void>(), 8, *T()); + template<class T> static auto f() -> decltype(f<void>(), 9, *T()); + template<class T> static auto f() -> decltype(f<void>(), 10, *T()); + template<class T> static auto f() -> decltype(f<void>(), 11, *T()); + template<class T> static auto f() -> decltype(f<void>(), 12, *T()); + template<class T> static auto f() -> decltype(f<void>(), 13, *T()); + template<class T> static auto f() -> decltype(f<void>(), 14, *T()); + template<class T> static auto f() -> decltype(f<void>(), 15, *T()); + template<class T> static auto f() -> decltype(f<void>(), 16, *T()); + template<class T> static auto f() -> decltype(f<void>(), 17, *T()); + template<class T> static auto f() -> decltype(f<void>(), 18, *T()); + template<class T> static auto f() -> decltype(f<void>(), 19, *T()); + template<class T> static auto f() -> decltype(f<void>(), 20, *T()); + template<class T> static auto f() -> decltype(f<void>(), 21, *T()); + template<class T> static auto f() -> decltype(f<void>(), 22, *T()); + template<class T> static auto f() -> decltype(f<void>(), 23, *T()); + template<class T> static auto f() -> decltype(f<void>(), 24, *T()); + template<class T> static auto f() -> decltype(f<void>(), 25, *T()); +}; + +int main() { + A::f<void>(); +} diff --git a/gcc/testsuite/g++.dg/template/non-dependent17.C b/gcc/testsuite/g++.dg/template/non-dependent17.C new file mode 100644 index 00000000000..bc664999e84 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/non-dependent17.C @@ -0,0 +1,21 @@ +// A variant of deduce4.C with multiple overloads of foo. Verify we don't +// crash when after ahead-of-time pruning of the overload set for the +// non-dependent call to foo. +// { dg-do compile } + +template <typename T> +struct S { + template <typename U, typename V> + static void foo(V) { } + template <typename U> + static void foo(...) { } + + void bar () { foo<int>(10); } +}; + +void +test () +{ + S<int> s; + s.bar (); +}
On 12/16/21 14:53, Patrick Palka wrote: > On Thu, 16 Dec 2021, Patrick Palka wrote: > >> On Wed, 15 Dec 2021, Jason Merrill wrote: >> >>> On 12/15/21 12:49, Patrick Palka wrote: >>>> This patch makes us remember the function selected by overload >>>> resolution during ahead of time processing of a non-dependent call >>>> expression, so that we avoid repeating most of the work of overload >>>> resolution at instantiation time. This mirrors what we already do for >>>> non-dependent operator expressions via build_min_non_dep_op_overload. >>>> >>>> Some caveats: >>>> >>>> * When processing ahead of time a non-dependent call to a member >>>> function template inside a class template (as in >>>> g++.dg/template/deduce4.C), we end up generating an "inverted" partial >>>> instantiation such as S<T>::foo<int, int>(), the kinds of which we're >>>> apparently not prepared to fully instantiate (e.g. tsubst_baselink >>>> mishandles it). So this patch disables this optimization for such >>>> functions and adds a FIXME. >>> >>> I wonder if it would be worthwhile to build a TEMPLATE_ID_EXPR to remember the >>> deduced template args, even if we are failing to remember the actual function? >> >> Hmm, that transformation could have observable effects, since overload >> resolution for f<int>(0) might end up instantiating more things than for >> f(0) due to the explicit-args substitution step: >> >> template<class T> struct A { using type = typename T::type; }; >> >> template<class T> void f(T); >> template<class T, class U = typename T::type> typename A<T>::type f(T); >> >> Here overload resolution for f(0) succeeds and selects the first >> overload but for f<int>(0) induces a hard error. Also I worry that such >> a transformation might affect declaration matching in weird ways due >> to conflating f(0) with f<int>(0). >> >> We could at least though prune the overload set to the corresponding >> selected function template rather than the "inside-out" specialization; >> I'll try to implement that. > > Here's a bootstrapped and regtested patch which implements that: OK. > -- >8 -- > > This patch makes us remember the function selected by overload resolution > during ahead of time processing of a non-dependent call expression, so > that we avoid repeating most of the work of overload resolution for the > call at instantiation time. Note that we already do this for > non-dependent operator expressions via build_min_non_dep_op_overload. > > Some caveats: > > * When processing ahead of time a non-dependent call to a member > function template inside a common class template (as in > g++.dg/template/deduce4.C), we end up generating an "inverted" partial > instantiation such as S<T>::foo<int, int>(), the likes of which we're > apparently not prepared to fully instantiate (e.g. tsubst_baselink > mishandles it). So this patch makes us prune to the selected > template instead of the specialization in this case. > > * This change triggered a latent FUNCTION_DECL pretty printing issue > in cpp0x/error2.C -- since we now resolve the call to foo<0> ahead > of time, the error now looks like: > > error: expansion pattern ‘foo()()=0’ contains no parameter pack > > where the FUNCTION_DECL for foo<0> is clearly misprinted. But this > pretty-printing issue could be reproduced without this patch if > we define foo as a non-template function. Since this testcase was > added to verify pretty printing of TEMPLATE_ID_EXPR, I work around > this test failure by making the call to foo type-dependent and thus > immune to this ahead of time pruning. > > * We now reject parts of cpp0x/fntmp-equiv1.C because we notice that > the call d(f, b) in > > template <unsigned long f, unsigned b, typename> e<d(f, b)> d(); > > is always non-constexpr because the selected d isn't. I tried fixing > this by making it constexpr, but then the call to d from main becomes > ambiguous. So I settled with removing this part of the testcase. > > gcc/cp/ChangeLog: > > * call.c (build_new_method_call): For a non-dependent call > expression inside a template, returning a templated tree > whose overload set contains just the selected function. > * semantics.c (finish_call_expr): Likewise. > > gcc/testsuite/ChangeLog: > > * g++.dg/cpp0x/error2.C: Make the call to foo type-dependent in > order to avoid latent pretty-printing issue for FUNCTION_DECL > inside MODOP_EXPR. > * g++.dg/cpp0x/fntmp-equiv1.C: Remove ill-formed parts of > testcase that we now diagnose. > * g++.dg/template/non-dependent16.C: New test. > * g++.dg/template/non-dependent16a.C: New test. > * g++.dg/template/non-dependent17.C: New test. > --- > gcc/cp/call.c | 27 ++++++++++++++ > gcc/cp/semantics.c | 15 ++++++++ > gcc/testsuite/g++.dg/cpp0x/error2.C | 4 +- > gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C | 4 -- > .../g++.dg/template/non-dependent16.C | 37 +++++++++++++++++++ > .../g++.dg/template/non-dependent16a.C | 36 ++++++++++++++++++ > .../g++.dg/template/non-dependent17.C | 21 +++++++++++ > 7 files changed, 138 insertions(+), 6 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/template/non-dependent16.C > create mode 100644 gcc/testsuite/g++.dg/template/non-dependent16a.C > create mode 100644 gcc/testsuite/g++.dg/template/non-dependent17.C > > diff --git a/gcc/cp/call.c b/gcc/cp/call.c > index 495dcdd77b3..1fbfc580a1e 100644 > --- a/gcc/cp/call.c > +++ b/gcc/cp/call.c > @@ -11163,6 +11163,33 @@ build_new_method_call (tree instance, tree fns, vec<tree, va_gc> **args, > } > if (INDIRECT_REF_P (call)) > call = TREE_OPERAND (call, 0); > + > + /* Prune all but the selected function from the original overload > + set so that we can avoid some duplicate work at instantiation time. */ > + if (really_overloaded_fn (fns)) > + { > + if (DECL_TEMPLATE_INFO (fn) > + && DECL_MEMBER_TEMPLATE_P (DECL_TI_TEMPLATE (fn)) > + && dependent_type_p (DECL_CONTEXT (fn))) > + { > + /* FIXME: We're not prepared to fully instantiate "inside-out" > + partial instantiations such as A<T>::f<int>(). So instead > + use the selected template, not the specialization. */ > + > + if (OVL_SINGLE_P (fns)) > + /* If the original overload set consists of a single function > + template, this isn't beneficial. */ > + goto skip_prune; > + > + fn = ovl_make (DECL_TI_TEMPLATE (fn)); > + if (template_only) > + fn = lookup_template_function (fn, explicit_targs); > + } > + orig_fns = copy_node (orig_fns); > + BASELINK_FUNCTIONS (orig_fns) = fn; > + } > + > +skip_prune: > call = (build_min_non_dep_call_vec > (call, > build_min (COMPONENT_REF, TREE_TYPE (CALL_EXPR_FN (call)), > diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c > index 6603066c620..6ffd82cb0ec 100644 > --- a/gcc/cp/semantics.c > +++ b/gcc/cp/semantics.c > @@ -2893,6 +2893,21 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual, > { > if (INDIRECT_REF_P (result)) > result = TREE_OPERAND (result, 0); > + > + /* Prune all but the selected function from the original overload > + set so that we can avoid some duplicate work at instantiation time. */ > + if (TREE_CODE (result) == CALL_EXPR > + && really_overloaded_fn (orig_fn)) > + { > + orig_fn = CALL_EXPR_FN (result); > + if (TREE_CODE (orig_fn) == COMPONENT_REF) > + { > + /* The non-dependent result of build_new_method_call. */ > + orig_fn = TREE_OPERAND (orig_fn, 1); > + gcc_assert (BASELINK_P (orig_fn)); > + } > + } > + > result = build_call_vec (TREE_TYPE (result), orig_fn, orig_args); > SET_EXPR_LOCATION (result, input_location); > KOENIG_LOOKUP_P (result) = koenig_p; > diff --git a/gcc/testsuite/g++.dg/cpp0x/error2.C b/gcc/testsuite/g++.dg/cpp0x/error2.C > index e6af294c180..eb966362ccb 100644 > --- a/gcc/testsuite/g++.dg/cpp0x/error2.C > +++ b/gcc/testsuite/g++.dg/cpp0x/error2.C > @@ -3,7 +3,7 @@ > > template<int> int foo(); > > -template<typename F> void bar(F f) > +template<typename F, int N> void bar(F f) > { > - f((foo<0>()=0)...); // { dg-error "pattern '\\(foo\\<0\\>\\)\\(\\)=0'" } > + f((foo<N>()=0)...); // { dg-error "pattern '\\(foo\\<N\\>\\)\\(\\)=0'" } > } > diff --git a/gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C b/gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C > index 833ae6fc85c..60ebad8d1d3 100644 > --- a/gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C > +++ b/gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C > @@ -1,10 +1,7 @@ > // PR c++/86946, DR 1321 > // { dg-do compile { target c++11 } } > > -int d(int, int); > template <long> class e {}; > -template <unsigned long f, unsigned b, typename> e<sizeof(d(f, b))> d(); > -template <unsigned long f, unsigned b, typename> e<d(f, b)> d(); > > template <class T, class U> constexpr T d2(T, U) { return 42; } > template <unsigned long f, unsigned b, typename> e<d2(f, b)> d2(); > @@ -17,7 +14,6 @@ template <unsigned long f, unsigned b, typename> e<sizeof(d3(f, b))> d3(); > > int main() > { > - d<1,2,int>(); > d2<1,2,int>(); > d3<1,2,int>(); > } > diff --git a/gcc/testsuite/g++.dg/template/non-dependent16.C b/gcc/testsuite/g++.dg/template/non-dependent16.C > new file mode 100644 > index 00000000000..ee8ef902529 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/template/non-dependent16.C > @@ -0,0 +1,37 @@ > +// This test verifies that after resolving a non-dependent call expression > +// ahead of time, we prune all but the selected candidate from the overload > +// set. Without this optimization, overload resolution for the final call to > +// f<void>() would be exponential in the size of the overload set. > + > +// { dg-do compile { target c++11 } } > + > +template<class T> void f(); > +template<class T> auto f() -> decltype(f<void>(), 1, *T()); > +template<class T> auto f() -> decltype(f<void>(), 2, *T()); > +template<class T> auto f() -> decltype(f<void>(), 3, *T()); > +template<class T> auto f() -> decltype(f<void>(), 4, *T()); > +template<class T> auto f() -> decltype(f<void>(), 5, *T()); > +template<class T> auto f() -> decltype(f<void>(), 6, *T()); > +template<class T> auto f() -> decltype(f<void>(), 7, *T()); > +template<class T> auto f() -> decltype(f<void>(), 8, *T()); > +template<class T> auto f() -> decltype(f<void>(), 9, *T()); > +template<class T> auto f() -> decltype(f<void>(), 10, *T()); > +template<class T> auto f() -> decltype(f<void>(), 11, *T()); > +template<class T> auto f() -> decltype(f<void>(), 12, *T()); > +template<class T> auto f() -> decltype(f<void>(), 13, *T()); > +template<class T> auto f() -> decltype(f<void>(), 14, *T()); > +template<class T> auto f() -> decltype(f<void>(), 15, *T()); > +template<class T> auto f() -> decltype(f<void>(), 16, *T()); > +template<class T> auto f() -> decltype(f<void>(), 17, *T()); > +template<class T> auto f() -> decltype(f<void>(), 18, *T()); > +template<class T> auto f() -> decltype(f<void>(), 19, *T()); > +template<class T> auto f() -> decltype(f<void>(), 20, *T()); > +template<class T> auto f() -> decltype(f<void>(), 21, *T()); > +template<class T> auto f() -> decltype(f<void>(), 22, *T()); > +template<class T> auto f() -> decltype(f<void>(), 23, *T()); > +template<class T> auto f() -> decltype(f<void>(), 24, *T()); > +template<class T> auto f() -> decltype(f<void>(), 25, *T()); > + > +int main() { > + f<void>(); > +} > diff --git a/gcc/testsuite/g++.dg/template/non-dependent16a.C b/gcc/testsuite/g++.dg/template/non-dependent16a.C > new file mode 100644 > index 00000000000..0e04d646c0b > --- /dev/null > +++ b/gcc/testsuite/g++.dg/template/non-dependent16a.C > @@ -0,0 +1,36 @@ > +// Like non-dependent16.C, but using member functions. > + > +// { dg-do compile { target c++11 } } > + > +struct A { > + template<class T> static void f(); > + template<class T> static auto f() -> decltype(f<void>(), 1, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 2, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 3, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 4, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 5, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 6, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 7, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 8, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 9, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 10, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 11, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 12, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 13, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 14, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 15, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 16, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 17, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 18, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 19, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 20, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 21, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 22, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 23, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 24, *T()); > + template<class T> static auto f() -> decltype(f<void>(), 25, *T()); > +}; > + > +int main() { > + A::f<void>(); > +} > diff --git a/gcc/testsuite/g++.dg/template/non-dependent17.C b/gcc/testsuite/g++.dg/template/non-dependent17.C > new file mode 100644 > index 00000000000..bc664999e84 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/template/non-dependent17.C > @@ -0,0 +1,21 @@ > +// A variant of deduce4.C with multiple overloads of foo. Verify we don't > +// crash when after ahead-of-time pruning of the overload set for the > +// non-dependent call to foo. > +// { dg-do compile } > + > +template <typename T> > +struct S { > + template <typename U, typename V> > + static void foo(V) { } > + template <typename U> > + static void foo(...) { } > + > + void bar () { foo<int>(10); } > +}; > + > +void > +test () > +{ > + S<int> s; > + s.bar (); > +}
diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 53a391cbc6b..92d96c19f5c 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -11165,6 +11165,23 @@ build_new_method_call (tree instance, tree fns, vec<tree, va_gc> **args, } if (INDIRECT_REF_P (call)) call = TREE_OPERAND (call, 0); + + /* Prune all but the selected function from the original overload + set so that we can avoid some duplicate work at instantiation time. */ + if (really_overloaded_fn (fns)) + { + if (DECL_TEMPLATE_INFO (fn) + && DECL_MEMBER_TEMPLATE_P (DECL_TI_TEMPLATE (fn)) + && dependent_type_p (DECL_CONTEXT (fn))) + /* FIXME: We're not prepared to fully instantiate "inverted" + partial instantiations such as A<T>::f<int>(). */; + else + { + orig_fns = copy_node (orig_fns); + BASELINK_FUNCTIONS (orig_fns) = fn; + } + } + call = (build_min_non_dep_call_vec (call, build_min (COMPONENT_REF, TREE_TYPE (CALL_EXPR_FN (call)), diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 2340139b238..b114114e617 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -1566,18 +1566,10 @@ register_specialization (tree spec, tree tmpl, tree args, bool is_friend, && TREE_CODE (spec) == NONTYPE_ARGUMENT_PACK)); if (TREE_CODE (spec) == FUNCTION_DECL - && uses_template_parms (DECL_TI_ARGS (spec))) + && uses_template_parms (INNERMOST_TEMPLATE_ARGS (DECL_TI_ARGS (spec)))) /* This is the FUNCTION_DECL for a partial instantiation. Don't - register it; we want the corresponding TEMPLATE_DECL instead. - We use `uses_template_parms (DECL_TI_ARGS (spec))' rather than - the more obvious `uses_template_parms (spec)' to avoid problems - with default function arguments. In particular, given - something like this: - - template <class T> void f(T t1, T t = T()) - - the default argument expression is not substituted for in an - instantiation unless and until it is actually needed. */ + register it; we want to register the corresponding TEMPLATE_DECL + instead. */ return spec; if (optimize_specialization_lookup_p (tmpl)) @@ -13960,9 +13952,7 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain, /* Calculate the complete set of arguments used to specialize R. */ - argvec = tsubst_template_args (DECL_TI_ARGS - (DECL_TEMPLATE_RESULT - (DECL_TI_TEMPLATE (t))), + argvec = tsubst_template_args (DECL_TI_ARGS (t), args, complain, in_decl); if (argvec == error_mark_node) return error_mark_node; diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 7078af03d3c..57f689042b9 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -2893,6 +2893,21 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual, { if (INDIRECT_REF_P (result)) result = TREE_OPERAND (result, 0); + + /* Prune all but the selected function from the original overload + set so that we can avoid some duplicate work at instantiation time. */ + if (TREE_CODE (result) == CALL_EXPR + && really_overloaded_fn (orig_fn)) + { + orig_fn = CALL_EXPR_FN (result); + if (TREE_CODE (orig_fn) == COMPONENT_REF) + { + /* The result of build_new_method_call. */ + orig_fn = TREE_OPERAND (orig_fn, 1); + gcc_assert (BASELINK_P (orig_fn)); + } + } + result = build_call_vec (TREE_TYPE (result), orig_fn, orig_args); SET_EXPR_LOCATION (result, input_location); KOENIG_LOOKUP_P (result) = koenig_p; diff --git a/gcc/testsuite/g++.dg/cpp0x/error2.C b/gcc/testsuite/g++.dg/cpp0x/error2.C index e6af294c180..eb966362ccb 100644 --- a/gcc/testsuite/g++.dg/cpp0x/error2.C +++ b/gcc/testsuite/g++.dg/cpp0x/error2.C @@ -3,7 +3,7 @@ template<int> int foo(); -template<typename F> void bar(F f) +template<typename F, int N> void bar(F f) { - f((foo<0>()=0)...); // { dg-error "pattern '\\(foo\\<0\\>\\)\\(\\)=0'" } + f((foo<N>()=0)...); // { dg-error "pattern '\\(foo\\<N\\>\\)\\(\\)=0'" } } diff --git a/gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C b/gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C index 833ae6fc85c..60ebad8d1d3 100644 --- a/gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C +++ b/gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C @@ -1,10 +1,7 @@ // PR c++/86946, DR 1321 // { dg-do compile { target c++11 } } -int d(int, int); template <long> class e {}; -template <unsigned long f, unsigned b, typename> e<sizeof(d(f, b))> d(); -template <unsigned long f, unsigned b, typename> e<d(f, b)> d(); template <class T, class U> constexpr T d2(T, U) { return 42; } template <unsigned long f, unsigned b, typename> e<d2(f, b)> d2(); @@ -17,7 +14,6 @@ template <unsigned long f, unsigned b, typename> e<sizeof(d3(f, b))> d3(); int main() { - d<1,2,int>(); d2<1,2,int>(); d3<1,2,int>(); } diff --git a/gcc/testsuite/g++.dg/template/non-dependent16.C b/gcc/testsuite/g++.dg/template/non-dependent16.C new file mode 100644 index 00000000000..ee8ef902529 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/non-dependent16.C @@ -0,0 +1,37 @@ +// This test verifies that after resolving a non-dependent call expression +// ahead of time, we prune all but the selected candidate from the overload +// set. Without this optimization, overload resolution for the final call to +// f<void>() would be exponential in the size of the overload set. + +// { dg-do compile { target c++11 } } + +template<class T> void f(); +template<class T> auto f() -> decltype(f<void>(), 1, *T()); +template<class T> auto f() -> decltype(f<void>(), 2, *T()); +template<class T> auto f() -> decltype(f<void>(), 3, *T()); +template<class T> auto f() -> decltype(f<void>(), 4, *T()); +template<class T> auto f() -> decltype(f<void>(), 5, *T()); +template<class T> auto f() -> decltype(f<void>(), 6, *T()); +template<class T> auto f() -> decltype(f<void>(), 7, *T()); +template<class T> auto f() -> decltype(f<void>(), 8, *T()); +template<class T> auto f() -> decltype(f<void>(), 9, *T()); +template<class T> auto f() -> decltype(f<void>(), 10, *T()); +template<class T> auto f() -> decltype(f<void>(), 11, *T()); +template<class T> auto f() -> decltype(f<void>(), 12, *T()); +template<class T> auto f() -> decltype(f<void>(), 13, *T()); +template<class T> auto f() -> decltype(f<void>(), 14, *T()); +template<class T> auto f() -> decltype(f<void>(), 15, *T()); +template<class T> auto f() -> decltype(f<void>(), 16, *T()); +template<class T> auto f() -> decltype(f<void>(), 17, *T()); +template<class T> auto f() -> decltype(f<void>(), 18, *T()); +template<class T> auto f() -> decltype(f<void>(), 19, *T()); +template<class T> auto f() -> decltype(f<void>(), 20, *T()); +template<class T> auto f() -> decltype(f<void>(), 21, *T()); +template<class T> auto f() -> decltype(f<void>(), 22, *T()); +template<class T> auto f() -> decltype(f<void>(), 23, *T()); +template<class T> auto f() -> decltype(f<void>(), 24, *T()); +template<class T> auto f() -> decltype(f<void>(), 25, *T()); + +int main() { + f<void>(); +} diff --git a/gcc/testsuite/g++.dg/template/non-dependent16a.C b/gcc/testsuite/g++.dg/template/non-dependent16a.C new file mode 100644 index 00000000000..0e04d646c0b --- /dev/null +++ b/gcc/testsuite/g++.dg/template/non-dependent16a.C @@ -0,0 +1,36 @@ +// Like non-dependent16.C, but using member functions. + +// { dg-do compile { target c++11 } } + +struct A { + template<class T> static void f(); + template<class T> static auto f() -> decltype(f<void>(), 1, *T()); + template<class T> static auto f() -> decltype(f<void>(), 2, *T()); + template<class T> static auto f() -> decltype(f<void>(), 3, *T()); + template<class T> static auto f() -> decltype(f<void>(), 4, *T()); + template<class T> static auto f() -> decltype(f<void>(), 5, *T()); + template<class T> static auto f() -> decltype(f<void>(), 6, *T()); + template<class T> static auto f() -> decltype(f<void>(), 7, *T()); + template<class T> static auto f() -> decltype(f<void>(), 8, *T()); + template<class T> static auto f() -> decltype(f<void>(), 9, *T()); + template<class T> static auto f() -> decltype(f<void>(), 10, *T()); + template<class T> static auto f() -> decltype(f<void>(), 11, *T()); + template<class T> static auto f() -> decltype(f<void>(), 12, *T()); + template<class T> static auto f() -> decltype(f<void>(), 13, *T()); + template<class T> static auto f() -> decltype(f<void>(), 14, *T()); + template<class T> static auto f() -> decltype(f<void>(), 15, *T()); + template<class T> static auto f() -> decltype(f<void>(), 16, *T()); + template<class T> static auto f() -> decltype(f<void>(), 17, *T()); + template<class T> static auto f() -> decltype(f<void>(), 18, *T()); + template<class T> static auto f() -> decltype(f<void>(), 19, *T()); + template<class T> static auto f() -> decltype(f<void>(), 20, *T()); + template<class T> static auto f() -> decltype(f<void>(), 21, *T()); + template<class T> static auto f() -> decltype(f<void>(), 22, *T()); + template<class T> static auto f() -> decltype(f<void>(), 23, *T()); + template<class T> static auto f() -> decltype(f<void>(), 24, *T()); + template<class T> static auto f() -> decltype(f<void>(), 25, *T()); +}; + +int main() { + A::f<void>(); +}