feat(ir): throws is Vec<u16> — schema-global error ordinals, cross-schema - #47
Merged
Conversation
…hema
The 4th core-target-contract IR change (docs Design 4.4). `Function.throws`
goes from `Vec<String>` (bare names) to `Vec<u16>` (schema-global error
ordinals), resolved at freeze.
## The error space
`plan_error_space` runs before the main freeze loop:
- Locally-declared `error`s take ordinals 0..N in declaration order.
- Each distinct `! Name` a function throws that isn't local is appended as a
re-exported import: resolved through the schema's in-scope `use`s (when
there's a ProjectContext) to the foreign `error` decl, which is frozen into
this schema's stream with `imported_from: Some(<ns>)`. Unresolvable names
still get a stable slot, marked `imported_from: Some("<unresolved: Name>")`.
So every `! Name` ends up a `u16`, and cross-schema throws keep a single-u16
wire id -- the design's "re-export slot in the importing schema's ordinal
space".
## FrozenUnit::Error
Two new fields: `ordinal: u16` (the slot -- what travels in the envelope's
`err` id) and `imported_from: Option<String>` (`None` local, `Some(ns)` a
re-export). The ordinal is explicit rather than stream position, so a local
`error` declared after a `protocol` still has a stable id.
Append-only discipline (retire in place, never reorder/reuse) is the author's
to keep; version-diff enforcement of it is a separate follow-up, same as
Error fields/message not being diffed yet.
## Not in scope
- Multiple throws per function (`! A, B`) -- grammar is still single `! Name`;
`Vec<u16>` is 0-or-1 today, forward-compatible.
- Errors as general importable symbols (glob-expansion, validation) -- this
resolves them only for `throws`, via a local walk of the `use`s, without
touching `schema_declares_symbol` / the validator.
## Tests
`incremental` + `imports`: local ordinal assignment, an unresolved throw gets
a marker slot, a foreign `use`d error gets a re-export slot after the locals
with its fields/message carried over, and the function points at the right
ordinal. Full core suite green (247 passed).
## Downstream
`FrozenUnit` shape change -> coordinated `comline-core` rev-bump across
generation / cli / comline-{rust,typescript} when this + core#46 land (they
only match `Function` / `Error` with `..`, so rev-bump not code).
This was referenced Sep 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The 4th
core ↔ targetIR change (docs Design §4.4).Function.throwsgoes fromVec<String>(bare names) toVec<u16>(schema-global error ordinals), resolved at freeze. Follows #46.The error space
plan_error_spaceruns before the main freeze loop:errors take ordinals0..Nin declaration order.! Namea function throws that isn't local is appended as a re-exported import: resolved through the schema's in-scopeuses (when there's aProjectContext) to the foreignerrordecl, which is frozen into this schema's stream withimported_from: Some(<ns>). Unresolvable names still get a stable slot, markedimported_from: Some("<unresolved: Name>").Every
! Nameends up au16, and a cross-schema throw keeps a single-u16wire id — the design's "re-export slot in the importing schema's ordinal space".FrozenUnit::ErrorTwo new fields:
ordinal: u16errid. Explicit, not stream position, so a localerrordeclared after aprotocolstill has a stable id.imported_from: Option<String>None= local;Some(ns)= a re-export (or<unresolved: Name>).Append-only discipline (retire in place, never reorder/reuse) is the author's to keep; version-diff enforcement of it is a separate follow-up — same as
Errorfields/message not being diffed yet.Not in scope
! A, B) — grammar is still single! Name;Vec<u16>is 0-or-1 today, forward-compatible.throws, via a local walk of theuses, without touchingschema_declares_symbolor the validator.Tests
incremental+imports: local ordinal assignment; an unresolved throw gets a marker slot; a foreignused error gets a re-export slot after the locals with its fields/message carried over; the function points at the right ordinal. Full core suite green — 247 passed, 0 failed.Downstream
FrozenUnitshape change → one coordinatedcomline-corerev-bump acrossgeneration/cli/comline-{rust,typescript}when this + #46 land (they only matchFunction/Errorwith.., so rev-bump, not code changes).