On Wed, 10 Apr 2024, Jason Merrill wrote:

> On 3/27/24 10:01, Patrick Palka wrote:
> > On Mon, 25 Mar 2024, Patrick Palka wrote:
> > > On Mon, 25 Mar 2024, Patrick Palka wrote:
> > > > 
> > > > The below testcases use a lambda-expr as a template argument and they
> > > > all trip over the below added tsubst_lambda_expr sanity check ultimately
> > > > because current_template_parms is empty, which causes push_template_decl
> > > > to return error_mark_node from the call to begin_lambda_type.  Were it
> > > > not for the sanity check this silent error_mark_node result leads to
> > > > nonsensical errors down the line, or silent breakage.
> > > > 
> > > > In the first testcase, we hit this assert during instantiation of the
> > > > dependent alias template-id c1_t<_Data> from instantiate_template, which
> > > > clears current_template_parms via push_to_top_level.  Similar story for
> > > > the second testcase.  For the third testcase we hit the assert during
> > > > partial instantiation of the member template from
> > > > instantiate_class_template
> > > > which similarly calls push_to_top_level.
> > > > 
> > > > These testcases illustrate that templated substitution into a
> > > > lambda-expr
> > > > is not always possible, in particular when we lost the relevant template
> > > > context.  I experimented with recovering the template context by making
> > > > tsubst_lambda_expr fall back to using scope_chain->prev->template_parms
> > > > if
> > > > current_template_parms is empty which worked but seemed like a hack.  I
> > > > also experimented with preserving the template context by keeping
> > > > current_template_parms set during instantiate_template for a dependent
> > > > specialization which also worked but it's at odds with the fact that we
> > > > cache dependent specializations (and so they should be independent of
> > > > the template context).
> 
> I suspect the problem comes from this bit in type_unification_real:
> 
> >                   /* First instatiate in template context, in case we still
> > depend on undeduced template parameters.  */
> >                   ++processing_template_decl;
> >                   substed = tsubst_template_arg (arg, full_targs, complain,
> >                                                  NULL_TREE);
> >                   --processing_template_decl;
> >                   if (substed != error_mark_node
> >                       && !uses_template_parms (substed))
> >                     /* We replaced all the tparms, substitute again out of
> > template context.  */
> >                     substed = NULL_TREE;
> 
> and perhaps we should switch to searching the argument for undeduced template
> parameters rather than doing a trial substitution.

Interesting, you also mentioned that approach would help fix PR101463
(which I need to get back to):
https://gcc.gnu.org/pipermail/gcc-patches/2024-January/642340.html

> 
> But the pattern of setting processing_template_decl, substituting, and
> clearing it again is very widespread, so we still want to handle lambdas in
> that context.
> 
> > > > +  if (processing_template_decl && !in_template_context)
> > > > +    {
> > > > +      /* Defer templated substitution into a lambda-expr when arguments
> > > > +        are dependent or when we lost the necessary template context,
> > > > +        which may happen for a lambda-expr used as a template 
> > > > argument.  */
> > > 
> > > And this comment is stale (an earlier version of the patch also deferred
> > > for dependent arguments even when current_template_parms is non-empty,
> > > which I backed out to make the fix as narrow as possible).
> > 
> > FWIW I also experimented with unconditionally deferring templated
> > substitution into a lambda-expr (i.e. iff processing_template_decl)
> > which passed bootstrap+regtest, and turns out to also fix the
> > (non-regression) PR114167.  I didn't analyze the underlying issue
> > very closely though, there might very well be a better way to fix
> > that particular non-regression PR.
> > 
> > One downside of unconditionally deferring is that it'd mean less
> > ahead-of-time checking of uninvoked deeply-nested generic lambdas,
> > e.g.:
> > 
> >    int main() {
> >      [](auto x) {
> >        [](auto) {
> >          [](auto) { decltype(x)::fail; }; // not diagnosed anymore
> >        };
> >      }(0);
> >    }
> 
> Hmm, unconditionally deferring would probably also help to resolve issues with
> local classes in generic lambdas.  It might be worth going that way rather
> than continue to grapple with partial substitution problems.
> 
> > > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> > > index 8cf0d5b7a8d..c25bdd283f1 100644
> > > --- a/gcc/cp/pt.cc
> > > +++ b/gcc/cp/pt.cc
> > > @@ -19571,6 +19572,18 @@ tsubst_lambda_expr (tree t, tree args,
> > > tsubst_flags_t complain, tree in_decl)
> > >     tree oldfn = lambda_function (t);
> > >     in_decl = oldfn;
> > >   +  args = add_extra_args (LAMBDA_EXPR_EXTRA_ARGS (t), args, complain,
> > > in_decl);
> > > +  if (processing_template_decl && !in_template_context)
> > > +    {
> > > +      /* Defer templated substitution into a lambda-expr when we lost the
> > > +  necessary template context, which may happen for a lambda-expr
> > > +  used as a template argument.  */
> 
> as a *default* template argument.  OK with that tweak.

Thanks a lot, pushed as r14-9938-g081c1e93d56d35.  Unfortunately this
isn't enough to fix the original testcase from the PR due a related but
distinct return type confusion issue.  It ultimately seems we need to
defer substitution in more cases, not just when we don't have a template
context.  I submitted a follow-up patch at
https://gcc.gnu.org/pipermail/gcc-patches/2024-April/649387.html

The patch makes us defer for all dependent substitution.
Unconditionally deferring would do the trick as well, but I reckon
a narrower fix might be better at this point.

> 
> Jason
> 
> 

Reply via email to