Programming Language

Nocter

A self-contained systems language built around simplicity, encapsulation, and foolproof design.

/development/milestones/v0.14.0.md

Nocter v0.14.0 Semantic Architecture Migration

Status: Phases 0 through 3 complete. Phase 4 is next.

v0.14.0 replaces the compiler's span-, string-, and AST-shape-driven semantic spine with explicit compiler identities and typed intermediate representations. It is an incremental migration under the complete v0.13.0 regression suite, not a rewrite and not a language-feature milestone.

The published v0.13.0 language, tag, archive, release notes, and qualification record are immutable. A migration phase may preserve public behavior or improve diagnostics, but it must not add compatibility syntax or silently retain two semantic authorities.

Migration Invariants

  • Source syntax remains authored AST. Resolver, type checking, analysis, and lowering do not add semantic identities by inventing source names or treating byte ranges as identifiers.
  • Each compile-unit definition, body, expression, type, requirement, intrinsic, and monomorphized item receives a typed ID from one semantic database.
  • A span is location metadata owned by the database. It may diagnose or navigate an ID, but map equality on spans must not decide whether two semantic entities are identical.
  • The checker produces the semantic result consumed by ownership, provenance, LSP, specialization, and lowering. No post-check AST walker may recompute a successful semantic decision.
  • Recovery produces explicit error or partial semantic nodes. LSP support for incomplete source is not permission to create a second successful-language model.
  • Every temporary adapter has a named consumer, an explicit removal phase, and a regression that prevents new callers after the removal boundary.
  • Old paths are deleted in the same phase that completes their replacement. The migration never ends with two permanent type systems, call identities, or lowering pipelines.

Phase 0: Semantic Identity and Declaration Core

Goal

Establish one compile-unit identity domain and make authored declarations structurally honest before typed HIR depends on them. Phase 0 removes the most edit-sensitive identities first: instance member ordering, operator/coercion synthetic names, callable summary keys, trusted declaration roles, and source-backed editor targets.

Semantic IDs

Phase 0 and Phase 1 introduce opaque IDs with separate domains:

DefId          source declaration or member
BodyId         executable authored body
ExprId         expression in one body
TyId           interned semantic type

RequirementId, IntrinsicId, and MonoItemId remain names for later migration boundaries, not empty Phase 0 scaffolding. They are introduced only when the requirement, intrinsic, and backend convergence phases can replace their old authorities in the same change.

SemanticDb owns definition, body, and expression source records plus their locations. The checker-owned typed HIR owns the TyId arena for each checked file in the same semantic generation. IDs are meaningful only inside that generation. Package-wide LSP generations retain their existing stable source identity for edit planning, but they map that identity to a fresh DefId instead of making a stable byte span the compiler's internal identity.

Anonymous opaque result declarations use a separate OpaqueTypeId. They are not named declarations and therefore do not borrow an enclosing DefId; their authored some span is only the diagnostic and navigation projection of the opaque type identity.

The first implementation may add typed IDs and definition records while existing span-bearing diagnostic fields remain. It may not call a span wrapper a semantic ID or make raw numeric IDs public API.

Declaration Model

InstanceDecl owns one source-ordered Vec<InstanceMember>:

InstanceMember::Method(MethodDecl)
InstanceMember::Operator(OperatorDecl)
InstanceMember::Coercion(CoercionDecl)

Filtering iterators serve method, operator, coercion, and callable consumers without creating parallel vectors. Formatting and AST JSON preserve authored member order.

Method, operator, coercion, literal, function, construct, conformance, and destructor bodies share a name-free semantic callable record after resolution. Source declarations retain their actual syntax; an operator or coercion is not represented as a MethodDecl with a name beginning __nocter$. Resolved lookup and lowering dispatch by DefId and callable kind.

Work Order

  1. 1. Replace parallel instance member vectors with one source-ordered enum and migrate every AST visitor, formatter, serializer, declaration-surface filter, resolver, checker, analysis query, and lowering entry point.
  2. Add opaque ID types and an immutable compile-unit SemanticDb definition index with typed source-location records and deterministic construction tests.
  3. Introduce a name-free callable signature/body abstraction. Give every authored callable a DefId; remove operator and coercion synthetic names from parser AST.
  4. Migrate callable provenance summaries, allocation summaries, trusted roles, coercion plans, operator plans, specialization targets, editor occurrences, and lowering call targets to IDs.
  5. Remove span-keyed callable identity and string-keyed target dispatch from migrated paths. Keep spans only as database metadata and diagnostic locations.
  6. Run the complete repository and distributed-home matrix, update architecture documentation, and record the exact removed paths and remaining Phase 1 boundary.

Completion Definition

Phase 0 is complete only when every source declaration and executable body in a compile unit has one typed semantic identity; instance members retain source order in one collection; operator and coercion declarations contain no synthetic method or name; callable summaries, trusted roles, selection plans, specialization indexes, lowering resolution, and editor targets use DefId or BodyId for semantic equality; diagnostic, navigation, and backend symbol locations project from those IDs; no span-to-span callable adapter remains; all public behavior is unchanged except deliberate diagnostic improvements; and the complete verification, documentation, formatting, Clippy, and diff gates pass. String backend labels may exist after semantic target selection, but they are presentation and linkage output rather than source-level identity.

Non-goals

Phase 0 does not add language syntax, standard-library APIs, dynamic dispatch, persistent IDs across compiler processes, incremental query execution, typed HIR, MIR, a new ABI, or another target. It does not rename old fields without changing their authority.

Completion Record

  • InstanceDecl now stores one source-ordered Vec<InstanceMember>; formatting, AST JSON, resolution, checking, analysis, and lowering no longer reconstruct mixed member order from parallel collections.
  • One compile-unit SemanticDb assigns deterministic DefId values to top-level, member, and block-scoped import declarations. Every per-file resolver and the post-opaque-elaboration pass share the same database generation.
  • Resolver symbols retain their table-local SymbolId only as an indexing handle and separately carry the declaration's compile-unit DefId.
  • Borrow-coercion selection carries DefId through resolved type surfaces, conversion selection, type-check facts, generic requirement specialization, call-specialization collection, and IR callable indexing. Its former declaration-span and synthetic-target-name identity fields have been removed; backend symbol spelling is generated only at the IR boundary.
  • Trusted-library validation now produces pre-semantic TrustedDeclarationInputs; analysis binds those registrations once into HashMap<DefId, TrustedDeclarationRole>. Type checking no longer uses byte spans as trusted-role identity.
  • Protocol-method and ordinary method specialization records carry DefId. Collection, interpolation, comparison, expansion, and indexing plans feed one DefId-keyed specialization table shared by buildability and IR indexing.
  • Callable contract/body pairs and their receivers, parameters, and literal captures are keyed by DefId. The index retains source locations only to project existing diagnostics and editor ranges, while all consumers share the same Arc<SemanticDb> owned by the compile unit.
  • Every authored callable block and nested closure has a source-ordered BodyId with an owning DefId and optional parent BodyId. Function, method, coercion, destructor, literal, and closure specialization tables use the appropriate definition or body domain.
  • Trusted interpolation and iteration discovery remains a pre-semantic shape-validation step, but it binds String, constructor, Format, Iterator, method, and associated-type locations to DefId before type checking. Semantic consumers no longer use those spans as identity.
  • Editor declarations use DefId across package indexes and convert IDs to source ranges only at navigation and edit boundaries. Generic parameters use explicit ordinal identity instead of stable byte ranges.
  • CallableDecl is name-free. MethodDecl alone owns a source name, while operator and coercion kinds remain semantic enums through selection and specialization; no synthetic method AST node or callable identity remains.
  • Callable names used by buildability and IR are keyed by canonical DefId. Contract/body lookup asks SemanticDb for the declaration body owned by a definition, so functions, methods, operators, and coercions do not depend on matching focused spans or pretending every callable is a named method.
  • The complete repository test suite, including the distributed installed-home iteration, sequence-spread, ordering, and standard-library runtime matrix, passes with formatting and warnings-denied Clippy.

Phase 1: Partial Typed HIR

Goal

Make one checker invocation own the semantic product consumed by analysis, specialization, buildability, and lowering. Establish expression and type identity plus explicit error semantics before Phase 2 migrates the remaining editor-facing fact tables and Phase 3 introduces MIR.

Implemented Boundary

  • SemanticDb assigns every authored expression an ExprId and owning BodyId.
  • A checked-file type arena interns normalized TypeExpr values as TyId; a TyId is never used as source spelling or across semantic generations.
  • TypedExpression records ExprId, BodyId, and PartialSemantic<TyId>. Unresolved expressions record PartialSemantic::Error rather than disappearing or receiving a guessed type.
  • TypecheckOutput returns diagnostics and the exact TypedHir from one checker invocation. Ordinary compile-unit analysis and single-file LSP recovery retain that result; downstream consumers cannot invoke the raw typed-HIR builder.
  • The only provisional entry point is the explicitly named opaque-result prepass. It exists before final result witnesses are known, is inaccessible outside type checking and analysis orchestration, and its result is replaced by the ordinary checker output after elaboration.
  • Legacy fact collections inside TypedHir remain a compatibility payload, not a second checker. Their keys are migrated to expression/definition records in Phase 2 and control-flow records in Phase 3; no new consumer may rebuild successful semantics from AST.

Completion Definition

Phase 1 is complete when authored expressions have body-scoped identity; known and erroneous expression types survive diagnostics in one partial semantic product; normal compilation and LSP recovery consume TypecheckOutput; no post-check collector or externally callable successful semantic recomputation path remains; existing downstream consumers receive the checker-owned instance; the provisional opaque prepass is explicit and isolated; and the complete repository, formatting, Clippy, and diff gates pass.

Completion Record

Phase 1 satisfies the definition above. Tests cover deterministic expression/body identity, type interning, explicit error expressions, preservation of checker output after diagnostics, and LSP single-file recovery. The full verification matrix passes after Phase 0's final callable-identity migration.

Later Phases

Phase 2: Editor Projection

Goal

Build hover, completion, navigation, semantic tokens, signature help, and edits from one semantic occurrence index plus analysis-time syntax projections. A successful editor query must not rescan the complete AST, rediscover a resolver member by span, or recompute compile-unit specialization.

Completed Work

  • SemanticOccurrenceIndex uses DefId and LocalSymbolId; navigation, references, and rename project source locations only after semantic selection.
  • EditorSyntaxIndex owns calls, literal sites and expressions, interpolation sites, module paths, import selectors, export surfaces, documentation, callable syntax, destructor syntax, and source edit targets built once per analyzed file.
  • Resolver declarations have one DefId-keyed projection index covering top-level symbols, fields, variants, associated types and functions, methods, conformance methods, destructors, literals, and coercions. Hover no longer scans every resolved type surface by declaration span.
  • Semantic token classification and lexical-region facts are computed once per file. Literal, interpolation, region, signature-help, hover, and import-completion queries consume retained projections.
  • Opaque results use OpaqueTypeId; callable contract/body records carry DefId directly rather than resolving an identity span after matching.
  • Call specializations are lazily computed once per compile unit and shared by buildability, IR lowering, and literal editor facts.
  • EditorSyntaxIndex also owns member gaps, enum patterns, struct fields, literal shapes, region allocators, result-provenance clauses, where requirements, and instance operator slots. LexicalScopeIndex owns visible local bindings and scoped imports. Successful completion queries therefore perform indexed lookup; AST scope walkers exist only in explicitly named recovery paths for incomplete source.
  • Type occurrences identify their selected declarations by DefId. Generic-parameter occurrences carry the parameter's DefId, and binding type, scalar-view, mutability, and payload facts are keyed by LocalSymbolId. Declaration and reference spans are resolved to that local identity once rather than serving as fact identity.
  • Every instance callable syntax record covers methods, operators, and coercions together with its generic owner. Specialization no longer loses owner substitutions for an operator or coercion.

Completion Definition

Phase 2 is complete when successful-source editor requests perform indexed lookup rather than AST or resolver-surface discovery; every declaration target is selected by semantic identity; all retained syntax-only sites are built once per file; incomplete-source recovery is isolated and cannot override successful semantics; and the complete repository, documentation, formatting, Clippy, and diff gates pass.

Completion Record

Phase 2 satisfies the definition above. The complete repository matrix passes, including 2,557 library tests, 223 distributed installed-home tests, public examples, source-corpus tests, formatting, and warnings-denied Clippy. The matrix caught and permanently covered one missing instance-operator syntax identity that had removed generic owner substitutions from sequence expansion lowering.

Phase 3: Control-Flow MIR

Goal

Replace AST-shaped buildability and backend lowering with one checked control-flow representation. MIR owns places, operands, rvalues, basic blocks, terminators, loans, initialization state, and explicit drops. Source AST remains an input to typed-HIR-to-MIR construction and source spans remain diagnostic metadata; neither may determine backend semantics after MIR construction.

Migration Order

  1. 1. Introduce typed local and basic-block identities, the MIR body model, and a structural verifier. Cover deterministic construction, valid control flow, invalid targets, terminator completeness, and local/projection validity before routing production code through it.
  2. Migrate straight-line scalar evaluation and native control flow: local initialization, assignment, return, if, loops, break, continue, and short-circuit operations.
  3. Represent calls and outcome edges explicitly, then migrate optional/fallible propagation, catch, and otherwise without terminal/nonterminal lowering variants.
  4. Add aggregate places and projections, moves, borrows, loans, initialization/drop flags, and explicit cleanup edges. Migrate records, variants, arrays, fields, indexing, and destructors.
  5. Migrate regions, closures, typed literals, interpolation, collection expansion, and iteration. Lower MIR to the existing machine IR and delete each AST-driven lowering family when its MIR route becomes authoritative.

Completion Record

  • mir owns body-local LocalId, BasicBlockId, and ScopeId domains, typed scalar locals, places, constants, copy operands, arithmetic and comparison rvalues, assignments, Goto, Switch, direct Call, failure propagation, loops, and Return.
  • MIR locals retain LocalSymbolId for parameters and bindings, ExprId for temporaries, TyId for checked type equality, and independent checked representation, ownership, logical-storage, source-origin, and lexical-scope contracts. Basic blocks retain the same validated ScopeId tree; MIR parameter storage is a source ordinal, while machine-local and parameter ABI indexes are projected only while lowering MIR to machine IR.
  • One backend parameter projection maps those ordinals to scalar ABI word positions or aggregate staging slots. A parameter after a multiword value no longer relies on ordinal/ABI-index coincidence.
  • Copy aggregate parameters now remain aggregate MIR locals. Checked field selection becomes a typed ProjectionPath, and MIR-to-machine-IR maps that path onto the parameter's ABI staging slot. Scalar field reads therefore no longer recover an aggregate member from source AST.
  • MIR construction receives one immutable compile-unit semantic input bundle. Aggregate layout follows the shared source-resolution map, so fields following cross-source aggregate members use the same layout authority as function ABI classification.
  • Copy aggregate parameters can flow through MIR calls as whole-place arguments. Backend projection preserves the validated direct/indirect ABI classification; indirect stack-backed arguments also disable tail-call frame reuse instead of leaking an address into a destroyed caller frame.
  • MIR call edges retain a semantic callable instance made from canonical DefId, optional concrete receiver TyId, and ordered concrete type-argument TyId values. Runtime symbol spelling stays in the backend index, and buildability uses the same canonical instance-key projection instead of a competing DefId -> name map. Direct and nested generic calls therefore cannot collide across monomorphizations.
  • Method calls now enter that same edge. Typed HIR retains the method's concrete receiver parameter TyId; MIR passes view receivers as views, models aggregate receiver borrows as ordinary loans, and moves owned receivers through the existing operand path. Generic instance methods and concrete receiver specializations consequently share one backend target projection.
  • Calls through closure and generic callable values retain callable type TyId plus capability as a closed semantic identity. Their receiver parameter type is fixed during type checking, and the shared instance registry alone projects the callable identity to its monomorphized runtime name.
  • Readonly and readwrite borrow parameters retain MIR borrow representation and may be forwarded as whole-place call arguments. ABI projection maps the source ordinal to the existing borrow parameter word; one IR-level frame-dependence predicate prevents both borrow and indirect aggregate arguments from entering an unsafe tail call in either lowering route.
  • Borrow bindings over stored scalar identifiers create explicit Loan records and BeginLoan statements. Cleanup derives EndLoan statements from lexical scope exits on the CFG, before backend projection materializes the borrow pointer; loan lifetimes are not inferred from AST block endings or kept as backend-only local metadata.
  • Copy aggregate call results may initialize MIR locals and feed checked field projections. The backend derives local aggregate slots after retained parameter/outcome slots, reserves them from the aggregate's checked ABI layout, and uses the same location projection for field reads and whole-place call arguments. Local forwarding preserves direct/indirect ABI classification from the checked TyId instead of treating only parameters as aggregate storage.
  • Nested checked member selections build parent-linked MIR projection paths from the root local to the selected field. Backend lowering folds relative layout offsets along that path, so nested and cross-source aggregate access does not flatten or rediscover source member syntax.
  • MIR scalar identity now covers every built-in integer. Narrow and wide integer parameters, constants, arithmetic, shifts, comparisons, calls, aggregate-field loads, and plain or fallible returns project to the existing machine IR without returning to AST lowering.
  • u8 retains a dedicated MIR scalar representation because the current machine IR gives it dedicated locations and calls. One parameter projection now maps it to the U8 ABI slot rather than pretending it occupies the generic-integer slot; all ten integer types therefore reach the MIR route without a hidden storage exception.
  • The structural verifier rejects missing locals and blocks, type or scalar-representation drift, invalid local or scope contracts, non-boolean conditions, invalid control-flow targets, and malformed loop regions before machine IR is produced.
  • A fixed-point definite-initialization verifier checks reachable operands and returns. Branch joins intersect initialized sets, while fallible calls initialize their destination only along the success edge.
  • Initialization and drop-obligation state is place-sensitive rather than local-sensitive. Whole roots, explicitly initialized projections, and invalidated projections remain distinct, so a partial move preserves sibling fields, prevents a later whole-value move, and survives CFG joins without flattening the aggregate into one bit.
  • Owned values and borrows now have independent path-sensitive MIR domains. Explicit drop edges are checked with may-live and must-live obligations; LoanId plus begin/end statements detect alias conflicts, moves or mutations while borrowed, and loans that cross an exit.
  • Loan conflicts compare projected-place overlap rather than only root-local equality. Root/child and ancestor/descendant accesses conflict, while distinct fields may carry independent mutable loans; unresolved index aliasing remains conservatively overlapping.
  • Aggregate places use typed, parent-linked ProjectionPathId records for fields and indexes. The verifier rejects scalar bases, forward parents, base drift, invalid index operands, and place references outside the body's projection arena.
  • Definite-initialization retains per-edge and per-exit states. A dedicated cleanup pass combines them with the validated scope tree and materializes explicit Drop chains without walking AST blocks or flattening outcome success and failure into one state.
  • Cleanup selects whole owned roots when available and maximal remaining owned projections after a partial move. This prevents both whole-value destruction of moved storage and loss of sibling field destructors.
  • Primitive lowering now uses a closed IntrinsicId after resolution. Pointer, view, process, allocation, I/O, and syscall operations no longer select backend behavior with string equality.
  • Every executable body is built and validated through MIR. A construction or validation failure is authoritative; no AST-driven buildability or machine-IR fallback remains.
  • Value if and terminal if normalize to the same control-flow diamond with one return join. The same conditional builder handles nested values inside arithmetic and call arguments, assigns both child scopes into one destination place, and rejoins in the parent scope. Nested scalar evaluation uses explicit MIR temporaries, so evaluation order is not reconstructed by backend lowering.
  • Scalar calls, fallible success/failure edges, while, unconditional loop, and i32/usize range loops now use the retained MIR body in both buildability and machine-IR lowering. Range setup uses an explicit loop-scope preheader, and branch joins plus loop exits derive innermost-first exited scopes from CFG block identities.
  • Scalar otherwise recovery is an ordinary MIR outcome failure branch that assigns the result place and rejoins the success block. Machine lowering derives OutcomeFailureMode::Recover from that edge shape for optional and fallible callees; it does not inspect the source expression.
  • Discarding scalar catch _ uses the same recovery edge and creates no synthetic error local. An unused named catch instead owns one logical error local initialized only by its failure edge; late machine-storage projection expands that value into code and message views. Named catches use typed BuiltinErrorField projections for code and message views, so those views may flow into ordinary call arguments without exposing their machine offsets. Returning the complete error payload uses the same logical MIR value and typed projections as failure returns.
  • str is the first nonnumeric MIR view representation. String literals, source parameters, ordinary calls, aliases, and returns keep logical locals while late storage projection assigns their two ABI words; pointer borrows and string views are no longer conflated.
  • Boolean && and || normalize to a MIR switch, a selected right-hand or constant short-circuit path, and one join. Right-hand calls and failures therefore cannot be evaluated by eager backend expression lowering.
  • Numeric negation and boolean inversion remain explicit unary MIR rvalues through structural validation. Only machine-IR projection rewrites them to target-independent arithmetic or boolean values, preventing invalid operator/scalar combinations from reaching code generation.
  • Exact and typechecked-lossless integer as expressions become MIR casts with explicit source and target types. MIR validation independently rejects narrowing or signed-to-unsigned drift; machine projection performs sign/zero extension without re-reading conversion syntax.
  • Scalar +=, -=, *=, /=, and %= normalize to ordinary MIR binary rvalues whose left operand and destination are the same place. Backend lowering no longer maintains a separate compound-assignment semantic path for MIR-routed integers.
  • One construction-only LoweringContext owns locals, semantic-symbol bindings, projections, scopes, loop regions, and the open CFG. Statement, expression, conditional, and outcome lowering mutate that authority directly instead of threading and potentially desynchronizing parallel state references through every recursive lowering call.
  • Scalar value blocks use one statement-plus-tail normalization path in conditional branches and outcome recovery. Leading bindings and nested conditionals no longer fall back to an AST route; backend structuring discovers the nearest shared MIR join recursively rather than requiring a one-block failure path.
  • Outcome recovery distinguishes implicit values from explicit returns in MIR. An explicit return writes function return storage and exits instead of being assigned at the recovery join.
  • Backend projection preserves one return after a shared terminal join. Redundant tests that fixed legacy AST-lowering temporary numbers or nested instruction shape were removed; MIR construction tests retain short-circuit and evaluation-order semantics, while representative backend tests retain the projection boundary.
  • Drop now names a body-local semantic destruction plan. Plans contain destructor DefIds and recursive struct or fixed-array structure, never linker names or machine-IR call targets. Backend projection alone selects runtime symbols and ABI offsets.
  • Owned aggregate parameters enter MIR, receive path-sensitive cleanup edges, and lower direct, recursively contained, and reverse-order array element destruction from those plans. A move-only type with no runtime destructor retains a no-op plan so ownership consumption is not confused with emitted cleanup code. Payload enums and outcome storage use the same plan domain.
  • MIR validation rejects missing or mismatched local/projection plans, invalid terminator plan references, and forward or cyclic plan references.
  • Scalar-field struct literals now use an aggregate MIR rvalue. Construction reserves its checked ABI slot, stores fields in resolved declaration order, activates the ownership obligation, and reaches the same semantic cleanup plan as owned parameters. Branch and loop structuring follow cleanup Goto/Drop chains instead of assuming syntax-shaped direct edges.
  • Aggregate construction now records scalar leaves under semantic field/index paths rather than embedding ABI offsets in MIR. Nested struct and fixed-array literals share this representation; validation rejects empty or duplicate leaf paths, and only backend projection computes offsets.
  • Struct and fixed-array construction is now staged explicitly: BeginAggregate reserves logical storage, projected assignments initialize fields in evaluation order, and FinishAggregate consolidates completed children into their parent. If a later field propagates failure, ordinary place-state cleanup drops only completed owned children; AST-era runtime live flags and the atomic aggregate rvalue have been removed from this route.
  • Borrow bindings over aggregate fields now create loans from projected MIR places; backend storage projection alone converts those places to slot offsets. Owned aggregate call arguments use explicit move operands, transferring initialization and drop obligations at the call edge.
  • Direct scalar and aggregate-field borrow arguments now materialize the same temporary MIR loan as stored borrow bindings. A one-definition/one-use backend projection elides only the machine borrow slot, preserving the checked loan without adding runtime work.
  • Fixed-array element borrows now use ProjectionElement::Index in production MIR. The checked projection retains its usize operand, array length, and element stride; backend projection emits the existing bounds-checked aggregate address for constant and dynamic indexes. Array fields and root arrays therefore share the same place and loan analyses.
  • Fixed-array scalar reads, assignments, and compound assignments now use that same index place. MIR validation checks the projected destination contract rather than the aggregate root type; one backend scalar projection emits field or bounds-checked index loads and stores for every built-in scalar representation.
  • Lexical allocation regions now have body-local RegionId records containing their scope, allocator capability, parent place, and saved allocation-context locals. MIR cleanup inserts ExitRegion on every scope-exiting CFG edge after owned-value cleanup; backend lowering no longer walks RegionStmt blocks or maintains a parallel region-cleanup stack for routed bodies.
  • Generic destructor plans keep semantic DefId identity plus the concrete TyId; a dedicated backend index selects the corresponding monomorphized symbol without putting that symbol in MIR.
  • Payload-variant construction retains the selected variant definition and semantic payload paths in MIR. Enum cleanup tests the stored tag and recursively destroys only the active payload. Optional and fallible cleanup retains ordered outcome layers plus a typed payload plan; backend projection derives offsets from the shared outcome storage layout.
  • Named-field moves retain projected place identity through calls. MIR expands the owned sibling projection tree before the partial move, and cleanup destroys only the remaining live fields. Machine IR addresses the selected aggregate range directly rather than staging a copy.
  • Value-block tails distinguish implicit results from explicit returns. Outcome recovery can now either assign its call destination and rejoin success or write function return storage and terminate; machine-IR failure modes derive that distinction from reachability.
  • Effect-only calls use destination-free MIR continuations. Plain void calls have one successor; fallible void! calls retain distinct success and failure edges for trapping or propagation. Initialization, ownership, loans, cleanup, and backend projection consume those same edges without inventing a scalar result local.
  • Plain aggregate literal and call returns now use aggregate MIR return locals. MIR retains value ownership without encoding direct or indirect return passing; machine-storage projection alone selects DirectReturn or the indirect return pointer. Direct literals write return registers without a staging slot, and eligible aggregate calls remain tail calls.
  • Conditional value blocks use one representation-independent MIR builder. Scalar, string-view, and aggregate branches assign the same logical destination and retain child scopes, calls, ownership cleanup, and nested conditionals without selecting separate AST-shaped return implementations. Shared post-branch cleanup executes once after the join.
  • Stored optional, fallible, and composed-outcome call results now enter aggregate MIR locals. The checked body retains the outcome's semantic TyId; only machine-IR projection derives recursive tag, error, and payload storage from the shared outcome layout authority. MIR does not encode ABI offsets or introduce a second outcome-layout model.
  • One semantic InspectOutcome terminator now owns stored single-layer optional/fallible recovery, propagation, trapping, and catch edges. Definite initialization, cleanup, drop obligations, and loans consume those edges directly. A dedicated backend projection derives tag, error, and payload offsets and emits the existing machine-IR inspection operations without consulting AST.
  • Stored optional and composed-outcome forwarding now uses an ownership-aware ReturnOutcome MIR edge. Validation checks the source operand's aggregate representation and copy/move contract; backend projection independently verifies the callable result shape before emitting recursive outcome ABI unpacking. Whole-place aggregate copies use the same checked local/location boundary.
  • Static and evaluated error.new(code, message) failure returns now terminate MIR with logical string-view operands. Evaluation order, initialization, loans, and cleanup are checked before backend projection emits ReturnFallibleFailure; the backend no longer parses constructor AST for this route.
  • Buildability now walks reachable MIR call instances and validates the retained body used by machine-IR projection. Static error helpers are classified from checked MIR rather than by a special AST body parser.
  • Closures, typed literals, interpolation, expansion, iteration, composed outcomes, aggregate indexing, and specialized calls all use the same production MIR route.
  • The temporary feature routers and AST-driven buildability and lowering families were deleted. Machine-IR projection reads checked MIR and uses source blocks only at the separate cache/build boundary; it never inspects source expressions or statements for execution semantics.
  • The complete verification matrix passes after the migration, including 2,028 library tests and 223 distributed installed-home tests, formatting, warnings-denied Clippy, documentation generation, and diff checks.

Completion Definition

Phase 3 is complete when buildability is MIR construction and validation, machine-IR lowering does not inspect source Expr, Stmt, or Block nodes, control-flow joins and cleanup are represented once, drops and loans are explicit and path-sensitive, spans are metadata rather than equality, all temporary routers and migrated AST lowerers are deleted, and the complete verification, documentation, formatting, Clippy, and diff gates pass.

Later Phases

  • Phase 4 — Backend Convergence: replace string-based primitive dispatch with IntrinsicId, centralize checked-type-to-ABI projection, unify duplicated integer and aggregate projection paths, and remove backend adapters that restate MIR contracts.
  • Phase 5 — Stabilization: remove migration adapters, narrow lint exceptions, consolidate tests around the new boundaries, qualify, and publish v0.14.0.