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()?

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.

@@ -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?

@@ -287,7 +288,7 @@ struct GTY(()) cp_binding_level {
   /* The kind of scope that this object represents.  However, a
       SK_TEMPLATE_SPEC scope is represented with KIND set to
       SK_TEMPLATE_PARMS and EXPLICIT_SPEC_P set to true.  */
-  ENUM_BITFIELD (scope_kind) kind : 4;
+  ENUM_BITFIELD (scope_kind) kind : 5;
/* True if this scope is an SK_TEMPLATE_SPEC scope. This field is
       only valid if KIND == SK_TEMPLATE_PARMS.  */

You also need to adjust the count of available bits.

@@ -11807,6 +11807,9 @@ cp_parser_lambda_expression (cp_parser* parser)
     if (type == error_mark_node)
       return error_mark_node;
+ /* 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);
     record_lambda_scope (lambda_expr);
     record_lambda_scope_discriminator (lambda_expr);

I would put the begin_scope after the record_lambda_scope*, since the latter are about the enclosing scope.

@@ -11857,6 +11860,10 @@ cp_parser_lambda_expression (cp_parser* parser)
     if (cp_parser_start_tentative_firewall (parser))
       start = token;
+ /* Inject the captures for the sake of the possible
+       trailing-return-type -- we have to be able to look them up.  */
+    push_capture_proxies (lambda_expr);

...maybe this far after, since the proxies are what live in the lambda scope?

Jason

Reply via email to