Compiler design / 16. Target planning and validation

Per-function target assignment and validation catch capability mismatches before any backend emits code.

Deciding where code can run

Target planning receives an already-validated lifetime topology. It assigns targets and checks capabilities; it does not change region ownership or source legality.

Semantically valid Moth can still be unhostable on a chosen target. A function that touches the DOM does not belong in a pure Wasm partition that has no DOM. A backend should learn that before it emits half a module and shrugs.

Target planning assigns functions. Target validation checks the assignment against capabilities.

Roots and capabilities

The build system supplies validation roots and candidate assignments from entry or package link planning. The compiler owns validation semantics over those inputs.

Distinguish two ideas people conflate:

A function can be perfectly typed and still require a capability its assigned target lacks.

For the greeting walkthrough, start, greet and any template runtime work arrive with link facts and capability requirements. Even a humble string page teaches the machinery that larger apps need for DOM and friends.

Diagram placeholder: Each function carries capability facts and target affinity

Caption: simplified link input. Question: what constrains assignment?

Affinity and propagation

Some functions are born with affinity. A JavaScript-owned start that must orchestrate page lifecycle wants the JS side. A DOM-touching helper wants JS. Neutral pure computation may default toward Wasm where the design supports it.

Requirements propagate backwards along call edges. If a callee needs the DOM, callers that must reach it inherit the constraint under the accepted propagation rules. Neutral callees can still sit in Wasm when callers bridge correctly.

Diagram placeholder: A DOM requirement travels from callee to callers

Caption: accepted propagation rule. Question: why does a caller move targets?

Deterministic assignment

A clean mental loop:

  1. take the reachable union and validated lifetime topology
  2. run affinity/capability analysis
  3. assign targets
  4. record reasons
  5. validate assigned functions
  6. validate cross-target edges

Determinism matters. The same program should not flip partitions because a hash map iterated differently at breakfast.

Diagram placeholder: One reachable union splits into target-labelled functions

Caption: conceptual assignment. Question: where does each function run?

Cross-target calls

Mixed output needs bridges.

JavaScript-to-Wasm calls may use wrappers under explicit ABI rules.

Wasm-to-JavaScript Moth calls remain prohibited after propagation in the accepted model. The planner should push the JS requirement outward rather than leave a forbidden reverse edge for emission to discover.

That asymmetry is deliberate engineering, not an accident of today's engine.

Diagram placeholder: JS-to-Wasm wrapper is allowed while Wasm-to-JS edge is rejected

Caption: exact edge rule simplified. Question: which bridge is legal?

Entry-specific variants and check

Partitioning is entry-specific. Different entries may need different physical variants of shared logic when their capability sets differ. Variant keys make those physical artefacts addressable.

The check command plans and validates, then stops before writing a full page output set. Tooling overlays extend analysis. They do not become a second artefact builder competing with HTML output.

What can fail here

Unsupported features in unreachable private functions should not fail validation.

Why Moth does it this way

Alternative: compile a whole page to one target, or let emission discover unsupported calls mid-write.

Choice: per-function deterministic partitioning with explicit validation.

Benefit: mixed output can keep browser work in JavaScript and neutral work in Wasm.

Cost: wrappers, ABI rules and assignment reasons add planning machinery.

Roadmap and current status

Exit state

You leave with validated target assignments, capabilities, imports and physical variants. Next: how lowerers turn these explicit inputs into JavaScript and Wasm without reopening source.

Compiler design / Previous / Next