Hi Jason, On Mon Jan 27, 2025 at 9:10 PM CET, Simon Martin wrote: > Hi Jason, > > On 27 Jan 2025, at 20:53, Jason Merrill wrote: > >> On 1/27/25 2:34 PM, Simon Martin wrote: >>> Hi Jason, >>> >>> On 27 Jan 2025, at 15:23, Jason Merrill wrote: >>> >>>> On 1/27/25 8:14 AM, Simon Martin wrote: >>>>> Hi Jason, >>>>> >>>>> On 24 Jan 2025, at 16:51, Jason Merrill wrote: >>>>> >>>>>> On 1/24/25 6:19 AM, Simon Martin wrote: >>>>>>> Hi Jason, >>>>>>> >>>>>>> On 23 Jan 2025, at 22:56, Jason Merrill wrote: >>>>>>> >>>>>>>> On 1/23/25 12:06 PM, Simon Martin wrote: >>>>>>>>> Hi Marek, >>>>>>>>> >>>>>>>>> On 23 Jan 2025, at 16:45, Marek Polacek wrote: >>>>>>>>> >>>>>>>>>> On Thu, Jan 23, 2025 at 02:40:09PM +0000, Simon Martin wrote:
>>>>>>>>>>> Hi Jason, >>>>>>>>>>> >>>>>>>>>>> On 20 Jan 2025, at 22:49, Jason Merrill wrote: >>>>>>>>>>> >>>>>>>>>>>> On 1/20/25 2:11 PM, Simon Martin wrote: >>>>>>>>>>>>> Hi Jason, >>>>>>>>>>>>> >>>>>>>>>>>>> On 15 Jan 2025, at 22:42, Jason Merrill wrote: >>>>>>>>>>>>> >>>>>>>>>>>>>> On 12/12/24 2:07 PM, 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 >>>>>>>>>>>>>>> remove the proxy declaration while it should not, and we >>>>>>>>>>>>>>> trip >>>>> >>>>>>>>>>>>>>> on >>>>>>>>>>>>>>> an >>>>>>>>>>> >>>>>>>>>>>>>>> assert at instantiation time. >>>>>>>>>>>>>> >>>>>>>>>>>>>> Why does prune_lambda_captures remove the declaration if >>>>>>>>>>>>>> it's >>>>>>>>>>>>>> still >>>>>>>>>>>>>> used in the function body? Setting >>>>>>>>>>>>>> LAMBDA_EXPR_CAPTURE_OPTIMIZED >>>>>>>>>>>>>> just >>>>>>>>>>>>>> means "we might have optimized away some captures", the >>>>>>>>>>>>>> tree >>>>>>>>>>>>>> walk >>>>>>>>>>>>>> should have found the use put back by build_x_binary_op. >>>>>>>>>>>>> I think that this is due to a bug in mark_const_cap_r, >>>>>>>>>>>>> that’s >>>>>>>>>>>>> been >>>>>>>>>>>>> here since the beginning, i.e. r8-7213-g1577f10a637352: it >>>>>>>>>>>>> decides >>>>>>>>>>> >>>>>>>>>>>>> NOT >>>>>>>>>>>>> to walk sub-trees when encountering a DECL_EXPR for a >>>>>>>>>>>>> constant >>>>>>>>>>>>> capture >>>>>>>>>>>>> proxy (at lambda.cc:1769). I don’t understand why we >>>>> >>>>>>>>>>>>> wouldn’t >>>>>>>>>>>>> want >>>>>>>>>>>>> to continue. >>>>>>>>>>>> >>>>>>>>>>>> Because that's the declaration of the capture proxy, not a >>>>>>>>>>>> use >>>>>>>>>>>> of >>>>>>>>>>>> it. >>>>>>>>>>> Indeed, thanks for clarifying. >>>>>>>>>>> >>>>>>>>>>>> Why aren't we finding the use in the declaration of t? >>>>>>>>>>> After further investigation, the problem is rather that >>>>>>>>>>> neither >>>>>>>>>>> walk_tree_1 nor cp_walk_subtrees walk the dimensions of array >>>>>>>>>>> types, >>>>>>>>>>> so >>>>>>>>>>> we miss the uses. >>>>>>>>>>> >>>>>>>>>>>>> Removing that line fixes the PR, but breaks 3 existing >>>>>>>>>>>>> tests >>>>>>>>>>>>> (lambda-capture1.C, lambda-const11.C and lambda-const11a.C, >>>>>>>>>>>>> that >>>>>>>>>>>>> all >>>>>>>>>>>>> assert that we optimise out the capture); that’s why I >>>>>>>>>>>>> did >>>>>>>>>>>>> not >>>>>>>>>>>>> pursue >>>>>>>>>>>>> it in the first place. >>>>>>>>>>>> >>>>>>>>>>>> Right. >>>>>>>>>>>> >>>>>>>>>>>>> But taking another look, it might not be that big a deal >>>>>>>>>>>>> that >>>>>>>>>>>>> we >>>>>>>>>>>>> don’t >>>>>>>>>>>>> optimise those out: as soon as we use -O1 or above, the >>>>>>>>>>>>> assignment >>>>>>>>>>>>> to >>>>>>>>>>> >>>>>>>>>>>>> the closure field actually disappears. >>>>>>>>>>>> >>>>>>>>>>>> Completely breaking this optimization is a big deal, >>>>>>>>>>>> particularly >>>>>>>>>>>> since it affects the layout of closure objects. We can't >>>>>>>>>>>> always >>>>> >>>>>>>>>>>> optimize everything away. >>>>>>>>>>> ACK. >>>>>>>>>>> >>>>>>>>>>> The attached updated patch makes cp_walk_subtrees walk array >>>>>>>>>>> type >>>>> >>>>>>>>>>> dimensions, which fixes the initial PR without those 3 >>>>>>>>>>> regressions. >>>>>>> >>>>>>>>>>> >>>>>>>>>>> Successfully tested on x86_64-pc-linux-gnu. Is it OK? >>>>>>>>>>> >>>>>>>>>>> Simon >>>>>>>>>>> >>>>>>>>>>> PS: In theory I think it’d make sense to do do this change >>>>>>>>>>> in >>>>>>>>>>> >>>>>>>>>>> walk_tree_1 since C also supports VLAs, but doing so breaks >>>>>>>>>>> some >>>>>>> >>>>>>>>>>> OMP >>>>>>>>>>> tests. OMP can do interesting things with array bounds (see >>>>>>>>>>> r9-5354-gda972c05f48637), and fixing those would require >>>>>>>>>>> teaching >>>>> >>>>>>>>>>> walk_tree_1 about the “omp dummy var” array bounds, which >>>>>>>>>>> I >>>>>>>>>>> think >>>>>>>>>>> would be a bit ugly. And I’m not aware of any C case that >>>>>>>>>>> would >>>>>>>>>>> be >>>>>>>>>>> improved/fixed by doing this change, so we’re probably fine >>>>>>>>>>> not >>>>>>>>>>> doing >>>>>>>>>>> it. >>>>>>>>>> >>>>>>>>>>> From e19bb6c943a422b1201c5c9a2a1d4f32141baf84 Mon Sep 17 >>>>>>>>>>> 00:00:00 >>>>>>>>>>> 2001 >>>>>>>>>>> From: Simon Martin <si...@nasilyan.com> >>>>>>>>>>> Date: Wed, 22 Jan 2025 16:19:47 +0100 >>>>>>>>>>> Subject: [PATCH] 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 when walking a >>>>>>>>>>> declaration >>>>>>>>>>> with an array type, it walks that array type's dimensions. >>>>>>>>>>> Since >>>>>>>>>>> C >>>>>>>>>>> also >>>>>>>>>>> supports VLAs, I thought it'd make sense to do this in >>>>>>>>>>> walk_tree_1, >>>>>>> >>>>>>>>>>> but >>>>>>>>>>> this breaks some omp-low related test cases (because OMP can >>>>>>>>>>> do >>>>>>>>>>> funky >>>>>>>>>>> things with array bounds when adjust_array_error_bounds is >>>>>>>>>>> set), >>>>>>> >>>>>>>>>>> and >>>>>>>>>>> I >>>>>>>>>>> don't really want to add code to walk_tree_1 to skips arrays >>>>>>>>>>> whose >>>>>>>>>>> dimension is a temporary variable with the "omp dummy var" >>>>>>>>>>> attribute. >>>>>>>>>>> >>>>>>>>>>> Successfully tested on x86_64-pc-linux-gnu. >>>>>>>>>>> >>>>>>>>>>> PR c++/114292 >>>>>>>>>>> >>>>>>>>>>> gcc/cp/ChangeLog: >>>>>>>>>>> >>>>>>>>>>> * tree.cc (cp_walk_subtrees): Walk array type dimensions. >>>>>>>>>>> >>>>>>>>>>> gcc/testsuite/ChangeLog: >>>>>>>>>>> >>>>>>>>>>> * g++.dg/cpp1y/lambda-ice4.C: New test. >>>>>>>>>>> >>>>>>>>>>> --- >>>>>>>>>>> gcc/cp/tree.cc | 11 ++++++ >>>>>>>>>>> gcc/testsuite/g++.dg/cpp1y/lambda-ice4.C | 44 >>>>>>>>>>> ++++++++++++++++++++++++ >>>>>>>>>>> 2 files changed, 55 insertions(+) >>>>>>>>>>> create mode 100644 >>>>>>>>>>> gcc/testsuite/g++.dg/cpp1y/lambda-ice4.C >>>>>>>>>>> >>>>>>>>>>> diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc >>>>>>>>>>> index 36581865a17..fc9a2fbff32 100644 >>>>>>>>>>> --- a/gcc/cp/tree.cc >>>>>>>>>>> +++ b/gcc/cp/tree.cc >>>>>>>>>>> @@ -5844,6 +5844,17 @@ cp_walk_subtrees (tree *tp, int >>>>>>>>>>> *walk_subtrees_p, walk_tree_fn func, >>>>>>>>>>> break; >>>>>>>>>>> >>>>>>>>>>> default: >>>>>>>>>>> + /* If this is an array, walk its dimensions. */ >>>>>>>>>>> + if (DECL_P (t) && TREE_TYPE (t) >>>>>>>>>>> + && TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE) >>>>>>>>>>> + { >>>>>>>>>>> + tree domain = TYPE_DOMAIN (TREE_TYPE (t)); >>>>>>>>>>> + if (domain) >>>>>>>>>>> + { >>>>>>>>>>> + WALK_SUBTREE (TYPE_MIN_VALUE (domain)); >>>>>>>>>>> + WALK_SUBTREE (TYPE_MAX_VALUE (domain)); >>>>>>>>>>> + } >>>>>>>>>>> + } >>>>>>>>>>> return NULL_TREE; >>>>>>>>>>> } >>>>>>>>>> >>>>>>>>>> Is there a reason I'm missing not to put this into the TYPE_P >>>>>>>>>> block >>>>>>>>>> above? >>>>>>>>> Do you mean put it in that block as well, or instead of where I >>>>>>>>> put >>>>> >>>>>>>>> it? >>>>>>>>> If the latter, we don’t see any TYPE_P node for >>>>>>>>> “int[r*n]”, >>>>>>>>> so >>>>>>>>> we >>>>>>>>> don’t go into that block, and continue ICE’ing without the >>>>>>>>> change >>>>>>>>> in >>>>>>>>> the “default:”. >>>>>>>>> >>>>>>>>> Anyway it’s a very good call (thanks!), because it got me to >>>>>>>>> check >>>>>>>>> what we get if we change the lambda body to just do “typedef >>>>>>>>> int >>>>>>>>> MyT[c*r];”, and we still ICE. And from a quick look, doing >>>>>>> >>>>>>>>> something >>>>>>>>> similar in the TYPE_P block does not fix it. >>>>>>>>> >>>>>>>>> I’ll work something out and report back. >>>>>>>> >>>>>>>> I would think this should go in the DECL_EXPR handling, so we >>>>>>>> don't >>>>>>>> walk into the type every time we see a use of an array variable. >>>>>>> Indeed, we can do the check there, thanks a lot. >>>>>>> >>>>>>> And concerning the case of a single “typedef int MyT[r*c];”, >>>>>>> there >>>>>>> already is code in walk_tree_1 that covers such typedefs, but >>>>>>> that >>>>> >>>>>>> does >>>>>>> not walk array type dimensions. >>>>>>> >>>>>>> So the result is the attached updated patch, successfully tested >>>>>>> on >>>>>>> x86_64-pc-linux-gnu. OK for trunk? And if so, also for branches? >>>>>> >>>>>>> * tree.cc (walk_tree_1): Walk array type dimensions, if any, for >>>>>>> TYPE_DECLs. >>>>>> >>>>>> Hmm, why isn't this covered by the existing call to >>>>>> walk_type_fields? >>>>>> >>>>>> Ah, I guess because that just walks into TYPE_DOMAIN, and nothing >>>>>> walks from there into the TYPE_MIN/MAX_VALUE. >>>>>> >>>>>> What if instead of touching walk_tree_1, mark_const_cap_r handled >>>>>> INTEGER_TYPE? >>>>> I don’t think we can look into TYPE_{MIN,MAX}_VALUE from >>>>> mark_const_cap_r since we might need to recurse (here we have a >>>>> MINUS_EXPR in TYPE_MAX_VALUE) and I don’t think we can from >>>>> there? >>>> >>>> Several tree walk functions recurse, in which case they need to >>>> handle >>>> the pointer_set themselves instead of using >>>> cp_walk_tree_without_duplicates. For instance, >>>> find_parameter_packs_r. But no need for this patch. >>>> >>>>>> I know walk_tree used to walk into VLA bounds, which is why we >>>>>> have >>>>>> stabilize_vla_size to split out any relevant expressions into >>>>>> temporaries; I'm reluctant to re-add that without a lot of >>>>>> investigation into the history. >>>>> Yeah, makes sense. I learnt that touching walk_tree requires >>>>> caution >>>>> through all that I broke with my few attempts at changing it :-) >>>> >>>> OK, I looked it up: this goes back to r119481 >>>> (r0-77782-g8f6e6bf375959d), the bounds walking was removed because >>>> it >>>> caused trouble for Ada. That patch added bounds walking to >>>> for_each_template_parm_r instead to avoid regressing C++. >>> Thanks for digging into this and providing extra context. >>> >>>>>> In cp_walk_subtrees, I think we also want to go through the >>>>>> typedef >>>>>> handling at the top, so in the DECL_EXPR handling, let's just walk >>>>>> into the type of the decl. So we walk into the ARRAY_TYPE, check >>>>>> whether it's a typedef, if not walk_type_fields walks into the >>>>>> TYPE_DOMAIN, and the mark_const_cap_r adjustment I suggested above >>>>>> checks the TYPE_MAX_VALUE from there. >>>>> I think that the typedef handling is already done in walk_tree (at >>>>> least >>>>> I was unable to craft a test case where doing something extra for >>>>> typedefs in cp_walk_subtrees was required). >>>> >>>> I agree that nothing extra is required; I was referring to the >>>> typedef_variant_p code at the top of cp_walk_subtree. >>>> >>>>> And following up on your suggestion, things end up pretty simple: >>>>> just >>>>> walk the type of declarations in DECL_EXPRs, and explicitly handle >>>>> the >>>>> min/max value of INTEGER_TYPEs in cp_walk_subtree. >>>>> >>>>> At least that’s what the updated patch does, successfully tested >>>>> on >>>>> x86_64-pc-linux-gnu. Ok for trunk? >>>> >>>>> @@ -5843,6 +5844,11 @@ cp_walk_subtrees (tree *tp, int >>>>> *walk_subtrees_p, walk_tree_fn func, >>>>> WALK_SUBTREE (STATIC_ASSERT_MESSAGE (t)); >>>>> break; >>>>> + case INTEGER_TYPE: >>>>> + WALK_SUBTREE (TYPE_MIN_VALUE (t)); >>>>> + WALK_SUBTREE (TYPE_MAX_VALUE (t)); >>>>> + break; >>>> >>>> Let's add a comment >>>> >>>> /* Removed from walk_type_fields in r119481. */ >>>> >>>> and remove the now-redundant INTEGER_TYPE handling from >>>> for_each_template_parm_r. >>>> >>>> OK with those adjustments. >>> Thanks a lot. Merged to trunk as r15-7238-gceabea405ffdc8. >>> >>> Even though it’s an “old” regression (from GCC 7), it’s a >>> reject-valid and I’m tempted to backport; OK for active branches as >>> well? >> >> For backports I'd be inclined to condition the INTEGER_TYPE handing on >> if (processing_template_decl) to reduce its impact. But first let's >> wait and see if this breaks anything on trunk. > ACK. I’ll resume the discussion about backports via this thread in 2-3 > weeks. Please find attached the updated patch for active branches with the restriction you suggested. Successfully tested on x86_64-pc-linux-gnu. OK for both GCC 13 and 14? Thanks! Simon
From 07fa1c226d5ffc3db070ad176d4c1fbebec074ef Mon Sep 17 00:00:00 2001 From: Simon Martin <si...@nasilyan.com> Date: Wed, 19 Mar 2025 09:30:28 +0100 Subject: [PATCH] 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. Successfully tested on x86_64-pc-linux-gnu. 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. --- gcc/cp/tree.cc | 10 ++++ gcc/testsuite/g++.dg/cpp1y/lambda-ice4.C | 63 ++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 gcc/testsuite/g++.dg/cpp1y/lambda-ice4.C diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index abed2ce859c..c6a4d89f4a3 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -5701,6 +5701,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)); @@ -5751,6 +5752,15 @@ cp_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func, WALK_SUBTREE (STATIC_ASSERT_MESSAGE (t)); break; + case INTEGER_TYPE: + if (processing_template_decl) + { + /* 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 00000000000..d8b7af9f992 --- /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); +} -- 2.44.0