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

Reply via email to