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