https://gcc.gnu.org/g:65e998d172e006cdf0dd4d58f83784a5fed61fc5
commit r13-9441-g65e998d172e006cdf0dd4d58f83784a5fed61fc5 Author: Simon Martin <si...@nasilyan.com> Date: Fri Mar 21 07:02:20 2025 +0100 c++: Don't prune constant capture proxies only used in array dimensions [PR114292] 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 === When parsing the lambda body, and more specifically the multiplication, we mark the lambda as LAMBDA_EXPR_CAPTURE_OPTIMIZED, which indicates to prune_lambda_captures that it might be possible to optimize out some captures. The problem is that prune_lambda_captures then misses the use of the r capture (because neither walk_tree_1 nor cp_walk_subtrees walks the dimensions of array types - here "r * c"), hence believes the capture can be pruned... and we trip on an assert when instantiating the lambda. This patch changes cp_walk_subtrees so that (1) when walking a DECL_EXPR, it also walks the DECL's type, and (2) when walking an INTEGER_TYPE and processing a template declaration, it also walks its TYPE_{MIN,MAX}_VALUE. PR c++/114292 gcc/cp/ChangeLog: * tree.cc (cp_walk_subtrees): Walk the type of DECL_EXPR declarations, as well as the TYPE_{MIN,MAX}_VALUE of INTEGER_TYPEs for template declarations. gcc/testsuite/ChangeLog: * g++.dg/cpp1y/lambda-ice4.C: New test. (cherry picked from commit f86d274ab76fdd89d7afd9b2eab28f3a61749cfb) Diff: --- gcc/cp/tree.cc | 10 +++++ gcc/testsuite/g++.dg/cpp1y/lambda-ice4.C | 63 ++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index c35f15baca2b..8ccb4ba70615 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -5663,6 +5663,7 @@ cp_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func, && !TREE_STATIC (TREE_OPERAND (*tp, 0))))) { tree decl = TREE_OPERAND (*tp, 0); + WALK_SUBTREE (TREE_TYPE (decl)); WALK_SUBTREE (DECL_INITIAL (decl)); WALK_SUBTREE (DECL_SIZE (decl)); WALK_SUBTREE (DECL_SIZE_UNIT (decl)); @@ -5713,6 +5714,15 @@ cp_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func, WALK_SUBTREE (STATIC_ASSERT_MESSAGE (*tp)); break; + case INTEGER_TYPE: + if (processing_template_decl) + { + /* Removed from walk_type_fields in r119481. */ + WALK_SUBTREE (TYPE_MIN_VALUE (*tp)); + WALK_SUBTREE (TYPE_MAX_VALUE (*tp)); + } + break; + default: return NULL_TREE; } diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-ice4.C b/gcc/testsuite/g++.dg/cpp1y/lambda-ice4.C new file mode 100644 index 000000000000..d8b7af9f9920 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/lambda-ice4.C @@ -0,0 +1,63 @@ +// PR c++/114292 +// { dg-do "compile" { target c++14 } } +// { dg-additional-options "-Wno-vla" } + +#define ASSERT_CAPTURE_NUMBER(Lambda, NumCaptures) \ + { \ + auto oneCapture = [&](auto) { int t[c]; }; \ + const auto sizeOneCapture = sizeof (oneCapture); \ + const auto expected = NumCaptures ? NumCaptures * sizeOneCapture : 1; \ + static_assert (sizeof (Lambda) == expected, ""); \ + } + +template<int r, int c> +struct Want_a_Typedef { typedef int Type[r*c]; }; + +void foo (int c) +{ + constexpr int r = 4; + + // This used to ICE. + auto ice_1 = [&](auto) { int t[c * r]; }; + ice_1 (0); + ASSERT_CAPTURE_NUMBER (ice_1, 2); + + // Another ICE identified following a great question in the patch submission + // mail thread. + auto ice_2 = [&](auto) { typedef int MyT[c*r]; }; + ice_2 (0); + ASSERT_CAPTURE_NUMBER (ice_2, 2); + + // All those worked already, but were not covered by any test - do it here. + auto ok_0 = [&](auto) { typedef int MyT[c*r]; MyT t; }; + ok_0 (0); + ASSERT_CAPTURE_NUMBER (ok_0, 2); + + auto ok_1 = [&](auto) { Want_a_Typedef<r, 42>::Type t; }; + ok_1 (0); + ASSERT_CAPTURE_NUMBER (ok_1, 0); + + auto ok_2 = [&](auto) { int t[c]; }; + ok_2 (0); + ASSERT_CAPTURE_NUMBER (ok_2, 1); + + auto ok_3 = [&](auto) { int n = r * c; int t[n]; }; + ok_3 (0); + ASSERT_CAPTURE_NUMBER (ok_3, 2); + + auto ok_4 = [&](auto) { int t[r]; }; + ok_4 (0); + ASSERT_CAPTURE_NUMBER (ok_4, 0); + + auto ok_5 = [&](auto) { int t[c * 4]; }; + ok_5 (0); + ASSERT_CAPTURE_NUMBER (ok_5, 1); + + auto ok_6 = [&](auto) { int t[1]; }; + ok_6 (0); + ASSERT_CAPTURE_NUMBER (ok_6, 0); + + auto ok_7 = [&](auto) { int t[c * r]; }; + ok_7 (0); + ASSERT_CAPTURE_NUMBER (ok_7, 2); +}