On Mon, Jul 06, 2026 at 01:33:46PM +0300, Vladislav Semykin wrote: > Made changes according to Jason's Merrill comments.
> From 9a42e71cd2244b80327cb37fe372c45b018b5958 Mon Sep 17 00:00:00 2001 > From: Vladislav Semykin <[email protected]> > Date: Mon, 6 Jul 2026 13:25:26 +0300 > Subject: [PATCH v4] cp: fix unevaluated operand context for typeid [PR > c++/125886] > > Per [expr.typeid]/4-5, a typeid operand is unevaluated by default and > is evaluated only for a glvalue of polymorphic class type whose dynamic > type is not known at compile time. Previously GCC always parsed the > operand in an evaluated context, which broke unevaluated uses (declval, > non-static data members per DR613, function parameters) and missed > lambda capture diagnostics for evaluated polymorphic operands. > > Implement a two-pass parse: first under cp_unevaluated, then - if > typeid_evaluated_p says the operand is evaluated - roll back and > re-parse under cp_evaluated. Share the evaluated/unevaluated predicate > via typeid_evaluated_p in rtti.cc, used from the parser, tsubst_expr, > and build_typeid. > > Also fixes PR c++/68604 and PR c++/116385, and removes a now-stale xfail > in g++.dg/coroutines/unevaluated.C. > > Changes since v1: > - Apply the same two-pass logic to template-dependent operands in > tsubst_expr (TYPEID_EXPR), as Jason requested. > - Fix lambda capture: a polymorphic glvalue typeid operand inside a > lambda without a capture-default is now correctly diagnosed as > "not captured". Root cause was typeid_evaluated_p rejecting > reference types and being gated on the "nonnull" flag, which is > unrelated to the evaluated/unevaluated decision. > - Factor the polymorphic-glvalue predicate into shared typeid_evaluated_p > used by the parser, tsubst, and build_typeid. > - Drop the cxx_dialect >= cxx11 gate: per Jason, DR613 applies in all > modes, since finish_non_static_data_member doesn't check cxx_dialect > either. > > Changes since v2: > - Restored the full commit message (rationale + ChangeLog entries). > - Shortened cp_unevaluated_operand comments in cp-tree.h and parser.cc > (git gcc-style). > - Fixed indentation in parser.cc and rtti.cc (git gcc-style). > - Use cp_unevaluated RAII in cp_parser_postfix_expression instead of > manual ++/--cp_unevaluated_operand, matching tsubst_expr. > - Kept glvalue_p in typeid_evaluated_p: [expr.typeid]/4 applies only to > a glvalue of polymorphic class type; a polymorphic class prvalue can > have resolves_to_fixed_type_p false yet still be unevaluated (see > "Not a glvalue" in g++.dg/cpp2a/constexpr-typeid2.C). Without > glvalue_p that case would incorrectly take the evaluated re-parse > path. > - Investigated the g++.dg/modules/compile-std1.C failure Jason flagged > as surprising (reproduced with > 'make check-c++ RUNTESTFLAGS="modules.exp=compile-std1.C"'). > g++.log shows cc1plus: fatal error: bits/std.cc: No such file or directory > and the same for bits/std.compat.cc under --compile-std-module; import > std then fails because gcm.cache/std.gcm was never built. Here the > libstdc++ symlinks at .../include/bits/std.{,compat.}cc point at > src/c++23/std.{,compat.}cc, but those generated files are missing > (only .in templates remain) - incomplete libstdc++ modules build, not > a cp/typeid regression. Unrelated to this patch. > > Changes since v3: > - Removed cp_evaluated on the re-parse path in both > cp_parser_postfix_expression and tsubst_expr. As Jason pointed > out, the re-parse should not override the typeid itself being > within an outer unevaluated operand (e.g. sizeof (typeid (b))); > it should just drop back to whatever context enclosed it. > - Added a comment on the glvalue_p check in typeid_evaluated_p > noting it's only needed because resolves_to_fixed_type_p doesn't > handle all prvalue cases (e.g. COMPOUND_EXPR), per Jason's > observation. > - Removed a stale test (lambda_sizeof_typeid) that encoded the old, > incorrect behavior (requiring capture for typeid nested inside > sizeof); added nested_unevaluated and nested_unevaluated_lambda > to cover the corrected behavior instead. > > Tested full CI on: x86_64 GNU/Linux 6.17.0-35-generic Ubuntu 24.04. > The following failures are pre-existing on trunk without this patch > and are unrelated to the changes here (full log via > 'make -C gcc -k check-c++-all' attached): > > FAIL: c-c++-common/analyzer/flex-without-call-summaries.c -std=c++11 > at line 885 (test for warnings, line 884) > XPASS: c-c++-common/analyzer/flex-without-call-summaries.c -std=c++11 > PR analyzer/103546 (test for bogus messages, line 892) > FAIL: c-c++-common/analyzer/flex-without-call-summaries.c -std=c++11 > (test for excess errors) > FAIL: g++.dg/guality/pr55665.C -O2 -flto -fno-use-linker-plugin > -flto-partition=none line 23 p == 40 > FAIL: g++.dg/modules/compile-std1.C -std=c++26 -fimplicit-constexpr > (test for excess errors) > FAIL: g++.dg/modules/xtreme-header-8.C -std=c++26 -fimplicit-constexpr > (test for excess errors) > FAIL: g++.dg/modules/xtreme-header-2_a.H -std=c++26 -fimplicit-constexpr > (test for excess errors) > FAIL: g++.dg/modules/xtreme-header-2_c.C -std=c++26 -fimplicit-constexpr > (test for excess errors) > FAIL: g++.dg/modules/xtreme-header-7_a.H -std=c++26 -fimplicit-constexpr > (test for excess errors) > FAIL: g++.dg/modules/xtreme-header_a.H -std=c++26 -fimplicit-constexpr > (test for excess errors) > FAIL: g++.dg/plugin/std-module-exports-c++20.C > -fplugin=./std_module_exports_plugin.so (test for excess errors) > FAIL: g++.dg/plugin/std-module-exports-c++23.C > -fplugin=./std_module_exports_plugin.so (test for excess errors) > FAIL: g++.dg/plugin/std-module-exports-c++26.C > -fplugin=./std_module_exports_plugin.so (test for excess errors) > > gcc/cp/ChangeLog: > PR c++/125886 > * parser.cc (cp_parser_postfix_expression): Two-pass typeid parse. > * pt.cc (tsubst_expr): Same for TYPEID_EXPR. > * rtti.cc (typeid_evaluated_p, build_typeid): Shared predicate. > * cp-tree.h: Declare typeid_evaluated_p. > > gcc/testsuite/ChangeLog: > * g++.dg/cpp0x/pr125886.C: New test. > * g++.dg/rtti/typeid14.C, g++.dg/rtti/typeid15.C: New tests. > * g++.dg/coroutines/unevaluated.C: Drop stale xfail. > > Signed-off-by: Vladislav Semykin <[email protected]> > --- > gcc/cp/cp-tree.h | 1 + > gcc/cp/parser.cc | 26 +++++- > gcc/cp/pt.cc | 17 +++- > gcc/cp/rtti.cc | 48 ++++++++--- > gcc/testsuite/g++.dg/coroutines/unevaluated.C | 3 +- > gcc/testsuite/g++.dg/cpp0x/pr125886.C | 82 +++++++++++++++++++ > gcc/testsuite/g++.dg/rtti/typeid14.C | 19 +++++ > gcc/testsuite/g++.dg/rtti/typeid15.C | 23 ++++++ > 8 files changed, 201 insertions(+), 18 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/cpp0x/pr125886.C > create mode 100644 gcc/testsuite/g++.dg/rtti/typeid14.C > create mode 100644 gcc/testsuite/g++.dg/rtti/typeid15.C > > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > index 132139f9d..df0f253cc 100644 > --- a/gcc/cp/cp-tree.h > +++ b/gcc/cp/cp-tree.h > @@ -7486,6 +7486,7 @@ extern tree current_nonlambda_class_type (void); > extern tree finish_struct (tree, tree); > extern void finish_struct_1 (tree); > extern int resolves_to_fixed_type_p (tree, int * = NULL); > +extern bool typeid_evaluated_p (tree); > extern void init_class_processing (void); > extern int is_empty_class (tree); > extern bool is_really_empty_class (tree, bool); > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc > index 8194106c6..5bb434903 100644 > --- a/gcc/cp/parser.cc > +++ b/gcc/cp/parser.cc > @@ -292,7 +292,8 @@ static void missing_template_diag > static FILE *cp_lexer_debug_stream; > > /* Nonzero if we are parsing an unevaluated operand: an operand to > - sizeof, typeof, or alignof. */ > + sizeof, typeof, or alignof. This is a count since operands to > + sizeof can be nested. */ > int cp_unevaluated_operand; > > /* Nonzero if we are parsing a reflect-expression and shouldn't strip > @@ -8570,9 +8571,28 @@ cp_parser_postfix_expression (cp_parser *parser, bool > address_p, bool cast_p, > else > { > tree expression; > + /* [expr.typeid]/4-5: parse the operand unevaluated first; if it is > + a polymorphic glvalue, roll back and re-parse it evaluated, > + since an evaluated parse has irreversible side-effects > + (mark_used -> instantiation; lambda capture). */ > + cp_lexer_save_tokens (parser->lexer); > + { > + cp_unevaluated u; > + expression = cp_parser_expression (parser, &idk); > + } > + if (expression != error_mark_node > + && processing_template_decl == 0 > + && typeid_evaluated_p (expression)) If we can already be in an unevaluated context, I would think this should check !cp_unevaluated_operand otherwise we'll parse in an unevaluated context again. Marek
