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?
decay_conversion has complete_type_or_maybe_complain. > 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. Ah I meant to get back to this and never did. We certainly don't need a separate op() for each capture, but do you mean you want one op() for each closure? > See also a few comments below. [ Thanks, I'll get to it this Sun/Mon. ] > > So I still don't have anything better. > > > > > > > > Another thing is that in "-> decltype(j)" we don't have the > > > > > > > > right > > > > > > > > current_function_decl yet, so I've added the > > > > > > > > in_lambda_declarator_p flag > > > > > > > > to be used in finish_decltype_type so that we know this > > > > > > > > decltype appertains > > > > > > > > to a lambda -- then current_lambda_expr should give us the > > > > > > > > right lambda, > > > > > > > > which has another new flag tracking whether mutable was seen. > > > > > > > > > > The flag to finish_decltype_type seems unneeded; we should be able to > > > > > tell > > > > > from the proxy that it belongs to a lambda. And I would think that > > > > > the new > > > > > handling in finish_decltype_type seems right in general; always refer > > > > > to > > > > > current_lambda_expr instead of current_function_decl, etc. > > > > > > > > Good point. I've removed the flag and simplified the patch quite a bit. > > > > However: > > > > - to honor [expr.prim.id.unqual]/4, I have to know if the decltype is > > > > in the lambda's parameter-declaration-clause or not: > > > > > > > > [=]() -> decltype((x)) // float const& > > > > [=](decltype((x)) y) // float& > > > > > > > > so I'm using LAMBDA_EXPR_CONST_QUAL_P for that. > > > > > > Makes sense. > > > > > > > - if we want to handle nested lambdas correctly: > > > > > > > > [=](decltype((x)) y) {} // float& > > > > > > > > [=] { > > > > [](decltype((x)) y) {}; // float const& > > > > } > > > > > > > > we probably will need a new flag for decltype. > > > > > > Hmm? Since the inner lambda has no capture-default, it doesn't qualify > > > under https://eel.is/c++draft/expr#prim.id.unqual-4.3 , so we look to the > > > outer lambda instead. > > > > > > I would believe that we need to improve finish_decltype_type to handle > > > this > > > properly (see also PR112926) but I don't see the need for a decltype flag. > > > > I'll come back to this later. > > > > > > > > @@ -3351,8 +3351,12 @@ check_local_shadow (tree decl) > > > > > > } > > > > > > /* Don't complain if it's from an enclosing function. */ > > > > > > else if (DECL_CONTEXT (old) == current_function_decl > > > > > > - && TREE_CODE (decl) != PARM_DECL > > > > > > - && TREE_CODE (old) == PARM_DECL) > > > > > > + && ((TREE_CODE (decl) != PARM_DECL > > > > > > + && TREE_CODE (old) == PARM_DECL) > > > > > > + || (is_capture_proxy (old) > > > > > > + && current_lambda_expr () > > > > > > + && DECL_CONTEXT (old) > > > > > > + == lambda_function (current_lambda_expr ())))) > > > > > > > > > > What case is this handling? Doesn't the previous if already deal with > > > > > parm/capture collision? > > > > > > > > The proposal says that > > > > > > > > [x=1]{ int x; } > > > > > > > > is invalid, so I wanted to give an error for it. But since -Wshadow > > > > warns for the case above, I've dropped that hunk and that simplifies > > > > the patch even more. > > > > > > It is good to give an error, not just a -Wshadow warning; now that I > > > understand the case you're trying to handle the above hunk makes more > > > sense, > > > just please add a comment. But for this case you shouldn't need to handle > > > proxies before the function body, so DECL_CONTEXT == current_function_decl > > > should be enough? > > > > Ok, I've put it back. And yes -- I don't have to check current_lambda_expr! > > So it's simpler now. > > > > Here's what I have now. > > > > -- >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. > > > > Another thing is that in "-> decltype(j)" we don't have the right > > current_function_decl yet. If current_lambda_expr gives us a lambda, > > we know this decltype appertains to a lambda. But we have to know if we > > are in a parameter-declaration-clause: as per [expr.prim.id.unqual]/4.4, > > if we are, we shouldn't be adding "const". The new LAMBDA_EXPR_CONST_QUAL_P > > flag tracks this. But it doesn't handle nested lambdas yet, specifically, > > [expr.prim.id.unqual]/14. > > > > I don't think this patch changes behavior for the tests in > > "capture-default with [=]" as the paper promises; clang++ behaves the > > same as gcc with this patch. > > > > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc > > index 0d9ed2ea82b..d238ecc3432 100644 > > --- a/gcc/cp/parser.cc > > +++ b/gcc/cp/parser.cc > > @@ -11868,6 +11868,14 @@ cp_parser_lambda_expression (cp_parser* parser) > > if (cp_parser_start_tentative_firewall (parser)) > > start = token; > > + /* A lambda scope starts immediately after the lambda-introducer of E > > + and extends to the end of the compound-statement of E. */ > > + begin_scope (sk_lambda, NULL_TREE); > > + > > + /* Inject the captures for the sake of the possible > > + trailing-return-type -- we have to be able to look them up. */ > > This comment seems misleading since they now are in scope throughout the > lambda declarator. > > > + push_capture_proxies (lambda_expr); > > + > > ok &= cp_parser_lambda_declarator_opt (parser, lambda_expr); > > if (ok && cp_parser_error_occurred (parser)) > > diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc > > index 86b09049677..4d07a00f89a 100644 > > --- a/gcc/cp/semantics.cc > > +++ b/gcc/cp/semantics.cc > > @@ -4514,6 +4514,14 @@ outer_automatic_var_p (tree decl) > > && !TREE_STATIC (decl)); > > } > > +/* Returns true iff DECL is an automatic variable. */ > > + > > +static bool > > +automatic_var_p (tree decl) > > +{ > > + return VAR_P (decl) && !TREE_STATIC (decl); > > +} > > + > > /* DECL satisfies outer_automatic_var_p. Possibly complain about it or > > rewrite it for lambda capture. > > @@ -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 ()? > > What value does cfun_is_lambda_p get if we're in a lambda-declarator inside > another lambda? > > > { > > /* [expr.prim.id.unqual]/3: If naming the entity from outside of an > > unevaluated operand within S would refer to an entity captured by > > @@ -12908,7 +12919,7 @@ finish_decltype_type (tree expr, bool > > id_expression_or_member_access_p, > > And we don't handle nested lambdas properly, where we need to > > consider the outer lambdas as well (PR112926). */ > > tree decl = STRIP_REFERENCE_REF (expr); > > - tree lam = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT > > (current_function_decl)); > > + tree lam = current_lambda_expr (); > > tree cap = lookup_name (DECL_NAME (decl), LOOK_where::BLOCK, > > LOOK_want::HIDDEN_LAMBDA); > > @@ -12924,17 +12935,26 @@ finish_decltype_type (tree expr, bool > > id_expression_or_member_access_p, > > if (type && !TYPE_REF_P (type)) > > { > > - tree obtype = TREE_TYPE (DECL_ARGUMENTS (current_function_decl)); > > - if (WILDCARD_TYPE_P (non_reference (obtype))) > > - /* We don't know what the eventual obtype quals will be. */ > > - goto dependent; > > - auto direct_type = [](tree t){ > > - if (INDIRECT_TYPE_P (t)) > > - return TREE_TYPE (t); > > - return t; > > - }; > > - int const quals = cp_type_quals (type) > > - | cp_type_quals (direct_type (obtype)); > > + int quals; > > + if (cfun_is_lambda_p) > > + { > > + tree obtype = TREE_TYPE (DECL_ARGUMENTS > > (current_function_decl)); > > + if (WILDCARD_TYPE_P (non_reference (obtype))) > > + /* We don't know what the eventual obtype quals will be. */ > > + goto dependent; > > + auto direct_type = [](tree t){ > > + if (INDIRECT_TYPE_P (t)) > > + return TREE_TYPE (t); > > + return t; > > + }; > > + quals = (cp_type_quals (type) > > + | cp_type_quals (direct_type (obtype))); > > We might change this block to just be for the explicit object parm case, and > handle all other lambdas with LAMBDA_EXPR_CONST_QUAL_P? > > > + } > > + else > > + /* We are in the parameter clause, trailing return type, or > > + the requires clause and have no relevant c_f_decl yet. */ > > + quals = (LAMBDA_EXPR_CONST_QUAL_P (lam) > > + ? TYPE_QUAL_CONST : TYPE_UNQUALIFIED); > > type = cp_build_qualified_type (type, quals); > > type = build_reference_type (type); > > } > Marek