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 [216.205.24.124]) by sourceware.org (Postfix) with ESMTP id 0DBA53959E54 for ; Wed, 30 Jun 2021 20:18:40 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 0DBA53959E54 Received: from mail-ej1-f69.google.com (mail-ej1-f69.google.com [209.85.218.69]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-311-OKTUXXnIMLi_mgd9NAy5hg-1; Wed, 30 Jun 2021 16:18:37 -0400 X-MC-Unique: OKTUXXnIMLi_mgd9NAy5hg-1 Received: by mail-ej1-f69.google.com with SMTP id bz12-20020a1709070aacb02904cae86564c7so1161343ejc.6 for ; Wed, 30 Jun 2021 13:18:37 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=nM+NrfTiaUlIQMJqzgCZESidjr/0xKOueFiLlIV7ulk=; b=ij6JhlCvW1Fjd0yyckmKGafoJn4tOLWPSM1vFQ2qOSQI6/nWJtRVBHICe0YbI0fRGb mj6EQ4Gdw7SILTLoT4nwkxnq/f60k1zgAcMnne92ddvqggvmapUngj4fuOkjYcYbJ+9L e3frk8EuEX19RpTWZZMkBnX5lG73aowSMOjMaduC4ptQFsM9LyGgLFQPY0VakmRxlYy+ X/lKf74pcQa3P5Z8gHPs0KO/PAUdEWudHsF24IRIyDbwF5gFDoEIpiFmdEbCDNseWZQt iB/mBtk1Ki0D8FOkQ7imMVB11wCdULZXvYczkdnL+GUPrJC79/C15YUleHXxsik17yg3 jdFQ== X-Gm-Message-State: AOAM533noIr582gSjYSPtDXxmEGUPJKSqkNBNJnfSbkYuo4szT2AR1BA ZVVA/QV88zfgr5zELVYQ8/WCu30WWldsiqnkU17F5+iS8f0h3C0FFbxJ3VYxZckDMLetasfssMU czoaKzxu0KEuCambdpfuzOvRv6qroa2WqOQ== X-Received: by 2002:a17:906:ce29:: with SMTP id sd9mr37765416ejb.56.1625084315352; Wed, 30 Jun 2021 13:18:35 -0700 (PDT) X-Google-Smtp-Source: ABdhPJw6QzyGR4ewqmqz4GbF4Mrih955qToqIUUT0X7KTogNrnnhFnaS3XYbIqiRJmfXqNPej+VeHoazpN0PdxOegw0= X-Received: by 2002:a17:906:ce29:: with SMTP id sd9mr37765400ejb.56.1625084315006; Wed, 30 Jun 2021 13:18:35 -0700 (PDT) MIME-Version: 1.0 References: <20210624204529.4136660-1-ppalka@redhat.com> <3802ce35-3e48-fb97-8868-d1274ed0a778@redhat.com> <0719adf2-7a17-a8a7-9374-cbfa41846499@redhat.com> <8b6edd86-9db0-f6e2-386f-4693d1692f5f@idea> <67855539-bf99-ac7-a995-35871cd23712@idea> <7f944b48-7c8f-755a-8cea-0880cbda4ccc@redhat.com> In-Reply-To: <7f944b48-7c8f-755a-8cea-0880cbda4ccc@redhat.com> From: Patrick Palka Date: Wed, 30 Jun 2021 16:18:23 -0400 Message-ID: Subject: Re: [PATCH] c++: CTAD within alias template [PR91911] To: Jason Merrill Cc: GCC Patches X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset="UTF-8" X-Spam-Status: No, score=-14.5 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_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 30 Jun 2021 20:18:42 -0000 On Wed, Jun 30, 2021 at 3:51 PM Jason Merrill wrote: > > On 6/30/21 11:58 AM, Patrick Palka wrote: > > On Wed, 30 Jun 2021, Patrick Palka wrote: > > > >> On Fri, 25 Jun 2021, Jason Merrill wrote: > >> > >>> On 6/25/21 1:11 PM, Patrick Palka wrote: > >>>> On Fri, 25 Jun 2021, Jason Merrill wrote: > >>>> > >>>>> On 6/24/21 4:45 PM, Patrick Palka wrote: > >>>>>> In the first testcase below, during parsing of the alias template > >>>>>> ConstSpanType, transparency of alias template specializations means we > >>>>>> replace SpanType with SpanType's substituted definition. But this > >>>>>> substitution lowers the level of the CTAD placeholder for span(T()) from > >>>>>> 2 to 1, and so the later instantiantion of ConstSpanType > >>>>>> erroneously substitutes this CTAD placeholder with the template argument > >>>>>> at level 1 index 0, i.e. with int, before we get a chance to perform the > >>>>>> CTAD. > >>>>>> > >>>>>> In light of this, it seems we should avoid level lowering when > >>>>>> substituting through through the type-id of a dependent alias template > >>>>>> specialization. To that end this patch makes lookup_template_class_1 > >>>>>> pass tf_partial to tsubst in this situation. > >>>>> > >>>>> This makes sense, but what happens if SpanType is a member template, so > >>>>> that > >>>>> the levels of it and ConstSpanType don't match? Or the other way around? > >>>> > >>>> If SpanType is a member template of say the class template A (and > >>>> thus its level is greater than ConstSpanType): > >>>> > >>>> template > >>>> struct A { > >>>> template > >>>> using SpanType = decltype(span(T())); > >>>> }; > >>>> > >>>> template > >>>> using ConstSpanType = span >>>> A::SpanType::value_type>; > >>>> > >>>> using type = ConstSpanType; > >>>> > >>>> then this case luckily works even without the patch because > >>>> instantiate_class_template now reuses the specialization A::SpanType > >>>> that was formed earlier during instantiation of A, where we > >>>> substitute only a single level of template arguments, so the level of > >>>> the CTAD placeholder inside the defining-type-id of this specialization > >>>> dropped from 3 to 2, so still more than the level of ConstSpanType. > >>>> > >>>> This luck is short-lived though, because if we replace > >>>> A::SpanType with say A::SpanType then the testcase > >>>> breaks again (without the patch) because we no longer can reuse that > >>>> specialization, so we instead form it on the spot by substituting two > >>>> levels of template arguments (U=int,T=T) into the defining-type-id, > >>>> causing the level of the placeholder to drop to 1. I think the patch > >>>> causes its level to remain 3 (though I guess it should really be 2). > >>>> > >>>> > >>>> For the other way around, if ConstSpanType is a member template of > >>>> say the class template B (and thus its level is greater than > >>>> SpanType): > >>>> > >>>> template > >>>> using SpanType = decltype(span(T())); > >>>> > >>>> template > >>>> struct B { > >>>> template > >>>> using ConstSpanType = span::value_type>; > >>>> }; > >>>> > >>>> using type = B::ConstSpanType; > >>>> > >>>> then tf_partial doesn't help here at all; we end up substituting 'int' > >>>> for the CTAD placeholder... What it seems we need is to _increase_ the > >>>> level of the CTAD placeholder from 2 to 3 during the dependent > >>>> substitution.. > >>>> > >>>> Hmm, rather than messing with tf_partial, which is apparently only a > >>>> partial solution, maybe we should just make tsubst never substitute a > >>>> CTAD placeholder -- they should always be resolved from do_class_deduction, > >>>> and their level doesn't really matter otherwise. (But we'd still want > >>>> to substitute into the CLASS_PLACEHOLDER_TEMPLATE of the placeholder in > >>>> case it's a template template parm.) Something like: > >>>> > >>>> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c > >>>> index 5107bfbf9d1..dead651ed84 100644 > >>>> --- a/gcc/cp/pt.c > >>>> +++ b/gcc/cp/pt.c > >>>> @@ -15552,7 +15550,8 @@ tsubst (tree t, tree args, tsubst_flags_t complain, > >>>> tree in_decl) > >>>> levels = TMPL_ARGS_DEPTH (args); > >>>> if (level <= levels > >>>> - && TREE_VEC_LENGTH (TMPL_ARGS_LEVEL (args, level)) > 0) > >>>> + && TREE_VEC_LENGTH (TMPL_ARGS_LEVEL (args, level)) > 0 > >>>> + && !template_placeholder_p (t)) > >>>> { > >>>> arg = TMPL_ARG (args, level, idx); > >>>> > >>>> seems to work better. > >>> > >>> Makes sense. > >> > >> Here's a patch that implements that. I reckon it's good to have both > >> workarounds in place because the tf_partial workaround is necessary to > >> accept class-deduction93a.C below, and the tsubst workaround is > >> necessary to accept class-deduction-92b.C below. > > > > Whoops, forgot to git-add class-deduction93a.C: > > > > -- >8 -- > > > > Subject: [PATCH] c++: CTAD within alias template [PR91911] > > > > In the first testcase below, during parsing of the alias template > > ConstSpanType, transparency of alias template specializations means we > > replace SpanType with SpanType's substituted definition. But this > > substitution lowers the level of the CTAD placeholder for span{T()} from > > 2 to 1, and so the later instantiation of ConstSpanType erroneously > > substitutes this CTAD placeholder with the template argument at level 1 > > index 0, i.e. with int, before we get a chance to perform the CTAD. > > > > In light of this, it seems we should avoid level lowering when > > substituting through the type-id of a dependent alias template > > specialization. To that end this patch makes lookup_template_class_1 > > pass tf_partial to tsubst in this situation. > > > > Unfortunately, using tf_partial alone isn't sufficient because the > > template context in which we perform the dependent substitution may > > have more levels than the substituted alias template and so we > > end up substituting the CTAD placeholder anyway, as in > > class-deduction92b.C below. (There, it seems we'd need to _increase_ > > the level of the placeholder for span{T()} from 2 to 3 during the > > dependent substitution.) Since we never want to resolve a CTAD > > placeholder outside of CTAD proper, this patch takes the relatively > > ad-hoc approach of making tsubst explicitly avoid doing so. > > > > This tsubst workaround doesn't obviate the tf_partial workaround because > > it's still desirable to avoid prematurely level lowering a CTAD placeholder: > > it's less work for the compiler, and it gives us a chance to substitute > > a template placeholder that's a template template parameter with a > > concrete template template argument, as in the last testcase below. > > Hmm, what if we combine the template template parameter with the level > mismatch? So for e.g. template class Tmpl> using CallableTraitT = CallableTrait; template struct A { template class Tmpl> using ReturnType = typename CallableTraitT::ReturnType; }; using type = A::ReturnType; using type = int; sadly we crash, because during the dependent substitution of the innermost arguments into the defining-type-id, tf_partial means we don't lower the level of the CTAD placeholder and therefore don't substitute into CLASS_PLACEHOLDER_TEMPLATE, so CLASS_PLACEHOLDER_TEMPLATE remains a template template parm at index 1 level 1 (as opposed to level 2). Later during the full instantiation, there is no such template template argument at that position (it's at index 1 level 2 rather). To handle this testcase, it seems we need a way to substitute into CTAD placeholders without lowering their level I guess. > > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for > > trunk? > > > > PR c++/91911 > > > > gcc/cp/ChangeLog: > > > > * pt.c (lookup_template_class_1): When looking up a dependent > > alias template specialization, pass tf_partial to tsubst. > > (tsubst) : Avoid substituting a CTAD > > placeholder. > > > > gcc/testsuite/ChangeLog: > > > > * g++.dg/cpp1z/class-deduction92.C: New test. > > * g++.dg/cpp1z/class-deduction92a.C: New test. > > * g++.dg/cpp1z/class-deduction92b.C: New test. > > * g++.dg/cpp1z/class-deduction93.C: New test. > > * g++.dg/cpp1z/class-deduction93a.C: New test. > > --- > > gcc/cp/pt.c | 15 +++++++++-- > > .../g++.dg/cpp1z/class-deduction92.C | 17 ++++++++++++ > > .../g++.dg/cpp1z/class-deduction92a.C | 22 +++++++++++++++ > > .../g++.dg/cpp1z/class-deduction92b.C | 22 +++++++++++++++ > > .../g++.dg/cpp1z/class-deduction93.C | 25 +++++++++++++++++ > > .../g++.dg/cpp1z/class-deduction93a.C | 27 +++++++++++++++++++ > > 6 files changed, 126 insertions(+), 2 deletions(-) > > create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction92.C > > create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction92a.C > > create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction92b.C > > create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction93.C > > create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction93a.C > > > > diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c > > index f2039e09cd7..db769d59951 100644 > > --- a/gcc/cp/pt.c > > +++ b/gcc/cp/pt.c > > @@ -9954,7 +9954,12 @@ lookup_template_class_1 (tree d1, tree arglist, tree in_decl, tree context, > > template-arguments for the template-parameters in the > > type-id of the alias template. */ > > > > - t = tsubst (TREE_TYPE (gen_tmpl), arglist, complain, in_decl); > > + /* When substituting a dependent alias template specialization, > > + we pass tf_partial to avoid lowering the level of any 'auto's > > + within its type-id (91911). */ > > + t = tsubst (TREE_TYPE (gen_tmpl), arglist, > > + complain | (tf_partial * is_dependent_type), > > + in_decl); > > /* Note that the call above (by indirectly calling > > register_specialization in tsubst_decl) registers the > > TYPE_DECL representing the specialization of the alias > > @@ -15544,9 +15549,15 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) > > gcc_assert (TREE_VEC_LENGTH (args) > 0); > > template_parm_level_and_index (t, &level, &idx); > > > > + /* Retrieve the argument for this template parameter. */ > > levels = TMPL_ARGS_DEPTH (args); > > if (level <= levels > > - && TREE_VEC_LENGTH (TMPL_ARGS_LEVEL (args, level)) > 0) > > + && TREE_VEC_LENGTH (TMPL_ARGS_LEVEL (args, level)) > 0 > > + /* Avoid substituting CTAD placeholders; they get > > + resolved only during CTAD proper. We can get here > > + after dependent substitution of an alias template > > + whose defining-type-id uses CTAD (91911). */ > > + && !template_placeholder_p (t)) > > { > > arg = TMPL_ARG (args, level, idx); > > > > diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction92.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction92.C > > new file mode 100644 > > index 00000000000..379eb960da6 > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction92.C > > @@ -0,0 +1,17 @@ > > +// PR c++/91911 > > +// { dg-do compile { target c++17 } } > > + > > +template > > +struct span { > > + using value_type = T; > > + span(T); > > +}; > > + > > +template > > +using SpanType = decltype(span{T()}); > > + > > +template > > +using ConstSpanType = span::value_type>; > > + > > +using type = ConstSpanType; > > +using type = span; > > diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction92a.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction92a.C > > new file mode 100644 > > index 00000000000..b9aa8f3bbf0 > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction92a.C > > @@ -0,0 +1,22 @@ > > +// PR c++/91911 > > +// { dg-do compile { target c++17 } } > > +// A variant of class-deduction92.C where SpanType has more levels than > > +// ConstSpanType. > > + > > +template > > +struct span { > > + using value_type = T; > > + span(T); > > +}; > > + > > +template > > +struct A { > > + template > > + using SpanType = decltype(span{T()}); > > +}; > > + > > +template > > +using ConstSpanType = span::SpanType::value_type>; > > + > > +using type = ConstSpanType; > > +using type = span; > > diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction92b.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction92b.C > > new file mode 100644 > > index 00000000000..0ea0cef0238 > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction92b.C > > @@ -0,0 +1,22 @@ > > +// PR c++/91911 > > +// { dg-do compile { target c++17 } } > > +// A variant of class-deduction92.C where SpanType has fewer levels than > > +// ConstSpanType. > > + > > +template > > +struct span { > > + using value_type = T; > > + span(T); > > +}; > > + > > +template > > +using SpanType = decltype(span{T()}); > > + > > +template > > +struct B { > > + template > > + using ConstSpanType = span::value_type>; > > +}; > > + > > +using type = B::ConstSpanType; > > +using type = span; > > diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction93.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction93.C > > new file mode 100644 > > index 00000000000..20504780d32 > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction93.C > > @@ -0,0 +1,25 @@ > > +// PR c++/98077 > > +// { dg-do compile { target c++17 } } > > + > > +template > > +struct function { > > + template function(T); > > + using type = R; > > +}; > > + > > +template function(T) -> function; > > + > > +template > > +struct CallableTrait; > > + > > +template > > +struct CallableTrait> { using ReturnType = R; }; > > + > > +template > > +using CallableTraitT = CallableTrait; > > + > > +template > > +using ReturnType = typename CallableTraitT::ReturnType; > > + > > +using type = ReturnType; > > +using type = int; > > diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction93a.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction93a.C > > new file mode 100644 > > index 00000000000..7656e7845fe > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction93a.C > > @@ -0,0 +1,27 @@ > > +// PR c++/98077 > > +// { dg-do compile { target c++17 } } > > +// A variant of class-deduction93.C where the template placeholder is a template > > +// template parameter. > > + > > +template > > +struct function { > > + template function(T); > > + using type = R; > > +}; > > + > > +template function(T) -> function; > > + > > +template > > +struct CallableTrait; > > + > > +template > > +struct CallableTrait> { using ReturnType = R; }; > > + > > +template class Tmpl> > > +using CallableTraitT = CallableTrait; > > + > > +template class Tmpl> > > +using ReturnType = typename CallableTraitT::ReturnType; > > + > > +using type = ReturnType; > > +using type = int; > > >