https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127750
--- Comment #3 from Vladislav Semykin <vladislav.semykin at gmail dot com> --- Thanks. I read this against current trunk. Your guess about r17-2200-g621661f7d2e9 (PR c++/125886) is right. Preliminary only, no patch yet. The two-pass typeid parse is in cp_parser_postfix_expression (RID_TYPEID) and again in tsubst_expr for TYPEID_EXPR. The operand is parsed under cp_unevaluated, then, when typeid_evaluated_p is true, the tokens are rolled back and the operand is parsed again. The comment on that rollback says the unevaluated pass has nothing to undo. That is not true of a lambda. Parsing one allocates the closure type and consumes both discriminators, in record_lambda_scope_discriminator and record_lambda_scope_sig_discriminator (lambda.cc). Those counters are function-scope state, not lexer state, so cp_lexer_rollback_tokens does not put them back. The same double count happens at instantiation: tsubst_expr substitutes the operand twice, and each substitution goes through tsubst_lambda_expr, which records the discriminators again. The tree that survives is the second closure. That is why the only lambda in g is mangled as the second one. The Itanium ABI (https://itanium-cxx-abi.github.io/cxx-abi/abi.html#closure-types) says: <closure-type-name> ::= Ul <lambda-sig> E [ <nonnegative number> ] _ The number is omitted for the first closure type with a given in a context, and is n-2 for the nth. The ABI's own example is _ZZ1giENKUlvE_clEv for the first () lambda and _ZZ1giENKUlvE0_clEv for the second. GCC 16 emits the first form. Trunk emits the second, with no _GXX_ABI_VERSION change. write_discriminator in mangle.cc writes nothing for discriminator 0 and writes discriminator-1 otherwise, so a counter value of 1 becomes the 0 in UlvE0. I agree this should be restored to the GCC 16 spelling, not papered over with a new ABI version. The C++17 reject-valid is the same probe. cp_parser_lambda_expression diagnoses "lambda-expression in unevaluated context" as soon as cp_unevaluated_operand is set, which the probe does before typeid_evaluated_p is consulted. For a glvalue of polymorphic class type the operand is not an unevaluated operand: https://eel.is/c++draft/expr.typeid#4 is that case, and the "unevaluated operand" sentence is only in https://eel.is/c++draft/expr.typeid#5, for every other expression. The C++17 ban (https://timsong-cpp.github.io/cppwp/n4659/expr.prim.lambda#2) applies only to an unevaluated operand; P0315R4 lifted it in C++20. So a lambda inside an evaluated polymorphic typeid operand is well-formed in C++17. On a template the reparse is also skipped while parsing the definition (the guard is processing_template_decl == 0), which is why tmpl is rejected at the definition, with -Wtemplate-body. The other snippets split apart. [] { typeid(b); } on a non-final polymorphic b is ill-formed: the operand is evaluated under expr.typeid#4, so b is odr-used and there is no capture-default. That pattern is lambda_poly_capture in g++.dg/cpp0x/pr125886.C, which has expected "not captured" since r17-2200. The probe defers that diagnostic on purpose; the evaluated reparse is what emits it. [] { typeid(r); } and typeid(std::declval<F>()) for final F are silent for a different reason. CLASSTYPE_FINAL makes resolves_to_fixed_type_p true, so typeid_evaluated_p is false and there is no second pass. The missing "not captured" is then dropped by the probe (that deferral is a310db3ba39, PR c++/127132). A strict reading disagrees: https://eel.is/c++draft/class.virtual#1 defines a polymorphic class as one with a virtual member function, and final is not an exception, so expr.typeid#5 does not apply. GCC 16 diagnosed these because it parsed every typeid operand as evaluated. I do not want to fold that policy change into the mangling fix. typeid(*static_cast<F*>(nullptr)) is required to throw. https://eel.is/c++draft/expr.typeid#3 says that if the operand is a possibly-parenthesized unary * and the pointer evaluates to a null pointer value, the typeid expression throws std::bad_typeid. F has a virtual destructor, so it is a polymorphic class under class.virtual#1 even though it is final. The operand is therefore a glvalue of polymorphic class type: expr.typeid#4 applies, and expr.typeid#5 does not make it unevaluated. The pointer is a constant null, so the abstract machine throws. Skipping the vtable load for a live final object is fine, because the type_info is the static one either way. Skipping the throw is not: the program returns 0 instead of throwing. That is what the three compilers do on https://godbolt.org/z/6oG4ce8T4. Clang 22.1 emits a call to __cxa_bad_typeid. GCC 13.3 reduces main to mov eax, 0; ret. MSVC 19.51 does not mention __cxa_bad_typeid, but it zeroes rcx and calls __RTtypeid; that helper throws bad_typeid when its argument is null (https://learn.microsoft.com/en-us/cpp/cpp/typeid-operator). Locally, clang 22.1.8 aborts with std::bad_typeid and Ubuntu 24.04 g++ 13.3.0 does not. GCC is the one that deletes the check. build_typeid emits the throw only when typeid_evaluated_p is true, and a final class never gets there. This predicate is older than the two-pass parse, so I agree it is not a GCC 17 regression, but the missing throw is still wrong. What I intend to fix for this PR is the probe consuming a lambda (discriminator and the pre-C++20 diagnostic) for an operand that expr.typeid#4 evaluates. The missing bad_typeid on a null unary * of a final polymorphic type is a separate bug: the null check has to be emitted even when the dynamic type is known statically. I would not bundle it into the mangling change. I have filed two separate reports for the final-class cases and asked to be assigned to them. One is this missing std::bad_typeid. The other is that typeid treats a glvalue of a final polymorphic type as an unevaluated operand, so an odr-use inside a lambda is accepted with no capture and std::declval of that type is accepted. I will add the bug numbers here once they are assigned, and fix those reports as I can. See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127833 and https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127834
