https://gcc.gnu.org/g:ceabea405ffdc851736e240111be9b297ad86c53
commit r15-7238-gceabea405ffdc851736e240111be9b297ad86c53 Author: Simon Martin <si...@nasilyan.com> Date: Wed Jan 22 16:19:47 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, it also walks its TYPE_{MIN,MAX}_VALUE. Note that #2 makes a <case INTEGER_TYPE> redundant in for_each_template_parm_r, and removes it. PR c++/114292 gcc/cp/ChangeLog: * pt.cc (for_each_template_parm_r) <INTEGER_TYPE>: Remove case now handled by cp_walk_subtrees. * tree.cc (cp_walk_subtrees): Walk the type of DECL_EXPR declarations, as well as the TYPE_{MIN,MAX}_VALUE of INTEGER_TYPEs. gcc/testsuite/ChangeLog: * g++.dg/cpp1y/lambda-ice4.C: New test. Diff: --- gcc/cp/pt.cc | 5 --- gcc/cp/tree.cc | 7 ++++ gcc/testsuite/g++.dg/cpp1y/lambda-ice4.C | 63 ++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 5 deletions(-) diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 6442ae947c32..d4a6e2e0675f 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -10822,11 +10822,6 @@ for_each_template_parm_r (tree *tp, int *walk_subtrees, void *d) WALK_SUBTREE (TYPE_TI_ARGS (t)); break; - case INTEGER_TYPE: - WALK_SUBTREE (TYPE_MIN_VALUE (t)); - WALK_SUBTREE (TYPE_MAX_VALUE (t)); - break; - case METHOD_TYPE: /* Since we're not going to walk subtrees, we have to do this explicitly here. */ diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index 36581865a177..e35432f43af9 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -5793,6 +5793,7 @@ cp_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func, && !TREE_STATIC (TREE_OPERAND (t, 0))))) { tree decl = TREE_OPERAND (t, 0); + WALK_SUBTREE (TREE_TYPE (decl)); WALK_SUBTREE (DECL_INITIAL (decl)); WALK_SUBTREE (DECL_SIZE (decl)); WALK_SUBTREE (DECL_SIZE_UNIT (decl)); @@ -5843,6 +5844,12 @@ cp_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func, WALK_SUBTREE (STATIC_ASSERT_MESSAGE (t)); break; + case INTEGER_TYPE: + /* Removed from walk_type_fields in r119481. */ + WALK_SUBTREE (TYPE_MIN_VALUE (t)); + WALK_SUBTREE (TYPE_MAX_VALUE (t)); + 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); +}