feat: construct partial compile-time type records
This commit is contained in:
@@ -112,11 +112,13 @@ inline tuples may mix types and values, including nested aggregates, strings, op
|
|||||||
|
|
||||||
initialized fixed arrays of `type` live entirely inside compile-time execution, including nested arrays, explicit/inferred annotations, `.len`, checked `usize` indexing and collection captures. for example, `types [2]mut type := [i8, bool]`, then `types[1] = i16` and `return [types.len]types[1]`, generates `[2]i16` without runtime type metadata. whole-array and nested indexed assignments preserve shape and mutability at every level; copies and loop captures retain earlier values while borrowed array places observe later updates.
|
initialized fixed arrays of `type` live entirely inside compile-time execution, including nested arrays, explicit/inferred annotations, `.len`, checked `usize` indexing and collection captures. for example, `types [2]mut type := [i8, bool]`, then `types[1] = i16` and `return [types.len]types[1]`, generates `[2]i16` without runtime type metadata. whole-array and nested indexed assignments preserve shape and mutability at every level; copies and loop captures retain earlier values while borrowed array places observe later updates.
|
||||||
|
|
||||||
initialized records/tuples containing `type` fields live inside compile-time producers. local schemas, typed/contextual/anonymous construction, defaults, nested records/arrays, field/index reads and updates, whole-value replacement and by-value collection captures retain owned snapshots. for example, `config struct { child type, count usize } := { child = i8, count = 2 }`, then `config.child = i16` and `return [config.count]config.child`, generates `[2]i16`. earlier copies and loop captures remain independent, while borrowed array places observe later updates. type leaves retain nominal identity; ordinary value leaves reuse checked constant execution. unknown/duplicate/missing fields, invalid types/defaults, readonly places, narrow overflow and dependency provenance retain diagnostics. record validation/construction shares the type-control bound. runtime type-valued records, partial record initialization, metadata reflection and inline expansion remain open.
|
initialized records/tuples containing `type` fields live inside compile-time producers. local schemas, typed/contextual/anonymous construction, defaults, nested records/arrays, field/index reads and updates, whole-value replacement and by-value collection captures retain owned snapshots. for example, `config struct { child type, count usize } := { child = i8, count = 2 }`, then `config.child = i16` and `return [config.count]config.child`, generates `[2]i16`. earlier copies and loop captures remain independent, while borrowed array places observe later updates. type leaves retain nominal identity; ordinary value leaves reuse checked constant execution. unknown/duplicate/missing fields, invalid types/defaults, readonly places, narrow overflow and dependency provenance retain diagnostics. record validation/construction shares the type-control bound. runtime type-valued records remain unsupported.
|
||||||
|
|
||||||
|
type-valued records/tuples may also start with `undefined`: `config struct { child type, count usize } := undefined`, then `config.child = i16` and `config.count = 2`. nested record fields, arrays of records, tuple ordinals and `field!` updates use the same persistent snapshots as initialized storage; partial copies retain independent initialization state. defaults do not initialize `undefined` fields. initialized leaves and array lengths can be read while other leaves remain uninitialized, but premature leaf reads and incomplete procedure arguments/results diagnose read-before-initialization. fully initialized results cross compile-time procedure boundaries and supply erased generic inputs, generated storage and defaults. empty shapes are initialized vacuously; each expanded record field/array cell shares the construction bound.
|
||||||
|
|
||||||
fixed type arrays may also start with `undefined` inside type/type-array producers: `types [2]mut type := undefined`, then fill `types[index] = i16` in a loop. nested and whole-array updates preserve shape, mutability and independent initialization state in local copies; `.len` is available before elements initialize. reading an uninitialized type leaf, returning an incomplete array or passing one to a compile-time procedure diagnoses a read before initialization. empty shapes are valid, but every dimension must be explicit and nonnegative. construction charges each expanded cell against the shared execution bound, including nested zero-leaf shapes.
|
fixed type arrays may also start with `undefined` inside type/type-array producers: `types [2]mut type := undefined`, then fill `types[index] = i16` in a loop. nested and whole-array updates preserve shape, mutability and independent initialization state in local copies; `.len` is available before elements initialize. reading an uninitialized type leaf, returning an incomplete array or passing one to a compile-time procedure diagnoses a read before initialization. empty shapes are valid, but every dimension must be explicit and nonnegative. construction charges each expanded cell against the shared execution bound, including nested zero-leaf shapes.
|
||||||
|
|
||||||
ordinary fixed value arrays use the same construction inside type/type-array producers: `counts [2]mut usize := undefined`, then fill indexed elements or replace whole rows/arrays. initialized leaves retain exact scalar, string, optional, record and tagged-union values; local copies retain independent partial initialization state. generated lengths/defaults and compile-time arguments can use initialized elements, while incomplete arrays cannot enter checked IR or procedure arguments. array `.len` and empty shapes do not read leaves. partial type-valued record construction and constant materialization of mutable slices or nonnull data pointers remain unsupported.
|
ordinary fixed value arrays use the same construction inside type/type-array producers: `counts [2]mut usize := undefined`, then fill indexed elements or replace whole rows/arrays. initialized leaves retain exact scalar, string, optional, record and tagged-union values; local copies retain independent partial initialization state. generated lengths/defaults and compile-time arguments can use initialized elements, while incomplete arrays cannot enter checked IR or procedure arguments. array `.len` and empty shapes do not read leaves. constant materialization of mutable slices or nonnull data pointers remains unsupported.
|
||||||
|
|
||||||
ordinary procedures also accept `undefined` as the initializer of a mutable local with a concrete runtime type, including nested records and fixed arrays. field/index writes initialize storage without reading its previous contents; aggregate copies retain independent contents and initialization state in the interpreter. ordinary compile-time calls can fill this storage and return materializable values, but observing or materializing an uninitialized leaf is diagnosed. native storage uses LLVM poison, not zero initialization: assign every leaf before observing it, and do not rely on native read-before-initialization traps for `undefined` storage. immutable bindings, globals, inferred array lengths and other expression uses remain invalid.
|
ordinary procedures also accept `undefined` as the initializer of a mutable local with a concrete runtime type, including nested records and fixed arrays. field/index writes initialize storage without reading its previous contents; aggregate copies retain independent contents and initialization state in the interpreter. ordinary compile-time calls can fill this storage and return materializable values, but observing or materializing an uninitialized leaf is diagnosed. native storage uses LLVM poison, not zero initialization: assign every leaf before observing it, and do not rely on native read-before-initialization traps for `undefined` storage. immutable bindings, globals, inferred array lengths and other expression uses remain invalid.
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ captures and `usize` indices through both paths; type-producing bodies iterate
|
|||||||
materializable arrays/strings by value without retaining interpreter addresses.
|
materializable arrays/strings by value without retaining interpreter addresses.
|
||||||
type-valued fixed arrays support nested copies, indexed/whole updates,
|
type-valued fixed arrays support nested copies, indexed/whole updates,
|
||||||
checked projections and by-value collection captures in type-producing bodies;
|
checked projections and by-value collection captures in type-producing bodies;
|
||||||
`undefined` type/value arrays track initialization per leaf while loops fill generated types/defaults.
|
`undefined` type/value arrays and type-valued records/tuples track initialization
|
||||||
|
per leaf while field/index updates fill generated types/defaults.
|
||||||
type-array and type-record parameters/results cross compile-time procedure boundaries
|
type-array and type-record parameters/results cross compile-time procedure boundaries
|
||||||
with owned snapshots, cached nominal identity and erased arguments.
|
with owned snapshots, cached nominal identity and erased arguments.
|
||||||
initialized type-valued records/tuples support schemas/defaults, nested
|
initialized type-valued records/tuples support schemas/defaults, nested
|
||||||
@@ -131,9 +132,9 @@ test discovery and FFI remain outside these completed slices.
|
|||||||
- [x] type-array procedure checkpoint: fixed arrays of type values cross compile-time procedure inputs/results, including nested/aliased results, dependent `$N` lengths, empty shapes and erased `$types` parameters in runtime generic procedures. all inputs to type/type-array producers are explicit constants; owned snapshots preserve by-value isolation, exact shape/mutability and canonical/nominal cache identity. projections generate signatures, constructors, storage and defaults through interpreter/native execution. malformed inputs/results, runtime dependencies, imported provenance, shared execution bounds and repeatable compilation retain diagnostics and isolation. runtime type-array storage, type-valued record procedure boundaries and reflection remain open.
|
- [x] type-array procedure checkpoint: fixed arrays of type values cross compile-time procedure inputs/results, including nested/aliased results, dependent `$N` lengths, empty shapes and erased `$types` parameters in runtime generic procedures. all inputs to type/type-array producers are explicit constants; owned snapshots preserve by-value isolation, exact shape/mutability and canonical/nominal cache identity. projections generate signatures, constructors, storage and defaults through interpreter/native execution. malformed inputs/results, runtime dependencies, imported provenance, shared execution bounds and repeatable compilation retain diagnostics and isolation. runtime type-array storage, type-valued record procedure boundaries and reflection remain open.
|
||||||
- [x] field-selection reflection checkpoint: `field!(base, name)` evaluates/erases a constant string name and reuses ordinary selection through interpreter/native execution. records/tuples, enum/error-set members, tagged payloads, callbacks, pointer-qualified places and sequence `.len`/`.ptr` retain exact types, single base evaluation, address-taking, mutability and active-field checks. type-producing bodies snapshot changing selector names for nested aggregate updates without losing by-value isolation. runtime/nonstring names, malformed calls, missing members and noncanonical/overflowing tuple ordinals retain diagnostics; full metadata reflection and inline expansion remain open.
|
- [x] field-selection reflection checkpoint: `field!(base, name)` evaluates/erases a constant string name and reuses ordinary selection through interpreter/native execution. records/tuples, enum/error-set members, tagged payloads, callbacks, pointer-qualified places and sequence `.len`/`.ptr` retain exact types, single base evaluation, address-taking, mutability and active-field checks. type-producing bodies snapshot changing selector names for nested aggregate updates without losing by-value isolation. runtime/nonstring names, malformed calls, missing members and noncanonical/overflowing tuple ordinals retain diagnostics; full metadata reflection and inline expansion remain open.
|
||||||
- [x] uninitialized type-array checkpoint: fixed type arrays in type/type-array producers start with `undefined` and fill through loop-driven indexed, nested and whole-array updates. persistent local copies retain independent initialization state; `.len` works before leaves initialize. premature leaf reads and incomplete procedure arguments/results diagnose read-before-initialization without caching invalid values. explicit nonnegative dimensions, empty shapes, ordinary mutability/shape checks, nominal identity and repeatable compilation work through interpreter/native execution. construction shares the type-control bound per expanded cell, including nested zero-leaf shapes. ordinary value-array construction, local type-valued records, runtime undefined storage and metadata reflection are covered below.
|
- [x] uninitialized type-array checkpoint: fixed type arrays in type/type-array producers start with `undefined` and fill through loop-driven indexed, nested and whole-array updates. persistent local copies retain independent initialization state; `.len` works before leaves initialize. premature leaf reads and incomplete procedure arguments/results diagnose read-before-initialization without caching invalid values. explicit nonnegative dimensions, empty shapes, ordinary mutability/shape checks, nominal identity and repeatable compilation work through interpreter/native execution. construction shares the type-control bound per expanded cell, including nested zero-leaf shapes. ordinary value-array construction, local type-valued records, runtime undefined storage and metadata reflection are covered below.
|
||||||
- [x] uninitialized value-array checkpoint: ordinary fixed arrays in type/type-array producers start with `undefined`, fill nested indices or replace whole arrays/rows, and generate storage/defaults through interpreter/native execution. owned snapshots preserve independent partial initialization in local copies; initialized scalar/string/optional/record/tagged-union elements retain exact types and ordinary checked operations. premature leaf reads and incomplete procedure arguments diagnose read-before-initialization. empty shapes, `.len` before initialization, mutability/type/index/bounds/overflow diagnostics, per-cell construction limits and repeatable compilation are verified. runtime undefined storage and ordinary value-returning procedures are covered below; partial type-valued record construction and storage-bearing constant materialization remain open.
|
- [x] uninitialized value-array checkpoint: ordinary fixed arrays in type/type-array producers start with `undefined`, fill nested indices or replace whole arrays/rows, and generate storage/defaults through interpreter/native execution. owned snapshots preserve independent partial initialization in local copies; initialized scalar/string/optional/record/tagged-union elements retain exact types and ordinary checked operations. premature leaf reads and incomplete procedure arguments diagnose read-before-initialization. empty shapes, `.len` before initialization, mutability/type/index/bounds/overflow diagnostics, per-cell construction limits and repeatable compilation are verified. runtime undefined storage, ordinary value-returning procedures and partial type-valued records are covered below; storage-bearing constant materialization remains open.
|
||||||
- [x] type-record storage checkpoint: initialized records/tuples containing type values live inside type/type-array producers, including local schemas/defaults, typed/contextual/inferred construction, nested records/arrays, checked field/index reads and updates, whole-value replacement and immutable by-value collection captures. persistent snapshots isolate copies/captures while borrowed array places observe later updates; type leaves retain nominal identity and ordinary value leaves reuse checked IR. generated storage/defaults, invalid field/type/default diagnostics, readonly places, narrow overflow, imported producers/provenance, cached nominal results and repeatable compilation work through interpreter/native execution. field validation/construction shares the type-control bound. type-record procedure inputs/results are covered below; runtime type-valued records, partial record initialization, metadata reflection and inline expansion remain open.
|
- [x] type-record storage checkpoint: initialized records/tuples containing type values live inside type/type-array producers, including local schemas/defaults, typed/contextual/inferred construction, nested records/arrays, checked field/index reads and updates, whole-value replacement and immutable by-value collection captures. persistent snapshots isolate copies/captures while borrowed array places observe later updates; type leaves retain nominal identity and ordinary value leaves reuse checked IR. generated storage/defaults, invalid field/type/default diagnostics, readonly places, narrow overflow, imported producers/provenance, cached nominal results and repeatable compilation work through interpreter/native execution. field validation/construction shares the type-control bound. procedure inputs/results, partial record initialization, metadata reflection and inline expansion are covered below; runtime type-valued records remain open.
|
||||||
- [x] type-record procedure checkpoint: records/tuples containing type values cross compile-time procedure inputs/results, including nested arrays/records, aliases, defaults and dependent annotations. compile-time producers accept ordinary constant inputs; runtime generics accept erased `$` metadata parameters. owned snapshots isolate argument/result copies, exact type/value leaves retain checked diagnostics, and normalized cache keys preserve nominal generated types. generated storage/defaults, imported producers/provenance, shared execution bounds and repeatable compilation work through interpreter/native execution. runtime metadata storage, partial record initialization, reflection and inline expansion remain open.
|
- [x] type-record procedure checkpoint: records/tuples containing type values cross compile-time procedure inputs/results, including nested arrays/records, aliases, defaults and dependent annotations. compile-time producers accept ordinary constant inputs; runtime generics accept erased `$` metadata parameters. owned snapshots isolate argument/result copies, exact type/value leaves retain checked diagnostics, and normalized cache keys preserve nominal generated types. generated storage/defaults, imported producers/provenance, shared execution bounds and repeatable compilation work through interpreter/native execution. partial record initialization, reflection and inline expansion are covered below; runtime metadata storage remains open.
|
||||||
- [x] generated-record checkpoint: `struct_type!(layout, names, types, defaults)` builds ordinary records from constant fixed arrays or tuples with unique ASCII field names, concrete types and contextual optional defaults. literal heterogeneous collections, helper results and loop-filled producer arrays preserve exact types, owned defaults/copies and cached nominal identity through interpreter/native execution. `.auto` permits empty records; `.c` requires nonempty C-compatible fields and retains native field order/layout. malformed inputs, incomplete arrays, runtime dependencies, field/default validation, imported provenance, shared construction bounds and repeatable compilation retain diagnostics and isolation. metadata inspection, inline expansion, aggregate C calling conventions and storage-bearing defaults remain open.
|
- [x] generated-record checkpoint: `struct_type!(layout, names, types, defaults)` builds ordinary records from constant fixed arrays or tuples with unique ASCII field names, concrete types and contextual optional defaults. literal heterogeneous collections, helper results and loop-filled producer arrays preserve exact types, owned defaults/copies and cached nominal identity through interpreter/native execution. `.auto` permits empty records; `.c` requires nonempty C-compatible fields and retains native field order/layout. malformed inputs, incomplete arrays, runtime dependencies, field/default validation, imported provenance, shared construction bounds and repeatable compilation retain diagnostics and isolation. metadata inspection, inline expansion, aggregate C calling conventions and storage-bearing defaults remain open.
|
||||||
- [x] inline-loop checkpoint: `inline for` expands constant integer ranges, fixed arrays (including type-valued arrays) and strings into separately checked runtime bodies through interpreter/native execution. exact immutable captures, `usize` collection indices, narrow inclusive maxima, selected conditional/tagged-match bodies, static break/continue tail pruning, nested inline/ordinary loops, runtime returns/errors and scoped cleanup retain ordinary semantics. empty loops skip body elaboration; runtime-dependent inputs/transfers, pointer captures, storage-bearing escapes and shared expansion bounds retain diagnostics. imported producers/provenance and repeatable compilation are verified. heterogeneous tuples and inline-match syntax are covered below; metadata iteration remains open.
|
- [x] inline-loop checkpoint: `inline for` expands constant integer ranges, fixed arrays (including type-valued arrays) and strings into separately checked runtime bodies through interpreter/native execution. exact immutable captures, `usize` collection indices, narrow inclusive maxima, selected conditional/tagged-match bodies, static break/continue tail pruning, nested inline/ordinary loops, runtime returns/errors and scoped cleanup retain ordinary semantics. empty loops skip body elaboration; runtime-dependent inputs/transfers, pointer captures, storage-bearing escapes and shared expansion bounds retain diagnostics. imported producers/provenance and repeatable compilation are verified. heterogeneous tuples and inline-match syntax are covered below; metadata iteration remains open.
|
||||||
- [x] heterogeneous inline-tuple checkpoint: `inline for` iterates constant tuples with exact per-field value/type captures and `usize` indices through interpreter/native execution. literal/helper/aliased tuples, erased generic inputs, nested tuple projections, generated storage, empty shapes, static exits, cleanup and aggregate copy isolation retain existing semantics. type-producing bodies borrow tuple places so later fields observe updates while saved copies remain isolated. runtime dependencies and storage-bearing escapes are rejected before iteration, including discarded captures and early exits; named records and ordinary tuple loops remain invalid. imported producers, nominal type leaves and repeatable compilation are verified. inline-match syntax is covered below; metadata collections remain open.
|
- [x] heterogeneous inline-tuple checkpoint: `inline for` iterates constant tuples with exact per-field value/type captures and `usize` indices through interpreter/native execution. literal/helper/aliased tuples, erased generic inputs, nested tuple projections, generated storage, empty shapes, static exits, cleanup and aggregate copy isolation retain existing semantics. type-producing bodies borrow tuple places so later fields observe updates while saved copies remain isolated. runtime dependencies and storage-bearing escapes are rejected before iteration, including discarded captures and early exits; named records and ordinary tuple loops remain invalid. imported producers, nominal type leaves and repeatable compilation are verified. inline-match syntax is covered below; metadata collections remain open.
|
||||||
@@ -153,6 +154,7 @@ test discovery and FFI remain outside these completed slices.
|
|||||||
- [x] nonzero-sentinel byte-slice checkpoint: readonly `[;sentinel]u8` compile-time results own exact payload bytes and any byte sentinel through interpreter/native execution. canonical string-backed views preserve logical lengths, empty views, embedded NULs, high bytes, computed/direct specialization-cache identity, generated defaults, inline captures, nested aggregates, optionals and both fallible channels. sentinel checks observe final evaluation storage, diagnosing mutation, uninitialized bytes and invalid addresses; imported helpers, source/session disposal and repeated compilation retain ownership. nonbyte/nominal-byte slices are covered below; mutable slices, nonnull data pointers and address-bearing untagged union layouts retain the escape boundary.
|
- [x] nonzero-sentinel byte-slice checkpoint: readonly `[;sentinel]u8` compile-time results own exact payload bytes and any byte sentinel through interpreter/native execution. canonical string-backed views preserve logical lengths, empty views, embedded NULs, high bytes, computed/direct specialization-cache identity, generated defaults, inline captures, nested aggregates, optionals and both fallible channels. sentinel checks observe final evaluation storage, diagnosing mutation, uninitialized bytes and invalid addresses; imported helpers, source/session disposal and repeated compilation retain ownership. nonbyte/nominal-byte slices are covered below; mutable slices, nonnull data pointers and address-bearing untagged union layouts retain the escape boundary.
|
||||||
- [x] untagged-union materialization checkpoint: address-free untagged unions own full native-layout byte snapshots through interpreter/native execution, including scalar/IEEE reinterpretation, nested padded records/arrays, smaller overlapping updates, zero-sized storage, distinct members and generated nominal unions. explicit `$()` values, compile-time arguments, full-byte specialization-cache identity, generated defaults, nested optionals/tagged payloads and both fallible channels preserve exact types and independent copies. uninitialized tail/padding bytes diagnose read-before-initialization; every member layout is checked recursively to reject data pointers, callbacks, strings and slices rather than retain interpreter handles. imported helpers/diagnostics, source/session disposal and repeated compilation retain ownership. address-bearing union layouts and broader constant materialization remain open.
|
- [x] untagged-union materialization checkpoint: address-free untagged unions own full native-layout byte snapshots through interpreter/native execution, including scalar/IEEE reinterpretation, nested padded records/arrays, smaller overlapping updates, zero-sized storage, distinct members and generated nominal unions. explicit `$()` values, compile-time arguments, full-byte specialization-cache identity, generated defaults, nested optionals/tagged payloads and both fallible channels preserve exact types and independent copies. uninitialized tail/padding bytes diagnose read-before-initialization; every member layout is checked recursively to reject data pointers, callbacks, strings and slices rather than retain interpreter handles. imported helpers/diagnostics, source/session disposal and repeated compilation retain ownership. address-bearing union layouts and broader constant materialization remain open.
|
||||||
- [x] readonly typed-slice materialization checkpoint: ordinary compile-time calls snapshot readonly slices of materializable elements into module-lifetime backing through interpreter/native execution. nominal bytes, wider integers, arrays/records, source-bound callbacks, nested readonly slices, empty/zero-sized elements and full-width signed/unsigned sentinels retain exact types and logical lengths. explicit `$()` values, computed/direct content-based specialization identity, generated defaults and nested optional/tagged/fallible channels preserve ownership across helper returns, imported private declarations, source/session disposal and repeat compilation. initialization, address-range, sentinel, mutable-slice and nonnull-pointer escape diagnostics remain checked; 100,000-value/128-level snapshot bounds reject oversized and cyclic storage. mutable slices, nonnull data pointers and address-bearing untagged union layouts remain open.
|
- [x] readonly typed-slice materialization checkpoint: ordinary compile-time calls snapshot readonly slices of materializable elements into module-lifetime backing through interpreter/native execution. nominal bytes, wider integers, arrays/records, source-bound callbacks, nested readonly slices, empty/zero-sized elements and full-width signed/unsigned sentinels retain exact types and logical lengths. explicit `$()` values, computed/direct content-based specialization identity, generated defaults and nested optional/tagged/fallible channels preserve ownership across helper returns, imported private declarations, source/session disposal and repeat compilation. initialization, address-range, sentinel, mutable-slice and nonnull-pointer escape diagnostics remain checked; 100,000-value/128-level snapshot bounds reject oversized and cyclic storage. mutable slices, nonnull data pointers and address-bearing untagged union layouts remain open.
|
||||||
|
- [x] uninitialized type-record checkpoint: compile-time records/tuples containing type values start with `undefined` and fill through nested field/index updates or whole-value replacement. persistent snapshots preserve independent partial copies; defaults do not initialize undefined fields. initialized leaves and empty shapes work while premature type/value reads and incomplete procedure arguments/results diagnose read-before-initialization. fully initialized results supply erased generic inputs, generated storage/defaults and cached nominal types through interpreter/native execution. ordinary mutability/schema validation and shared per-field/cell construction bounds remain checked; general type-valued execution and broader materialization remain open.
|
||||||
- [ ] remaining gate: general type-valued compile-time execution, broader constant materialization (including address-bearing untagged unions, nonnull data pointers and mutable slices), plus remaining existing-library acceptance for broader `meta` operations and `io.print`. formatting parsing and broader imported C-binding support may interleave with later gates.
|
- [ ] remaining gate: general type-valued compile-time execution, broader constant materialization (including address-bearing untagged unions, nonnull data pointers and mutable slices), plus remaining existing-library acceptance for broader `meta` operations and `io.print`. formatting parsing and broader imported C-binding support may interleave with later gates.
|
||||||
- [ ] remaining source constructs: multiline strings. boolean and optional-capture conditional expressions, yielding value blocks, `yield`, compound updates and bitwise operators are covered above. examples: `source/parser/parse.hon`, `source/llvm/runtime.hon`.
|
- [ ] remaining source constructs: multiline strings. boolean and optional-capture conditional expressions, yielding value blocks, `yield`, compound updates and bitwise operators are covered above. examples: `source/parser/parse.hon`, `source/llvm/runtime.hon`.
|
||||||
- [ ] standard-library milestone: compile/run a multi-package program exercising existing allocation and IO, including error cleanup; no replacement miniature standard library.
|
- [ ] standard-library milestone: compile/run a multi-package program exercising existing allocation and IO, including error cleanup; no replacement miniature standard library.
|
||||||
|
|||||||
@@ -231,6 +231,17 @@ recompiles_specialized_calls_without_borrowing_previous_ir test {
|
|||||||
`}
|
`}
|
||||||
`BoundaryConfig :: struct { child type, counts [2]mut usize, bias i16 = 2 }
|
`BoundaryConfig :: struct { child type, counts [2]mut usize, bias i16 = 2 }
|
||||||
`record_config proc() BoundaryConfig { return { child = i16, counts = [1, 2] } }
|
`record_config proc() BoundaryConfig { return { child = i16, counts = [1, 2] } }
|
||||||
|
`partial_record_config proc() BoundaryConfig {
|
||||||
|
` config BoundaryConfig := undefined
|
||||||
|
` config.child = i8
|
||||||
|
` config.counts[0] = 1
|
||||||
|
` copied := config
|
||||||
|
` config.child = bool
|
||||||
|
` copied.child = i16
|
||||||
|
` copied.counts[1] = 2
|
||||||
|
` copied.bias = 2
|
||||||
|
` return copied
|
||||||
|
`}
|
||||||
`change_record_config proc(input BoundaryConfig) BoundaryConfig {
|
`change_record_config proc(input BoundaryConfig) BoundaryConfig {
|
||||||
` copied := input
|
` copied := input
|
||||||
` copied.child = u8
|
` copied.child = u8
|
||||||
@@ -310,6 +321,9 @@ recompiles_specialized_calls_without_borrowing_previous_ir test {
|
|||||||
` same_record_boundary RecordBoundary({ child = i16, counts = [1, 2] }) :: record_boundary
|
` same_record_boundary RecordBoundary({ child = i16, counts = [1, 2] }) :: record_boundary
|
||||||
` if (same_record_boundary.values[0] + same_record_boundary.values[1] + same_record_boundary.bias != 42) return 0
|
` if (same_record_boundary.values[0] + same_record_boundary.values[1] + same_record_boundary.bias != 42) return 0
|
||||||
` if (read_record_config(record_config(), 40) != 42) return 0
|
` if (read_record_config(record_config(), 40) != 42) return 0
|
||||||
|
` partial_boundary RecordBoundary(partial_record_config()) :: record_boundary
|
||||||
|
` if (partial_boundary.values[0] + partial_boundary.values[1] + partial_boundary.bias != 42) return 0
|
||||||
|
` if (read_record_config(partial_record_config(), 40) != 42) return 0
|
||||||
` if (read_record_pair(record_pair(), 40) != 42 or read_record_pair(record_pair(), 40) != 42) return 0
|
` if (read_record_pair(record_pair(), 40) != 42 or read_record_pair(record_pair(), 40) != 42) return 0
|
||||||
` copied_initialized = same_initialized.second
|
` copied_initialized = same_initialized.second
|
||||||
` payload :: packet.choices[0].payload
|
` payload :: packet.choices[0].payload
|
||||||
|
|||||||
@@ -3679,14 +3679,14 @@ type_array_projection proc(state @mut State, id ast.NodeId) bool ! Error {
|
|||||||
if (ast.expr_kind(&state.tree, base) != .typed_constant) return true
|
if (ast.expr_kind(&state.tree, base) != .typed_constant) return true
|
||||||
array :: ast.typed_constant(&state.tree, base)
|
array :: ast.typed_constant(&state.tree, base)
|
||||||
type :: try canonical_type(state, array.type)
|
type :: try canonical_type(state, array.type)
|
||||||
record :: (type != no_node and type_record_storage(state, type, 0)
|
record :: (type != no_node and (ast.expr_kind(&state.tree, type) == .record_type
|
||||||
and (ast.expr_kind(&state.tree, type) == .record_type
|
or (type_record_storage(state, type, 0) and ast.expr_kind(&state.tree, type) == .union_type)))
|
||||||
or ast.expr_kind(&state.tree, type) == .union_type))
|
|
||||||
if (
|
if (
|
||||||
!record and !try execution_array_storage(state, array.type)
|
!record and !try execution_array_storage(state, array.type)
|
||||||
and !try metadata_sequence(state, array.type)
|
and !try metadata_sequence(state, array.type)
|
||||||
) return true
|
) return true
|
||||||
selected := no_node
|
selected := no_node
|
||||||
|
selected_type := no_node
|
||||||
if (record) {
|
if (record) {
|
||||||
if (kind != .selection) return true
|
if (kind != .selection) return true
|
||||||
member :: ast.selection_expr(&state.tree, id).member
|
member :: ast.selection_expr(&state.tree, id).member
|
||||||
@@ -3705,23 +3705,15 @@ type_array_projection proc(state @mut State, id ast.NodeId) bool ! Error {
|
|||||||
}
|
}
|
||||||
selected = active.value?
|
selected = active.value?
|
||||||
} else selected = ast.field(&state.tree, ast.record_field(&state.tree, array.value, slot)).value?
|
} else selected = ast.field(&state.tree, ast.record_field(&state.tree, array.value, slot)).value?
|
||||||
|
if (!type_record_storage(state, array.type, 0))
|
||||||
|
selected_type = ast.field(&state.tree, ast.record_field(&state.tree, type, slot)).type?
|
||||||
} else if (kind == .index) {
|
} else if (kind == .index) {
|
||||||
index :: try type_array_index(state, ast.index_expr(&state.tree, id).index,
|
index :: try type_array_index(state, ast.index_expr(&state.tree, id).index,
|
||||||
ast.array_element_count(&state.tree, array.value))
|
ast.array_element_count(&state.tree, array.value))
|
||||||
if (index == no_index) return false
|
if (index == no_index) return false
|
||||||
selected = ast.array_element(&state.tree, array.value, index)
|
selected = ast.array_element(&state.tree, array.value, index)
|
||||||
if (ast.expr_kind(&state.tree, selected) == .literal_undefined) {
|
if (!type_record_storage(state, array.type, 0))
|
||||||
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
selected_type = ast.array_type(&state.tree, type).element
|
||||||
.read_before_initialization, ast.node_span(&state.tree, id))
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (!type_record_storage(state, array.type, 0)
|
|
||||||
and ast.expr_kind(&state.tree, selected) != .typed_constant) {
|
|
||||||
descriptor :: ast.array_type(&state.tree, try canonical_type(state, array.type))
|
|
||||||
selected = try ast.add_typed_constant(state.builder, ast.node_span(&state.tree, id),
|
|
||||||
descriptor.element, selected)
|
|
||||||
try refresh(state)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
member :: ast.selection_expr(&state.tree, id).member
|
member :: ast.selection_expr(&state.tree, id).member
|
||||||
if (!type_record_storage(state, array.type, 0)
|
if (!type_record_storage(state, array.type, 0)
|
||||||
@@ -3737,6 +3729,16 @@ type_array_projection proc(state @mut State, id ast.NodeId) bool ! Error {
|
|||||||
try builtin(state, id, .usize), count)
|
try builtin(state, id, .usize), count)
|
||||||
try refresh(state)
|
try refresh(state)
|
||||||
}
|
}
|
||||||
|
if (ast.expr_kind(&state.tree, selected) == .literal_undefined) {
|
||||||
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
||||||
|
.read_before_initialization, ast.node_span(&state.tree, id))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (selected_type != no_node and ast.expr_kind(&state.tree, selected) != .typed_constant) {
|
||||||
|
selected = try ast.add_typed_constant(state.builder, ast.node_span(&state.tree, id),
|
||||||
|
selected_type, selected)
|
||||||
|
try refresh(state)
|
||||||
|
}
|
||||||
# copying a nominal definition into the projection would create a new
|
# copying a nominal definition into the projection would create a new
|
||||||
# identity on every read. retain its original node behind a type alias.
|
# identity on every read. retain its original node behind a type alias.
|
||||||
if (nominal_type_value(state, selected)) {
|
if (nominal_type_value(state, selected)) {
|
||||||
@@ -3888,13 +3890,15 @@ type_record_check proc(state @mut State, annotation ast.NodeId) bool ! Error {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
# normalize complete record snapshots in declaration order, evaluating every leaf
|
# normalize record snapshots in declaration order through their declared context.
|
||||||
# through its declared context. no record containing type values enters runtime IR.
|
# undefined records retain per-leaf initialization state without applying defaults.
|
||||||
@hide:file
|
@hide:file
|
||||||
type_record_value proc(state @mut State, expression, annotation ast.NodeId) ast.NodeId ! Error {
|
type_record_value proc(state @mut State, expression, annotation ast.NodeId) ast.NodeId ! Error {
|
||||||
type :: try canonical_type(state, annotation)
|
type :: try canonical_type(state, annotation)
|
||||||
if (ast.expr_kind(&state.tree, type) == .union_type)
|
if (ast.expr_kind(&state.tree, type) == .union_type)
|
||||||
return try type_union_value(state, expression, type)
|
return try type_union_value(state, expression, type)
|
||||||
|
if (ast.expr_kind(&state.tree, expression) == .literal_undefined)
|
||||||
|
return try type_storage_undefined(state, expression, annotation)
|
||||||
if (ast.expr_kind(&state.tree, expression) == .typed_constant) {
|
if (ast.expr_kind(&state.tree, expression) == .typed_constant) {
|
||||||
actual :: ast.typed_constant(&state.tree, expression)
|
actual :: ast.typed_constant(&state.tree, expression)
|
||||||
if (try same_type(state, type, actual.type)) return expression
|
if (try same_type(state, type, actual.type)) return expression
|
||||||
@@ -4023,9 +4027,28 @@ type_array_initialized proc(state @mut State, value, at ast.NodeId) bool ! Error
|
|||||||
# bound the expanded snapshot, not just its dimensions, so later reads and
|
# bound the expanded snapshot, not just its dimensions, so later reads and
|
||||||
# initialization checks cannot traverse an exponentially shared empty shape.
|
# initialization checks cannot traverse an exponentially shared empty shape.
|
||||||
@hide:file
|
@hide:file
|
||||||
type_array_undefined proc(state @mut State, expression, annotation ast.NodeId) ast.NodeId ! Error {
|
type_storage_undefined proc(state @mut State, expression, annotation ast.NodeId) ast.NodeId ! Error {
|
||||||
if (is_type(state, annotation)) return expression
|
if (is_type(state, annotation)) return expression
|
||||||
type :: try canonical_type(state, annotation)
|
type :: try canonical_type(state, annotation)
|
||||||
|
if (type != no_node and ast.expr_kind(&state.tree, type) == .record_type) {
|
||||||
|
if (!try type_record_check(state, type)) return no_node
|
||||||
|
fields := arraylist.init(ast.NodeId, state.allocator)
|
||||||
|
defer arraylist.deinit(&fields)
|
||||||
|
for (0..ast.record_field_count(&state.tree, type)) |i| {
|
||||||
|
field :: ast.field(&state.tree, ast.record_field(&state.tree, type, i))
|
||||||
|
if (!try type_step(state, expression)) return no_node
|
||||||
|
value :: try type_storage_undefined(state, expression, field.type?)
|
||||||
|
if (value == no_node) return no_node
|
||||||
|
generated :: try ast.add_field(state.builder, field.span, field.name, null, value)
|
||||||
|
try refresh(state)
|
||||||
|
try arraylist.append(&fields, generated)
|
||||||
|
}
|
||||||
|
span :: ast.node_span(&state.tree, expression)
|
||||||
|
literal :: try ast.add_record_literal(state.builder, span, null, fields.items)
|
||||||
|
result :: try ast.add_typed_constant(state.builder, span, type, literal)
|
||||||
|
try refresh(state)
|
||||||
|
return result
|
||||||
|
}
|
||||||
if (type == no_node or ast.expr_kind(&state.tree, type) != .array_type) return expression
|
if (type == no_node or ast.expr_kind(&state.tree, type) != .array_type) return expression
|
||||||
descriptor :: ast.array_type(&state.tree, type)
|
descriptor :: ast.array_type(&state.tree, type)
|
||||||
length := Value{}
|
length := Value{}
|
||||||
@@ -4036,14 +4059,14 @@ type_array_undefined proc(state @mut State, expression, annotation ast.NodeId) a
|
|||||||
return no_node
|
return no_node
|
||||||
}
|
}
|
||||||
# validate child dimensions even when this dimension is empty.
|
# validate child dimensions even when this dimension is empty.
|
||||||
child :: try type_array_undefined(state, expression, descriptor.element)
|
child :: try type_storage_undefined(state, expression, descriptor.element)
|
||||||
if (child == no_node) return no_node
|
if (child == no_node) return no_node
|
||||||
elements := arraylist.init(ast.NodeId, state.allocator)
|
elements := arraylist.init(ast.NodeId, state.allocator)
|
||||||
defer arraylist.deinit(&elements)
|
defer arraylist.deinit(&elements)
|
||||||
for (0..length.magnitude) |i| {
|
for (0..length.magnitude) |i| {
|
||||||
if (!try type_step(state, expression)) return no_node
|
if (!try type_step(state, expression)) return no_node
|
||||||
element := child
|
element := child
|
||||||
if (i != 0) element = try type_array_undefined(state, expression, descriptor.element)
|
if (i != 0) element = try type_storage_undefined(state, expression, descriptor.element)
|
||||||
if (element == no_node) return no_node
|
if (element == no_node) return no_node
|
||||||
try arraylist.append(&elements, element)
|
try arraylist.append(&elements, element)
|
||||||
}
|
}
|
||||||
@@ -4062,7 +4085,7 @@ type_array_undefined proc(state @mut State, expression, annotation ast.NodeId) a
|
|||||||
@hide:file
|
@hide:file
|
||||||
type_array_value proc(state @mut State, expression, annotation ast.NodeId) ast.NodeId ! Error {
|
type_array_value proc(state @mut State, expression, annotation ast.NodeId) ast.NodeId ! Error {
|
||||||
if (ast.expr_kind(&state.tree, expression) == .literal_undefined)
|
if (ast.expr_kind(&state.tree, expression) == .literal_undefined)
|
||||||
return try type_array_undefined(state, expression, annotation)
|
return try type_storage_undefined(state, expression, annotation)
|
||||||
type :: try canonical_type(state, annotation)
|
type :: try canonical_type(state, annotation)
|
||||||
array :: ast.array_type(&state.tree, type)
|
array :: ast.array_type(&state.tree, type)
|
||||||
if (ast.expr_kind(&state.tree, expression) == .typed_constant) {
|
if (ast.expr_kind(&state.tree, expression) == .typed_constant) {
|
||||||
|
|||||||
+153
@@ -2278,6 +2278,159 @@ CASES = {
|
|||||||
}
|
}
|
||||||
""", b"", b"initialization",
|
""", b"", b"initialization",
|
||||||
),
|
),
|
||||||
|
"type_records_undefined_nested_updates_preserve_partial_copies": (
|
||||||
|
"""
|
||||||
|
Config :: struct {
|
||||||
|
child type = bool
|
||||||
|
detail struct { count usize, bias i16 }
|
||||||
|
rows [2]mut struct { child type, count usize }
|
||||||
|
}
|
||||||
|
config proc() Config {
|
||||||
|
value Config := undefined
|
||||||
|
value.child = i16
|
||||||
|
value.detail.count = 2
|
||||||
|
copied := value
|
||||||
|
value.detail.bias = 99
|
||||||
|
copied.detail.bias = 2
|
||||||
|
for (usize(0)..copied.rows.len) |index| {
|
||||||
|
copied.rows[index].child = copied.child
|
||||||
|
copied.rows[index].count = index + 1
|
||||||
|
}
|
||||||
|
return copied
|
||||||
|
}
|
||||||
|
Storage proc(input Config) type {
|
||||||
|
return struct { values [input.detail.count]input.rows[1].child, bias i16 = input.detail.bias }
|
||||||
|
}
|
||||||
|
main proc() i16 {
|
||||||
|
value Storage(config()) :: { values = [19, 21] }
|
||||||
|
same Storage(config()) :: value
|
||||||
|
return same.values[0] + same.values[1] + same.bias
|
||||||
|
}
|
||||||
|
""", b"42\n", None,
|
||||||
|
),
|
||||||
|
"type_records_undefined_tuples_replacement_and_empty_fields": (
|
||||||
|
"""
|
||||||
|
Pair :: struct { type, struct { child type, values [0]mut type } }
|
||||||
|
pair proc() Pair {
|
||||||
|
value Pair := undefined
|
||||||
|
value.0 = bool
|
||||||
|
value.1.child = i8
|
||||||
|
copied := value
|
||||||
|
value = {i16, { child = i16, values = [] }}
|
||||||
|
copied.0 = value.0
|
||||||
|
copied.1 = value.1
|
||||||
|
return copied
|
||||||
|
}
|
||||||
|
Element proc(input Pair) type { return field!(input, "0") }
|
||||||
|
add proc($schema Pair, value Element(schema)) Element(schema) {
|
||||||
|
if (schema.1.child != i16) return 0
|
||||||
|
return value + 2
|
||||||
|
}
|
||||||
|
main proc() i16 { return add(pair(), 40) }
|
||||||
|
""", b"42\n", None,
|
||||||
|
),
|
||||||
|
"type_records_undefined_reject_type_leaf_read": (
|
||||||
|
"""
|
||||||
|
Bad proc() type {
|
||||||
|
value struct { child type } := undefined
|
||||||
|
return value.child
|
||||||
|
}
|
||||||
|
main proc() Bad() { return 42 }
|
||||||
|
""", b"", b"local value was read before initialization",
|
||||||
|
),
|
||||||
|
"type_records_undefined_reject_value_leaf_read": (
|
||||||
|
"""
|
||||||
|
Bad proc() type {
|
||||||
|
value struct { child type, count usize } := undefined
|
||||||
|
value.child = i16
|
||||||
|
return [value.count]value.child
|
||||||
|
}
|
||||||
|
main proc() Bad() { return 42 }
|
||||||
|
""", b"", b"local value was read before initialization",
|
||||||
|
),
|
||||||
|
"type_records_undefined_defaults_do_not_initialize_fields": (
|
||||||
|
"""
|
||||||
|
config proc() struct { child type = i16 } {
|
||||||
|
value struct { child type = i16 } := undefined
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
main proc() config().child { return 42 }
|
||||||
|
""", b"", b"local value was read before initialization",
|
||||||
|
),
|
||||||
|
"type_records_undefined_copy_retains_uninitialized_leaves": (
|
||||||
|
"""
|
||||||
|
Bad proc() type {
|
||||||
|
value struct { child type, count usize } := undefined
|
||||||
|
value.child = i16
|
||||||
|
copied :: value
|
||||||
|
value.count = 2
|
||||||
|
return [copied.count]copied.child
|
||||||
|
}
|
||||||
|
main proc() Bad() { return 42 }
|
||||||
|
""", b"", b"local value was read before initialization",
|
||||||
|
),
|
||||||
|
"type_records_undefined_reject_partial_argument": (
|
||||||
|
"""
|
||||||
|
Config :: struct { child type, count usize }
|
||||||
|
Selected proc(value Config) type { return value.child }
|
||||||
|
Bad proc() type {
|
||||||
|
value Config := undefined
|
||||||
|
value.child = i16
|
||||||
|
return Selected(value)
|
||||||
|
}
|
||||||
|
main proc() Bad() { return 42 }
|
||||||
|
""", b"", b"local value was read before initialization",
|
||||||
|
),
|
||||||
|
"type_records_undefined_reject_partial_erased_argument": (
|
||||||
|
"""
|
||||||
|
Config :: struct { child type, count usize }
|
||||||
|
selected proc($value Config) i16 { return 42 }
|
||||||
|
Bad proc() type {
|
||||||
|
value Config := undefined
|
||||||
|
value.child = i16
|
||||||
|
return [usize(selected(value))]i16
|
||||||
|
}
|
||||||
|
main proc() Bad() { return [] }
|
||||||
|
""", b"", b"local value was read before initialization",
|
||||||
|
),
|
||||||
|
"type_records_undefined_reject_readonly_field_update": (
|
||||||
|
"""
|
||||||
|
Bad proc() type {
|
||||||
|
value struct { child type } :: undefined
|
||||||
|
value.child = i16
|
||||||
|
return i16
|
||||||
|
}
|
||||||
|
main proc() Bad() { return 42 }
|
||||||
|
""", b"", b"indexed assignment requires a writable array element",
|
||||||
|
),
|
||||||
|
"type_records_undefined_reject_readonly_nested_array_update": (
|
||||||
|
"""
|
||||||
|
Bad proc() type {
|
||||||
|
value struct { children [1]type } := undefined
|
||||||
|
value.children[0] = i16
|
||||||
|
return i16
|
||||||
|
}
|
||||||
|
main proc() Bad() { return 42 }
|
||||||
|
""", b"", b"indexed assignment requires a writable array element",
|
||||||
|
),
|
||||||
|
"type_records_undefined_reject_invalid_schema": (
|
||||||
|
"""
|
||||||
|
Bad proc() type {
|
||||||
|
value struct { child type, count i8 = 128 } := undefined
|
||||||
|
return i16
|
||||||
|
}
|
||||||
|
main proc() Bad() { return 42 }
|
||||||
|
""", b"", b"numeric literal is outside its runtime type's range",
|
||||||
|
),
|
||||||
|
"type_records_undefined_construction_is_bounded": (
|
||||||
|
"""
|
||||||
|
Bad proc() type {
|
||||||
|
value struct { child type, rows [1000000000]mut struct { child type } } := undefined
|
||||||
|
return i16
|
||||||
|
}
|
||||||
|
main proc() Bad() { return 42 }
|
||||||
|
""", b"", b"compile-time execution exceeds its recursion or step limit",
|
||||||
|
),
|
||||||
"type_records_local_schema_defaults_and_owned_copies": (
|
"type_records_local_schema_defaults_and_owned_copies": (
|
||||||
"""
|
"""
|
||||||
Storage proc() type {
|
Storage proc() type {
|
||||||
|
|||||||
Reference in New Issue
Block a user