On Fri, Jun 26, 2026 at 09:23:15PM +0300, Vladislav Semykin wrote: > This v2 handles the non-template case via reparse. Two cases are left for > follow-up, and I'd appreciate your guidance on whether they belong in this > PR or separately:
A few random comments. The new patch doesn't seem to have a ChangeLog entry (or I missed it). The subject should be something like c++: fix unevaluated operand context for typeid [PR125886] > 1. Template-dependent operand: the decision needs to happen at instantiation > time in tsubst (TYPEID_EXPR). My first attempt with an unconditional > cp_unevaluated there regressed typeid10.C (PR c++/25466), so it needs the > same type-dependent two-pass logic. I personally would require this to be handled in this patch... > 2. Polymorphic glvalue inside a lambda: should be odr-used and captured, > but currently isn't. ...but not this. > For completeness: the template case is broken on current trunk in the same > way - wrongly triggers static_assert, just like the non-template case did > before this patch, here is the link: https://godbolt.org/z/Thnf6Mrx1 Does this also fix PR116385 and PR68604? > From aed304a308a9d33191285b0f23a45a2ad7070b75 Mon Sep 17 00:00:00 2001 > From: Vladislav Semykin <[email protected]> > Date: Fri, 26 Jun 2026 20:50:15 +0300 > Subject: [PATCH] cp: fix unevaluated operand context for typeid [PR > c++/125886] > > Signed-off-by: Vladislav Semykin <[email protected]> > --- > gcc/cp/cp-tree.h | 4 ++- > gcc/cp/parser.cc | 41 ++++++++++++++++++++++--- > gcc/testsuite/g++.dg/cpp0x/pr125886-2.C | 10 ++++++ > gcc/testsuite/g++.dg/cpp0x/pr125886.C | 21 +++++++++++++ > 4 files changed, 71 insertions(+), 5 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/cpp0x/pr125886-2.C > create mode 100644 gcc/testsuite/g++.dg/cpp0x/pr125886.C > > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > index 132139f9d..ccf2a5f5b 100644 > --- a/gcc/cp/cp-tree.h > +++ b/gcc/cp/cp-tree.h > @@ -6340,7 +6340,9 @@ extern bool cp_preserve_using_decl; > > /* Nonzero if we are parsing an unevaluated operand: an operand to > sizeof, typeof, or alignof. This is a count since operands to > - sizeof can be nested. */ > + sizeof can be nested. For typeid, the unevaluated/evaluated > + decision depends on the operand type ([expr.typeid]/4-5) and is > + taken in cp_parser_postfix_expression (RID_TYPEID) via reparse. */ > > extern int cp_unevaluated_operand; > > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc > index 8194106c6..08793e470 100644 > --- a/gcc/cp/parser.cc > +++ b/gcc/cp/parser.cc > @@ -292,7 +292,10 @@ 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. For typeid, the unevaluated/evaluated > + decision depends on the operand type ([expr.typeid]/4-5) and is > + taken in cp_parser_postfix_expression (RID_TYPEID) via reparse. */ > int cp_unevaluated_operand; I'm not sure we need to talk about cp_parser_postfix_expression in this comment. > /* Nonzero if we are parsing a reflect-expression and shouldn't strip > @@ -8569,10 +8572,40 @@ cp_parser_postfix_expression (cp_parser *parser, bool > address_p, bool cast_p, > /* Otherwise, fall back to the expression variant. */ > else > { > - tree expression; > + cp_lexer_save_tokens (parser->lexer); > + ++cp_unevaluated_operand; > + ++c_inhibit_evaluation_warnings; > + tree expression = cp_parser_expression(parser, &idk); Missing space before ). > + --c_inhibit_evaluation_warnings; > + --cp_unevaluated_operand; > + > + bool need_eval = false; > + if (expression != error_mark_node > + && processing_template_decl == 0) > + { > + /* Reuse the same predicate as build_typeid (rtti.cc) so > + there is a single source of truth for the [expr.typeid]/4 > + condition. */ > + int nonnull = 0; > + tree t = TREE_TYPE (expression); > + need_eval = (CLASS_TYPE_P (t) > + && TYPE_POLYMORPHIC_P (t) > + && !resolves_to_fixed_type_p (expression, &nonnull) > + && !nonnull > + /* Only glvalues are evaluated. */ > + /* prvalues are unevaluated even for > + polymorphic types. */ > + && glvalue_p (expression)); This should be a new routine otherwise they will get out of sync at some point. > + } > + if (need_eval) > + { > + /* Roll back to unevaluated parse. */ I'm not sure I understand this comment. There should be a comment before the first cp_parser_expression explaining what's going on here. > + cp_lexer_rollback_tokens (parser->lexer); > + expression = cp_parser_expression (parser, &idk); > + } > + else > + cp_lexer_commit_tokens (parser->lexer); > > - /* Look for an expression. */ > - expression = cp_parser_expression (parser, & idk); > /* Compute its typeid. */ > postfix_expression = build_typeid (expression, tf_warning_or_error); > /* Look for the `)' token. */ > diff --git a/gcc/testsuite/g++.dg/cpp0x/pr125886-2.C > b/gcc/testsuite/g++.dg/cpp0x/pr125886-2.C > new file mode 100644 > index 000000000..f5e565fc0 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp0x/pr125886-2.C Let's call the test typeid14.C and put it into rtti/. > @@ -0,0 +1,10 @@ Add // PR c++/125886 as the first line. > +/* { dg-do compile } */ > +/* { dg-options "-std=c++11" } */ Make this // { dg-do compile { target c++11 } } ... actually, why do we need C++11 here? > + > +#include <typeinfo> > +#include <utility> > + > +// Polymorphic glvalue: [expr.typeid]/4 evaluated -> declval<B>() is > +// instantiated and __declval_protector's static_assert fires. > +class B { virtual ~B(); }; > +void bar() { char const *b = typeid(std::declval<B>()).name(); } // { > dg-error "static assertion failed: declval" "" { target *-*-* } 0 } > diff --git a/gcc/testsuite/g++.dg/cpp0x/pr125886.C > b/gcc/testsuite/g++.dg/cpp0x/pr125886.C > new file mode 100644 > index 000000000..9e9673d02 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp0x/pr125886.C > @@ -0,0 +1,21 @@ > +/* { dg-do compile } */ > +/* { dg-options "-std=c++11" } */ Same as above. > +#include <typeinfo> > +#include <utility> > + > +// 1. Non-polymorphic: [expr.typeid]/5 unevaluated -> declval<A>() is ok. > +class A {}; > +void foo() { char const *a = typeid(std::declval<A>()).name(); } > + > +// 2. Non-polymorphic lambda operand: unevaluated, no capture needed. > +void qux(int n) { [&]{ typeid(n); }; } > + > +// 3. final-class polymorphic glvalue: resolves_to_fixed_type_p -> static > +// typeid, unevaluated (no vtable lookup, no odr-use). > +class F final { virtual ~F(); }; > +void fin(F& f) { typeid(f); } > + > +// TODO: The template-dependent operand and polymorphic > +// lambda-capture cases are not yet handled and > +// will be addressed in a follow-up. > -- > 2.43.0 > Thanks, Marek
