Skip to content

Provenance

Source identity through HIR, MIR, WasmIR, diagnostics, traces, and debug information.

Updated View as Markdown

Provenance answers two related questions:

  1. where in source did this compiler object come from?
  2. why does this generated object exist when there is no direct source node?

Those questions power diagnostics, IR dumps, trace stories, source navigation, line tables, breakpoints, and variable inspection. A byte range alone cannot answer both, so the compiler preserves identity and classification as separate facts.

Chain

The ordinary source chain is:

file + AST node
    ↓ semantic lowering
HIR node + Origin
    ↓ MIR construction
MIR local/op/terminator + source/HIR side tables
    ↓ WasmIR lowering
WasmIR instruction → MIR operation
    ↓ encoding
function-body byte offset → source/debug record

The arrows are many-to-many in practice. One HIR call can become several MIR operations; one MIR operation can become several WasmIR instructions; folding can remove an instruction completely. Provenance records causality, not a promise of one output instruction per source expression.

Identity and location

Identity is stronger than a range. Two expressions can share a zero-width or recovery range, and a generated operation can use the range of the surrounding construct. Conversely, an HIR node ID by itself needs its project/file context to resolve a range.

Consumers therefore prefer:

  1. a retained HIR identity for “which source construct?”;
  2. a precise operation or local range for presentation;
  3. a block, continuation, function, or declaration range as a fallback;
  4. an explicit synthetic classification when no source node applies.

Fallback is presentation behavior. It must not be inserted back into the IR as false precision.

HIR origins

Every HIR node uses Origin::Expr, Stmt, Pattern, Decl, or Synthetic. A synthetic origin includes both a source range and a closed reason such as loop desugaring, default argument insertion, string template expansion, optional-member lowering, generic specialization, or error recovery.

When per-file HIR is merged, AST-backed origins keep their file context and every HIR child identity is relocated. A local HIR ID must never be compared directly with a merged project ID.

Specialization retains the generic declaration relationship separately from the concrete clone. Tools can present the concrete call site while still explaining which generic definition produced its body.

MIR provenance

MIR maintains several complementary maps:

Map Key Payload Use
local origins LocalId source range First source definition of a value
operation origins block and operation index source range Fine-grained diagnostics and line data
operation HIR block and operation index HIR node Cross-stage navigation and trace identity
terminator HIR block HIR node Return, branch, suspension, and transfer origins
operation synthesis block and operation index synthetic reason Why a generated operation has no direct HIR node
function synthesis function synthetic reason Generated walker, specialization, or helper identity

An operation may have both an originating HIR node and a synthetic context. For “where?” tools prefer HIR. For “why was this primitive introduced?” they can also show the synthetic category.

Operation-index keys are positional. A pass that inserts, erases, compacts, merges, splits, or moves operations must migrate the corresponding maps in the same mutation. MIR rewrite helpers centralize that bookkeeping, and a final prune removes stale keys as a safety net. Pruning is not a replacement for preserving a known origin.

WasmIR origins

During lowering, the builder sets the current MIR operation before emitting its WasmIR expansion. Every emitted instruction receives:

(WasmIR block, instruction index)
    → (MIR block, operation index)

This permits multiple primitive instructions to inherit one MIR origin. WasmIR optimizations rebuild the map when they compact instruction vectors. New instructions created solely by an optimization may lack a source entry; that absence means generated backend work, not the source file’s beginning.

In debug mode, encoding snapshots the function-body byte offset before an originated instruction. Module assembly converts body-relative offsets into module offsets and can feed line/debug sections. Release mode leaves this capture disabled so provenance instrumentation does not perturb emitted program behavior.

Variables

Statement location and source-variable value are separate problems. A line table can say which source construct is executing without knowing where an optimized variable lives.

MIR owns source-variable debug bindings because HIR-to-MIR lowering is the last point with both source variable truth and concrete value identities. A binding states that a source variable is represented by a MIR value or a small pure debug expression from a program point onward.

When optimization destroys a value, it can:

  • replace references with a surviving value;
  • salvage a bounded expression over constants and surviving values;
  • represent aggregate pieces explicitly;
  • mark the value unavailable with a reason.

It must not display a guessed stale value. Multi-assigned variables, unsalvageable calls or loads, and expressions exceeding the bounded debug vocabulary become honestly unavailable.

This variable-location pipeline is still evolving. The MIR model and verification hooks exist, while complete downstream serialization and runtime evaluation should be treated as provisional until the debugger consumes the full protocol end to end.

Pass checklist

A transformation that changes program shape must answer:

  • Which output nodes retain each input’s HIR or source origin?
  • Which outputs are synthetic, and what is their category?
  • Did positional block/op/instruction keys move?
  • Were eliminated source-variable values replaced, salvaged, or marked unavailable?
  • Can diagnostics still select a user-visible range?
  • Does disabling tracing or debug data leave program bytes and behavior unchanged?

Tests should cover provenance density for user-authored operations, expected synthetic gaps, key remapping under rewrites, and release/debug semantic parity.

See HIR Nodes, MIR Validation, and WasmIR Effects for the representation-specific contracts.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close