On Wed, Jul 30, 2025 at 11:55:53PM -0400, Jason Merrill wrote:
> On 7/25/25 4:55 PM, Marek Polacek wrote:
> > On Thu, Jul 17, 2025 at 05:20:31PM -0400, Jason Merrill wrote:
> > > On 7/16/25 5:59 PM, Marek Polacek wrote:
> > > > On Mon, Jul 14, 2025 at 12:52:41PM -0400, Jason Merrill wrote:
> > > > > On 7/11/25 5:49 PM, Marek Polacek wrote:
> > > > > > On Thu, Jul 10, 2025 at 02:13:06PM -0400, Jason Merrill wrote:
> > > > > > > On 7/9/25 4:27 PM, Marek Polacek wrote:
> > > > > > > > On Tue, Jul 08, 2025 at 12:15:03PM -0400, Jason Merrill wrote:
> > > > > > > > > On 7/7/25 4:52 PM, Marek Polacek wrote:
> > > > > > > > > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> > > > > > > > > > 
> > > > > > > > > > -- >8 --
> > > > > > > > > > This patch is an attempt to implement P2036R3 along with 
> > > > > > > > > > P2579R0, fixing
> > > > > > > > > > build breakages caused by P2036R3.
> > > > > > > > > > 
> > > > > > > > > > The simplest example is:
> > > > > > > > > > 
> > > > > > > > > >        auto counter1 = [j=0]() mutable -> decltype(j) {
> > > > > > > > > >            return j++;
> > > > > > > > > >        };
> > > > > > > > > > 
> > > > > > > > > > which currently doesn't compile because the 'j' in the 
> > > > > > > > > > capture isn't
> > > > > > > > > > visible in the trailing return type.  With these proposals, 
> > > > > > > > > > the 'j'
> > > > > > > > > > will be in a lambda scope which spans the trailing return 
> > > > > > > > > > type, so
> > > > > > > > > > this test will compile.
> > > > > > > > > > 
> > > > > > > > > > This oughtn't be difficult but decltype and other issues 
> > > > > > > > > > made this patch
> > > > > > > > > > much more challenging.
> > > > > > > > > > 
> > > > > > > > > > We have to push the explicit captures before going into 
> > > > > > > > > > _lambda_declarator_opt
> > > > > > > > > > because that is what parses the trailing return type.  Yet 
> > > > > > > > > > we can't build
> > > > > > > > > > any captures until after _lambda_body -> 
> > > > > > > > > > start_lambda_function which
> > > > > > > > > > creates the lambda's operator(), without which we can't 
> > > > > > > > > > build a proxy,
> > > > > > > > > > but _lambda_body happens only after parsing the declarator. 
> > > > > > > > > >  This patch
> > > > > > > > > > works around it by creating a fake operator() in 
> > > > > > > > > > make_dummy_lambda_op.
> > > > > > > > > 
> > > > > > > > > I was thinking that we could build the real operator() 
> > > > > > > > > earlier, before the
> > > > > > > > > trailing return type, so that it's there for the above uses, 
> > > > > > > > > and then splice
> > > > > > > > > in the trailing return type to the already-built function 
> > > > > > > > > declaration,
> > > > > > > > > perhaps with apply_deduced_return_type.
> > > > > > > > 
> > > > > > > > Ah, I see what you mean.  But it's not just the return type 
> > > > > > > > that we don't
> > > > > > > > have at the point where we have to have the operator(): it's 
> > > > > > > > also tx_qual,
> > > > > > > > exception_spec, std_attrs, and trailing_requires_clause.  
> > > > > > > > Especially the
> > > > > > > > requires clause seems to be awkward to set post grokmethod; it 
> > > > > > > > seems I'd
> > > > > > > > have to replicate the flag_concepts block in grokfndecl?
> > > > > > > > 
> > > > > > > > Maybe I could add (by that I mean add it to the lambda via
> > > > > > > > finish_member_declaration) a bare bones operator() for the 
> > > > > > > > purposes of
> > > > > > > > parsing the return type/noexcept/requires, then after parsing 
> > > > > > > > them
> > > > > > > > construct a real operator(), then find a slot of the bare bones 
> > > > > > > > op(),
> > > > > > > > and replace it with the complete one.  I'm not sure if that 
> > > > > > > > makes sense
> > > > > > > > to do though.
> > > > > > > 
> > > > > > > I was hoping to avoid building more than one op().  But really, 
> > > > > > > why do you
> > > > > > > need an op() at all for building the proxies?  Could you use
> > > > > > > build_dummy_object instead of DECL_ARGUMENTS of some fake op()?
> > > > > > 
> > > > > > The problem is that we need operator() to be the var's DECL_CONTEXT
> > > > > > for is_capture_proxy:
> > > > > > 
> > > > > >      && LAMBDA_FUNCTION_P (DECL_CONTEXT (decl)));
> > > > > 
> > > > > Maybe we could set their DECL_CONTEXT to the closure type and adjust
> > > > > is_capture_proxy to handle that case as well?
> > > > 
> > > > Ah, now I recall why I had so much trouble when I tried that in GCC 15.
> > > > The problem is that the closure type is not complete at that point, and
> > > > that causes problems.  E.g., equal2 in lambda-scope3.C has
> > > > 
> > > >     return [t = t](const auto& obj) -> decltype(obj == t)
> > > > 
> > > > and the incomplete 't' in the decltype is problematic for 
> > > > cp_build_binary_op.
> > > 
> > > Hmm, problematic how?
> > 
> > [ Responded elsewhere. ]
> > > In any case, I guess it's reasonable to build a dummy op(), but building a
> > > new one for each capture is not; they should all use the same op(). Adding
> > > it to the closure and then clobbering it later (perhaps by getting
> > > duplicate_decls to accept them as duplicates?) seems like a fine way to do
> > > that.
> > 
> > I've overhauled the patch to use one dummy op() per lambda.  But it was
> > pretty complicated.
> > 
> > I still have to do the whole make_call_declarator + grokmethod dance once
> > for the dummy op() and once for the real op() (and then when instantiating);
> > there could have been a requires clause/attributes on the real op() and
> > I can't simply clobber the old decl.
> > 
> > When finish_member_declaration -> add_method wants to add the real op(),
> > the dummy op() can't be in the closure or else we fail with "cannot be
> > overloaded".  So I have to remove the dummy op() prior to add_method,
> > this is done via remove_dummy_lambda_op.
> 
> Maybe we could special case add_method to combine them instead of giving an
> error?  ...but perhaps that won't be significantly less hackish than this
> approach.

Yeah, probably not by much :/.
 
> > Now that even dummy lambdas have an operator(), I had to tweak
> > resolvable_dummy_lambda to give the right answer.  This I did by
> > checking DECL_LAMBDA_FUNCTION_P which isn't set for the dummy op().
> 
> Hmm, that's a new one to me.  Perhaps we should remove LAMBDA_FUNCTION_P
> now?

I would like to remove it but if I'm using it here then I guess I can't,
yet.

> In any case this new check needs a comment.

Added.

> > In tsubst_lambda_expr I have to add the dummy op() as well.  That meant
> > I had to play some push_nested_namespace/class games, kind of like in
> > instantiate_decl.
> 
> Hmm, when I comment out the push/pop, none of the tests break?

Ah, yes.  That's because I started only adding the dummy op() if there
are no captures.  I've added lambda-scope9.C (reduced from lambda-uneval17.C)
to show the error we'd get without the push/pop.  Unfortunately it
captures a non-automatic variable.  I don't know if there is a better
way to test it.

> > If there are no explicit captures, as it's often the case, we can skip
> > the dummy op() altogether.
> 
> > > > @@ -12888,9 +12896,12 @@ finish_decltype_type (tree expr, bool 
> > > > id_expression_or_member_access_p,
> > > >        }
> > > >      else
> > > >        {
> > > > -      if (outer_automatic_var_p (STRIP_REFERENCE_REF (expr))
> > > > -         && current_function_decl
> > > > -         && LAMBDA_FUNCTION_P (current_function_decl))
> > > > +      const bool cfun_is_lambda_p
> > > > +       = (current_function_decl && LAMBDA_FUNCTION_P 
> > > > (current_function_decl));
> > > > +      if ((outer_automatic_var_p (STRIP_REFERENCE_REF (expr))
> > > > +          && cfun_is_lambda_p)
> > > > +         || (automatic_var_p (STRIP_REFERENCE_REF (expr))
> > > > +             && current_lambda_expr ()))
> > > 
> > > Can we make outer_automatic_var_p give the right answer in this context
> > > instead of adding a separate automatic_var_p, and then just change the
> > > current_function_decl test to current_lambda_expr ()?
> > 
> > Sorry, I had no luck with this.  First I think process_outer_var_ref
> > can't handle these vars.  Also, like in lambda-generic-ice5.C, we can't
> > tell that in:
> > 
> >    int f = 3;
> >    auto l = [](auto of_type_X)->
> >                Void<(decltype(of_type_X)::foo(f), 0)>
> >      {return;};
> > 
> > 'f' is outer, but 'of_type_X' is not.
> 
> But we need to make that distinction.  Which shouldn't be so hard, since
> they should have different DECL_CONTEXT and are in different binding levels.

Sorry, I don't understand.  Yes, we need to make that distinction.
But outer_automatic_var_p only gets one decl, and otherwise only looks
at current_function_decl.  And for both 'f' and 'of_type_X'
DECL_CONTEXT (decl) == current_function_decl.  And for both there is
current_lambda_expr ().
 
> And to address the FIXMEs there, the way process_outer_var_ref walks through
> intervening lambdas seems like exactly what we need to do.

FIXMEs in finish_decltype_type?

Marek

Reply via email to