On Thu, Dec 12, 2024 at 07:07:38PM +0000, Simon Martin wrote:
> We currently ICE upon the following valid (under -Wno-vla) code
> 
> === cut here ===
> void f(int c) {
>   constexpr int r = 4;
>   [&](auto) { int t[r * c]; }(0);
> }
> === cut here ===
> 
> The problem is that when parsing the lambda body, and more specifically
> the multiplication, we mark the lambda as LAMBDA_EXPR_CAPTURE_OPTIMIZED
> even though the replacement of r by 4 is "undone" by the call to
> build_min_non_dep in build_x_binary_op. This makes prune_lambda_captures

Ah yeah, because build_min_non_dep gets the original operands.

> remove the proxy declaration while it should not, and we trip on an
> assert at instantiation time.
> 
> This patch fixes the ICE by making sure that lambdas are only marked as
> LAMBDA_EXPR_CAPTURE_OPTIMIZED when they're instantiated (I tried other
> strategies like not undoing constant folding in build_min_non_dep, but
> it is pretty intrusive and breaks lots of things).

I've tried that too and I also ran into a number of issues.  I also tried
checking p_t_d in prune_lambda_capture since it already says "Don't bother
pruning in a template" but that doesn't work, either.
 
> The test I added also shows that we don't always optimize out captures
> to constants for lambdas that are not within a template (see ok_2 for
> example, or ok_3 that unlike ok_2 "regresses" a bit with my patch) - I'm
> curious if we consider it a problem or not? If so, I can try to fix this
> in a follow-up patch.

Since "P0588R1: Simplifying implicit lambda capture" they are captures,
though [expr.prim.lambda.capture] gives us license to optimize the
captures if not ODR-used.  I couldn't find a test where this patch
would be an ABI change.
 
> Successfully tested on x86_64-pc-linux-gnu.
> 
>       PR c++/114292
> 
> gcc/cp/ChangeLog:
> 
>       * cp-tree.h (mark_const_var_capture_optimized): Declare.
>       * expr.cc (mark_use): Call mark_const_var_capture_optimized.
>       * lambda.cc (mark_const_var_capture_optimized): New. Only set
>       LAMBDA_EXPR_CAPTURE_OPTIMIZED at lambda instantiation time.
> 
> gcc/testsuite/ChangeLog:
> 
>       * g++.dg/cpp1y/lambda-ice4.C: New test.
> 
> ---
>  gcc/cp/cp-tree.h                         |  1 +
>  gcc/cp/expr.cc                           | 10 ++----
>  gcc/cp/lambda.cc                         | 13 +++++++
>  gcc/testsuite/g++.dg/cpp1y/lambda-ice4.C | 44 ++++++++++++++++++++++++
>  4 files changed, 60 insertions(+), 8 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/cpp1y/lambda-ice4.C
> 
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index c5e0fc5c440..ce050032fdb 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -8058,6 +8058,7 @@ extern bool is_constant_capture_proxy           (tree);
>  extern void register_capture_members         (tree);
>  extern tree lambda_expr_this_capture            (tree, int);
>  extern void maybe_generic_this_capture               (tree, tree);
> +extern void mark_const_var_capture_optimized (void);
>  extern tree maybe_resolve_dummy                      (tree, bool);
>  extern tree current_nonlambda_function               (void);
>  extern tree nonlambda_method_basetype                (void);
> diff --git a/gcc/cp/expr.cc b/gcc/cp/expr.cc
> index de4991e616c..d6a2454c46e 100644
> --- a/gcc/cp/expr.cc
> +++ b/gcc/cp/expr.cc
> @@ -120,10 +120,7 @@ mark_use (tree expr, bool rvalue_p, bool read_p,
>           {
>             tree val = RECUR (cap);
>             if (!is_capture_proxy (val))
> -             {
> -               tree l = current_lambda_expr ();
> -               LAMBDA_EXPR_CAPTURE_OPTIMIZED (l) = true;
> -             }
> +             mark_const_var_capture_optimized ();
>             return val;
>           }
>       }
> @@ -171,10 +168,7 @@ mark_use (tree expr, bool rvalue_p, bool read_p,
>               {
>                 tree val = RECUR (cap);
>                 if (!is_capture_proxy (val))
> -                 {
> -                   tree l = current_lambda_expr ();
> -                   LAMBDA_EXPR_CAPTURE_OPTIMIZED (l) = true;
> -                 }
> +                 mark_const_var_capture_optimized ();
>                 return val;
>               }
>           }
> diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc
> index d8a15d97d5d..4fd3b39c99b 100644
> --- a/gcc/cp/lambda.cc
> +++ b/gcc/cp/lambda.cc
> @@ -945,6 +945,19 @@ resolvable_dummy_lambda (tree object)
>    return NULL_TREE;
>  }
>  
> +/* Called when optimizing out a capture to a const variable.  */
> +
> +void
> +mark_const_var_capture_optimized ()
> +{
> +  /* The actual optimizing out only occurs when instantiating the lambda.  */
> +  if (processing_template_decl)
> +    return;
> +
> +  tree l = current_lambda_expr ();
> +  LAMBDA_EXPR_CAPTURE_OPTIMIZED (l) = true;
> +}

If we do this, perhaps the function can be a static function in expr.cc,
and maybe we can check processing_template_decl before is_capture_proxy.

Marek

Reply via email to