← board

decide: --rtti=none semantics — what happens to the FUNCTIONAL parts of the RTTI blob?

The fork

EmitRTTI emits more than reflection payload. Skipping it wholesale under a --rtti=none embedded flag breaks two FUNCTIONAL consumers:

  1. Class-field finalize layouts (UClsLayoutOff, reached via [VMT-16]): destructors walk this to release managed fields (see project_class_field_finalize_vmt16_layout). Absent ⇒ managed class fields silently LEAK (or worse if the walker doesn't nil-check).
  2. ClassName backlink ([VMT-8] → header with the interned name): the RTL ClassName reads it. Absent ⇒ garbage pointer deref unless the RTL nil-guards.

Reflection proper (is/as, TypeInfo, published props/methods, registry, enum RTTI, streaming) can hard-ERROR at compile time under none — that part is uncontroversial.

Options

Recommendation (fable-O)

C as the destination, A as the cheap tonight-able step if a flag is wanted sooner: A is safe-by-construction (functional data untouched) and still removes the prop/meth/registry/enum payload that dominates RTTI size on real class-using programs. B's error-on-managed-fields is a footgun for exactly the ESP audience the flag targets.

Also decided-needed

The classless quick win (bare program still ships a TObject name remnant) is uncontroversial under ANY option — can land independently: skip ALL RTTI when no user class exists AND no reflection op appears (builtin TObject row alone does not count).

DECIDED 2026-07-20 — Option C, usage-driven; no --rtti=none flag

User's call: C. Emit per-class RTTI only where it is actually used. The flag is not implemented at all.

Safe by construction — functional RTTI (finalize layouts, the data the RTL genuinely needs) is never at risk, because nothing is stripped on the user's say-so. It also removes a build-time decision users would have had to make correctly, and a footgun class (B's error-on-managed-fields) aimed squarely at the ESP audience the flag was for.

A (reflection-only strip, shippable sooner) was available as an interim and is NOT being taken: C is the destination and there is no pressure forcing an earlier partial step.

Consequence: [[feature-opt-rtti-emit-on-use]] IS this decision's implementation — it is no longer "an optimization alongside a flag", it is the whole mechanism. Anything in the tree referring to --rtti=none as a planned user-facing switch should be read as superseded.

Log