Skip to content

Native compiler — change journey

TypeRB Native has two connected journeys:

  1. compile an application; and
  2. use a Native compiler to build the next Native compiler.

The second journey amplifies mistakes in the first. This page keeps the responsibilities separate so that a change is not judged only by whether a bootstrap happens to converge.

Use the versioned big map when you need current source addresses.

Question Owner Evidence to ask for first
What should TypeRB source mean? The TypeRB language and reference conformance behavior A settled portable case
Does Native accept the case? Native subset and exact compatibility claim Valid and invalid differential cases
How is checked meaning represented natively? Native compiler internals Deterministic checked form or QBE IL
How are values laid out and executed? Native MIR, runtime, ABI, and target profile Boundary, runtime, and target tests
Can the compiler reproduce itself? Native bootstrap chain Exact adjacent-generation evidence
Is a result faster or smaller? A registered measurement boundary Correctness first, then comparable measurements

If the first row is unsettled, stop. Native is not a second place to define a different TypeRB language.

The Native repository’s compatibility declaration names the exact TypeRB identity backed by current evidence. Its versioning guide explains why Native implementation identity, language compatibility, internal protocols, runtime ABI, targets, and bootstrap evidence remain separate axes.

  1. Start from portable meaning

    Name the accepted TypeRB behavior and the exact Native compatibility claim. Unsupported behavior must fail clearly instead of falling back to different semantics.

  2. Recognize and check the supported source

    The TypeRB-authored frontend lexes, parses, resolves, and checks the bounded subset. Valid and invalid inputs should agree with the reference behavior inside the claimed boundary.

  3. Make native execution decisions explicit

    Checked operations cross into Native-owned representation. Layout, calling convention, managed references, and runtime services belong here, not in the portable source language.

  4. Emit deterministic QBE IL

    QBE IL is a visible handoff. It should change when the intended native representation changes and remain identical when an internal refactor does not change that representation.

  5. Cross the external tool boundary

    QBE, the assembler, linker, C toolchain boundary, platform ABI, and system libraries are explicit dependencies. Record which side owns a failure.

  6. Observe the program

    Exit status, standard output, diagnostics, artifacts, and cleanup are stronger evidence than “the compiler process completed.”

A normal application stops after it is built. A self-hosted compiler becomes the tool used by the next build:

previous Native seed
-> builds the current TypeRB compiler source
-> candidate compiler
-> builds the same compiler source again
-> next candidate compiler

The exact generation names are less important than three questions:

  1. Did each candidate actually run as the compiler for the next generation?
  2. Was recovery tooling kept outside the ordinary Native-to-Native chain?
  3. Did the required compiler, QBE, and executable identities converge under the declared policy?
Order Evidence What it rules out
1 Portable valid and invalid cases A Native-only dialect or silent fallback
2 Deterministic diagnostics and QBE IL Hidden nondeterminism and source-specific output
3 Representative application output and failure behavior A compiler artifact that does not preserve program behavior
4 Adjacent Native-to-Native generations A seed that cannot drive the next complete build
5 Exact fixed point and target inspection Drift across regeneration or target boundaries
6 Registered time, memory, and size measurements Performance claims made from incomparable or incorrect runs

Correctness evidence comes first because a fast fixed point that accepts the wrong language is not progress toward a Native TypeRB implementation.

Read the Native evidence for a deeper explanation of what each claim can prove, how bootstrap generations depend on one another, and which inferences remain invalid after a successful fixed point.

Do not read gate numbers as compiler stages

Section titled “Do not read gate numbers as compiler stages”

A gate records an experiment question and its acceptance evidence. It is history and rationale, not a stage traversed by every application.

For ordinary compiler work:

  1. follow the application responsibilities above;
  2. identify the native boundary the change affects;
  3. use a gate or decision record to learn why that boundary exists; and
  4. rerun only the evidence that the boundary requires, plus its declared regressions.

This keeps historical directory names from becoming the reader’s architecture.

Useful search terms survive file moves better than helper names:

Terminal window
rg -n '<command or diagnostic fragment>' --glob '*.trb' --glob '*.sh'
rg -n '<QBE symbol or emitted operation>' --glob '*.trb' --glob '*.ssa'
rg -n '<manifest field or target profile>' --glob '*.json' --glob '*.md'

Trace one producer and one consumer. For a runtime or ABI change, also identify the source-level case that reaches it and the target evidence that observes it.

  1. Run the Native executable trace.
  2. Follow its String and puts call in the Native code clinic.
  3. Run the nearest check from the workflow and test matrix.
  4. Use this page to separate the application path from the bootstrap path.
  5. Use the big map to find the corresponding producer in the newer pinned version.
  6. Read the nearest conformance case, then the decision or gate record that explains the boundary.
  7. Read bootstrap machinery only if the change can alter compiler reproduction.

This order lets the compiler remain an understandable program before it becomes an artifact that builds itself.