> Does this also fix PR116385 and PR68604?

Yes, both, confirmed with the reproducers from those PRs:
- for PR116385 all four cases (lambda, local class, and the two
default-argument uses) now compile;
- for PR68604, typeid(A::i) compiles in C++11+.

One thing I caught while testing:
typeid(A::i) must remain ill-formed in C++98 (DR613 only applies from
C++11),
so I'm gating the unevaluated parse on cxx_dialect >= cxx11 to avoid
changing
the C++98 behavior. I'll send a v3 to gcc-patches that also handles the
template
operand in tsubst and factors the polymorphic-glvalue check into a shared
helper,
as you suggested.

> ...actually, why do we need C++11 here?

For the declval tests, C++11 is required because std::declval is a C++11
feature.
For typeid(A::i) (PR68604), it's required semantically: that case is only
valid from
C++11 onwards per DR613 - in C++98 it must stay ill-formed -
https://godbolt.org/z/WfG1xhsz1
Also, final is keyword introduced in C++11, but can be used with extension
as here: https://godbolt.org/z/G6bzToE5b


пт, 26 июн. 2026 г. в 22:32, Marek Polacek <[email protected]>:

> 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