feat: execute readonly type-valued compile-time slices

This commit is contained in:
2026-10-30 21:30:36 +01:00
parent 15cbebe40f
commit 4a1b30d9d5
5 changed files with 610 additions and 20 deletions
+2
View File
@@ -130,6 +130,8 @@ ordinary procedures also accept `undefined` as the initializer of a mutable loca
procedures returning fixed arrays or records/tuples containing `type`, such as `types proc() [2]type { return [i16, bool] }` or `config proc() struct { child type, count usize } { return { child = i16, count = 2 } }`, execute entirely at compile time. every argument is explicitly supplied, constant, cached and erased; nested/aliased results, empty shapes and dependent `$N` lengths retain exact shape and mutability. compile-time producers accept ordinary type-array/type-record inputs, and runtime generic procedures accept erased `$` parameters with these shapes. direct projections generate signatures, constructors, storage and defaults; explicit `$()` calls inside producers retain owned values. cache identity uses canonical type leaves without merging distinct nominal types, and copies cannot mutate cached inputs/results. imported diagnostics, shared type-control execution limits and repeatable compilation work through interpreter/native execution. ordinary parameters remain excluded from dependent signatures; use `$N` for lengths. runtime type-array/type-record storage and reflection remain open.
readonly type-valued slices (`[]type` and slices of metadata records/arrays) also cross compile-time procedure boundaries as owned snapshots. contextual array literals, including `[]`, construct them; explicit views of immutable readonly arrays and existing metadata slices support checked `usize` bounds, empty end views, indexing and `.len`. nested slices, ordinary/inline by-value iteration, erased `$` inputs and optional/fallible channels retain exact nominal leaves and content-based specialization identity. snapshots generate runtime types/defaults, but metadata itself has no runtime storage. mutable slice types, sentinels and views of mutable array places remain rejected rather than silently losing borrowing semantics; copy an array into an immutable binding before taking an owned view. construction shares the type-control step bound, and uninitialized leaves cannot cross procedure boundaries.
optional `if`/`while` captures also execute in type-producing bodies: `if count |length| { return [length]i16 }` generates storage from an optional `usize`. ordinary checked IR validates each executed guard; present payloads become immutable owned constants with exact scalar/aggregate types and strings, and absent optionals skip the body. while guards observe updated locals on each iteration; captures stay body-local across break/continue and early return. generated lengths/defaults, imported producers, cache identity and repeated compilation preserve these values. null data/function pointers materialize as null; callbacks retain source declaration identity, while nonnull data pointers and unsupported slice types retain the constant-materialization escape boundary.
type-producing bodies also execute scalar/enum `match` statements and type-valued match expressions in initializers, assignments and returns, such as `return match wide { true: i16, false: i8 }`. selected expression arms may themselves be matches. an ordinary checked IR match returns the selected arm index, preserving subject/pattern typing, range boundaries, overlap, unreachable-arm and exhaustiveness diagnostics without checking unexecuted bodies. statement arms retain scoped locals, assignments, early returns and enclosing-loop break/continue; expression selection shares the type-control step bound.
+4
View File
@@ -50,6 +50,9 @@ optional type-valued storage preserves nested presence, owned metadata payloads,
checked unwraps and immutable captures across compile-time procedure boundaries.
fallible type-valued channels own their active payloads, propagate checked errors,
and select generated storage through `try`/`catch` and scoped cleanup.
readonly type-valued slices own nested metadata leaves across compile-time calls,
checked immutable-array/slice views, by-value iteration and erased arguments;
mutable metadata borrowing remains open.
`field!` selects record/tuple/enum and sequence members using compile-time names,
including runtime places and type-control updates without runtime reflection.
`struct_type!` generates ordinary/C-layout records from constant name/type/default
@@ -161,6 +164,7 @@ test discovery and FFI remain outside these completed slices.
- [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.
- [x] optional type-storage checkpoint: `?type` and optional type-valued records/arrays use owned compile-time snapshots, including nested `some!(null)`, contextual payload construction, mutable whole-value/field/index replacement, immutable if/while/conditional captures, null comparisons and checked unwraps. constant-input procedures and erased generic arguments preserve specialization-cache identity and nominal leaves while generating runtime storage through interpreter/native execution. undefined presence, incomplete payloads, non-type values, capture mutation and runtime dependencies retain diagnostics. imported private nominal types, session/source disposal and repeat compilation preserve ownership. fallible type-valued channels are covered below; general type-valued execution and broader constant materialization remain open.
- [x] fallible type-storage checkpoint: explicit success/error channels containing types use owned compile-time snapshots, including nested optional/record/array payloads, whole-value updates, independent copies, constant-input procedures and erased generic inputs. `try` validates error subsets and propagates through enclosing producers; `catch` binds immutable exact error payloads and selects generated types or runtime handlers. composed metadata errors preserve named variants, nominal leaves and cache identity. returned snapshots precede scoped LIFO `defer`/`errdefer` cleanup; fallible `void` supports bare returns and successful fallthrough. incomplete/uninitialized channels, invalid payloads/compositions, capture mutation and runtime dependencies retain diagnostics. imported private types, source/session disposal and repeat compilation preserve ownership through interpreter/native execution. runtime metadata storage, general type-valued execution and broader materialization remain open.
- [x] readonly type-slice checkpoint: `[]type` and slices of type-valued records/arrays use owned compile-time snapshots. contextual literals, immutable readonly array views, nested slice views, checked `usize` bounds/indexing, `.len`, empty shapes and ordinary/inline by-value iteration preserve exact nominal leaves. constant-input procedure parameters/results, erased generic inputs, optional/fallible nesting and normalized cache keys generate runtime storage/defaults through interpreter/native execution. mutable annotations/backing, metadata sentinels, invalid ranges/indices, readonly writes, incomplete leaves and runtime dependencies retain diagnostics. imported private types, source/session disposal, shared construction bounds and repeat compilation preserve ownership. mutable metadata borrowing, runtime metadata storage, general type-valued execution and broader constant 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 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.
+84
View File
@@ -1296,3 +1296,87 @@ fallible_type_metadata_recompiles_owned_private_nominal_payloads test {
try testing.expect_equal(i64(42), outcome.value.data.integer)
}
}
readonly_type_slices_recompile_owned_private_nominal_payloads test {
diagnostic_store := diagnostics.init(mem.c_allocator)
defer diagnostics.deinit(&diagnostic_store)
modules [2]mut ir.Module := undefined
{
session := init(mem.c_allocator)
defer deinit(&session)
root :: try add_package(&session)
dependency :: try add_package(&session)
bytes := try owned_text(
`lib :: import "../library"
`Child :: struct { value bool }
`Storage proc() type {
` saved :: $(lib.make())
` if (lib.Box(saved) != lib.Box(lib.make())) return bool
` if (lib.Box(saved) != lib.Box(lib.literal())) return bool
` Selected :: lib.types()[0]
` if (Selected == Child or saved[0].child != Selected) return bool
` return lib.Box(saved)
`}
`main proc() i16 {
` value lib.types()[0] :: { value = 40 }
` if (lib.read(lib.make(), value) != 42) return -1
` box Storage() :: { values = [{ value = 19 }, { value = 21 }] }
` same lib.Box(lib.literal()) :: box
` return same.values[0].value + same.values[1].value + same.bias
`}
,
)
defer arraylist.deinit(&bytes)
root_source :: try add_source(&session, root, "/readonly_type_slices/app/main.hon", &bytes, &diagnostic_store)
imported := try owned_text(
`@hide:file
`Child :: struct { value i16 }
`@hide:file
`Config :: struct { child type, count usize }
`types proc() []type { return [Child] }
`literal proc() []Config {
` return [{ child = Child, count = 2 }, { child = Child, count = 2 }]
`}
`make proc() []Config {
` rows [3]Config := [
` { child = Child, count = 99 },
` { child = Child, count = 2 },
` { child = Child, count = 2 },
` ]
` frozen :: rows
` saved :: frozen[1..][..2]
` rows = [
` { child = bool, count = 7 },
` { child = bool, count = 7 },
` { child = bool, count = 7 },
` ]
` return saved
`}
`Box proc(input []Config) type {
` return struct { values [input[0].count]input[0].child, bias i16 = i16(input[1].count) }
`}
`read proc($input []Config, value input[0].child) i16 {
` return value.value + i16(input[1].count)
`}
,
)
defer arraylist.deinit(&imported)
_ = try add_source(&session, dependency, "/readonly_type_slices/library/types.hon", &imported, &diagnostic_store)
try arraylist.append(&session.imports, Import{
node = declaration(&session, root_source, 0).node,
target = dependency,
})
modules[0] = try compile(&session, root, &diagnostic_store)
errdefer ir.deinit(&modules[0])
modules[1] = try compile(&session, root, &diagnostic_store)
}
defer ir.deinit(&modules[0])
defer ir.deinit(&modules[1])
try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
for modules |module| {
outcome := try interpreter.run(mem.c_allocator, &module, &diagnostic_store)
defer interpreter.deinit(&outcome)
try testing.expect(outcome.trap == null)
try testing.expect_equal(i64(42), outcome.value.data.integer)
}
}
+157 -20
View File
@@ -3122,6 +3122,35 @@ prepare_type_snapshot proc(state @mut State, execution @mut TypeExecution, id as
}
if (execution.evaluate_values and ast.node_class(&state.tree, id) == .expression) {
kind :: ast.expr_kind(&state.tree, id)
if (kind == .slice) {
base :: ast.slice_expr(&state.tree, id).base
root := base
while (true) {
root_kind :: ast.expr_kind(&state.tree, root)
if (root_kind == .selection) root = ast.selection_expr(&state.tree, root).base
else if (root_kind == .index) root = ast.index_expr(&state.tree, root).base
else if (root_kind == .unary and ast.unary_expr(&state.tree, root).operator == .unwrap)
root = ast.unary_expr(&state.tree, root).operand
else if (root_kind == .builtin_call
and mem.eql(ast.literal_text(&state.tree, ast.builtin_call(&state.tree, root).name), "field")
and ast.call_arg_count(&state.tree, root) == 2)
root = ast.call_arg(&state.tree, root, 0)
else break
}
place :: binding(state, root)
if (place.kind == .declaration
and ast.node_class(&state.tree, place.data.declaration) == .statement
and ast.statement_decl(&state.tree, place.data.declaration).mutability == .variable) {
annotation :: try expression_type(state, base, 0)
if (type_record_storage(state, annotation, 0)
and ast.expr_kind(&state.tree, annotation) == .array_type) {
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
.comptime_value_escape, ast.node_span(&state.tree, id))
execution.pending_flow = .invalid
return
}
}
}
if (kind == .block_expr) {
execution.mapping[usize(id)] = try execute_type_value_block(state, execution,
ast.block_expr(&state.tree, id), no_node)
@@ -3874,6 +3903,15 @@ expand_type_matches proc(state @mut State, id ast.NodeId) bool ! Error {
type_array_projection proc(state @mut State, id ast.NodeId) bool ! Error {
if (ast.node_class(&state.tree, id) != .expression) return true
kind :: ast.expr_kind(&state.tree, id)
if (kind == .slice) {
annotation :: try expression_type(state, id, 0)
if (!try metadata_sequence(state, annotation)) return true
value :: try type_slice_value(state, id, annotation)
if (value == no_node) return false
try replace(state, id, value)
try refresh(state)
return true
}
if (kind == .unary and ast.unary_expr(&state.tree, id).operator == .unwrap) {
operand :: ast.unary_expr(&state.tree, id).operand
annotation :: try expression_type(state, operand, 0)
@@ -3912,6 +3950,12 @@ type_array_projection proc(state @mut State, id ast.NodeId) bool ! Error {
!record and !try execution_array_storage(state, array.type)
and !try metadata_sequence(state, array.type)
) return true
if (try metadata_sequence(state, array.type)
and ast.expr_kind(&state.tree, array.value) == .literal_undefined) {
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
.read_before_initialization, ast.node_span(&state.tree, id))
return false
}
selected := no_node
selected_type := no_node
if (record) {
@@ -3936,7 +3980,7 @@ type_array_projection proc(state @mut State, id ast.NodeId) bool ! Error {
selected_type = ast.field(&state.tree, ast.record_field(&state.tree, type, slot)).type?
} else if (kind == .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), false)
if (index == no_index) return false
selected = ast.array_element(&state.tree, array.value, index)
if (!type_record_storage(state, array.type, 0))
@@ -4114,8 +4158,15 @@ type_record_check proc(state @mut State, annotation ast.NodeId) bool ! Error {
.invalid_sum_type, ast.node_span(&state.tree, type))
return false
}
if (ast.expr_kind(&state.tree, type) == .slice_type)
return try type_record_check(state, ast.slice_type(&state.tree, type).element)
if (ast.expr_kind(&state.tree, type) == .slice_type) {
descriptor :: ast.slice_type(&state.tree, type)
if (descriptor.mutable or descriptor.sentinel != null) {
code diagnostics.Code :: if (descriptor.mutable) .comptime_value_escape else .invalid_operand
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, code, ast.node_span(&state.tree, type))
return false
}
return try type_record_check(state, descriptor.element)
}
if (ast.expr_kind(&state.tree, type) == .array_type) {
if (!try type_step(state, type)) return false
descriptor :: ast.array_type(&state.tree, type)
@@ -4231,7 +4282,7 @@ type_record_value proc(state @mut State, expression, annotation ast.NodeId) ast.
}
@hide:file
type_array_index proc(state @mut State, expression ast.NodeId, count usize) usize ! Error {
type_array_index proc(state @mut State, expression ast.NodeId, count usize, boundary bool) usize ! Error {
usize_type :: try builtin(state, expression, .usize)
actual :: try expression_type(state, expression, 0)
if (!flexible(state, expression) and (actual == no_node or !try same_type(state, actual, usize_type))) {
@@ -4241,9 +4292,9 @@ type_array_index proc(state @mut State, expression ast.NodeId, count usize) usiz
}
value :: try evaluate_ir(state, expression, usize_type)
if (value.kind != .integer) return no_index
if (value.magnitude >= u64(count)) {
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
.index_out_of_bounds, ast.node_span(&state.tree, expression))
if (value.magnitude > u64(count) or (!boundary and value.magnitude == u64(count))) {
code diagnostics.Code :: if (boundary) .invalid_slice_bounds else .index_out_of_bounds
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, code, ast.node_span(&state.tree, expression))
return no_index
}
return usize(value.magnitude)
@@ -4428,6 +4479,100 @@ type_array_value proc(state @mut State, expression, annotation ast.NodeId) ast.N
return result
}
# readonly metadata views own their leaves. mutable backing needs borrowing semantics,
# not a snapshot that silently stops observing later element writes.
@hide:file
type_slice_value proc(state @mut State, expression, annotation ast.NodeId) ast.NodeId ! Error {
if (state.type_execution_depth == 0) state.type_execution_remaining = 4096
state.type_execution_depth += 1
defer state.type_execution_depth -= 1
type :: try canonical_type(state, annotation)
if (!try type_record_check(state, type)) return no_node
descriptor :: ast.slice_type(&state.tree, type)
span :: ast.node_span(&state.tree, expression)
kind :: ast.expr_kind(&state.tree, expression)
if (kind == .literal_undefined) {
result :: try ast.add_typed_constant(state.builder, span, type, expression)
try refresh(state)
return result
}
if (kind == .typed_constant) {
actual :: ast.typed_constant(&state.tree, expression)
if (try same_type(state, type, actual.type)) return expression
}
source := expression
start := usize(0)
end := no_index
if (kind == .slice) {
view :: ast.slice_expr(&state.tree, expression)
if (view.sentinel != null) {
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, .invalid_operand, span)
return no_node
}
source = view.base
}
if (ast.expr_kind(&state.tree, source) == .array_literal) {
for (0..ast.array_element_count(&state.tree, source)) |_| {
if (!try type_step(state, expression)) return no_node
}
length :: try ast.add_integer(state.builder, span, u64(ast.array_element_count(&state.tree, source)))
array :: try ast.add_array_type(state.builder, span, length, descriptor.element, false)
try refresh(state)
source = try type_array_value(state, source, array)
} else {
actual :: try expression_type(state, source, 0)
if (actual == no_node) return no_node
actual_kind :: ast.expr_kind(&state.tree, actual)
if ((actual_kind != .array_type and actual_kind != .slice_type)
or (actual_kind == .array_type and kind != .slice)) {
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, .type_mismatch, span)
return no_node
}
mutable :: if (actual_kind == .array_type) ast.array_type(&state.tree, actual).mutable
else ast.slice_type(&state.tree, actual).mutable
element :: if (actual_kind == .array_type) ast.array_type(&state.tree, actual).element
else ast.slice_type(&state.tree, actual).element
if (mutable) {
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, .comptime_value_escape, span)
return no_node
}
if (!try same_type(state, descriptor.element, element)) {
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, .type_mismatch, span)
return no_node
}
source = try type_execution_value(state, source, actual)
}
if (source == no_node) return no_node
values :: ast.typed_constant(&state.tree, source).value
if (ast.expr_kind(&state.tree, values) == .literal_undefined) {
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, .read_before_initialization, span)
return no_node
}
count :: ast.array_element_count(&state.tree, values)
end = count
if (kind == .slice) {
view :: ast.slice_expr(&state.tree, expression)
if (view.start) |bound| start = try type_array_index(state, bound, count, true)
if (start == no_index) return no_node
if (view.end) |bound| end = try type_array_index(state, bound, count, true)
if (end == no_index) return no_node
if (start > end) {
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, .invalid_slice_bounds, span)
return no_node
}
}
elements := arraylist.init(ast.NodeId, state.allocator)
defer arraylist.deinit(&elements)
for (start..end) |i| {
if (!try type_step(state, expression)) return no_node
try arraylist.append(&elements, ast.array_element(&state.tree, values, i))
}
literal :: try ast.add_array_literal(state.builder, span, elements.items)
result :: try ast.add_typed_constant(state.builder, span, type, literal)
try refresh(state)
return result
}
# rebuild only the field/index path so previous snapshots retain their owned values.
@hide:file
type_array_update proc(
@@ -4483,7 +4628,7 @@ type_array_update proc(
expression :: try type_snapshot(state, execution, ast.index_expr(&state.tree, first).index)
if (!try expand_type_matches(state, expression)) return no_node
count = ast.array_element_count(&state.tree, array.value)
index = try type_array_index(state, expression, count)
index = try type_array_index(state, expression, count, false)
if (index == no_index) return no_node
element_type = descriptor.element
previous = ast.array_element(&state.tree, array.value, index)
@@ -4691,6 +4836,8 @@ type_execution_value proc(state @mut State, input, annotation ast.NodeId) ast.No
declaration :: ast.statement_decl(&state.tree, resolved.data.declaration)
if (declaration.mutability != .constant or !try type_step(state, expression)) return no_node
if (declaration.type != null and !try same_type(state, annotation, declaration.type?)) {
if (try metadata_sequence(state, annotation))
return try type_slice_value(state, expression, annotation)
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
.type_mismatch, ast.node_span(&state.tree, expression))
return no_node
@@ -4708,18 +4855,8 @@ type_execution_value proc(state @mut State, input, annotation ast.NodeId) ast.No
}
return value
}
if (
!is_type(state, annotation) and type_record_storage(state, annotation, 0)
and ast.expr_kind(&state.tree, try canonical_type(state, annotation)) == .slice_type
) {
if (ast.expr_kind(&state.tree, expression) == .typed_constant) {
actual :: ast.typed_constant(&state.tree, expression)
if (try same_type(state, annotation, actual.type)) return expression
}
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
.type_mismatch, ast.node_span(&state.tree, expression))
return no_node
}
if (try metadata_sequence(state, annotation))
return try type_slice_value(state, expression, annotation)
if (!is_type(state, annotation) and type_record_storage(state, annotation, 0)) {
type :: try canonical_type(state, annotation)
if (type != no_node and ast.expr_kind(&state.tree, type) == .optional_type)
+363
View File
@@ -2278,6 +2278,369 @@ CASES = {
}
""", b"", b"initialization",
),
"metadata_slices_direct_literal_views_start_bounded_evaluation": (
"""
Selected proc($items []type) type { return items[0] }
main proc() [2]usize {
value Selected(([i16, bool])[..1]) :: 42
return [usize(value), $(([i16, bool])[1..].len)]
}
""", b"[42, 1]\n", None,
),
"metadata_slices_contextual_literals_inputs_results_and_erased_inputs": (
"""
Id :: distinct i16
types proc() []type { return [Id, i16] }
pass proc(values []type) []type { return values }
window proc(values []type, start usize, end usize) []type { return values[start..end] }
Selected proc(values []type) type { return values[0] }
exact proc($values []type, value Selected(values)) Selected(values) { return value }
Storage proc() type {
empty []type :: []
if (pass(empty).len != 0 or types().len != 2) return bool
return Selected(window(pass([bool, Id]), 1, 2))
}
main proc() i16 {
value Storage() :: 40
return i16(exact(types(), value)) + 2
}
""", b"42\n", None,
),
"metadata_slices_cache_keys_preserve_nominal_leaves_order_and_length": (
"""
Id :: distinct i16
Other :: distinct i16
Token proc($values []type) type { return struct {} }
types proc() []type {
backing [4]type :: [bool, Id, i16, u8]
return backing[1..3]
}
Storage proc() type {
if (Token(types()) != Token([Id, i16])) return bool
if (Token(types()) == Token([Other, i16])) return bool
if (Token(types()) == Token([i16, Id])) return bool
if (Token(types()) == Token([Id])) return bool
if (Token([]) != Token([])) return bool
return types()[0]
}
main proc() i16 {
value Storage() :: 42
return i16(value)
}
""", b"42\n", None,
),
"metadata_record_slices_nested_leaves_and_exact_cache_identity": (
"""
Id :: distinct i16
Other :: distinct i16
Info :: struct { child type, count usize, name string }
Row :: struct { types []type, fields []Info }
Token proc($values []Info) type { return struct {} }
fields proc() []Info { return [{ child = Id, count = 2, name = "value" }] }
rows proc() []Row {
return [{ types = [Id, i16], fields = fields() }]
}
Storage proc() type {
empty []Info :: []
if (empty.len != 0 or rows()[0].types[1] != i16) return bool
source :: rows()[0].fields
copied := source[0]
copied.child = bool
copied.count = 3
if (source[0].child != Id or source[0].count != 2) return bool
if (Token(source) != Token(fields())) return bool
if (Token(source) == Token([{ child = Other, count = 2, name = "value" }])) return bool
if (Token(source) == Token([{ child = Id, count = 3, name = "value" }])) return bool
if (Token(source) == Token([{ child = Id, count = 2, name = "other" }])) return bool
return [source[0].count]source[0].child
}
main proc() i16 {
values Storage() :: [40, 2]
return i16(values[0]) + i16(values[1])
}
""", b"42\n", None,
),
"metadata_slices_nested_slices_by_value_and_inline_iteration": (
"""
Info :: struct { child type, count usize }
rows proc() [][]type { return [[i8, i16], [], [bool]] }
fields proc() []Info { return [{ child = i16, count = 2 }] }
Storage proc() type {
total usize := 0
for rows() |row, index| {
total += row.len + index
for row |T| {
if (T == i16) total += 1
}
}
inline for rows() |row| {
inline for row |T| {
if (T == bool) total += 1
}
}
for fields() |field| {
copied := field
copied.child = bool
copied.count = 99
}
inline for fields() |field| {
if (field.child != i16 or field.count != 2) return bool
}
if (total != 8) return bool
return [fields()[0].count]fields()[0].child
}
main proc() Storage() { return [40, 2] }
""", b"[40, 2]\n", None,
),
"metadata_slices_subviews_empty_boundaries_and_readonly_backing_replacement": (
"""
types proc() []type {
backing [3]type := [bool, i16, u8]
frozen :: backing
saved :: frozen[1..2]
backing = [u8, bool, bool]
if (saved[0] != i16 or backing[1] != bool) return []
return saved
}
Storage proc() type {
source :: types()
full :: source[..]
nested :: full[0..full.len][..1]
if (nested.len != 1 or nested[0] != i16) return bool
if (source[0..0].len != 0 or source[1..1].len != 0) return bool
empty []type :: []
if (empty[..].len != 0 or empty[0..0].len != 0) return bool
return nested[0]
}
main proc() Storage() { return 42 }
""", b"42\n", None,
),
"metadata_slices_optional_fallible_parameters_results_and_captures": (
"""
Failure :: struct { types []type }
Info :: struct { child type, count usize }
choose proc(present bool, failed bool) ?[]Info ! Failure {
if (failed) return Failure { types = [i16] }
if (present) return some!([{ child = i16, count = 2 }])
return null
}
pass proc(value ?[]Info ! Failure) ?[]Info ! Failure { return value }
Token proc($value ?[]Info ! Failure) type { return struct {} }
Storage proc() type {
original :: pass(choose(true, false))
if (Token(original) != Token(choose(true, false))) return bool
if (Token(original) == Token(choose(false, false))) return bool
if (Token(original) == Token(choose(false, true))) return bool
missing :: pass(choose(false, false)) catch null
if (missing != null) return bool
pass(choose(false, true)) catch |err| {
if (err.types[0] != i16) return bool
yield null
}
selected :: original catch null
if selected |fields| {
return [fields[0].count]fields[0].child
}
return bool
}
main proc() Storage() { return [40, 2] }
""", b"[40, 2]\n", None,
),
"metadata_slices_reject_mutable_slice_annotation": (
"""
types proc() []mut type { return [i16] }
main proc() types()[0] { return 42 }
""", b"", b"compile-time result cannot escape evaluation as a source value",
),
"metadata_slices_reject_mutable_array_backing": (
"""
types proc() []type {
backing [1]mut type := [i16]
return backing[..]
}
main proc() types()[0] { return 42 }
""", b"", b"compile-time result cannot escape evaluation as a source value",
),
"metadata_slices_reject_mutable_binding_with_readonly_array_elements": (
"""
types proc() []type {
backing [1]type := [i16]
return backing[..]
}
main proc() types()[0] { return 42 }
""", b"", b"compile-time result cannot escape evaluation as a source value",
),
"metadata_record_slices_reject_mutable_array_backing": (
"""
Info :: struct { child type, count usize }
fields proc() []Info {
backing [1]mut Info := [{ child = i16, count = 2 }]
return backing[..]
}
main proc() fields()[0].child { return 42 }
""", b"", b"compile-time result cannot escape evaluation as a source value",
),
"metadata_slices_reject_sentinel_annotation": (
"""
types proc() [;0]type { return [i16] }
main proc() types()[0] { return 42 }
""", b"", b"operator requires compatible operand types",
),
"metadata_slices_reject_sentinel_subview": (
"""
types proc() []type {
backing [2]type :: [i16, bool]
return backing[..1;0]
}
main proc() types()[0] { return 42 }
""", b"", b"operator requires compatible operand types",
),
"metadata_slices_reject_reversed_bounds": (
"""
types proc() []type {
backing [2]type :: [i16, bool]
return backing[2..1]
}
main proc() types()[0] { return 42 }
""", b"", b"slice bounds are invalid",
),
"metadata_slices_reject_excessive_array_bounds": (
"""
types proc() []type {
backing [1]type :: [i16]
return backing[..2]
}
main proc() types()[0] { return 42 }
""", b"", b"slice bounds are invalid",
),
"metadata_slices_reject_excessive_subview_bounds": (
"""
types proc() []type { return [i16, bool] }
bad proc() []type { return types()[1..2][..2] }
main proc() bad()[0] { return 42 }
""", b"", b"slice bounds are invalid",
),
"metadata_slices_reject_start_beyond_empty_boundary": (
"""
types proc() []type { return [] }
bad proc() []type { return types()[1..1] }
main proc() bad()[0] { return 42 }
""", b"", b"slice bounds are invalid",
),
"metadata_slices_reject_non_usize_start": (
"""
types proc() []type { return [i16] }
bad proc() []type { return types()[u8(0)..] }
main proc() bad()[0] { return 42 }
""", b"", b"array index must have type usize",
),
"metadata_slices_reject_non_usize_end": (
"""
types proc() []type { return [i16] }
bad proc() []type { return types()[..i16(1)] }
main proc() bad()[0] { return 42 }
""", b"", b"array index must have type usize",
),
"metadata_slices_reject_non_usize_element_index": (
"""
types proc() []type { return [i16] }
main proc() types()[u8(0)] { return 42 }
""", b"", b"array index must have type usize",
),
"metadata_slices_reject_element_index_at_length": (
"""
types proc() []type { return [i16] }
main proc() types()[types().len] { return 42 }
""", b"", b"index out of bounds",
),
"metadata_slices_reject_empty_element_index": (
"""
types proc() []type { return [] }
main proc() types()[0] { return 42 }
""", b"", b"index out of bounds",
),
"metadata_slices_reject_non_type_literal_leaf": (
"""
types proc() []type { return [42] }
main proc() types()[0] { return 42 }
""", b"", b"incompatible value type",
),
"metadata_record_slices_reject_incompatible_literal_field": (
"""
Info :: struct { child type, count usize }
fields proc() []Info { return [{ child = i16, count = false }] }
main proc() fields()[0].child { return 42 }
""", b"", b"incompatible value type",
),
"metadata_slices_reject_readonly_element_assignment": (
"""
types proc() []type {
values []type := [i16]
values[0] = bool
return values
}
main proc() types()[0] { return 42 }
""", b"", b"writable",
),
"metadata_record_slices_reject_readonly_field_assignment": (
"""
Info :: struct { child type, count usize }
fields proc() []Info {
values []Info := [{ child = i16, count = 2 }]
values[0].count = 3
return values
}
main proc() fields()[0].child { return 42 }
""", b"", b"writable",
),
"metadata_slices_reject_uninitialized_type_leaf": (
"""
types proc() []type {
backing [1]type := undefined
frozen :: backing
return frozen[..]
}
main proc() types()[0] { return 42 }
""", b"", b"read before initialization",
),
"metadata_record_slices_reject_uninitialized_value_leaf": (
"""
Info :: struct { child type, count usize }
fields proc() []Info {
partial Info := undefined
partial.child = i16
return [partial]
}
main proc() fields()[0].child { return 42 }
""", b"", b"read before initialization",
),
"metadata_slices_reject_runtime_producer_dependencies": (
"""
types proc(wide bool) []type {
if (wide) return [i16]
return [i8]
}
read proc(wide bool) i16 {
value types(wide)[0] :: 42
return i16(value)
}
main proc() i16 { return read(true) }
""", b"", b"compile-time value",
),
"metadata_slices_inline_captures_specialize_runtime_bodies": (
"""
types proc() []type { return [i16, u8] }
add proc(value i16) i16 { return value + 1 }
main proc() i16 {
total i16 := 0
inline for types() |T, index| {
value T :: 19
total += add(i16(value)) + i16(index + 1)
}
return total - 1
}
""", b"42\n", None,
),
"optional_types_procedure_boundaries_copies_and_static_captures": (
"""
Token :: distinct i16