Skip to content

Type table emits unreachable types and members (O(N^2)) and carries vestigial member-dedup infrastructure #473

Description

@zharinov

Problem

The emitted type table carries dead weight. Neither issue affects correctness. They are pure binary bloat plus leftover code.

(a) Unreachable type defs/members are serialized into the binary.

TypeTableBuilder::build does two passes:

  1. Collect reachable types from iter_def_types().
  2. Then unconditionally sweep every interned type via iter_types().

The second sweep drags in every intermediate "progressive-merge" struct that inference created. There is no type DCE. The number of dead members grows O(N^2) with alternation width and nesting depth.

# An 8-branch unlabeled alternation: 14 dead type_defs + 35 dead members; >80% of the type table is dead.
cargo run -p plotnik -- dump -l javascript -q 'Q = [(identifier) @a (number) @b (string) @c (array) @d (object) @e (true) @f (false) @g (null) @h]'

The live members sit at the lowest indices (the named/result type is collected first), so the materializer never reads a wrong index. This is bloat, not miscompilation.

(b) Vestigial member-dedup infrastructure.

  • member_cache (emit/type_table.rs:34) is built and read but never written, so it is always empty.
  • MemberRef::Deferred is never constructed by production code. Every production site uses deferred_by_index.
  • crates/plotnik-compiler/src/bytecode/ir.rs:141 has .expect("deferred member reference must resolve"). This is a latent panic. It fires the day someone wires up the deferred dedup that the compile/scope.rs:45 comment promises but does not populate the cache.
  • Two comments are misleading:
    • scope.rs:45 says "will be added later" for dedup that was never added.
    • emit/emitter.rs:202 says "struct fields are deduplicated by field identity". That is false; members are emitted per-struct with duplication (e.g. a: T16 repeats across many structs in the dump above).

Cause

  • (a) emit/type_table.rs:64-72 (the unconditional second sweep), analyze/type_check/context.rs:217-222 (iter_types returns all interned types), and no type DCE in compile/dce.rs (it prunes instructions only).
  • (b) member_cache at emit/type_table.rs:34/:45/:350; MemberRef::Deferred resolve at crates/plotnik-compiler/src/bytecode/ir.rs:140-141; misleading comments at scope.rs:45 and emitter.rs:202.

Fix

  • (a) After build, mark-and-sweep type_defs reachable from entrypoint result types plus TypeName entries, remap member_start, drop the rest. Or drop the iter_types() sweep if the enum-inside-named-node edge it guards is reachable through iter_def_types.
  • (b) Pick one:
    • Remove member_cache, TypeTableBuilder::lookup_member, the emitter.rs:205 closure, and MemberRef::Deferred/deferred().
    • Or implement the deferred dedup and actually populate the cache.
    • Either way, fix or delete the two misleading comments.

Acceptance

  • dump of an N-branch unlabeled alternation shows no unreferenced type_defs/members.
  • No dead member_cache/Deferred surface remains, or the latent .expect is provably unreachable by construction.

Related

The latent .expect panic in (b) blocks the deferred "call-site member dedup" feature that the scope.rs:45 comment defers. That feature cannot be built via MemberRef::deferred() until member_cache is populated, or compilation aborts. The O(N^2) dead members in (a) compound with the merge-alternation null-fill growth.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions