> I generally filter these out by comparing to an earlier baseline testrun > of my own, but you can also check gcc-testresults.
Ok, thanks, took in account. > Please name testcases by what they test, not only their PR number, to > enable testing like that you mention doing. The PR number can come at > the end (our usual convention of a sequence number often causes > rebase issues). Renamed pr127132.C to typeid-lambda-capture-pr127132.C. > Instead of a new class, how about making this case a defaulted parameter > to the cp_unevaluated constructor? I considered a class derived from cp_unevaluated, but couldn't find precedent anywhere in the compiler for inheriting from a small RAII state-guard like this, and didn't want to be the first to extend cp_unevaluated's contract that way - it's been essentially unchanged since Andrew Sutton introduced it for Concepts TS support in 2015 (r971e17ff873), formalized from struct to class by Martin Sebor in 2019 (r6c1dae73cd2c). A parallel class seemed the safer choice at the time. Your defaulted-parameter suggestion is simpler than either; done in v4. > Are there any tests that depend on this? In general mark_used doesn't > do much in a template. No, dropped it. Reverted to processing_template_decl == 0, the original PR125886 condition - mark_used and build_typeid both already no-op unconditionally in any template context regardless of which pass reaches them, so the extra gate had nothing to add. > These || are suprising. They seem to be needed in this patch because > the in_typeid_probe check is limited to the lambda_expr case. If we > instead check it immediately after the capture case, i.e. before Done, exactly that - moved the in_typeid_probe check right after the capture attempt and dropped the ||. > This test isn't reliable; the compiler is allowed to optimize away the > capture if n it isn't actually odr-used, as in > https://eel.is/c++draft/expr.prim#lambda.capture-example-4 > I'd actually consider this comparison succeeding to be a (minor) > missed-optimization bug. But let's not mess with that in this patch, > since the assert passed in GCC 16 so fixing the optimization would be a > lambda ABI change that we'd need to control with -fabi-version. > So I guess let's keep this test as it is for now, but add a comment > // If this starts to fail that's OK, the compiler is allowed to optimize > // away the capture, but we need to control it with -fabi-version. Added, verbatim. > > +// [expr.prim.lambda.capture]/7: capture must not depend on whether the > > +// typeid operand is evaluated. F is final, so [expr.typeid]/4 would > > +// leave typeid (f) unevaluated, but f is still captured. Generic-lambda > > +// variant of typeid14.C's final_glvalue. > > + > > +struct F final { virtual ~F (); }; > > + > > +void > > +final_glvalue_generic (F &f) > > +{ > > + [=](auto a) { (void) typeid (f); }(1); > > +} > This could also call foo(), and/or check the size? Done. Now calls foo() and checks sizeof (lam) == sizeof (F). > > +// D::i names no local entity, so [expr.prim.lambda.capture]/7 does not > > +// apply and a DR613 non-static member id-expression stays unevaluated > > +// (no capture). Generic-lambda variant of typeid14.C's nsm. > > + > > +struct D { int i; }; > > + > > +void > > +nsm_generic () > > +{ > > + [=](auto a) { (void) typeid (D::i); }(1); > > +} > This could also check the size. The same, done. Checks sizeof (lam) == 1 (as expected nothing captured). > It would also be good to also have tests of 'this' capture, such as if typeid(D::i) were in a member function of D. Added this_capture_regardless_of_evaluated and a generic-lambda variant, using [&] to sidestep the unrelated C++20 warning about implicit this-capture via [=]. > Also the typeid14.C references seem stale, the current patch no longer > modifies that file. Fixed. Those comments actually meant pr125886.C's final_glavlue/nsm (typeid14.C never had those), corrected. --- gcc-verify and gcc-style - are ok. Tested sweep for [ cpp0x/pr125886.C coroutines/unevaluated.C rtti/*.C cpp0x/constexpr-typeid*.C cpp2a/constexpr-typeid*.C template/typeid*.C cpp0x/lambda/*.C cpp1y/*lambda*.C cpp2a/*lambda*.C ], all 3553 green.
From 922478b73fbb14cbbc70a994392839413d783212 Mon Sep 17 00:00:00 2001 From: Vladislav Semykin <[email protected]> Date: Sat, 5 Sep 2026 00:19:35 +0300 Subject: [PATCH v4] c++: fix typeid capture in generic lambdas [PR c++/127132] [expr.prim.lambda.capture]/7 (P0588R1 / CWG1468) requires a lambda to capture a local entity if the expression would be potentially evaluated were the effect of any enclosing typeid expressions ignored. Capture must not depend on whether the typeid operand is itself evaluated. The PR125886 two-pass parse gated the capture-triggering evaluated re-parse on processing_template_decl == 0. A generic lambda's own auto parameter makes that nonzero regardless of the operand, so capture of an outer polymorphic glvalue was skipped and the resulting uncaptured decl ICEd at RTL expand (PR127132). Do not teach typeid_evaluated_p to treat a template context as evaluated: mark_used must still suppress instantiation while the operand is genuinely unevaluated (declval<T>()), and resolves_to_fixed_type_p already returns "fixed" whenever in_template_context. Instead, add cp_unevaluated_typeid_cutoff: process_outer_var_ref and lambda_expr_this_capture suppress capture only when cp_unevaluated_operand exceeds that cutoff. A typeid probe passes typeid_operand=true to cp_unevaluated, which raises the cutoff only when operand == cutoff at probe start, so sizeof (typeid (b)) stays suppressed while typeid (typeid (x)) looks through every enclosing typeid. Inside the cutoff, capture is attempted as usual, but a missing capture-default is not diagnosed there - the real evaluated-ness isn't known yet, and a later, non-probe pass will diagnose it if the operand turns out evaluated. Outside a lambda (a local class, a default argument) the capture attempt has nothing to attach to and falls through unchanged, so params() in pr125886.C still sees an unevaluated operand with no extra special-casing. Every force-reset site that clears cp_unevaluated_operand also save/reset/restores the cutoff, including cp_evaluated. processing_template_decl == 0 turned out to still be the right gate for the evaluated re-parse itself: mark_used and build_typeid both already no-op unconditionally in any template context, independent of which pass hits them, so the two-pass parse needs no extra gate there either. Changes since v3: - Jason: fold cp_unevaluated_typeid_operand into cp_unevaluated as a defaulted TYPEID_OPERAND parameter. - Jason: lambda_expr_this_capture was missing the in_typeid_probe deferral that process_outer_var_ref has for 'this'. - Jason: drop the process_outer_var_ref || conditions by checking in_typeid_probe right after the capture attempt instead of only inside the lambda_expr error case. - Jason: the instantiation_dependent_expression_p gate on the evaluated re-parse isn't needed; reverted to processing_template_decl == 0. - Tests: renamed pr127132.C to typeid-lambda-capture-pr127132.C (named for what it tests, PR at the end); moved the /7 cases back out of pr125886.C into it; added foo()/sizeof checks to final_glvalue_generic and nsm_generic; added this-capture tests; fixed stale typeid14.C references; noted the -fabi-version caveat on capture_regardless_of_evaluated. Changes since v2: - Jason: capture must ignore enclosing typeid entirely ([expr.prim.lambda.capture]/7). Do not change typeid_evaluated_p or mark_used. - New cutoff + typeid-operand RAII on the probe (parser and tsubst_expr). process_outer_var_ref looks through typeid only for lambdas; local classes keep the unevaluated early-return (pr125886.C params). - this-capture and every force-reset of cp_unevaluated_operand follow the cutoff, including cp_evaluated. - Tests: pr127132.C plus /7 cases on pr125886.C, including 10-deep nested typeid and the same nest inside sizeof. Changes since v1: - Jason: typeid_evaluated_p should err on the side of "evaluated" when it can't tell, not try to compute a precise answer. That v2 approach is superseded: capture no longer depends on typeid_evaluated_p. Bootstrapped on x86_64-pc-linux-gnu. Targeted g++.dg sweep (rtti, constexpr-typeid, template/typeid, lambda dirs, pr125886.C, coroutines/unevaluated.C): clean, 3553 expected passes. Full CI (make -k check-c++-all) in progress; will follow up with results. gcc/cp/ChangeLog: PR c++/127132 * cp-tree.h (saved_scope): Save unevaluated_typeid_cutoff. (cp_unevaluated_typeid_cutoff): Declare. (cp_unevaluated): Add a defaulted TYPEID_OPERAND parameter; save and restore the cutoff. (cp_evaluated): Save, reset, and restore the cutoff. * parser.cc (cp_unevaluated_typeid_cutoff): Define. (cp_unevaluated::cp_unevaluated, cp_unevaluated::~cp_unevaluated): Implement TYPEID_OPERAND cutoff handling. (cp_parser_postfix_expression): Pass typeid_operand=true for the typeid probe. * pt.cc (instantiate_class_template): Save/reset/restore the cutoff around NSDMI / lambda capture substitution. (tsubst_expr): Pass typeid_operand=true for TYPEID_EXPR's probe. * semantics.cc (process_outer_var_ref): Allow lambda capture through a typeid probe per [expr.prim.lambda.capture]/7; defer the "not captured" diagnostic to a later, non-probe pass. * lambda.cc (lambda_expr_this_capture): Same cutoff and deferral for 'this'. * constraint.cc (tsubst_constraint_variables): Save/reset/restore the cutoff with cp_unevaluated_operand. * name-lookup.cc (push_to_top_level, pop_from_top_level): Same. (local_state_t): Same. gcc/testsuite/ChangeLog: PR c++/127132 * g++.dg/rtti/typeid-lambda-capture-pr127132.C: New test, consolidating the original PR127132 reproducer with the /7 cases previously added to pr125886.C, plus this-capture coverage and closure-size checks. Signed-off-by: Vladislav Semykin <[email protected]> --- gcc/cp/constraint.cc | 3 + gcc/cp/cp-tree.h | 29 ++- gcc/cp/lambda.cc | 13 +- gcc/cp/name-lookup.cc | 7 + gcc/cp/parser.cc | 15 +- gcc/cp/pt.cc | 5 +- gcc/cp/semantics.cc | 14 +- .../rtti/typeid-lambda-capture-pr127132.C | 169 ++++++++++++++++++ 8 files changed, 242 insertions(+), 13 deletions(-) create mode 100644 gcc/testsuite/g++.dg/rtti/typeid-lambda-capture-pr127132.C diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index fa271a96282..d0d39da7979 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -1703,9 +1703,12 @@ tsubst_constraint_variables (tree t, tree args, subst_info info) /* Clear cp_unevaluated_operand across tsubst so that we get a proper chain of PARM_DECLs. */ int saved_unevaluated_operand = cp_unevaluated_operand; + int saved_unevaluated_typeid_cutoff = cp_unevaluated_typeid_cutoff; cp_unevaluated_operand = 0; + cp_unevaluated_typeid_cutoff = 0; tree vars = tsubst (t, args, info.complain, info.in_decl); cp_unevaluated_operand = saved_unevaluated_operand; + cp_unevaluated_typeid_cutoff = saved_unevaluated_typeid_cutoff; if (vars == error_mark_node) return error_mark_node; return declare_constraint_vars (t, vars); diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 369d2a9119d..05b0d2ac19f 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -2133,6 +2133,7 @@ struct GTY(()) saved_scope { bool expansion_stmt : 1; int unevaluated_operand; + int unevaluated_typeid_cutoff; int inhibit_evaluation_warnings; int noexcept_operand; int ref_temp_count; @@ -6303,13 +6304,25 @@ extern bool cp_preserve_using_decl; extern int cp_unevaluated_operand; +/* cp_unevaluated_operand depth up to which lambda capture is still + allowed. A typeid operand's own unevaluated probe raises this so + that probe does not by itself suppress capture + ([expr.prim.lambda.capture]/7). */ + +extern int cp_unevaluated_typeid_cutoff; + /* RAII class used to inhibit the evaluation of operands during parsing - and template instantiation. Evaluation warnings are also inhibited. */ + and template instantiation. Evaluation warnings are also inhibited. + TYPEID_OPERAND: this is a typeid operand's own probe, so it should + not by itself suppress lambda capture + ([expr.prim.lambda.capture]/7) - see cp_unevaluated_typeid_cutoff. */ class cp_unevaluated { public: - cp_unevaluated (); + int saved_cutoff; + + cp_unevaluated (bool typeid_operand = false); ~cp_unevaluated (); }; @@ -6321,13 +6334,19 @@ class cp_evaluated public: int uneval; int inhibit; + int typeid_cutoff; cp_evaluated (bool reset = true) - : uneval(cp_unevaluated_operand), inhibit(c_inhibit_evaluation_warnings) + : uneval (cp_unevaluated_operand), inhibit (c_inhibit_evaluation_warnings), + typeid_cutoff (cp_unevaluated_typeid_cutoff) { if (reset) - cp_unevaluated_operand = c_inhibit_evaluation_warnings = 0; } + { + cp_unevaluated_operand = c_inhibit_evaluation_warnings = 0; + cp_unevaluated_typeid_cutoff = 0; + } } ~cp_evaluated () { cp_unevaluated_operand = uneval; - c_inhibit_evaluation_warnings = inhibit; } + c_inhibit_evaluation_warnings = inhibit; + cp_unevaluated_typeid_cutoff = typeid_cutoff; } }; /* in pt.cc */ diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc index e0d676cae81..dfb0e472fb5 100644 --- a/gcc/cp/lambda.cc +++ b/gcc/cp/lambda.cc @@ -847,8 +847,10 @@ lambda_expr_this_capture (tree lambda, int add_capture_p) this_capture = spec; } - /* In unevaluated context this isn't an odr-use, so don't capture. */ - if (cp_unevaluated_operand) + /* In unevaluated context this isn't an odr-use, so don't capture. + A typeid operand's own probe is capture-transparent + ([expr.prim.lambda.capture]/7). */ + if (cp_unevaluated_operand > cp_unevaluated_typeid_cutoff) add_capture_p = false; /* If we captured 'this' but don't have a capture proxy yet, look up the @@ -948,16 +950,19 @@ lambda_expr_this_capture (tree lambda, int add_capture_p) } } - if (cp_unevaluated_operand) + if (cp_unevaluated_operand > cp_unevaluated_typeid_cutoff) result = this_capture; else if (!this_capture) { - if (add_capture_p == 1) + if (add_capture_p == 1 && !cp_unevaluated_operand) { error ("%<this%> was not captured for this lambda function"); result = error_mark_node; } else + /* Either add_capture_p != 1, or we're inside a typeid operand's + own probe and don't know yet whether it's really evaluated; + defer instead of diagnosing. */ result = NULL_TREE; } else diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc index 60a6e6f4430..13cee628faa 100644 --- a/gcc/cp/name-lookup.cc +++ b/gcc/cp/name-lookup.cc @@ -9069,6 +9069,7 @@ push_to_top_level (void) s->need_pop_function_context = need_pop; s->function_decl = current_function_decl; s->unevaluated_operand = cp_unevaluated_operand; + s->unevaluated_typeid_cutoff = cp_unevaluated_typeid_cutoff; s->inhibit_evaluation_warnings = c_inhibit_evaluation_warnings; s->suppress_location_wrappers = suppress_location_wrappers; s->x_stmt_tree.stmts_are_full_exprs_p = true; @@ -9080,6 +9081,7 @@ push_to_top_level (void) current_namespace = global_namespace; push_class_stack (); cp_unevaluated_operand = 0; + cp_unevaluated_typeid_cutoff = 0; c_inhibit_evaluation_warnings = 0; suppress_location_wrappers = 0; } @@ -9112,6 +9114,7 @@ pop_from_top_level (void) pop_function_context (); current_function_decl = s->function_decl; cp_unevaluated_operand = s->unevaluated_operand; + cp_unevaluated_typeid_cutoff = s->unevaluated_typeid_cutoff; c_inhibit_evaluation_warnings = s->inhibit_evaluation_warnings; suppress_location_wrappers = s->suppress_location_wrappers; @@ -9129,6 +9132,7 @@ namespace { struct local_state_t { int cp_unevaluated_operand; + int cp_unevaluated_typeid_cutoff; int c_inhibit_evaluation_warnings; int cp_noexcept_operand_; bool has_cfun; @@ -9139,6 +9143,8 @@ struct local_state_t local_state_t s; s.cp_unevaluated_operand = ::cp_unevaluated_operand; ::cp_unevaluated_operand = 0; + s.cp_unevaluated_typeid_cutoff = ::cp_unevaluated_typeid_cutoff; + ::cp_unevaluated_typeid_cutoff = 0; s.c_inhibit_evaluation_warnings = ::c_inhibit_evaluation_warnings; ::c_inhibit_evaluation_warnings = 0; s.cp_noexcept_operand_ = ::cp_noexcept_operand; @@ -9153,6 +9159,7 @@ struct local_state_t restore () const { ::cp_unevaluated_operand = this->cp_unevaluated_operand; + ::cp_unevaluated_typeid_cutoff = this->cp_unevaluated_typeid_cutoff; ::c_inhibit_evaluation_warnings = this->c_inhibit_evaluation_warnings; ::cp_noexcept_operand = this->cp_noexcept_operand_; if (this->has_cfun) diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 967ba3fd542..634dd410ff7 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -296,6 +296,12 @@ static FILE *cp_lexer_debug_stream; sizeof can be nested. */ int cp_unevaluated_operand; +/* cp_unevaluated_operand depth up to which lambda capture is still + allowed; raised by a typeid operand's own unevaluated probe so that + probe does not by itself suppress capture + ([expr.prim.lambda.capture]/7). */ +int cp_unevaluated_typeid_cutoff; + /* Nonzero if we are parsing a reflect-expression and shouldn't strip using-declarations. */ bool cp_preserve_using_decl; @@ -3241,16 +3247,21 @@ static tree cp_parser_omp_loop_nest (cp_parser *, bool *); // Unevaluated Operand Guard // // Implementation of an RAII helper for unevaluated operand parsing. -cp_unevaluated::cp_unevaluated () +cp_unevaluated::cp_unevaluated (bool typeid_operand) + : saved_cutoff (cp_unevaluated_typeid_cutoff) { + bool transparent = (cp_unevaluated_operand == cp_unevaluated_typeid_cutoff); ++cp_unevaluated_operand; ++c_inhibit_evaluation_warnings; + if (typeid_operand && transparent) + cp_unevaluated_typeid_cutoff = cp_unevaluated_operand; } cp_unevaluated::~cp_unevaluated () { --c_inhibit_evaluation_warnings; --cp_unevaluated_operand; + cp_unevaluated_typeid_cutoff = saved_cutoff; } // -------------------------------------------------------------------------- // @@ -8579,7 +8590,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, (mark_used -> instantiation; lambda capture). */ cp_lexer_save_tokens (parser->lexer); { - cp_unevaluated u; + cp_unevaluated u (/* typeid_operand= */ true); expression = cp_parser_expression (parser, &idk); } /* If we're already within an unevaluated operand, everything diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index dd0efa54112..28845214eed 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -12962,6 +12962,7 @@ instantiate_class_template (tree type) maybe_diagnose_erroneous_template (t ? TI_TEMPLATE (t) : templ); int saved_unevaluated_operand = cp_unevaluated_operand; + int saved_unevaluated_typeid_cutoff = cp_unevaluated_typeid_cutoff; int saved_inhibit_evaluation_warnings = c_inhibit_evaluation_warnings; fn_context = decl_function_context (TYPE_MAIN_DECL (type)); @@ -12973,6 +12974,7 @@ instantiate_class_template (tree type) else { cp_unevaluated_operand = 0; + cp_unevaluated_typeid_cutoff = 0; c_inhibit_evaluation_warnings = 0; } @@ -13390,6 +13392,7 @@ instantiate_class_template (tree type) /* Restore these before substituting into the lambda capture initializers. */ cp_unevaluated_operand = saved_unevaluated_operand; + cp_unevaluated_typeid_cutoff = saved_unevaluated_typeid_cutoff; c_inhibit_evaluation_warnings = saved_inhibit_evaluation_warnings; } @@ -23098,7 +23101,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) tree operand; tree uneval; { - cp_unevaluated u; + cp_unevaluated u (/* typeid_operand= */ true); uneval = RECUR (operand_0); } /* If we're already within an unevaluated operand, everything diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index bb952e2f62c..f1a52b196a4 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -4682,15 +4682,21 @@ tree process_outer_var_ref (tree decl, tsubst_flags_t complain, bool odr_use/*=false*/) { + bool in_typeid_probe = false; if (cp_unevaluated_operand) { tree type = TREE_TYPE (decl); if (!dependent_type_p (type) && variably_modified_type_p (type, NULL_TREE)) /* VLAs are used even in unevaluated context. */; - else + else if (cp_unevaluated_operand > cp_unevaluated_typeid_cutoff) /* It's not a use (3.2) if we're in an unevaluated context. */ return decl; + else + /* Inside a typeid operand's own probe: capture is still + attempted below ([expr.prim.lambda.capture]/7), but a missing + capture-default must not be diagnosed yet - see below. */ + in_typeid_probe = true; } if (decl == error_mark_node) return decl; @@ -4777,6 +4783,12 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain, if (flag_contracts && processing_contract_condition) set_contract_capture_flag (decl, true); } + /* Capture was attempted above; whether it's really needed depends on + an evaluated-ness we don't know yet. Defer instead of diagnosing: + a later, non-probe pass will complain if the operand turns out + evaluated, and there's nothing to complain about if it doesn't. */ + else if (in_typeid_probe) + return var; /* Only an odr-use of an outer automatic variable causes an error, and a constant variable can decay to a prvalue constant without odr-use. So don't complain yet. */ diff --git a/gcc/testsuite/g++.dg/rtti/typeid-lambda-capture-pr127132.C b/gcc/testsuite/g++.dg/rtti/typeid-lambda-capture-pr127132.C new file mode 100644 index 00000000000..ccb426db8a4 --- /dev/null +++ b/gcc/testsuite/g++.dg/rtti/typeid-lambda-capture-pr127132.C @@ -0,0 +1,169 @@ +// PR c++/127132: typeid inside a generic lambda's capture-default failed +// to capture, because capture incorrectly depended on whether the +// typeid operand was evaluated ([expr.typeid]/4-5), rather than on +// [expr.prim.lambda.capture]/7 (P0588R1 / CWG1468), which says capture +// must ignore the effect of any enclosing typeid. Regression against +// the PR c++/125886 fix (r17-2200-g621661f7d2e9). +// { dg-do compile { target c++14 } } +// c++14 target: several cases below use a generic lambda's auto +// parameter, valid from C++14 on. + +#include <typeinfo> + +void foo(const std::type_info &type); + +// [expr.typeid]/4: *c is a polymorphic glvalue, so typeid's operand is +// evaluated, and the generic lambda's by-copy capture-default must +// capture c for that use. On trunk this capture is currently missing, +// leaving a decl reference with nothing behind it once the lambda's +// call operator is expanded - crashing at RTL expand time instead of +// compiling cleanly. + +class C { + virtual ~C(); +}; + +void +bar (C *c) +{ + [=](auto a) { foo(typeid(*c)); }(1); +} + +// [expr.prim.lambda.capture]/7 (P0588R1 / CWG1468): capture must ignore +// the effect of enclosing typeid expressions. n is not a polymorphic +// glvalue, so the operand is unevaluated ([expr.typeid]/5), but n is +// still captured. +// +// If this starts to fail that's OK, the compiler is allowed to optimize +// away the capture, but we need to control it with -fabi-version. + +void +capture_regardless_of_evaluated (int n) +{ + auto lam = [=] { (void) typeid (n); }; + static_assert (sizeof (lam) == sizeof (int), ""); +} + +// Generic-lambda variant. + +void +capture_regardless_of_evaluated_generic (int n) +{ + auto lam = [=](auto a) { (void) typeid (n); }; + lam (0); + static_assert (sizeof (lam) == sizeof (int), ""); +} + +// No capture-default and a genuinely unevaluated operand: must not +// diagnose "not captured". + +void +no_capture_default_non_poly (int n) +{ + [] { (void) typeid (n); }; +} + +// [expr.prim.lambda.capture]/7: capture must not depend on whether the +// typeid operand is evaluated. F is final, so [expr.typeid]/4 leaves +// typeid (f) unevaluated, but f is still captured. Generic-lambda +// variant of pr125886.C's final_glvalue. + +struct F final { virtual ~F (); }; + +void +final_glvalue_generic (F &f) +{ + auto lam = [=](auto a) { foo (typeid (f)); }; + lam (1); + static_assert (sizeof (lam) == sizeof (F), ""); +} + +// D::i names no local entity, so [expr.prim.lambda.capture]/7 does not +// apply and a DR613 non-static member id-expression stays unevaluated +// (no capture). Generic-lambda variant of pr125886.C's nsm. + +struct D { int i; }; + +void +nsm_generic () +{ + auto lam = [=](auto a) { foo (typeid (D::i)); }; + lam (1); + static_assert (sizeof (lam) == 1, ""); +} + +// sizeof (typeid (b)) is a genuinely unevaluated context wrapping +// typeid, so the cutoff stays unraised and b is not captured. + +struct B { virtual ~B (); }; + +void +nested_unevaluated_lambda_size (B &b) +{ + auto lam = [=] { (void) sizeof (typeid (b)); }; + static_assert (sizeof (lam) == 1, ""); +} + +// Nested typeid looks through every enclosing typeid +// ([expr.prim.lambda.capture]/7, "any enclosing typeid expressions"). + +void +nested_typeid_capture (int x) +{ + auto lam = [=] { (void) typeid (typeid (x)); }; + static_assert (sizeof (lam) == sizeof (int), ""); +} + +// Extremely nested typeid: look-through on each probe +// (operand == cutoff -> cutoff += 1). + +void +nested_typeid_capture_extreme (int x) +{ + auto lam = [=] { + (void) typeid (typeid (typeid (typeid (typeid (typeid (typeid (typeid (typeid (typeid (typeid (x))))))))))); + }; + static_assert (sizeof (lam) == sizeof (int), ""); +} + +// sizeof does not raise the cutoff, so all 10 typeid probes stay above +// it and x is not captured. + +void +nested_typeid_capture_extreme_v2 (int x) +{ + auto lam = [=] { (void) sizeof (typeid (typeid (typeid (typeid (typeid (typeid (typeid (typeid (typeid (typeid (x))))))))))); }; + static_assert (sizeof (lam) == 1, ""); +} + +// this-capture variant of capture_regardless_of_evaluated: +// [expr.prim.lambda.capture]/7 applies to *this like any other local +// entity. [&] avoids the (unrelated) C++20 deprecation warning for +// implicit this-capture via [=]. + +struct E { + int i; + void this_capture_regardless_of_evaluated (); +}; + +void +E::this_capture_regardless_of_evaluated () +{ + auto lam = [&] { (void) typeid (this->i); }; + static_assert (sizeof (lam) == sizeof (E *), ""); +} + +// Generic-lambda variant. + +struct E2 { + int i; + void this_capture_regardless_of_evaluated_generic (); +}; + +void +E2::this_capture_regardless_of_evaluated_generic () +{ + auto lam = [&](auto a) { (void) typeid (this->i); }; + lam (1); + static_assert (sizeof (lam) == sizeof (E2 *), ""); +} -- 2.43.0
