Skip to content

Experimental: --emit-types — write the inferred types back out as TypeScript from the opt-report Entry stream #7685

Description

@proggeramlug

Experimental. Perry proves static types to select unboxed representations. --opt-report already surfaces that analysis — but only its negative half: which values it could not type, and why. The positive half is computed and then discarded.

The proposal: a --emit-types mode that writes the inferred types back out as TypeScript, so a JavaScript codebase compiled by Perry gets .d.ts (or inline annotations) derived from whole-program native compilation rather than from a heuristic type-inferencer.

Why the plumbing already exists

opt_report::Entry carries, per named binding:

pub module: String,
pub function: String,
pub name: String,           // source-level binding name — HIR keeps names through lowering
pub local_id: Option<u32>,
pub rep: String,            // the representation SELECTED — Ptr<Shape>, I32, Boxed, …
pub analysis: Analysis,
pub outcome: Outcome,

The sink is a process-global Mutex<Vec<Entry>> drained by the CLI after codegen, and --opt-report=json already emits a stable schema. This is a new consumer of an existing stream, not new analysis.

The honest scoping caveat, up front

rep is a representation, not a TypeScript type, and the mapping is lossy in both directions:

  • I32 / typed-f64 → number. Sound and useful.
  • Ptr<Shape> → an object with a known field set. This is the valuable case: a real interface, recovered from JS.
  • Boxednothing. And --opt-report exists precisely because Boxed is common.

So the deliverable is "emit what we proved, any (or omit) the rest", and its usefulness is bounded by exactly the proof rate --opt-report was built to measure. Do not ship a number for coverage without measuring it on real dependency JS#7152/#7170/#7234 record that dependency JS behaves very differently from benchmarks here, and #7234 is an open panic on real dependency JS in the report's own renderer.

What would make this worth keeping

  1. Round-trip check: emit types for a .ts input whose annotations were erased, and diff against the original. That is a measurable accuracy number rather than a demo.
  2. Never emit a wrong type. A missing annotation costs nothing; a wrong one poisons a downstream tsc. Where the proof is conditional (a shape guarded by a versioned loop, say), emit nothing.
  3. Field-set output for Ptr<Shape> is the differentiating feature — structural interfaces recovered from untyped JS is something a JS-only inferencer cannot do as well, because it has no representation-selection pressure forcing the question.

Not in scope for the prototype

Editor integration, incremental emission, or wiring into perry check. This is a flag that writes a file, plus an accuracy measurement.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions