Semantic pipeline stage ordering
How semantic rule stages relate to lower-spine type checking in the reference compiler.
Document layout: Feature capability layoutconforms
## Purpose## Requirements
Semantic pipeline stage ordering Specification
Purpose
How semantic rule stages relate to lower-spine type checking in the reference compiler.
Requirements
Requirement: Single lower-spine type check
Expression and declaration type checking SHALL run exactly once on the lower spine as pipeline phase id lower.type_check (LOWER_TYPE_CHECK). The reference compiler MUST NOT run a second full body-typing pass under semantic.type_check; that phase id is not authoritative for typed HIR or for E12xx type diagnostics.
Scenario: Type check observed once as lower.type_check
- GIVEN a program that requires expression and declaration typing
- WHEN the reference compiler runs the production semantic and lower pipeline
- THEN full body typing runs under
lower.type_checkand not as a second full pass undersemantic.type_check
Requirement: Lower type-check three-pass spine
Within lower.type_check, the reference compiler MUST run index_program first, then a three-pass spine: surface (UnitTypeSurface merge for entry), check (TypeChecker::check_entry with constraint solving writing node_types), and lowering prep (LoweringPrep with call_kinds and cast_intents keyed by HirNodeId). TypeResult assembly MUST follow those three passes.
Scenario: Index before surface check prep
- GIVEN a compilation unit entering
lower.type_check - WHEN the phase executes
- THEN
index_programcompletes before the surface, check, and lowering-prep passes assembleTypeResult
Requirement: Structural semantic rules without duplicate typing
Staged semantic rules MAY emit diagnostics that mention types when the check is structural and does not require a completed TypeResult, but MUST NOT duplicate full expression typing already performed at lower.type_check. CLI analyze, LSP, and prepare_compilation_diagnostics MUST surface type errors via the lower spine (or LowerResolveTypeError::Type via emit_type_error), and consumers MUST treat stable E12xx codes as the conformance surface.
Scenario: Structural immutability without second type pass
- GIVEN staged semantic rule
E1214immutable-assignment checking - WHEN the semantic pipeline emits that diagnostic
- THEN the check remains structural and does not perform a second full expression-typing pass
Informative Source Provenance
The records below preserve migration history and are not normative except where text was extracted into a requirement above.
Source Record: Semantic pipeline stage ordering
Authority: informative provenance
Legacy path: /platform-spec/compiler/semantic-pipeline/stage-ordering/
Source: site/spec-content/platform-spec/compiler/semantic-pipeline/stage-ordering/content.md
SHA-256: c496700437f5877bfad2ab97f1ce99833b40f76db6d92568505dc44041867b7a
Migrated source text
This feature hub pins **where type checking runs** relative to staged semantic rules. End-to-end compile ordering (parse → mods → semantic → lower → codegen) remains under [Build pipeline / Stage ordering](/platform-spec/compiler/build-pipeline/stage-ordering/).
## Implementation anchors
- `compiler/crates/beskid_pipeline/src/phases.rs` — `lower.type_check` phase id
- `compiler/crates/beskid_analysis/src/services/lower.rs` — typed HIR spine (`typed_hir_from_lowered`, `type_check_lowered_hir`)
- `compiler/crates/beskid_analysis/src/hir/index.rs` — `index_program` before type checking
- `compiler/crates/beskid_analysis/src/types/checker/entry.rs` — `TypeChecker::check_entry` (surface → check → lowering prep)
- `compiler/crates/beskid_analysis/src/analysis/rules/staged/type_checking.rs` — structural immutability only
- `compiler/crates/beskid_tests/src/analysis/type_check_diagnostics.rs` — diagnostic code conformance
- [Design model](./design-model/)
- [Verification and traceability](./verification-and-traceability/)
## Decisions
<!-- spec:generate:adr-index -->
No ADRs published under **`adr/`** yet.
<!-- /spec:generate:adr-index -->
## Articles
<!-- spec:generate:article-index -->
- [Semantic pipeline stage ordering - Design model](./articles/design-model/)
- [Semantic pipeline stage ordering - Verification and traceability](./articles/verification-and-traceability/)
<!-- /spec:generate:article-index -->
Source Record: Semantic pipeline stage ordering - Design model
Authority: informative provenance
Legacy path: /platform-spec/compiler/semantic-pipeline/stage-ordering/articles/design-model/
Source: site/spec-content/platform-spec/compiler/semantic-pipeline/stage-ordering/articles/design-model/content.md
SHA-256: c00b941ca6ec6500c3854ac3f3d5d1538a8d0fd6432bab43e9cdaea0fd9276dd
Migrated source text
## Type-check authority
**Normative:** Expression and declaration **type checking** runs exactly once on the **lower spine**, observed as pipeline phase id **`lower.type_check`** (`LOWER_TYPE_CHECK` in `beskid_pipeline::phases`).
The reference compiler **must not** run a second full body-typing pass under **`semantic.type_check`**. That phase id is **not** authoritative for typed HIR or for `E12xx` type diagnostics.
Within **`lower.type_check`**, the reference compiler runs **`index_program`** first, then a **three-pass spine**:
1. **Surface** — build or load per-unit `UnitTypeSurface` values and merge for entry
2. **Check** — [`TypeChecker::check_entry`](compiler/crates/beskid_analysis/src/types/checker/entry.rs) body typing with constraint solving; write `node_types`
3. **Lowering prep** — `LoweringPrep` (`call_kinds`, `cast_intents`) keyed by `HirNodeId`
`TypeResult` assembly follows the three passes. See [Type-system pass contract](/platform-spec/compiler/semantic-pipeline/type-system-pass-contract/design-model/) for the full step list.
```mermaid
flowchart TB
semantic[semantic staged rules]
structural[structural checks only]
lower[lower spine]
index[index_program]
surface[surface pass]
check[TypeChecker::check_entry]
prep[lowering prep]
semantic --> structural
lower --> index --> surface --> check --> prep
```
## What stays in semantic rules
Staged semantic rules (`SemanticPipelineRule` in `staged.rs`) **may** still emit diagnostics that mention types when the check is **structural** and does not require a completed `TypeResult`, for example:
- immutable assignment (`E1214`) in `staged/type_checking.rs`
- invalid `?` shape probes in `staged/error_handling.rs` where resolution alone suffices
Those checks **must not** duplicate full expression typing already performed at **`lower.type_check`**.
## Diagnostic collection
CLI analyze, LSP, and `prepare_compilation_diagnostics` **must** surface type errors by running the lower spine (or collecting `LowerResolveTypeError::Type` into semantic diagnostics via `emit_type_error`). Consumers **must** treat stable **`E12xx`** codes as the conformance surface regardless of whether the error was collected during semantic snapshot assembly or lower failure handling.
## Cross references
- End-to-end phase DAG: [Build pipeline / Stage ordering](/platform-spec/compiler/build-pipeline/stage-ordering/)
- Typed HIR contract: [Type-system pass contract](/platform-spec/compiler/semantic-pipeline/type-system-pass-contract/)
- Staged rule ordering (non-type): [Rules pipeline contract](/platform-spec/compiler/semantic-pipeline/rules-pipeline-contract/)
Source Record: Semantic pipeline stage ordering - Verification and traceability
Authority: informative provenance
Legacy path: /platform-spec/compiler/semantic-pipeline/stage-ordering/articles/verification-and-traceability/
Source: site/spec-content/platform-spec/compiler/semantic-pipeline/stage-ordering/articles/verification-and-traceability/content.md
SHA-256: 1be9d922a74f6250b0b9842751c021e6907a99cd21e0e2c5e10fa7b281879ff5
Migrated source text
## Conformance tests
| Evidence | Location | What it proves |
| --- | --- | --- |
| Lower-spine type codes via prepare | `compiler/crates/beskid_tests/src/analysis/type_check_diagnostics.rs` | `prepare_compilation_diagnostics` still emits stable `E12xx` codes after type-check consolidation |
| Issue kind → code mapping | `compiler/crates/beskid_tests/src/analysis/diagnostics.rs` | `SemanticIssueKind::code()` strings unchanged |
| Lower integration typing | `compiler/crates/beskid_tests/src/analysis/types.rs` | `TypeError` shapes and lower helper behavior |
| Semantic structural rules | `compiler/crates/beskid_tests/src/analysis/pipeline/core.rs` | Non-type semantic diagnostics (resolve, control flow, visibility) |
Tests in **`type_check_diagnostics.rs`** assert **diagnostic code sets only**. They **must not** assert relative ordering of `semantic.*` vs `lower.*` observer phases—ordering may change when mods run or when diagnostics are collected during prepare.
## Manual verification
```bash
cd compiler && CARGO_INCREMENTAL=0 cargo test -p beskid_tests analysis::type_check_diagnostics
```
With pipeline tracing enabled, observers **should** show `lower.type_check` during prepare; they **must not** treat a nested `semantic.type_check` observation (if still emitted for structural work) as a second authoritative type pass.