Follow-up to #6952 (--opt-report, which shipped v1).
What v1 does
--opt-report reports each value by function + binding name:
batch.ts :: local `acc` [loop depth 0]
in function totalsRow -> Boxed
ptr-shape rule 2 (containment)
returned from this function. ...
The gap
HIR carries names through lowering but not source positions. Stmt::Let
is { id, name, ty, mutable, init } — there is nowhere to hang a span. So a
denied value cannot be rendered as file:line with a source snippet, which
is what makes an LLVM-style optimization remark actually pleasant to read.
One exception already exists and v1 uses it: Expr::New carries byte_offset
(#5253, captured for constructor TypeError locations), so unbound allocation
sites — the .map(x => ({...})) idiom, which has no binding name at all —
report a byte offset. Everything else has nothing.
Proposal
A LocalId -> Span side-table populated during AST→HIR lowering:
- SWC spans exist upstream at every
Stmt::Let lowering site.
perry-diagnostics (span.rs, source_cache.rs, emitter.rs) can already
resolve a span to file:line:col and render a snippet with a caret.
- Strictly additive: v1's output gains a location line; nothing about the
existing schema changes. Entry.byte_offset is already in the
--opt-report=json schema (schema_version: 1) and would simply be
populated for more positions.
Why it is its own PR
Threading spans through AST→HIR lowering touches every Stmt::Let construction
site across perry-hir, and the side-table has to survive the transform passes
(closure conversion, async lowering, inlining) that rewrite locals. That is a
scope-check of its own, not a rider on a reporting feature.
Acceptance
--opt-report on a program with a boxed local prints the declaration's
file:line:col and a source snippet, and the gap suite is unaffected.
Follow-up to #6952 (
--opt-report, which shipped v1).What v1 does
--opt-reportreports each value by function + binding name:The gap
HIR carries names through lowering but not source positions.
Stmt::Letis
{ id, name, ty, mutable, init }— there is nowhere to hang a span. So adenied value cannot be rendered as
file:linewith a source snippet, whichis what makes an LLVM-style optimization remark actually pleasant to read.
One exception already exists and v1 uses it:
Expr::Newcarriesbyte_offset(#5253, captured for constructor
TypeErrorlocations), so unbound allocationsites — the
.map(x => ({...}))idiom, which has no binding name at all —report a byte offset. Everything else has nothing.
Proposal
A
LocalId -> Spanside-table populated during AST→HIR lowering:Stmt::Letlowering site.perry-diagnostics(span.rs,source_cache.rs,emitter.rs) can alreadyresolve a span to
file:line:coland render a snippet with a caret.existing schema changes.
Entry.byte_offsetis already in the--opt-report=jsonschema (schema_version: 1) and would simply bepopulated for more positions.
Why it is its own PR
Threading spans through AST→HIR lowering touches every
Stmt::Letconstructionsite across
perry-hir, and the side-table has to survive the transform passes(closure conversion, async lowering, inlining) that rewrite locals. That is a
scope-check of its own, not a rider on a reporting feature.
Acceptance
--opt-reporton a program with a boxed local prints the declaration'sfile:line:coland a source snippet, and the gap suite is unaffected.