On Thu, Mar 26, 2026 at 05:28:38PM +0000, Iain Sandoe wrote:
> This does also lead to an ICE on invalid, per the referenced PR.
> 
> Tested on x86_64 darwin, x86_64, powerpc64le Linux,
> OK for trunk?
> thanks
> Iain
> 
> --- 8< ---
> 
> We were currently accepting invalid code by allowing contract assertions
> to trigger implicit lambda captures contrary to:
> [expr.prim.lambda.closure] / p10.
> 
> The solution here is to mark captures that occur within contract
> assertion scopes and then clear that mark if we then see a normal
> capture for the same entity - we must defer the error handling since
> the following is valid:
> 
>   auto f5 = [=] {
>     contract_assert (i > 0); // OK, i is referenced elsewhere.
>     return i;
>   };
> 
>       PR c++/124648
> 
> gcc/cp/ChangeLog:
> 
>       * cp-tree.h (DECL_CONTRACT_CAPTURE_P): New.
>       * parser.cc (cp_parser_lambda_body): Scan the captures for
>       ones were only added in contract assertion scopes.  Issue
>       errors for those found.
>       * semantics.cc (process_outer_var_ref): Mark implicit
>       captures that occur in contract assertion scopes.  Clear
>       the mark if the entity is subsequently captured 'normally'.
> 
> gcc/testsuite/ChangeLog:
> 
>       * g++.dg/contracts/cpp26/expr.prim.lambda.closure.p10.C: New test.
> 
> Signed-off-by: Iain Sandoe <[email protected]>
> ---
>  gcc/cp/cp-tree.h                              |  3 ++
>  gcc/cp/parser.cc                              | 20 +++++++
>  gcc/cp/semantics.cc                           | 27 ++++++++--
>  .../cpp26/expr.prim.lambda.closure.p10.C      | 53 +++++++++++++++++++
>  4 files changed, 100 insertions(+), 3 deletions(-)
>  create mode 100644 
> gcc/testsuite/g++.dg/contracts/cpp26/expr.prim.lambda.closure.p10.C
> 
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index ea3cb049785..7beaae6bbfd 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -5367,6 +5367,9 @@ get_vec_init_expr (tree t)
>  #define DECL_NORMAL_CAPTURE_P(NODE) \
>    DECL_LANG_FLAG_7 (FIELD_DECL_CHECK (NODE))
>  
> +#define DECL_CONTRACT_CAPTURE_P(NODE) \
> +  DECL_LANG_FLAG_8 (FIELD_DECL_CHECK (NODE))

All the #defines above have a comment so this one should have one too.
Please also update the "Usage of DECL_LANG_FLAG_?" comment above.

>  /* Nonzero if TYPE is an anonymous union or struct type.  We have to use a
>     flag for this because "A union for which objects or pointers are
>     declared is not an anonymous union" [class.union].  */
> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> index 8d88dc9c312..e5157cd7386 100644
> --- a/gcc/cp/parser.cc
> +++ b/gcc/cp/parser.cc
> @@ -13740,6 +13740,26 @@ cp_parser_lambda_body (cp_parser* parser, tree 
> lambda_expr)
>         finish_function (which will try to emit the contracts).  */
>      cp_parser_late_contracts (parser, fco);
>  
> +    /* Check that we have not caused captures that only relate to contracts.
> +       [expr.prim.lambda.closure] / P10.  */
> +    if (flag_contracts)
> +      {
> +     tree le = LAMBDA_EXPR_CAPTURE_LIST (current_lambda_expr ());

Was this really meant to be current_lambda_expr() instead of lambda_expr?

> +     for (; le; le = TREE_CHAIN (le))

Why not put the LAMBDA_EXPR_CAPTURE_LIST in the for and then lose the
{ } in the if (flag_contracts)?

> +       {
> +         tree cap_fld = TREE_PURPOSE (le);
> +         if (TREE_CODE (cap_fld) == FIELD_DECL
> +             && DECL_CONTRACT_CAPTURE_P (cap_fld))
> +           {
> +             auto_diagnostic_group d;
> +             tree expr = TREE_VALUE (le);
> +             location_t loc = DECL_SOURCE_LOCATION (cap_fld);
> +             error_at (loc, "%qE is not implicitly captured by a contract"
> +                       " assertion", expr);
> +             inform (DECL_SOURCE_LOCATION (expr), "%q#E declared here", 
> expr);
> +           }
> +       }
> +      }
>      finish_lambda_function (body);
>    }
>  
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index ac08b598f3d..992ae29a4f4 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -4619,6 +4619,17 @@ process_outer_var_ref (tree decl, tsubst_flags_t 
> complain, bool odr_use)
>  
>        if (d && d != decl && is_capture_proxy (d))
>       {
> +       if (flag_contracts && !processing_contract_condition)
> +         {
> +           /* We might have created a capture for a contract_assert ref. to
> +              some var, if that is now captured 'normally' then this is OK.
> +              Otherwise we leave the capture marked as incorrect.  */
> +           gcc_checking_assert (DECL_HAS_VALUE_EXPR_P (d));
> +           tree proxy = DECL_VALUE_EXPR (d);
> +           gcc_checking_assert (TREE_CODE (proxy) == COMPONENT_REF
> +                             && TREE_CODE (TREE_OPERAND (proxy, 1)) == 
> FIELD_DECL);
> +              DECL_CONTRACT_CAPTURE_P (TREE_OPERAND (proxy, 1)) = false;
> +         }
>         if (DECL_CONTEXT (d) == containing_function)
>           /* We already have an inner proxy.  */
>           return d;
> @@ -4667,10 +4678,20 @@ process_outer_var_ref (tree decl, tsubst_flags_t 
> complain, bool odr_use)
>        return error_mark_node;
>      }
>    /* Do lambda capture when processing the id-expression, not when
> -     odr-using a variable.  */
> +     odr-using a variable, but uses in a contract must not cause a capture.  
> */
>    if (!odr_use && context == containing_function)
> -    decl = add_default_capture (lambda_stack,
> -                             /*id=*/DECL_NAME (decl), initializer);
> +    {
> +      decl = add_default_capture (lambda_stack,
> +                               /*id=*/DECL_NAME (decl), initializer);
> +      if (flag_contracts && processing_contract_condition)
> +     {
> +       gcc_checking_assert (DECL_HAS_VALUE_EXPR_P (decl));
> +       tree proxy = DECL_VALUE_EXPR (decl);
> +       gcc_checking_assert (TREE_CODE (proxy) == COMPONENT_REF
> +                             && TREE_CODE (TREE_OPERAND (proxy, 1)) == 
> FIELD_DECL);
> +          DECL_CONTRACT_CAPTURE_P (TREE_OPERAND (proxy, 1)) = true;
> +        }
> +    }
>    /* Only an odr-use of an outer automatic variable causes an
>       error, and a constant variable can decay to a prvalue
>       constant without odr-use.  So don't complain yet.  */
> diff --git 
> a/gcc/testsuite/g++.dg/contracts/cpp26/expr.prim.lambda.closure.p10.C 
> b/gcc/testsuite/g++.dg/contracts/cpp26/expr.prim.lambda.closure.p10.C
> new file mode 100644
> index 00000000000..5a165c48166
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/contracts/cpp26/expr.prim.lambda.closure.p10.C
> @@ -0,0 +1,53 @@
> +// N5008 :
> +// [expr.prim.lambda.closure]/p10
> +// If all potential references to a local entity implicitly captured by a
> +// lambda-expression L occur within the function contract assertions of the
> +// call operator or operator template of L or within assertion-statements
> +// within the body of L, the program is ill-formed.
> +// [Note 4: Adding a contract assertion to an existing C++ program cannot
> +//  cause additional captures. — end note]
> +// { dg-do compile { target c++26 } }
> +// { dg-additional-options "-fcontracts -fsyntax-only" }
> +
> +auto gl0 = [] (int x) 
> +  pre (x > 10) { return x; }; // OK
> +
> +static int i = 0;
> +
> +void
> +foo ()
> +{
> +  auto f1 = [=]
> +    pre (i > 0) {};  // OK, no local entities are captured.
> +
> +  int i = 1;
> +
> +  auto f2 = [=]
> +    pre (i > 0) {};  // { dg-error {'i' is not implicitly captured by a 
> contract assertion} }
> +
> +  auto f3 = [i]
> +    pre (i > 0) {};  // OK, i is captured explicitly.
> +
> +  auto f4 = [=] {
> +    contract_assert (i > 0); // { dg-error {'i' is not implicitly captured 
> by a contract assertion} }
> +  };
> +
> +  auto f5 = [=] {
> +    contract_assert (i > 0); // OK, i is referenced elsewhere.
> +    return i;
> +  };
> +
> +  auto f6 = [=] pre (                // #1
> +    []{
> +      bool x = true;
> +      return [=]{ return x; }();    // OK, #1 captures nothing.
> +    }()) {};
> +
> +// TODO: lambda captures in function contract specifiers are not yet
> +// fully functional.
> +#if 0
> +  bool y = true;
> +  auto f7 = [=]
> +    pre([=]{ return y; }()); // error: outer capture of y is invalid.
> +#endif
> +}
> -- 
> 2.50.1 (Apple Git-155)
> 

Marek

Reply via email to