Bug 102610 - [C++23] P2036R3 - Change scope of lambda trailing-return-type
Summary: [C++23] P2036R3 - Change scope of lambda trailing-return-type
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 12.0
: P3 enhancement
Target Milestone: ---
Assignee: Marek Polacek
URL:
Keywords: c++23
Depends on: 117602
Blocks: c++23-core
  Show dependency treegraph
 
Reported: 2021-10-05 16:13 UTC by Marek Polacek
Modified: 2026-01-09 14:56 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2022-08-16 00:00:00


Attachments
WIP patch (9.13 KB, patch)
2024-11-18 16:36 UTC, Marek Polacek
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Marek Polacek 2021-10-05 16:13:54 UTC
See https://wg21.link/p2036r3
Comment 1 Marek Polacek 2022-08-16 16:16:52 UTC
https://wg21.link/p2579r0, Mitigation strategies for P2036 ”Changing scope for lambda trailing-return-type”, was also approved.
Comment 2 Jakub Jelinek 2023-08-31 11:38:44 UTC
Related question for the last paper: https://github.com/cplusplus/draft/issues/6536
Other than that, I guess opening a new sk_lambda scope in cp_parser_lambda_declarator_opt at the start and leaving it at the end should be easy,
but we only build_capture_proxy later on.  Shall we pushdecl into the sk_lambda scope
instead the LAMBDA_CAPTURE_EXPLICIT_P && !DECL_NORMAL_CAPTURE_P captures and somehow special-case them in name lookup or just finish_decltype_type where we currently handle
the
      if (outer_automatic_var_p (expr)
          && current_function_decl
          && LAMBDA_FUNCTION_P (current_function_decl))
        type = capture_decltype (expr);
case?  Though, in the lambda declarator, current_function_decl is still the containing function and outer_automatic_var_p also will not work.  I guess we need some way to know whether we are in the lambda declarator (and also whether it is before the end of
parameter declarations or after and whether in the latter case the lambda is mutable or not) and special case in that case both the init-captures and automatic? vars from the current function (which will be outer vars in lambda body).
Comment 3 Marek Polacek 2024-11-18 16:36:28 UTC
Created attachment 59623 [details]
WIP patch

Regtested/bootstrapped patch that needs polishing for GCC 16.
Comment 4 Marek Polacek 2024-11-26 19:47:56 UTC
Mine for GCC 16.
Comment 5 GCC Commits 2025-08-13 22:54:38 UTC
The trunk branch has been updated by Marek Polacek <mpolacek@gcc.gnu.org>:

https://gcc.gnu.org/g:d2dccd1bf79b862b9989c1b62ed8c074980cd457

commit r16-3195-gd2dccd1bf79b862b9989c1b62ed8c074980cd457
Author: Marek Polacek <polacek@redhat.com>
Date:   Wed Nov 13 16:56:40 2024 -0500

    c++: P2036R3 - Change scope of lambda trailing-return-type [PR102610]
    
    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() and adding it to the capture and then removing it when we
    have the real operator().
    
    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.
    
            PR c++/102610
    
    gcc/cp/ChangeLog:
    
            * cp-tree.h (LAMBDA_EXPR_CONST_QUAL_P): Define.
            (maybe_add_dummy_lambda_op): Declare.
            (remove_dummy_lambda_op): Declare.
            (push_capture_proxies): Adjust.
            * lambda.cc (build_capture_proxy): No longer static.  New early_p
            parameter.  Use it.
            (add_capture): Adjust the call to build_capture_proxy.
            (resolvable_dummy_lambda): Check DECL_LAMBDA_FUNCTION_P.
            (push_capture_proxies): New.
            (start_lambda_function): Use it.
            * name-lookup.cc (check_local_shadow): Give an error for
            is_capture_proxy.
            (cp_binding_level_descriptor): Add lambda-scope.
            (begin_scope) <case sk_lambda>: New case.
            * name-lookup.h (enum scope_kind): Add sk_lambda.
            (struct cp_binding_level): Widen kind.
            * parser.cc (cp_parser_lambda_expression): Create a new (lambda) scope
            after the lambda-introducer.
            (cp_parser_lambda_declarator_opt): Set LAMBDA_EXPR_CONST_QUAL_P.
            Create a dummy operator() if needed.  Inject the captures into the
            lambda scope.  Remove the dummy operator().
            (make_dummy_lambda_op): New.
            (maybe_add_dummy_lambda_op): New.
            (remove_dummy_lambda_op): New.
            * pt.cc (tsubst_lambda_expr): Begin/end a lambda scope.  Push the
            capture proxies.  Build/remove a dummy operator() if needed.  Set
            LAMBDA_EXPR_CONST_QUAL_P.
            * semantics.cc (parsing_lambda_declarator): New.
            (outer_var_p): Also consider captures as outer variables if in a lambda
            declarator.
            (process_outer_var_ref): Reset containing_function when
            parsing_lambda_declarator.
            (finish_decltype_type): Process decls in the lambda-declarator as well.
            Look at LAMBDA_EXPR_CONST_QUAL_P unless we have an xobj function.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/cpp0x/lambda/lambda-decltype3.C: Remove xfail.
            * g++.dg/warn/Wshadow-19.C: Add -Wpedantic.  Adjust a dg-warning.
            * g++.dg/warn/Wshadow-6.C: Adjust expected diagnostics.
            * g++.dg/cpp23/lambda-scope1.C: New test.
            * g++.dg/cpp23/lambda-scope2.C: New test.
            * g++.dg/cpp23/lambda-scope3.C: New test.
            * g++.dg/cpp23/lambda-scope4.C: New test.
            * g++.dg/cpp23/lambda-scope4b.C: New test.
            * g++.dg/cpp23/lambda-scope5.C: New test.
            * g++.dg/cpp23/lambda-scope6.C: New test.
            * g++.dg/cpp23/lambda-scope7.C: New test.
            * g++.dg/cpp23/lambda-scope8.C: New test.
            * g++.dg/cpp23/lambda-scope9.C: New test.
    
    Reviewed-by: Jason Merrill <jason@redhat.com>
Comment 6 Marek Polacek 2025-08-13 22:55:21 UTC
Implemented in GCC 16.