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