feat: execute readonly type-valued compile-time slices
This commit is contained in:
@@ -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.
|
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.
|
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.
|
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.
|
||||||
|
|||||||
@@ -50,6 +50,9 @@ optional type-valued storage preserves nested presence, owned metadata payloads,
|
|||||||
checked unwraps and immutable captures across compile-time procedure boundaries.
|
checked unwraps and immutable captures across compile-time procedure boundaries.
|
||||||
fallible type-valued channels own their active payloads, propagate checked errors,
|
fallible type-valued channels own their active payloads, propagate checked errors,
|
||||||
and select generated storage through `try`/`catch` and scoped cleanup.
|
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,
|
`field!` selects record/tuple/enum and sequence members using compile-time names,
|
||||||
including runtime places and type-control updates without runtime reflection.
|
including runtime places and type-control updates without runtime reflection.
|
||||||
`struct_type!` generates ordinary/C-layout records from constant name/type/default
|
`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] 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] 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] 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 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.
|
||||||
|
|||||||
@@ -1296,3 +1296,87 @@ fallible_type_metadata_recompiles_owned_private_nominal_payloads test {
|
|||||||
try testing.expect_equal(i64(42), outcome.value.data.integer)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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) {
|
if (execution.evaluate_values and ast.node_class(&state.tree, id) == .expression) {
|
||||||
kind :: ast.expr_kind(&state.tree, id)
|
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) {
|
if (kind == .block_expr) {
|
||||||
execution.mapping[usize(id)] = try execute_type_value_block(state, execution,
|
execution.mapping[usize(id)] = try execute_type_value_block(state, execution,
|
||||||
ast.block_expr(&state.tree, id), no_node)
|
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 {
|
type_array_projection proc(state @mut State, id ast.NodeId) bool ! Error {
|
||||||
if (ast.node_class(&state.tree, id) != .expression) return true
|
if (ast.node_class(&state.tree, id) != .expression) return true
|
||||||
kind :: ast.expr_kind(&state.tree, id)
|
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) {
|
if (kind == .unary and ast.unary_expr(&state.tree, id).operator == .unwrap) {
|
||||||
operand :: ast.unary_expr(&state.tree, id).operand
|
operand :: ast.unary_expr(&state.tree, id).operand
|
||||||
annotation :: try expression_type(state, operand, 0)
|
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)
|
!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
|
||||||
|
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 := no_node
|
||||||
selected_type := no_node
|
selected_type := no_node
|
||||||
if (record) {
|
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?
|
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), false)
|
||||||
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 (!type_record_storage(state, array.type, 0))
|
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))
|
.invalid_sum_type, ast.node_span(&state.tree, type))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (ast.expr_kind(&state.tree, type) == .slice_type)
|
if (ast.expr_kind(&state.tree, type) == .slice_type) {
|
||||||
return try type_record_check(state, ast.slice_type(&state.tree, type).element)
|
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 (ast.expr_kind(&state.tree, type) == .array_type) {
|
||||||
if (!try type_step(state, type)) return false
|
if (!try type_step(state, type)) return false
|
||||||
descriptor :: ast.array_type(&state.tree, type)
|
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
|
@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)
|
usize_type :: try builtin(state, expression, .usize)
|
||||||
actual :: try expression_type(state, expression, 0)
|
actual :: try expression_type(state, expression, 0)
|
||||||
if (!flexible(state, expression) and (actual == no_node or !try same_type(state, actual, usize_type))) {
|
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)
|
value :: try evaluate_ir(state, expression, usize_type)
|
||||||
if (value.kind != .integer) return no_index
|
if (value.kind != .integer) return no_index
|
||||||
if (value.magnitude >= u64(count)) {
|
if (value.magnitude > u64(count) or (!boundary and value.magnitude == u64(count))) {
|
||||||
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
code diagnostics.Code :: if (boundary) .invalid_slice_bounds else .index_out_of_bounds
|
||||||
.index_out_of_bounds, ast.node_span(&state.tree, expression))
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, code, ast.node_span(&state.tree, expression))
|
||||||
return no_index
|
return no_index
|
||||||
}
|
}
|
||||||
return usize(value.magnitude)
|
return usize(value.magnitude)
|
||||||
@@ -4428,6 +4479,100 @@ type_array_value proc(state @mut State, expression, annotation ast.NodeId) ast.N
|
|||||||
return result
|
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.
|
# rebuild only the field/index path so previous snapshots retain their owned values.
|
||||||
@hide:file
|
@hide:file
|
||||||
type_array_update proc(
|
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)
|
expression :: try type_snapshot(state, execution, ast.index_expr(&state.tree, first).index)
|
||||||
if (!try expand_type_matches(state, expression)) return no_node
|
if (!try expand_type_matches(state, expression)) return no_node
|
||||||
count = ast.array_element_count(&state.tree, array.value)
|
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
|
if (index == no_index) return no_node
|
||||||
element_type = descriptor.element
|
element_type = descriptor.element
|
||||||
previous = ast.array_element(&state.tree, array.value, index)
|
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)
|
declaration :: ast.statement_decl(&state.tree, resolved.data.declaration)
|
||||||
if (declaration.mutability != .constant or !try type_step(state, expression)) return no_node
|
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 (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,
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
||||||
.type_mismatch, ast.node_span(&state.tree, expression))
|
.type_mismatch, ast.node_span(&state.tree, expression))
|
||||||
return no_node
|
return no_node
|
||||||
@@ -4708,18 +4855,8 @@ type_execution_value proc(state @mut State, input, annotation ast.NodeId) ast.No
|
|||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
if (
|
if (try metadata_sequence(state, annotation))
|
||||||
!is_type(state, annotation) and type_record_storage(state, annotation, 0)
|
return try type_slice_value(state, expression, annotation)
|
||||||
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 (!is_type(state, annotation) and type_record_storage(state, annotation, 0)) {
|
if (!is_type(state, annotation) and type_record_storage(state, annotation, 0)) {
|
||||||
type :: try canonical_type(state, annotation)
|
type :: try canonical_type(state, annotation)
|
||||||
if (type != no_node and ast.expr_kind(&state.tree, type) == .optional_type)
|
if (type != no_node and ast.expr_kind(&state.tree, type) == .optional_type)
|
||||||
|
|||||||
+363
@@ -2278,6 +2278,369 @@ CASES = {
|
|||||||
}
|
}
|
||||||
""", b"", b"initialization",
|
""", 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": (
|
"optional_types_procedure_boundaries_copies_and_static_captures": (
|
||||||
"""
|
"""
|
||||||
Token :: distinct i16
|
Token :: distinct i16
|
||||||
|
|||||||
Reference in New Issue
Block a user