v0.15.0: Practical Application Foundation
Status: published (2026-08-24). Phase 0 changes compiler contracts only. Phase 1 is the first implemented v0.15.0 language change. Qualification evidence belongs to the release preparation.
Goal
Prepare Nocter for sustained application and standard-library work without allowing later compiler layers to reopen earlier semantic decisions. Each responsibility may consume only the explicit contract published by the preceding responsibility; it may not inspect that responsibility's storage or reconstruct facts from a presentation index.
Phase 0: Compiler Boundary Closure
Phase 0 changes no public language syntax or runtime behavior. It removes four architectural escape hatches found after the v0.14.0 release audit.
Frontend Bindings
Declaration lowering must publish an explicit syntax-input contract for checking. Body ownership, module ownership, block-import targets, standard semantic roles, and syntax-to-local identities must not be recovered from SourceIndex. SourceIndex remains an independently derived diagnostic and editor projection. Changing documentation, references, access annotations, or presentation ranges must not change the checked semantic program.
Concrete Evidence
Executable specialization may consume a checked StaticSelection, a concrete substitution, and the checked conformance authority. It must not receive a general source-operation selector, search by name, or reopen lexical visibility. Direct dispatch only maps an exact callable instance to a dense executable item. Structural, interface, and opaque dispatch open only the evidence route frozen by checking.
Runtime Types
The MIR backend boundary must publish a closed concrete runtime type table rather than the semantic TypeStore. Runtime shapes may describe scalar storage, pointers, views, fixed arrays, structs, enums, outcomes, closures, and opaque witnesses. They must not expose generic parameters, interface Self, associated projections, callable contracts, result provenance, or declaration storage. Machine lowering cannot import or match semantic TypeKind.
ABI Authority
TargetProgram selects one ABI identity. Machine lowering owns the corresponding complete layout and callable transport plan. ARM64 lowering consumes that plan and maps already assigned numeric locations to physical registers; it must not redeclare word size, stack alignment, argument windows, direct-value limits, indirect-result lanes, or pack lanes. Runtime descriptor layouts shared by machine planning and instruction lowering have one owner.
Completion Gate
Phase 0 is complete only when all of the following hold:
- semantic checking no longer queries
SourceIndexto select a body, module, import, declaration, local, capture, or standard role; - executable specialization has no general member/name lookup capability;
RuntimeEnvironmentno longer exposesTypeStore, andnocter-machinedoes not depend on semanticTypeKindorBuiltinType;- one ABI schema owns every value also consumed by ARM64 lowering;
- focused adversarial boundary tests, the complete workspace tests, warnings-denied Clippy, Rust formatting, generated documentation, and repository whitespace checks pass;
- a final design review finds no active superseded authority or undocumented exception.
Phase 0 does not change module discovery, use, source-file composition, sequence literals, variadic parameters, or standard-library APIs.
Phase 0 Outcome
- Declaration lowering now emits
FrontendBindingsthrough explicit module, body, parameter, and declaration projection operations. Generic presentation bindings cannot create semantic input, and checking succeeds with an emptySourceIndexwhen no diagnostic is required. - Concrete specialization accepts checked
StaticSelectionvalues and a closed evidence authority. It receives neither source names nor a lexical module and therefore cannot repeat visibility selection. RuntimeEnvironmentcontains a closedRuntimeTypeTable, concrete member representations, and one ABI identity. Opaque runtime shapes retain only their concrete witness; declaration identity and semanticTypeStorestorage do not cross the MIR backend boundary.RuntimeAbiSchemais the sole owner of numeric facts shared by machine planning and ARM64 lowering. ARM64 retains only physical register classification that no earlier layer consumes.
Phase 1: Source and Module Separation
Phase 1 gives physical-source visibility and module imports disjoint syntax and compiler contracts.
include ./file.nctnames one exact physical source and exposes only declarations authored in that source to the including source. Visibility is direct-only, directional, cycle-safe, and independent of whether a directory module exists.useselects directory modules only. Namespace, selected-name, re-export, and block-scope forms remain available in this phase; their possible later simplification is not part of Phase 1.- Every reached source owns its authored namespace. Discovery publishes separate include edges and module-use edges; lowering must not preserve a source-or-module union or reconstruct either edge from path spelling.
index.nctis the sole public contract. Bodyless public callables and opaque nominal contracts join exactly one private definition through reciprocal direct includes. Public presentation uses the contract, while checking and diagnostics retain the definition source.- Existing standard-library and example source composition migrates to
include; obsolete same-module source-import probing, module-wide private namespaces, and root-only implementation member exceptions are removed rather than adapted.
Phase 1 completes only after focused syntax, discovery, namespace, contract/definition, editor, and native conformance cases pass; the complete workspace gates pass; and an authority review finds no remaining source/module union, transitive include namespace, or implementation-derived public API.
Phase 1 Outcome
- Syntax and discovery now retain
includeanduseas disjoint edge families. An include names one exact./file.nct; a use names one directory module. Discovery neither probes a same-named source forusenor interprets an include as a module path. - Declaration lowering freezes a direct-only
SourceNamespaceTablefor names and a separateSourceAccessTablefor private declaration sites and nominal representations. Checking receives these as semantic input and cannot reconstruct private access from module membership orSourceIndex. index.nctowns every public declaration. Reciprocal direct includes join bodyless callable, nominal, construction, instance-operation, and conformance contracts to one private definition. An implementation source cannot add public API or program-wide conformance. Private inline construction members remain valid withoutpub; explicit visibility is required only on a bodyless public construction contract member.- Field, method, operator, coercion, enum-variant, raw structural-construction, and named construction selection all consume the same exact source-access context. Empty opaque structs retain an explicit sealed-representation fact instead of becoming public through an empty field list.
- Completion uses the same checked or retained prepared source-access authority after successful, semantic-failed, and syntax-incomplete generations. Definition navigation selects the public contract while implementation navigation selects the private body.
- The authority review found no remaining source/module union, transitive include namespace, module-wide private selection, or implementation-derived public construction surface. The full workspace passed 1,157 tests with one intentional public-HTTPS integration test ignored; warnings-denied Clippy and formatting also passed.
Phase 2: Contract-First Module Surfaces
Phase 2 makes nontrivial public behavior separable from the module contract and applies that model to the standard library.
- An interface default method writes an explicit
defaultmodifier. The modifier, rather than the presence of a block, distinguishes reusable default behavior from a conformance requirement. - A bodyless
pub default methodinindex.nctjoins exactly one privatedefault methodbody in a reciprocally directly included source. The private source repeats the interface and method headers but cannot add associated types, requirements, or uncontracted defaults. - Inline defaults remain legal only with the same explicit modifier. The obsolete implicit form
pub method ... { ... }is rejected rather than retained as a compatibility spelling. - Definition, implementation, references, rename, hover, completion, signature help, body checking, conformance selection, and specialization treat the contract and body as one callable identity while retaining their distinct source roles.
- Standard-library module roots become contract-first API documents. Public and package-visible
contracts, intentional public data representation, re-exports, and documentation remain in
index.nct; private representation, ordinary bodies, allocation and pointer work, platform operations, and helper algorithms move to responsibility-named implementation sources.
Phase 2 completes only after focused syntax, joining, conformance, editor, target-gated-body, and native standard-library cases pass; the complete workspace gates pass; and a final review finds no implementation-derived public API, implicit interface default, or nontrivial standard-library body left in a module root without a documented contract reason.
Completion record:
defaultis an explicit syntax-surface modifier. Required interface methods cannot acquire a block, while inline and separated defaults use one spelling and one semantic callable identity.- Contract joining now carries nested declaration identities such as
some Interfaceresults with their owning callable instead of leaving duplicate semantic declarations. - Toolchain role discovery selects visible standard contracts and ignores their matching private
interface fragments; it no longer mistakes separated
Iteratordeclarations for competing builtin roles. - Every standard-library child-module root is bodyless. Opaque representation, ordinary behavior,
destruction, and target work live in responsibility-named sources such as
storage.nct,defaults.nct,formatting.nct, anddarwin.nct. Package-shared raw storage remains opaque and crosses modules through restricted accessor contracts rather than restricted fields. - The authored-standard-library test rejects every executable
Blockin a module root and then checks the complete discovered unit through declaration lowering and body checking. Focused parser, contract, discovery, LSP navigation and hover, native conformance, workspace, Clippy, formatting, generated documentation, and diff-integrity gates passed at completion.
Phase 3: Typed Argument Packs
Phase 3 generalizes the sequence-literal element pack into one typed callable argument-pack contract shared by functions, methods, and literals. It defines ordinary parameters before a final pack, call-site spread and tail forwarding, callable-type identity, ownership, provenance, cleanup, and the compiler-owned pack ABI. It does not introduce untyped C variadics.
Completion record:
- Shared parameter syntax now accepts one final
...items: Ton functions, construction functions, methods, interface methods, and matching implementations. Sequence literals use the same grammar with the stricter one-pack-only contract;E0326reports malformed declaration shapes before declaration-program integrity checking.ParameterRole::ArgumentPackkeeps the compiler-owned input distinct from ordinary parameters in the declaration model. - Call checking owns one inference session across fixed arguments, fixed pack elements, and spread
contributions.
CheckedArgumentPackis shared by calls and typed sequences, and ownership, loans, provenance, dependency collection, and cleanup consume that contract instead of rebuilding element facts. - Callable structural identity distinguishes a final pack from an ordinary final parameter.
Presentation and signature help render
..., and signature-help cursor selection maps every additional authored argument to the final pack parameter. - TargetProgram separates
ExecutableArgumentPackPlanfromExecutableSequencePlanwhile both consume sharedExecutablePackSegmentfacts. MIR, MachineProgram, and ARM64 retain ordinary arguments and the hidden descriptor lane simultaneously; native tests execute fixed and spread pack calls through that ABI. - The standard library exposes
String.concat(...parts: &str)as the first practical general-pack API. Its public contract remains instd/string/index.nct, with the body instorage.nct. target(fixed, ...items)tail-forwards an incoming pack as the sole pack contribution. Checked, executable, MIR, machine, and ARM64 contracts distinguish descriptor forwarding from creation; no stage exposes the descriptor as a source value or reconstructs its callback semantics.
Phase 4: Compile-Time Constants
Phase 4 introduces one storage-independent named-value contract and makes it the sole authority for fixed-array length expressions.
const name: Type = expressionrequires an explicit scalar type. Supported values arebool, built-in integers, and static readonly&str; constants are values rather than addressable places.- A bodyless visible root constant joins exactly one private initializer in a reciprocally directly included implementation source. Inline initialized declarations remain available when the value is itself the clearest contract.
- One header-constant pass resolves dependencies, detects cycles, evaluates builtin boolean and integer operations, checks overflow and conversions, and freezes canonical values. Declaration definition, body checking, and backend lowering cannot read initializer syntax.
[T; expression]consumes the same evaluator. Header types use its normalized length directly; body annotations use the same expression rules under their body-owned lexical name authority.- Constant references participate in ordinary imports and direct includes. Value planning and readonly operator preparation explicitly distinguish them from places.
- Hover renders canonical evaluated values. Completion, semantic highlighting, definition, references, and rename use the same semantic constant identity.
- Darwin syscall numbers, open flags, errno values, path limits, and standard stream descriptors in the standard library are constants rather than zero-argument value-returning functions.
Phase 4 does not add constant functions, associated constants, constant generics, named static storage, addressable globals, or compile-time owned values.
Completion record:
- Parser, contract joining, dependency evaluation, signed-minimum, short-circuit, arithmetic failure, fixed-array, checked-value, LSP identity, standard-library, and native image tests cover the new surface and its phase boundaries.
- The Phase 4 review found one header evaluator and an explicit syntax-to-body array-length projection. Phase 5 subsequently replaced that projection after proving that it could not retain body lexical scope without making declaration lowering reinterpret body names.
- Complete workspace tests, warnings-denied Clippy, Rust formatting, generated documentation, and repository integrity gates passed at completion.
Phase 5: Lexically Scoped Constant Expressions
Phase 5 closes the scope mismatch left by Phase 4. Constant-expression language rules are shared, but name and type selection remain owned by the semantic context in which the expression appears.
nocter-constant-evaluationis the sole owner of constant-expression typing, typed plans, arithmetic, conversions, short-circuit behavior, dependency ordering, and cycle detection. It cannot inspect declaration namespaces or body scopes and instead accepts a narrow resolver contract.- Declaration lowering supplies header-bound names and types, evaluates named constants and header fixed-array lengths, then freezes only semantic values and normalized types.
- Body name resolution visits type annotations and freezes block-import or runtime-value targets at their exact lexical scope. Body type checking supplies those decisions to the same planner and evaluates each body-local fixed-array length once.
FrontendBindingsno longer transports body expression nodes or evaluated array lengths. Declaration lowering does not inspect blocks, and body checking does not fall back to a header or source namespace after a lexical target has been selected.E0322throughE0325are one shared diagnostic family in header and body constant-expression contexts. Header-only invalid constant types remainE0321.
Completion record:
- Selected-name and namespace block imports work in both the element type and length expression of a body fixed-array annotation. Runtime lexical values are rejected as non-constant rather than being re-resolved against a source namespace.
- Shared evaluator tests cover signed minimum values, typed short-circuit operands, and authored dependency cycles. Checking tests cover lexical imports, conversions, arithmetic failures, type mismatches, and ordinary constant values.
- The final review found no body-array-length table, duplicated arithmetic implementation, name lookup fallback, or syntax dependency beyond declaration lowering and body checking.
- Complete workspace tests, warnings-denied Clippy, Rust formatting, generated documentation, and repository integrity gates passed at completion.
Phase 6: Practical Application Qualification
Phase 6 qualifies the v0.15.0 foundation through a nontrivial public application rather than adding another isolated language surface.
examples/text-reportcombines a package executable, a child directory module, a contract-first opaqueReporttype, reciprocal direct includes, borrowed line iteration and search, owned string construction, numeric formatting, process arguments, and fallible file I/O.nocter-test-support::public_examplesis the single acceptance catalog for public package examples. Session tests require every package directory underexamples/to appear in that catalog and compile every named executable. Command tests build each executable once and run all declared process scenarios with exact status, stdout, and stderr contracts.- A loop-owned iterator is represented consistently by ownership and provenance.
CheckedLooppublishes its body scope once. Ownership uses that scope for retained-temporary cleanup, while provenance uses a scoped-temporary source that remains valid throughout the body but cannot enter an outer binding or callable result. - Statement temporaries remain a distinct provenance source. Calls and sequence or argument-pack spreads therefore retain the existing statement-end escape rule and cannot acquire the longer loop lifetime accidentally.
Phase 6 changes no syntax or standard-library API. Its application exposed and removed a false E0398 caused by provenance treating the retained loop iterator as an ordinary statement temporary even though ownership already kept it alive for the complete loop body.
Completion record:
- Focused provenance tests accept independent work and inner bindings throughout a loop body, then reject an attempted borrow assignment into an outer binding.
- Both public package examples compile through the complete target session. Five native process scenarios cover successful file input and argument-validation failures with exact output and exit status.
- The editor audit found no application-specific semantic path: separated contract/body navigation, hover, opaque types, direct includes, and document-generation invalidation already cross protocol-level language-server tests through the same checked source identities.
- The final authority review moved the new acceptance catalog into its own file and moved loop body
scope into
CheckedLoop; neither provenance nor ownership reinterprets the other responsibility's storage or repeats loop-scope selection. - Complete workspace tests passed with 1,217 tests, zero failures, and one intentional network test ignored. Warnings-denied Clippy, Rust formatting, generated documentation, and repository integrity gates passed at completion.