https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127834
Bug ID: 127834
Summary: final polymorphic typeid operand treated as
unevaluated
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c++
Assignee: unassigned at gcc dot gnu.org
Reporter: vladislav.semykin at gmail dot com
Target Milestone: ---
A glvalue operand of typeid whose type is a final polymorphic class is
treated as an unevaluated operand. [expr.typeid]/4 does not allow that.
final means the dynamic type of a live object equals the static type.
It does not make the class non-polymorphic, and it does not move the
operand into [expr.typeid]/5.
Two programs that should be ill-formed are accepted.
#include <typeinfo>
#include <utility>
struct F final { virtual ~F(); };
void missing_capture(F& r) {
// r would be potentially evaluated if the typeid were ignored,
// so the lambda must capture it.
[] { (void) typeid(r); };
}
void declval_evaluated() {
// The call is potentially evaluated, so declval's body is
// instantiated and its static_assert fires.
(void) typeid(std::declval<F>());
}
The non-final counterparts are already rejected, and the tests lock
that in:
- gcc/testsuite/g++.dg/cpp0x/pr125886.C, lambda_poly_capture:
[] { typeid(b); } for non-final B is "b is not captured".
- gcc/testsuite/g++.dg/rtti/typeid14.C: typeid(std::declval<B>())
for non-final B instantiates std::declval and hits
static_assert(..., "declval() must not be used!") in
libstdc++-v3/include/std/type_traits.
For final F both are silent. pr125886.C final_glvalue documents
the current model in a comment: the operand is unevaluated, so there
is no vtable lookup and no odr-use. That comment describes what the
front end does. It is not what [expr.typeid]/4 requires.
Normative text:
- [expr.typeid]/4 (https://eel.is/c++draft/expr.typeid#4): when typeid
is applied to a glvalue of polymorphic class type, the result is the
type of the most derived object, and the expression is a
potentially-evaluated expression (it is not an unevaluated operand).
- [expr.typeid]/5 (https://eel.is/c++draft/expr.typeid#5): the operand
is an unevaluated operand only in every other case. That is the
static type.
- [class.virtual]/1 (https://eel.is/c++draft/class.virtual#1): a class
with a virtual member function is polymorphic. final is not an
exception.
- [expr.prim.lambda.capture]/7
(https://eel.is/c++draft/expr.prim.lambda.capture#7): an id-expression
is potentially evaluated for implicit capture if it would be
potentially evaluated ignoring any enclosing typeid. The
missing_capture example is ill-formed even before the evaluation
question is reached. declval_evaluated is the evaluation question
itself: the call is potentially evaluated, so the function body is
instantiated.
Trunk mechanism:
- resolves_to_fixed_type_p (gcc/cp/class.cc) returns 1 immediately
for CLASSTYPE_FINAL.
- typeid_evaluated_p (gcc/cp/rtti.cc) is then false, so the
two-pass parse keeps the unevaluated probe and never re-parses the
operand evaluated.
- build_typeid therefore never treats the operand as evaluated.
get_tinfo_ptr_dynamic returns the static type_info and does not
load the vtable.
Using the static type_info for a live final object is a valid as-if
choice: the most derived type is the static type. Treating the operand
as unevaluated is not, because potential evaluation is observable
(declval, odr-use, and the missing-capture diagnostic).
Please keep this separate from the null-pointer bug. typeid(*p) for
a null F* must throw std::bad_typeid ([expr.typeid]/3) even when
the dynamic type is known statically. That throw can be fixed without
changing the "fixed type" model. This report is the model: a final
polymorphic glvalue is still a potentially-evaluated operand. I am
filing the null-check bug separately and will add its number to See
Also.
Also out of scope here: PR c++/127750, where the unevaluated probe
consumes a lambda when the operand really is evaluated under
[expr.typeid]/4. That fix checkpoints lambda discriminators around the
probe. It does not change resolves_to_fixed_type_p.
Please assign this report to me.