feat: materialize readonly typed compile-time slices
This commit is contained in:
@@ -116,7 +116,7 @@ initialized records/tuples containing `type` fields live inside compile-time pro
|
|||||||
|
|
||||||
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 broader storage-bearing constant materialization beyond readonly byte slices 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. partial type-valued record construction and constant materialization of mutable slices or nonnull data pointers remain 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.
|
||||||
|
|
||||||
@@ -136,7 +136,9 @@ ordinary procedures may compute integer, boolean, float and string compile-time
|
|||||||
|
|
||||||
computed callbacks materialize as references to their original source declarations, never as temporary interpreter function IDs. `choose proc() @proc(value i16) i16 { return increment }` can supply `$(choose())`, compile-time arguments and generated defaults. optional callbacks preserve absence and nested presence; arrays, records, tagged payloads and both fallible channels retain exact callback signatures and specialization-cache identity. imported private targets remain bound to their originating declarations, including across repeated compilation and native emission.
|
computed callbacks materialize as references to their original source declarations, never as temporary interpreter function IDs. `choose proc() @proc(value i16) i16 { return increment }` can supply `$(choose())`, compile-time arguments and generated defaults. optional callbacks preserve absence and nested presence; arrays, records, tagged payloads and both fallible channels retain exact callback signatures and specialization-cache identity. imported private targets remain bound to their originating declarations, including across repeated compilation and native emission.
|
||||||
|
|
||||||
readonly byte-slice results (`[]u8` and `[;sentinel]u8` for any byte sentinel) snapshot their bytes into owned literals before evaluation storage is released. ordinary compile-time helpers can return string subviews, views of locally filled arrays or immutable global arrays, including nested records/arrays, tagged payloads, optionals and both fallible channels. explicit `$()` expressions, generic arguments and generated defaults preserve embedded NULs, arbitrary byte values, exact declared slice types and content-based specialization-cache identity. nonzero-sentinel snapshots retain an owned trailing sentinel outside the logical length; empty views also retain it. snapshots survive evaluation/session disposal and repeated compilation; uninitialized source bytes diagnose read-before-initialization, and sentinel bytes are rechecked after evaluation. mutable slices and nonbyte or nominal-byte slices remain unsupported.
|
readonly byte-slice results (`[]u8` and `[;sentinel]u8` for any byte sentinel) snapshot their bytes into owned literals before evaluation storage is released. ordinary compile-time helpers can return string subviews, views of locally filled arrays or immutable global arrays, including nested records/arrays, tagged payloads, optionals and both fallible channels. explicit `$()` expressions, generic arguments and generated defaults preserve embedded NULs, arbitrary byte values, exact declared slice types and content-based specialization-cache identity. nonzero-sentinel snapshots retain an owned trailing sentinel outside the logical length; empty views also retain it. snapshots survive evaluation/session disposal and repeated compilation; uninitialized source bytes diagnose read-before-initialization, and sentinel bytes are rechecked after evaluation.
|
||||||
|
|
||||||
|
readonly slices of other materializable element types also own their snapshots, including distinct bytes, wider integers, arrays, records, callbacks and nested readonly slices. generated backing has module lifetime, so returning a computed slice from a runtime helper does not borrow its stack. exact element types, logical lengths, zero-sized elements, full-width signed/unsigned sentinels, nested optional/tagged/fallible channels and content-based specialization identity survive `$()`, compile-time arguments, generated defaults and repeated compilation. snapshots recheck source initialization, address ranges and trailing sentinels; recursive expansion is bounded to 100,000 values and 128 evaluation/serialization levels. mutable slices, nonnull data-pointer elements and address-bearing untagged union elements retain the escape diagnostic.
|
||||||
|
|
||||||
untagged union constants snapshot every byte of their native-layout storage, including padding and bytes beyond a smaller overlapping member. scalar reinterpretation, nested records/arrays, optionals, tagged payloads, fallible channels and generated defaults preserve exact nominal types and by-value copies through `$()` and compile-time arguments. specialization keys compare the full snapshot, not one selected field. every storage byte must be initialized. because untagged storage has no active-member identity, all member layouts must be free of data pointers, callbacks, strings and slices, recursively; otherwise materialization diagnoses an escape rather than retaining interpreter handles, even if a numeric member was initialized.
|
untagged union constants snapshot every byte of their native-layout storage, including padding and bytes beyond a smaller overlapping member. scalar reinterpretation, nested records/arrays, optionals, tagged payloads, fallible channels and generated defaults preserve exact nominal types and by-value copies through `$()` and compile-time arguments. specialization keys compare the full snapshot, not one selected field. every storage byte must be initialized. because untagged storage has no active-member identity, all member layouts must be free of data pointers, callbacks, strings and slices, recursively; otherwise materialization diagnoses an escape rather than retaining interpreter handles, even if a numeric member was initialized.
|
||||||
|
|
||||||
|
|||||||
@@ -20,12 +20,14 @@ traps or compiler infrastructure failures.
|
|||||||
cached generic specialization and type-producing procedures support explicit/inferred
|
cached generic specialization and type-producing procedures support explicit/inferred
|
||||||
compile-time arguments and generated record/array storage through both paths.
|
compile-time arguments and generated record/array storage through both paths.
|
||||||
ordinary procedures can compute scalar, nested record/array, tagged-union,
|
ordinary procedures can compute scalar, nested record/array, tagged-union,
|
||||||
fallible, callback and readonly byte-slice compile-time values using mutable locals, loops and recursive calls;
|
fallible, callback and readonly-slice compile-time values using mutable locals, loops and recursive calls;
|
||||||
explicit `$()` expressions use the same constant evaluation path.
|
explicit `$()` expressions use the same constant evaluation path.
|
||||||
readonly `[]u8` and `[;sentinel]u8` snapshots own exact bytes and any byte sentinel after evaluation.
|
readonly `[]u8` and `[;sentinel]u8` snapshots own exact bytes and any byte sentinel after evaluation.
|
||||||
|
readonly typed slices snapshot materializable elements into module-lifetime backing,
|
||||||
|
including nominal bytes, arrays/records, callbacks, nested slices and integer sentinels.
|
||||||
address-free untagged unions snapshot every initialized storage byte, including padding.
|
address-free untagged unions snapshot every initialized storage byte, including padding.
|
||||||
mutable and nonbyte/nominal-byte slices, nonnull data pointers and address-bearing
|
mutable slices, nonnull data pointers and address-bearing untagged union layouts
|
||||||
untagged union layouts retain the constant-materialization escape boundary.
|
retain the constant-materialization escape boundary.
|
||||||
compile-time `if`/`match` selection and type comparisons specialize only the selected body,
|
compile-time `if`/`match` selection and type comparisons specialize only the selected body,
|
||||||
including owned by-value tagged payload captures in ordinary generic procedures.
|
including owned by-value tagged payload captures in ordinary generic procedures.
|
||||||
type-producing procedures also accept ordinary
|
type-producing procedures also accept ordinary
|
||||||
@@ -147,10 +149,11 @@ test discovery and FFI remain outside these completed slices.
|
|||||||
- [x] hash-map execution checkpoint: existing `std/hashmap.HashMap`, `AutoHashMap` and `StringHashMap` run collision chains, zero-hash normalization, growth/rehashing, duplicate-key errors, absent lookups and owned aggregate values without replacement library code. interpreter checks use separate typed allocation buffers and preserve the original map on allocation failure, including cleanup; native `mem.c_allocator` checks cover all integer widths/boundaries, booleans, sparse/aliased enums, nested distinct/array/record keys with padding ignored, content-based byte strings and custom callbacks at `-O0`/`-O2`. specialization retains `usize` collection indices, nominal/explicit pointer-cast result types, rigid slice borrowing/string context and source-known optional callbacks; ordinary checking rejects incompatible elements and readonly reversal. `unreachable` demand-traps with provenance through both paths and compile-time execution; literal-true loops distinguish escaping and nested breaks. `compile_error!` owns computed string diagnostics and skips unselected compile-time bodies. initializer inference from expected result types is covered below.
|
- [x] hash-map execution checkpoint: existing `std/hashmap.HashMap`, `AutoHashMap` and `StringHashMap` run collision chains, zero-hash normalization, growth/rehashing, duplicate-key errors, absent lookups and owned aggregate values without replacement library code. interpreter checks use separate typed allocation buffers and preserve the original map on allocation failure, including cleanup; native `mem.c_allocator` checks cover all integer widths/boundaries, booleans, sparse/aliased enums, nested distinct/array/record keys with padding ignored, content-based byte strings and custom callbacks at `-O0`/`-O2`. specialization retains `usize` collection indices, nominal/explicit pointer-cast result types, rigid slice borrowing/string context and source-known optional callbacks; ordinary checking rejects incompatible elements and readonly reversal. `unreachable` demand-traps with provenance through both paths and compile-time execution; literal-true loops distinguish escaping and nested breaks. `compile_error!` owns computed string diagnostics and skips unselected compile-time bodies. initializer inference from expected result types is covered below.
|
||||||
- [x] expected-result inference checkpoint: omitted compile-time type/value arguments infer from rigid runtime arguments and expected results before contextual literal defaults. typed bindings, assignments, returns, ordinary/nested calls, aggregate fields/elements/defaults, optional/fallible channels, callbacks, match arms, yielding/catch blocks, arithmetic/bitwise operands and explicit `$()` expressions retain exact checked types through interpreter/native execution. generated nominal results infer counts and private/custom callback identities; existing `hashmap.init(allocator)` initializes `AutoHashMap`, `StringHashMap` and custom `HashMap` without replacement library code. rigid conflicts, unrelated nominal generators and unresolved arguments diagnose; explicit calls and fully explicit type-producer inputs retain their contracts. owned specializations and repeat compilation are verified.
|
- [x] expected-result inference checkpoint: omitted compile-time type/value arguments infer from rigid runtime arguments and expected results before contextual literal defaults. typed bindings, assignments, returns, ordinary/nested calls, aggregate fields/elements/defaults, optional/fallible channels, callbacks, match arms, yielding/catch blocks, arithmetic/bitwise operands and explicit `$()` expressions retain exact checked types through interpreter/native execution. generated nominal results infer counts and private/custom callback identities; existing `hashmap.init(allocator)` initializes `AutoHashMap`, `StringHashMap` and custom `HashMap` without replacement library code. rigid conflicts, unrelated nominal generators and unresolved arguments diagnose; explicit calls and fully explicit type-producer inputs retain their contracts. owned specializations and repeat compilation are verified.
|
||||||
- [x] computed-callback materialization checkpoint: ordinary compile-time calls return source-bound callbacks through interpreter/native execution, including optional/nested presence, arrays/records, tagged payloads and both fallible channels. temporary IR function IDs map back to exact source declarations before evaluation storage is released. computed/direct aggregate arguments share specialization-cache identity and generated defaults; imported private targets, same-spelled functions, signature diagnostics, runtime trap provenance and repeated compilation retain their contracts. nonnull data pointers, unsupported slice types and address-bearing untagged union layouts retain their escape boundary.
|
- [x] computed-callback materialization checkpoint: ordinary compile-time calls return source-bound callbacks through interpreter/native execution, including optional/nested presence, arrays/records, tagged payloads and both fallible channels. temporary IR function IDs map back to exact source declarations before evaluation storage is released. computed/direct aggregate arguments share specialization-cache identity and generated defaults; imported private targets, same-spelled functions, signature diagnostics, runtime trap provenance and repeated compilation retain their contracts. nonnull data pointers, unsupported slice types and address-bearing untagged union layouts retain their escape boundary.
|
||||||
- [x] readonly byte-slice materialization checkpoint: ordinary compile-time calls snapshot readonly `[]u8` and zero-terminated `[;0]u8` results into owned literals before evaluation storage is released. string subviews, locally filled arrays, immutable global arrays and raw aggregate views preserve exact bytes, embedded NULs, declared slice types and normalized content-based cache identity through explicit `$()` expressions, generic inputs, generated defaults, nested records/arrays, tagged payloads, optionals and both fallible channels. imported helpers, session/source disposal and repeated compilation retain ownership through interpreter/native execution; uninitialized source bytes diagnose read-before-initialization and invalid ranges are rejected before dereference. nonzero sentinels are covered below; mutable and nonbyte/nominal-byte slices, nonnull data pointers and address-bearing untagged union layouts remain unsupported.
|
- [x] readonly byte-slice materialization checkpoint: ordinary compile-time calls snapshot readonly `[]u8` and zero-terminated `[;0]u8` results into owned literals before evaluation storage is released. string subviews, locally filled arrays, immutable global arrays and raw aggregate views preserve exact bytes, embedded NULs, declared slice types and normalized content-based cache identity through explicit `$()` expressions, generic inputs, generated defaults, nested records/arrays, tagged payloads, optionals and both fallible channels. imported helpers, session/source disposal and repeated compilation retain ownership through interpreter/native execution; uninitialized source bytes diagnose read-before-initialization and invalid ranges are rejected before dereference. nonzero sentinels and nonbyte/nominal-byte slices are covered below; mutable slices, nonnull data pointers and address-bearing untagged union layouts remain unsupported.
|
||||||
- [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. mutable and nonbyte/nominal-byte 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.
|
||||||
- [ ] remaining gate: general type-valued compile-time execution, broader constant materialization (including address-bearing untagged unions, nonnull data pointers and slice types beyond readonly bytes), 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.
|
- [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.
|
||||||
|
- [ ] 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.
|
||||||
- [ ] native driver integration: required C ABI declarations, `process.Init` startup arguments/IO, actual CLI exit statuses, file loading, temporary files and spawning Clang. Honey-generated programs currently accept only parameterless `main` and print its result; Bro's bootstrap startup support is not Honey backend support.
|
- [ ] native driver integration: required C ABI declarations, `process.Init` startup arguments/IO, actual CLI exit statuses, file loading, temporary files and spawning Clang. Honey-generated programs currently accept only parameterless `main` and print its result; Bro's bootstrap startup support is not Honey backend support.
|
||||||
|
|||||||
+24
-1
@@ -84,6 +84,7 @@ ExprKind :: enum {
|
|||||||
catch_expr
|
catch_expr
|
||||||
typed_constant
|
typed_constant
|
||||||
union_constant
|
union_constant
|
||||||
|
slice_constant
|
||||||
comptime_expr
|
comptime_expr
|
||||||
literal_void
|
literal_void
|
||||||
literal_undefined
|
literal_undefined
|
||||||
@@ -198,6 +199,7 @@ NodeKind :: enum {
|
|||||||
stmt_block
|
stmt_block
|
||||||
expr_typed_constant
|
expr_typed_constant
|
||||||
expr_union_constant
|
expr_union_constant
|
||||||
|
expr_slice_constant
|
||||||
expr_comptime
|
expr_comptime
|
||||||
literal_float_value
|
literal_float_value
|
||||||
literal_void
|
literal_void
|
||||||
@@ -624,6 +626,17 @@ add_union_constant proc(
|
|||||||
}, at)
|
}, at)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#! owns a readonly slice's typed fixed-array backing, including any sentinel.
|
||||||
|
add_slice_constant proc(
|
||||||
|
builder @mut Builder, at Span, type, elements NodeId,
|
||||||
|
) NodeId ! mem.AllocError {
|
||||||
|
return try add_node(builder, Node{
|
||||||
|
kind = .expr_slice_constant,
|
||||||
|
data0 = NodeData{ node_id = type },
|
||||||
|
data1 = NodeData{ node_id = elements },
|
||||||
|
}, at)
|
||||||
|
}
|
||||||
|
|
||||||
add_comptime_expr proc(builder @mut Builder, at Span, operand NodeId) NodeId ! mem.AllocError {
|
add_comptime_expr proc(builder @mut Builder, at Span, operand NodeId) NodeId ! mem.AllocError {
|
||||||
return try add_node(builder, Node{
|
return try add_node(builder, Node{
|
||||||
kind = .expr_comptime,
|
kind = .expr_comptime,
|
||||||
@@ -1613,6 +1626,7 @@ expression_kind proc(kind NodeKind) ExprKind {
|
|||||||
.expr_catch: .catch_expr
|
.expr_catch: .catch_expr
|
||||||
.expr_typed_constant: .typed_constant
|
.expr_typed_constant: .typed_constant
|
||||||
.expr_union_constant: .union_constant
|
.expr_union_constant: .union_constant
|
||||||
|
.expr_slice_constant: .slice_constant
|
||||||
.expr_comptime: .comptime_expr
|
.expr_comptime: .comptime_expr
|
||||||
.expr_block: .block_expr
|
.expr_block: .block_expr
|
||||||
.literal_float_value: .literal_float
|
.literal_float_value: .literal_float
|
||||||
@@ -1675,6 +1689,15 @@ union_constant proc(tree @Tree, id NodeId) TypedConstantView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
slice_constant proc(tree @Tree, id NodeId) TypedConstantView {
|
||||||
|
node :: get_node(&tree.storage, id)
|
||||||
|
debug.assert(node.kind == .expr_slice_constant)
|
||||||
|
return TypedConstantView{
|
||||||
|
type = node.data0.node_id,
|
||||||
|
value = node.data1.node_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
catch_expr proc(tree @Tree, id NodeId) CatchExprView {
|
catch_expr proc(tree @Tree, id NodeId) CatchExprView {
|
||||||
node :: get_node(&tree.storage, id)
|
node :: get_node(&tree.storage, id)
|
||||||
debug.assert(node.kind == .expr_catch)
|
debug.assert(node.kind == .expr_catch)
|
||||||
@@ -2030,7 +2053,7 @@ node_layout proc(node Node) NodeLayout {
|
|||||||
.literal_int, .literal_float, .literal_float_value, .literal_string, .literal_bool, .literal_null,
|
.literal_int, .literal_float, .literal_float_value, .literal_string, .literal_bool, .literal_null,
|
||||||
.literal_void, .literal_undefined, .expr_identifier, .invalid_expr: {}
|
.literal_void, .literal_undefined, .expr_identifier, .invalid_expr: {}
|
||||||
.expr_unary: layout.direct_children = 2
|
.expr_unary: layout.direct_children = 2
|
||||||
.expr_binary, .expr_range, .expr_index, .expr_selection, .expr_typed_constant, .expr_union_constant,
|
.expr_binary, .expr_range, .expr_index, .expr_selection, .expr_typed_constant, .expr_union_constant, .expr_slice_constant,
|
||||||
.expr_fallible_type, .expr_sum_type, .expr_array_type, .expr_slice_type:
|
.expr_fallible_type, .expr_sum_type, .expr_array_type, .expr_slice_type:
|
||||||
layout.direct_children = 6
|
layout.direct_children = 6
|
||||||
.expr_pointer_type: layout.direct_children = 4
|
.expr_pointer_type: layout.direct_children = 4
|
||||||
|
|||||||
@@ -1007,6 +1007,74 @@ computed_byte_slices_recompile_without_borrowing_evaluation_storage test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
computed_nonbyte_slices_recompile_owned_private_elements_and_callbacks 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"
|
||||||
|
`Code :: distinct i16
|
||||||
|
`Entry :: struct { value bool }
|
||||||
|
`increment proc(value Code) i16 { return i16(value) + 100 }
|
||||||
|
`main proc() i16 {
|
||||||
|
` entries :: $(lib.make())
|
||||||
|
` if (entries.len != 2 or entries[0].nested.len != 2) return -1
|
||||||
|
` if (entries[0].nested[0] != -300 or entries[0].nested[1] != 1024) return -2
|
||||||
|
` if (entries[1].nested.len != 1 or entries[1].nested[0] != 1024) return -3
|
||||||
|
` if (i16(entries[0].value) != 40 or i16(entries[1].value) != 41) return -4
|
||||||
|
` if (entries[1].run(entries[1].value) != 43) return -5
|
||||||
|
` return entries[0].run(entries[0].value)
|
||||||
|
`}
|
||||||
|
,
|
||||||
|
)
|
||||||
|
defer arraylist.deinit(&bytes)
|
||||||
|
root_source :: try add_source(&session, root, "/nonbyte_slices/app/main.hon", &bytes, &diagnostic_store)
|
||||||
|
imported := try owned_text(
|
||||||
|
`@hide:file
|
||||||
|
`Code :: distinct i16
|
||||||
|
`@hide:file
|
||||||
|
`Callback :: alias @proc(value Code) i16
|
||||||
|
`@hide:file
|
||||||
|
`Entry :: struct { value Code, run Callback, nested []i16 }
|
||||||
|
`bias i16 :: 2
|
||||||
|
`@hide:file
|
||||||
|
`increment proc(value Code) i16 { return i16(value) + bias }
|
||||||
|
`make proc() []Entry {
|
||||||
|
` values [2]i16 :: [-300, 1024]
|
||||||
|
` entries [2]Entry :: [
|
||||||
|
` { value = 40, run = increment, nested = values[..] },
|
||||||
|
` { value = 41, run = increment, nested = values[1..] },
|
||||||
|
` ]
|
||||||
|
` return entries[..]
|
||||||
|
`}
|
||||||
|
,
|
||||||
|
)
|
||||||
|
defer arraylist.deinit(&imported)
|
||||||
|
_ = try add_source(&session, dependency, "/nonbyte_slices/library/items.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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
computed_untagged_unions_recompile_without_borrowing_storage_bytes test {
|
computed_untagged_unions_recompile_without_borrowing_storage_bytes test {
|
||||||
diagnostic_store := diagnostics.init(mem.c_allocator)
|
diagnostic_store := diagnostics.init(mem.c_allocator)
|
||||||
defer diagnostics.deinit(&diagnostic_store)
|
defer diagnostics.deinit(&diagnostic_store)
|
||||||
|
|||||||
@@ -108,20 +108,34 @@ EvaluationMemory :: struct {
|
|||||||
frames arraylist.ArrayList(Frame)
|
frames arraylist.ArrayList(Frame)
|
||||||
}
|
}
|
||||||
|
|
||||||
#! borrows one checked byte from retained evaluation storage; null means an invalid pointer/range.
|
#! borrows one checked typed element from retained evaluation storage; null means an invalid pointer/range.
|
||||||
evaluation_byte proc(outcome @Outcome, module @ir.Module, pointer ir.Value, index usize) ?ir.Value {
|
#! aggregate storage may borrow scratch valid only until the next evaluation accessor call.
|
||||||
if (outcome.evaluation_memory == null) return null
|
evaluation_element proc(
|
||||||
|
outcome @Outcome,
|
||||||
|
module @ir.Module,
|
||||||
|
pointer ir.Value,
|
||||||
|
index usize,
|
||||||
|
type ir.Type,
|
||||||
|
shape ir.ArrayId,
|
||||||
|
) ?ir.Value {
|
||||||
|
if (outcome.evaluation_memory == null or pointer.type != .pointer) return null
|
||||||
memory :: &outcome.evaluation_memory?[0]
|
memory :: &outcome.evaluation_memory?[0]
|
||||||
handle :: pointer.data.unsigned
|
handle :: pointer.data.unsigned
|
||||||
if (handle >= u64(memory.pointers.items.len)) return null
|
if (handle >= u64(memory.pointers.items.len)) return null
|
||||||
location := memory.pointers.items[usize(handle)]
|
location := memory.pointers.items[usize(handle)]
|
||||||
if (index > maxval!(usize) - location.byte_offset) return null
|
stride :: ir.storage_size(module, type, shape)
|
||||||
location.byte_offset += index
|
if (stride != 0 and index > divtrunc!(maxval!(usize) - location.byte_offset, stride)) return null
|
||||||
if (pointer_address(module, location, .u8, ir.no_array)) |address|
|
location.byte_offset += index * stride
|
||||||
|
if (pointer_address(module, location, type, shape)) |address|
|
||||||
return read_address(module, address, memory.scratch)
|
return read_address(module, address, memory.scratch)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#! borrows one checked byte from retained evaluation storage; null means an invalid pointer/range.
|
||||||
|
evaluation_byte proc(outcome @Outcome, module @ir.Module, pointer ir.Value, index usize) ?ir.Value {
|
||||||
|
return evaluation_element(outcome, module, pointer, index, .u8, ir.no_array)
|
||||||
|
}
|
||||||
|
|
||||||
#! executes typed IR with invocation/global storage released on every exit.
|
#! executes typed IR with invocation/global storage released on every exit.
|
||||||
#! aggregate results own flat storage released by `deinit`; string IDs and shapes resolve through `module`.
|
#! aggregate results own flat storage released by `deinit`; string IDs and shapes resolve through `module`.
|
||||||
run proc(
|
run proc(
|
||||||
@@ -789,7 +803,7 @@ pointer_address proc(module @ir.Module, pointer Pointer, type ir.Type, shape ir.
|
|||||||
base := Address{
|
base := Address{
|
||||||
type = pointer.root_type, array_type = pointer.root_array, cells = &[],
|
type = pointer.root_type, array_type = pointer.root_array, cells = &[],
|
||||||
root = pointer.root, root_type = pointer.root_type, root_array = pointer.root_array,
|
root = pointer.root, root_type = pointer.root_type, root_array = pointer.root_array,
|
||||||
readonly = pointer.readonly, bytes = pointer.bytes,
|
readonly = pointer.readonly, bytes = pointer.bytes, raw = pointer.root^.raw,
|
||||||
}
|
}
|
||||||
if (pointer.bytes.len != 0) {
|
if (pointer.bytes.len != 0) {
|
||||||
base.type = type
|
base.type = type
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ State :: struct {
|
|||||||
arguments arraylist.ArrayList(ir.ValueId)
|
arguments arraylist.ArrayList(ir.ValueId)
|
||||||
cleanups arraylist.ArrayList(ast.NodeId)
|
cleanups arraylist.ArrayList(ast.NodeId)
|
||||||
records []mut ir.ArrayId
|
records []mut ir.ArrayId
|
||||||
|
slice_elements arraylist.ArrayList(typecheck.Type)
|
||||||
|
slice_backings arraylist.ArrayList(ast.NodeId)
|
||||||
|
evaluation_functions ?@mut arraylist.ArrayList(ast.NodeId) = null
|
||||||
function usize = 0
|
function usize = 0
|
||||||
block ir.BlockId = ir.BlockId(0)
|
block ir.BlockId = ir.BlockId(0)
|
||||||
terminated bool = false
|
terminated bool = false
|
||||||
@@ -156,11 +159,16 @@ lower_module proc(
|
|||||||
storage = storage,
|
storage = storage,
|
||||||
arrays = arrays,
|
arrays = arrays,
|
||||||
records = records,
|
records = records,
|
||||||
|
slice_elements = arraylist.init(typecheck.Type, allocator),
|
||||||
|
slice_backings = arraylist.init(ast.NodeId, allocator),
|
||||||
|
evaluation_functions = functions,
|
||||||
arguments = arraylist.init(ir.ValueId, allocator),
|
arguments = arraylist.init(ir.ValueId, allocator),
|
||||||
cleanups = arraylist.init(ast.NodeId, allocator),
|
cleanups = arraylist.init(ast.NodeId, allocator),
|
||||||
}
|
}
|
||||||
defer arraylist.deinit(&state.arguments)
|
defer arraylist.deinit(&state.arguments)
|
||||||
defer arraylist.deinit(&state.cleanups)
|
defer arraylist.deinit(&state.cleanups)
|
||||||
|
defer arraylist.deinit(&state.slice_elements)
|
||||||
|
defer arraylist.deinit(&state.slice_backings)
|
||||||
|
|
||||||
# allocate every package identity before lowering any initializer or body.
|
# allocate every package identity before lowering any initializer or body.
|
||||||
for (0..ast.package_decl_count(tree)) |i| {
|
for (0..ast.package_decl_count(tree)) |i| {
|
||||||
@@ -204,6 +212,22 @@ lower_module proc(
|
|||||||
.import, .invalid: {}
|
.import, .invalid: {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
backing_index := usize(0)
|
||||||
|
while (backing_index < state.slice_backings.items.len) {
|
||||||
|
try lower_slice_backing(&state, state.slice_backings.items[backing_index])
|
||||||
|
backing_index += 1
|
||||||
|
}
|
||||||
|
# source element shapes are metadata, not record layout dependencies. defer
|
||||||
|
# them until recursive records have cached their completed physical layout.
|
||||||
|
cursor := usize(0)
|
||||||
|
while (cursor < state.slice_elements.items.len) {
|
||||||
|
type :: state.slice_elements.items[cursor]
|
||||||
|
child :: typecheck.array_type(checked, type.array_type).element
|
||||||
|
shape :: state.arrays[usize(type.array_type)]
|
||||||
|
element_shape :: try array_shape(&state, child)
|
||||||
|
module.arrays.items[usize(shape)].element_array = element_shape
|
||||||
|
cursor += 1
|
||||||
|
}
|
||||||
return module
|
return module
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,6 +332,10 @@ prepare_container proc(state @mut State, type typecheck.Type) ir.ArrayId ! (mem.
|
|||||||
}
|
}
|
||||||
shape :: try ir.add_record(state.module, fields[..])
|
shape :: try ir.add_record(state.module, fields[..])
|
||||||
state.arrays[index] = shape
|
state.arrays[index] = shape
|
||||||
|
if (type.kind == .slice) {
|
||||||
|
state.module.arrays.items[usize(shape)].element = lower_type(state, descriptor.element)
|
||||||
|
try arraylist.append(&state.slice_elements, type)
|
||||||
|
}
|
||||||
return shape
|
return shape
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,6 +476,19 @@ lower_global proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diag
|
|||||||
finish(state, value, declaration.span)
|
finish(state, value, declaration.span)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@hide:file
|
||||||
|
lower_slice_backing proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnostics.CapacityError) {
|
||||||
|
global :: state.module.globals.items[usize(state.storage[usize(id)].index)]
|
||||||
|
state.function = usize(global.initializer)
|
||||||
|
state.block = ir.BlockId(0)
|
||||||
|
state.terminated = false
|
||||||
|
state.module.functions.items[state.function].result = global.type
|
||||||
|
state.module.functions.items[state.function].result_array_type = global.array_type
|
||||||
|
backing :: ast.slice_constant(state.tree, id).value
|
||||||
|
value :: try lower_expression(state, backing)
|
||||||
|
if (!state.terminated) finish(state, value, ast.node_span(state.tree, id))
|
||||||
|
}
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
lower_function proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnostics.CapacityError) {
|
lower_function proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnostics.CapacityError) {
|
||||||
tree :: state.tree
|
tree :: state.tree
|
||||||
@@ -2098,6 +2139,40 @@ lower_expression proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocEr
|
|||||||
if (trap_type(state, id)) return ir.no_value
|
if (trap_type(state, id)) return ir.no_value
|
||||||
return try lower_as(state, ast.typed_constant(tree, id).value, typecheck.type_of(state.checked, id))
|
return try lower_as(state, ast.typed_constant(tree, id).value, typecheck.type_of(state.checked, id))
|
||||||
}
|
}
|
||||||
|
.slice_constant: {
|
||||||
|
if (trap_type(state, id)) return ir.no_value
|
||||||
|
if (state.storage[usize(id)].kind == .none) {
|
||||||
|
backing :: ast.slice_constant(tree, id).value
|
||||||
|
initializer :: try add_function(state.module, span)
|
||||||
|
if (state.evaluation_functions) |declarations|
|
||||||
|
try arraylist.append(declarations, maxval!(ast.NodeId))
|
||||||
|
global :: u32(state.module.globals.items.len)
|
||||||
|
try arraylist.append(&state.module.globals, ir.Global{
|
||||||
|
type = lower_type(state, typecheck.type_of(state.checked, backing)),
|
||||||
|
array_type = try array_shape(state, typecheck.type_of(state.checked, backing)),
|
||||||
|
initializer = initializer,
|
||||||
|
span = span,
|
||||||
|
})
|
||||||
|
state.storage[usize(id)] = Storage{ kind = .global, index = global }
|
||||||
|
try arraylist.append(&state.slice_backings, id)
|
||||||
|
}
|
||||||
|
location :: state.storage[usize(id)]
|
||||||
|
global :: state.module.globals.items[usize(location.index)]
|
||||||
|
address :: try emit(state, ir.Instruction{
|
||||||
|
op = .address_global, type = .address, pointee = global.type,
|
||||||
|
array_type = global.array_type, slot = location.index, span = span,
|
||||||
|
})
|
||||||
|
pointer :: try emit(state, ir.Instruction{
|
||||||
|
op = .pointer_from_address, type = .pointer, left = address,
|
||||||
|
pointee = global.type, array_type = global.array_type, span = span,
|
||||||
|
})
|
||||||
|
descriptor :: typecheck.array_type(state.checked, typecheck.type_of(state.checked, id).array_type)
|
||||||
|
backing :: ast.slice_constant(tree, id).value
|
||||||
|
elements :: typecheck.array_type(state.checked, typecheck.type_of(state.checked, backing).array_type)
|
||||||
|
extra :: if (descriptor.sentinel != null) usize(1) else usize(0)
|
||||||
|
len :: elements.len - extra
|
||||||
|
return try record_pair(state, shape, pointer, try usize_constant(state, len, span), span)
|
||||||
|
}
|
||||||
.union_constant: {
|
.union_constant: {
|
||||||
if (trap_type(state, id)) return ir.no_value
|
if (trap_type(state, id)) return ir.no_value
|
||||||
bytes :: ast.union_constant(tree, id).value
|
bytes :: ast.union_constant(tree, id).value
|
||||||
|
|||||||
@@ -1301,6 +1301,14 @@ collect_expression proc(state @mut State, id ast.NodeId) void ! (mem.AllocError
|
|||||||
try depend(state, value.type, id)
|
try depend(state, value.type, id)
|
||||||
try depend(state, value.value, id)
|
try depend(state, value.value, id)
|
||||||
}
|
}
|
||||||
|
.slice_constant: {
|
||||||
|
value :: ast.slice_constant(tree, id)
|
||||||
|
state.cells[usize(id)].fixed = true
|
||||||
|
state.cells[usize(id)].type = try check_annotation(state, value.type, false)
|
||||||
|
try collect_expression(state, value.value)
|
||||||
|
try depend(state, value.type, id)
|
||||||
|
try depend(state, value.value, id)
|
||||||
|
}
|
||||||
.try_expr: {
|
.try_expr: {
|
||||||
operand :: ast.type_operand(tree, id)
|
operand :: ast.type_operand(tree, id)
|
||||||
try collect_expression(state, operand)
|
try collect_expression(state, operand)
|
||||||
@@ -2178,6 +2186,27 @@ validate proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnost
|
|||||||
if (!descriptor.is_union or descriptor.tag_type.kind != .none)
|
if (!descriptor.is_union or descriptor.tag_type.kind != .none)
|
||||||
try fail(state, id, .invalid_operand, ast.node_span(tree, id))
|
try fail(state, id, .invalid_operand, ast.node_span(tree, id))
|
||||||
}
|
}
|
||||||
|
.slice_constant: {
|
||||||
|
value :: ast.slice_constant(tree, id)
|
||||||
|
type :: state.cells[usize(id)].type
|
||||||
|
backing :: state.types[usize(value.value)]
|
||||||
|
if (type.kind == .invalid or backing.kind == .invalid) return
|
||||||
|
if (
|
||||||
|
type.kind != .slice or backing.kind != .array
|
||||||
|
or ast.expr_kind(tree, value.value) != .typed_constant
|
||||||
|
) {
|
||||||
|
try fail(state, id, .invalid_operand, ast.node_span(tree, id))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
descriptor :: state.arrays.items[usize(type.array_type)]
|
||||||
|
elements :: state.arrays.items[usize(backing.array_type)]
|
||||||
|
if (
|
||||||
|
descriptor.mutable or elements.mutable
|
||||||
|
or !same_type(state, descriptor.element, elements.element)
|
||||||
|
or (descriptor.sentinel != null and elements.len == 0)
|
||||||
|
or ast.expr_kind(tree, ast.typed_constant(tree, value.value).value) != .array_literal
|
||||||
|
) try fail(state, id, .invalid_operand, ast.node_span(tree, id))
|
||||||
|
}
|
||||||
.try_expr, .catch_expr: try validate_fallible_expression(state, id)
|
.try_expr, .catch_expr: try validate_fallible_expression(state, id)
|
||||||
.unary: {
|
.unary: {
|
||||||
expression :: ast.unary_expr(tree, id)
|
expression :: ast.unary_expr(tree, id)
|
||||||
|
|||||||
@@ -551,6 +551,11 @@ resolve_expr proc(
|
|||||||
try resolve_expr(state, scope, value.type)
|
try resolve_expr(state, scope, value.type)
|
||||||
try resolve_expr(state, scope, value.value)
|
try resolve_expr(state, scope, value.value)
|
||||||
}
|
}
|
||||||
|
.slice_constant: {
|
||||||
|
value :: ast.slice_constant(tree, id)
|
||||||
|
try resolve_expr(state, scope, value.type)
|
||||||
|
try resolve_expr(state, scope, value.value)
|
||||||
|
}
|
||||||
.literal_int, .literal_float, .literal_bool, .literal_string, .literal_null, .literal_void,
|
.literal_int, .literal_float, .literal_bool, .literal_string, .literal_null, .literal_void,
|
||||||
.literal_undefined: {}
|
.literal_undefined: {}
|
||||||
.enum_literal: {}
|
.enum_literal: {}
|
||||||
|
|||||||
@@ -1707,7 +1707,7 @@ branch_constant proc(state @State, id ast.NodeId, depth usize) bool {
|
|||||||
}
|
}
|
||||||
match ast.expr_kind(&state.tree, id) {
|
match ast.expr_kind(&state.tree, id) {
|
||||||
.literal_int, .literal_float, .literal_bool, .literal_string, .literal_null, .literal_void,
|
.literal_int, .literal_float, .literal_bool, .literal_string, .literal_null, .literal_void,
|
||||||
.union_constant: return true
|
.union_constant, .slice_constant: return true
|
||||||
.typed_constant: return branch_constant(state, ast.typed_constant(&state.tree, id).value, depth + 1)
|
.typed_constant: return branch_constant(state, ast.typed_constant(&state.tree, id).value, depth + 1)
|
||||||
.enum_literal: return true
|
.enum_literal: return true
|
||||||
.record_literal: {
|
.record_literal: {
|
||||||
@@ -2359,8 +2359,96 @@ union_bytes_materializable proc(checked @typecheck.Result, type typecheck.Type,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# rebuild exact slice backing annotations without retaining temporary checker IDs.
|
||||||
|
@hide:file
|
||||||
|
serialized_type proc(
|
||||||
|
state @mut State,
|
||||||
|
checked @typecheck.Result,
|
||||||
|
type typecheck.Type,
|
||||||
|
span ast.Span,
|
||||||
|
depth usize,
|
||||||
|
) ast.NodeId ! Error {
|
||||||
|
if (depth >= 128) return no_node
|
||||||
|
source := type.nominal
|
||||||
|
if (source == no_node and type.kind == .record) {
|
||||||
|
record :: typecheck.record_type(checked, type.record_type)
|
||||||
|
source = if (record.declaration != no_node) record.declaration else record.source
|
||||||
|
}
|
||||||
|
if (source == no_node and type.kind == .enumeration)
|
||||||
|
source = typecheck.enum_type(checked, type.enum_type).declaration
|
||||||
|
if (source == no_node and type.kind == .function) source = type.function
|
||||||
|
if (source != no_node) {
|
||||||
|
if (ast.node_class(&state.tree, source) == .function) {
|
||||||
|
params := arraylist.init(ast.NodeId, state.allocator)
|
||||||
|
defer arraylist.deinit(¶ms)
|
||||||
|
for (0..ast.function_param_count(&state.tree, source)) |i|
|
||||||
|
try arraylist.append(¶ms, ast.function_param(&state.tree, source, i))
|
||||||
|
source = try ast.add_function_type(state.builder, span,
|
||||||
|
ast.callable_result(&state.tree, source), params.items)
|
||||||
|
try refresh(state)
|
||||||
|
}
|
||||||
|
return (try materialize(state, Value{ kind = .type, node = source })).node
|
||||||
|
}
|
||||||
|
result := no_node
|
||||||
|
if (type.kind == .array or type.kind == .slice or type.kind == .optional or type.kind == .fallible) {
|
||||||
|
descriptor :: typecheck.array_type(checked, type.array_type)
|
||||||
|
child :: try serialized_type(state, checked, descriptor.element, span, depth + 1)
|
||||||
|
if (child == no_node) return no_node
|
||||||
|
if (type.kind == .array) {
|
||||||
|
count :: try ast.add_integer(state.builder, span, u64(descriptor.len))
|
||||||
|
result = try ast.add_array_type(state.builder, span, count, child, descriptor.mutable)
|
||||||
|
} else if (type.kind == .slice) {
|
||||||
|
sentinel ?ast.NodeId := null
|
||||||
|
if (descriptor.sentinel) |value| {
|
||||||
|
unsigned :: (descriptor.element.kind == .u8 or descriptor.element.kind == .u16
|
||||||
|
or descriptor.element.kind == .u32 or descriptor.element.kind == .u64 or descriptor.element.kind == .usize)
|
||||||
|
negative :: value < 0 and !unsigned
|
||||||
|
magnitude :: if (negative) u64(-(value + 1)) + 1 else ptrcast!(@u64, &value)^
|
||||||
|
literal := try ast.add_integer(state.builder, span, magnitude)
|
||||||
|
if (negative) literal = try ast.add_unary(state.builder, span, .negate, literal)
|
||||||
|
sentinel = literal
|
||||||
|
}
|
||||||
|
result = try ast.add_slice_type(state.builder, span, child, descriptor.mutable, sentinel)
|
||||||
|
} else if (type.kind == .optional) result = try ast.add_optional_type(state.builder, span, child)
|
||||||
|
else {
|
||||||
|
error_type :: try serialized_type(state, checked, descriptor.error_type, span, depth + 1)
|
||||||
|
if (error_type == no_node) return no_node
|
||||||
|
result = try ast.add_binary_type(state.builder, span, child, error_type, true)
|
||||||
|
}
|
||||||
|
} else if (type.kind == .pointer) {
|
||||||
|
descriptor :: typecheck.pointer_type(checked, type.pointer_type)
|
||||||
|
child :: try serialized_type(state, checked, descriptor.element, span, depth + 1)
|
||||||
|
if (child == no_node) return no_node
|
||||||
|
result = try ast.add_pointer_type(state.builder, span, child, descriptor.mutable, descriptor.many)
|
||||||
|
} else {
|
||||||
|
kind sema.BuiltinType :: match type.kind {
|
||||||
|
.int: .i64
|
||||||
|
.i8: .i8
|
||||||
|
.i16: .i16
|
||||||
|
.i32: .i32
|
||||||
|
.isize: .isize
|
||||||
|
.u8: .u8
|
||||||
|
.u16: .u16
|
||||||
|
.u32: .u32
|
||||||
|
.u64: .u64
|
||||||
|
.usize: .usize
|
||||||
|
.float: .f64
|
||||||
|
.bool: .bool
|
||||||
|
.string: .string
|
||||||
|
.void: .void
|
||||||
|
.anyopaque: .anyopaque
|
||||||
|
else: return no_node
|
||||||
|
}
|
||||||
|
result = try ast.add_identifier(state.builder, span, strpool.no_str)
|
||||||
|
try refresh(state)
|
||||||
|
state.bindings.items[usize(result)] = sema.Binding{ kind = .builtin, data = sema.BindingData{ builtin = kind } }
|
||||||
|
}
|
||||||
|
try refresh(state)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
# values leaving temporary evaluation must own no interpreter addresses or module IDs.
|
# values leaving temporary evaluation must own no interpreter addresses or module IDs.
|
||||||
# snapshot readonly bytes and rebuild source literals/references before releasing their backing.
|
# snapshot readonly storage and rebuild source literals/references before releasing their backing.
|
||||||
@hide:file
|
@hide:file
|
||||||
serialize_value proc(
|
serialize_value proc(
|
||||||
state @mut State,
|
state @mut State,
|
||||||
@@ -2371,7 +2459,15 @@ serialize_value proc(
|
|||||||
type typecheck.Type,
|
type typecheck.Type,
|
||||||
value ir.Value,
|
value ir.Value,
|
||||||
span ast.Span,
|
span ast.Span,
|
||||||
|
remaining @mut usize,
|
||||||
) ast.NodeId ! Error {
|
) ast.NodeId ! Error {
|
||||||
|
if (remaining^ == 0 or state.depth >= 128) {
|
||||||
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, .comptime_execution_limit, span)
|
||||||
|
return no_node
|
||||||
|
}
|
||||||
|
remaining^ -= 1
|
||||||
|
state.depth += 1
|
||||||
|
defer state.depth -= 1
|
||||||
uninitialized := value.uninitialized
|
uninitialized := value.uninitialized
|
||||||
if (ir.aggregate(value.type)) {
|
if (ir.aggregate(value.type)) {
|
||||||
if (module.arrays.items[usize(value.array_type)].is_union)
|
if (module.arrays.items[usize(value.array_type)].is_union)
|
||||||
@@ -2386,13 +2482,64 @@ serialize_value proc(
|
|||||||
result := no_node
|
result := no_node
|
||||||
if (type.kind == .slice) {
|
if (type.kind == .slice) {
|
||||||
slice :: typecheck.array_type(checked, type.array_type)
|
slice :: typecheck.array_type(checked, type.array_type)
|
||||||
if (slice.mutable or slice.element.kind != .u8 or slice.element.nominal != no_node) return no_node
|
if (slice.mutable) return no_node
|
||||||
pointer :: ir.aggregate_item(module, value, 0)
|
pointer :: ir.aggregate_item(module, value, 0)
|
||||||
length :: ir.aggregate_item(module, value, 1)
|
length :: ir.aggregate_item(module, value, 1)
|
||||||
if (pointer.uninitialized or length.uninitialized) {
|
if (pointer.uninitialized or length.uninitialized) {
|
||||||
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, .read_before_initialization, span)
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, .read_before_initialization, span)
|
||||||
return no_node
|
return no_node
|
||||||
}
|
}
|
||||||
|
if (slice.element.kind != .u8 or slice.element.nominal != no_node) {
|
||||||
|
shape :: module.arrays.items[usize(value.array_type)]
|
||||||
|
count :: usize(length.data.unsigned)
|
||||||
|
extra :: if (slice.sentinel != null) usize(1) else usize(0)
|
||||||
|
if (count > remaining^ or extra > remaining^ - count) {
|
||||||
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, .comptime_execution_limit, span)
|
||||||
|
return no_node
|
||||||
|
}
|
||||||
|
if (count != 0 and interpreter.evaluation_element(outcome, module, pointer,
|
||||||
|
count - 1, shape.element, shape.element_array) == null) return no_node
|
||||||
|
elements := arraylist.init(ast.NodeId, state.allocator)
|
||||||
|
defer arraylist.deinit(&elements)
|
||||||
|
for (0..(count + extra)) |index| {
|
||||||
|
borrowed :: interpreter.evaluation_element(outcome, module, pointer,
|
||||||
|
index, shape.element, shape.element_array)
|
||||||
|
if (borrowed == null) return no_node
|
||||||
|
child := borrowed?
|
||||||
|
# recursive slice reads may overwrite the accessor's raw-view scratch.
|
||||||
|
scratch_backed :: ir.aggregate(child.type) and child.raw
|
||||||
|
owned []mut ir.Value := &[]
|
||||||
|
if (scratch_backed) {
|
||||||
|
owned = try mem.alloc(ir.Value, state.allocator, child.data.elements.len)
|
||||||
|
memcopy!(owned, child.data.elements)
|
||||||
|
child.data = ir.ValueData{ elements = owned }
|
||||||
|
}
|
||||||
|
defer if (scratch_backed) mem.free(state.allocator, owned)
|
||||||
|
node :: try serialize_value(state, module, checked, outcome, functions,
|
||||||
|
slice.element, child, span, remaining)
|
||||||
|
if (node == no_node) return no_node
|
||||||
|
if (index == count and slice.sentinel != null) {
|
||||||
|
# checker sentinels retain full-width integer bits, including u64 maxima.
|
||||||
|
if (child.data.integer != slice.sentinel?) {
|
||||||
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, .sentinel_mismatch, span)
|
||||||
|
return no_node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try arraylist.append(&elements, node)
|
||||||
|
}
|
||||||
|
annotation :: try serialized_type(state, checked, type, span, 0)
|
||||||
|
if (annotation == no_node) return no_node
|
||||||
|
child_type :: if (type.nominal == no_node) ast.slice_type(&state.tree, annotation).element
|
||||||
|
else try serialized_type(state, checked, slice.element, span, 0)
|
||||||
|
if (child_type == no_node) return no_node
|
||||||
|
length_node :: try ast.add_integer(state.builder, span, u64(count + extra))
|
||||||
|
array_type :: try ast.add_array_type(state.builder, span, length_node, child_type, false)
|
||||||
|
literal :: try ast.add_array_literal(state.builder, span, elements.items)
|
||||||
|
backing :: try ast.add_typed_constant(state.builder, span, array_type, literal)
|
||||||
|
result = try ast.add_slice_constant(state.builder, span, annotation, backing)
|
||||||
|
try refresh(state)
|
||||||
|
return result
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
length.data.unsigned != 0
|
length.data.unsigned != 0
|
||||||
and interpreter.evaluation_byte(outcome, module, pointer, usize(length.data.unsigned - 1)) == null
|
and interpreter.evaluation_byte(outcome, module, pointer, usize(length.data.unsigned - 1)) == null
|
||||||
@@ -2437,7 +2584,17 @@ serialize_value proc(
|
|||||||
defer arraylist.deinit(&elements)
|
defer arraylist.deinit(&elements)
|
||||||
for (0..array.len) |i| {
|
for (0..array.len) |i| {
|
||||||
child :: ir.aggregate_item(module, value, i)
|
child :: ir.aggregate_item(module, value, i)
|
||||||
node :: try serialize_value(state, module, checked, outcome, functions, array.element, child, span)
|
node :: try serialize_value(
|
||||||
|
state,
|
||||||
|
module,
|
||||||
|
checked,
|
||||||
|
outcome,
|
||||||
|
functions,
|
||||||
|
array.element,
|
||||||
|
child,
|
||||||
|
span,
|
||||||
|
remaining,
|
||||||
|
)
|
||||||
if (node == no_node) return no_node
|
if (node == no_node) return no_node
|
||||||
try arraylist.append(&elements, node)
|
try arraylist.append(&elements, node)
|
||||||
}
|
}
|
||||||
@@ -2474,7 +2631,7 @@ serialize_value proc(
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
child :: ir.aggregate_item(module, value, i)
|
child :: ir.aggregate_item(module, value, i)
|
||||||
node :: try serialize_value(state, module, checked, outcome, functions, field.type, child, span)
|
node :: try serialize_value(state, module, checked, outcome, functions, field.type, child, span, remaining)
|
||||||
if (node == no_node) return no_node
|
if (node == no_node) return no_node
|
||||||
name := field.name
|
name := field.name
|
||||||
if (name != null) name = try member_name(state, name?)
|
if (name != null) name = try member_name(state, name?)
|
||||||
@@ -2489,7 +2646,7 @@ serialize_value proc(
|
|||||||
index :: if (failed) usize(1) else usize(0)
|
index :: if (failed) usize(1) else usize(0)
|
||||||
channel :: if (failed) container.error_type else container.element
|
channel :: if (failed) container.error_type else container.element
|
||||||
child :: ir.aggregate_item(module, value, index)
|
child :: ir.aggregate_item(module, value, index)
|
||||||
result = try serialize_value(state, module, checked, outcome, functions, channel, child, span)
|
result = try serialize_value(state, module, checked, outcome, functions, channel, child, span, remaining)
|
||||||
if (result == no_node) return no_node
|
if (result == no_node) return no_node
|
||||||
# keep named success/error types distinct even when their members overlap.
|
# keep named success/error types distinct even when their members overlap.
|
||||||
annotation := no_node
|
annotation := no_node
|
||||||
@@ -2518,12 +2675,12 @@ serialize_value proc(
|
|||||||
value.data.function == maxval!(ir.FunctionId)
|
value.data.function == maxval!(ir.FunctionId)
|
||||||
else value.data.unsigned == maxval!(u64)
|
else value.data.unsigned == maxval!(u64)
|
||||||
if (absent) result = try ast.add_literal(state.builder, .null, span)
|
if (absent) result = try ast.add_literal(state.builder, .null, span)
|
||||||
else result = try serialize_value(state, module, checked, outcome, functions, container.element, value, span)
|
else result = try serialize_value(state, module, checked, outcome, functions, container.element, value, span, remaining)
|
||||||
} else if (!ir.aggregate_item(module, value, 0).data.boolean) {
|
} else if (!ir.aggregate_item(module, value, 0).data.boolean) {
|
||||||
result = try ast.add_literal(state.builder, .null, span)
|
result = try ast.add_literal(state.builder, .null, span)
|
||||||
} else {
|
} else {
|
||||||
child :: ir.aggregate_item(module, value, 1)
|
child :: ir.aggregate_item(module, value, 1)
|
||||||
payload :: try serialize_value(state, module, checked, outcome, functions, container.element, child, span)
|
payload :: try serialize_value(state, module, checked, outcome, functions, container.element, child, span, remaining)
|
||||||
if (payload == no_node) return no_node
|
if (payload == no_node) return no_node
|
||||||
name :: try ast.add_generated_identifier(state.builder, span,
|
name :: try ast.add_generated_identifier(state.builder, span,
|
||||||
try strpool.intern(state.strings, "some"), "some")
|
try strpool.intern(state.strings, "some"), "some")
|
||||||
@@ -2557,9 +2714,11 @@ constant_equal proc(state @mut State, left, right ast.NodeId) bool ! Error {
|
|||||||
kind :: ast.expr_kind(&state.tree, left)
|
kind :: ast.expr_kind(&state.tree, left)
|
||||||
if (kind != ast.expr_kind(&state.tree, right)) return false
|
if (kind != ast.expr_kind(&state.tree, right)) return false
|
||||||
if (kind == .literal_void) return true
|
if (kind == .literal_void) return true
|
||||||
if (kind == .typed_constant or kind == .union_constant) {
|
if (kind == .typed_constant or kind == .union_constant or kind == .slice_constant) {
|
||||||
a :: if (kind == .union_constant) ast.union_constant(&state.tree, left) else ast.typed_constant(&state.tree, left)
|
a :: if (kind == .union_constant) ast.union_constant(&state.tree, left)
|
||||||
b :: if (kind == .union_constant) ast.union_constant(&state.tree, right) else ast.typed_constant(&state.tree, right)
|
else if (kind == .slice_constant) ast.slice_constant(&state.tree, left) else ast.typed_constant(&state.tree, left)
|
||||||
|
b :: if (kind == .union_constant) ast.union_constant(&state.tree, right)
|
||||||
|
else if (kind == .slice_constant) ast.slice_constant(&state.tree, right) else ast.typed_constant(&state.tree, right)
|
||||||
return try same_type_context(state, a.type, b.type, true) and try constant_equal(state, a.value, b.value)
|
return try same_type_context(state, a.type, b.type, true) and try constant_equal(state, a.value, b.value)
|
||||||
}
|
}
|
||||||
if (kind == .slice) {
|
if (kind == .slice) {
|
||||||
@@ -2605,8 +2764,9 @@ constant_hash proc(state @mut State, id ast.NodeId) usize ! Error {
|
|||||||
kind :: ast.expr_kind(&state.tree, id)
|
kind :: ast.expr_kind(&state.tree, id)
|
||||||
hash :: enum_index(kind)
|
hash :: enum_index(kind)
|
||||||
if (kind == .literal_void) return hash
|
if (kind == .literal_void) return hash
|
||||||
if (kind == .typed_constant or kind == .union_constant) {
|
if (kind == .typed_constant or kind == .union_constant or kind == .slice_constant) {
|
||||||
value :: if (kind == .union_constant) ast.union_constant(&state.tree, id) else ast.typed_constant(&state.tree, id)
|
value :: if (kind == .union_constant) ast.union_constant(&state.tree, id)
|
||||||
|
else if (kind == .slice_constant) ast.slice_constant(&state.tree, id) else ast.typed_constant(&state.tree, id)
|
||||||
return mix(mix(hash, try type_hash_context(state, value.type, true)), try constant_hash(state, value.value))
|
return mix(mix(hash, try type_hash_context(state, value.type, true)), try constant_hash(state, value.value))
|
||||||
}
|
}
|
||||||
if (kind == .slice) {
|
if (kind == .slice) {
|
||||||
@@ -2743,7 +2903,8 @@ evaluate_ir_body proc(state @mut State, at, body, result_type ast.NodeId) Value
|
|||||||
}
|
}
|
||||||
value :: outcome.value
|
value :: outcome.value
|
||||||
source_type :: typecheck.type_of(&checked, result_type)
|
source_type :: typecheck.type_of(&checked, result_type)
|
||||||
node :: try serialize_value(state, &module, &checked, &outcome, functions.items, source_type, value, span)
|
remaining := usize(100000)
|
||||||
|
node :: try serialize_value(state, &module, &checked, &outcome, functions.items, source_type, value, span, &remaining)
|
||||||
if (node == no_node) {
|
if (node == no_node) {
|
||||||
if (state.evaluation_diagnostic == diagnostics.no_diagnostic)
|
if (state.evaluation_diagnostic == diagnostics.no_diagnostic)
|
||||||
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, .comptime_value_escape, span)
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, .comptime_value_escape, span)
|
||||||
@@ -2751,7 +2912,8 @@ evaluate_ir_body proc(state @mut State, at, body, result_type ast.NodeId) Value
|
|||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
(ir.aggregate(value.type) and source_type.kind != .slice)
|
(ir.aggregate(value.type) and source_type.kind != .slice)
|
||||||
or (source_type.kind == .slice and ast.expr_kind(&state.tree, node) == .slice)
|
or (source_type.kind == .slice and (ast.expr_kind(&state.tree, node) == .slice
|
||||||
|
or ast.expr_kind(&state.tree, node) == .slice_constant))
|
||||||
or source_type.kind == .enumeration or source_type.kind == .void
|
or source_type.kind == .enumeration or source_type.kind == .void
|
||||||
)
|
)
|
||||||
return Value{ kind = .aggregate, node = node, annotation = result_type }
|
return Value{ kind = .aggregate, node = node, annotation = result_type }
|
||||||
@@ -4847,8 +5009,8 @@ evaluate proc(state @mut State, id ast.NodeId) Value ! Error {
|
|||||||
return Value{}
|
return Value{}
|
||||||
}
|
}
|
||||||
match kind {
|
match kind {
|
||||||
.union_constant: {
|
.union_constant, .slice_constant: {
|
||||||
value :: ast.union_constant(&state.tree, id)
|
value :: if (kind == .union_constant) ast.union_constant(&state.tree, id) else ast.slice_constant(&state.tree, id)
|
||||||
return Value{ kind = .aggregate, node = id, annotation = value.type }
|
return Value{ kind = .aggregate, node = id, annotation = value.type }
|
||||||
}
|
}
|
||||||
.typed_constant: {
|
.typed_constant: {
|
||||||
@@ -5335,8 +5497,9 @@ expression_type proc(state @mut State, id ast.NodeId, depth usize) ast.NodeId !
|
|||||||
.record_type, .array_type, .slice_type, .pointer_type, .function_type, .optional_type,
|
.record_type, .array_type, .slice_type, .pointer_type, .function_type, .optional_type,
|
||||||
.enum_type, .union_type, .union_tag_type, .distinct_type, .fallible_type, .sum_type, .alias_expr:
|
.enum_type, .union_type, .union_tag_type, .distinct_type, .fallible_type, .sum_type, .alias_expr:
|
||||||
return try builtin(state, id, .type)
|
return try builtin(state, id, .type)
|
||||||
.typed_constant, .union_constant: {
|
.typed_constant, .union_constant, .slice_constant: {
|
||||||
annotation :: if (kind == .union_constant) ast.union_constant(&state.tree, id).type
|
annotation :: if (kind == .union_constant) ast.union_constant(&state.tree, id).type
|
||||||
|
else if (kind == .slice_constant) ast.slice_constant(&state.tree, id).type
|
||||||
else ast.typed_constant(&state.tree, id).type
|
else ast.typed_constant(&state.tree, id).type
|
||||||
return if (is_type(state, annotation)) annotation else try canonical_type(state, annotation)
|
return if (is_type(state, annotation)) annotation else try canonical_type(state, annotation)
|
||||||
}
|
}
|
||||||
|
|||||||
+325
-6
@@ -5399,25 +5399,344 @@ CASES = {
|
|||||||
main proc() usize { return $(make())[0].len }
|
main proc() usize { return $(make())[0].len }
|
||||||
""", b"", b"compile-time result cannot escape evaluation",
|
""", b"", b"compile-time result cannot escape evaluation",
|
||||||
),
|
),
|
||||||
"comptime_aggregate_rejects_nonbyte_slice_escape": (
|
"comptime_nonbyte_slices_materialize_callback_elements": (
|
||||||
|
"""
|
||||||
|
Callback :: alias @proc(value i16) i16
|
||||||
|
increment proc(value i16) i16 { return value + 1 }
|
||||||
|
decrement proc(value i16) i16 { return value - 1 }
|
||||||
|
make proc() []Callback {
|
||||||
|
callbacks [2]Callback :: [increment, decrement]
|
||||||
|
return callbacks[..]
|
||||||
|
}
|
||||||
|
main proc() [3]i16 {
|
||||||
|
callbacks :: $(make())
|
||||||
|
return [callbacks[0](41), callbacks[1](43), i16(callbacks.len)]
|
||||||
|
}
|
||||||
|
""", b"[42, 42, 2]\n", None,
|
||||||
|
),
|
||||||
|
"comptime_nonbyte_slices_preserve_full_width_sentinels": (
|
||||||
|
"""
|
||||||
|
make proc() [;18446744073709551615]u64 {
|
||||||
|
values [3]u64 :: [9, 4294967298, 18446744073709551615]
|
||||||
|
return values[1..2;18446744073709551615]
|
||||||
|
}
|
||||||
|
main proc() [3]u64 {
|
||||||
|
values :: $(make())
|
||||||
|
return [u64(values.len), values[0], values.ptr[1]]
|
||||||
|
}
|
||||||
|
""", b"[1, 4294967298, 18446744073709551615]\n", None,
|
||||||
|
),
|
||||||
|
"comptime_aggregate_materializes_readonly_nonbyte_slices": (
|
||||||
|
"""
|
||||||
|
make proc() []i16 {
|
||||||
|
values [2]mut i16 := [-300, 1024]
|
||||||
|
return values[..]
|
||||||
|
}
|
||||||
|
main proc() [3]i16 {
|
||||||
|
values :: $(make())
|
||||||
|
return [values[0], values[1], i16(values.len)]
|
||||||
|
}
|
||||||
|
""", b"[-300, 1024, 2]\n", None,
|
||||||
|
),
|
||||||
|
"comptime_aggregate_materializes_nominal_byte_slices": (
|
||||||
|
"""
|
||||||
|
Byte :: distinct u8
|
||||||
|
exact proc(value Byte) usize { return usize(value) }
|
||||||
|
make proc() []Byte {
|
||||||
|
values [3]Byte :: [0, 128, 255]
|
||||||
|
return values[..]
|
||||||
|
}
|
||||||
|
main proc() [4]usize {
|
||||||
|
values :: $(make())
|
||||||
|
return [exact(values[0]), exact(values[1]), exact(values[2]), values.len]
|
||||||
|
}
|
||||||
|
""", b"[0, 128, 255, 3]\n", None,
|
||||||
|
),
|
||||||
|
"comptime_nonbyte_slices_preserve_subviews_and_full_element_stride": (
|
||||||
|
"""
|
||||||
|
make proc() []i64 {
|
||||||
|
values [4]i64 :: [9, -4294967297, 4294967298, 8]
|
||||||
|
return values[1..3]
|
||||||
|
}
|
||||||
|
main proc() [3]i64 {
|
||||||
|
values :: $(make())
|
||||||
|
return [i64(values.len), values[0], values[1]]
|
||||||
|
}
|
||||||
|
""", b"[2, -4294967297, 4294967298]\n", None,
|
||||||
|
),
|
||||||
|
"comptime_nonbyte_slices_own_array_elements": (
|
||||||
|
"""
|
||||||
|
make proc() [][2]i16 {
|
||||||
|
rows [3][2]i16 :: [[9, 8], [-300, 1024], [40, 2]]
|
||||||
|
return rows[1..]
|
||||||
|
}
|
||||||
|
main proc() [5]i16 {
|
||||||
|
rows :: $(make())
|
||||||
|
return [i16(rows.len), rows[0][0], rows[0][1], rows[1][0], rows[1][1]]
|
||||||
|
}
|
||||||
|
""", b"[2, -300, 1024, 40, 2]\n", None,
|
||||||
|
),
|
||||||
|
"comptime_nonbyte_slices_own_records_callbacks_and_nested_slices": (
|
||||||
|
"""
|
||||||
|
Callback :: alias @proc(value i16) i16
|
||||||
|
Entry :: struct { children [][]i16, run Callback, values [2]i16 }
|
||||||
|
increment proc(value i16) i16 { return value + 1 }
|
||||||
|
decrement proc(value i16) i16 { return value - 1 }
|
||||||
|
make proc() []Entry {
|
||||||
|
first [2]i16 :: [11, 12]
|
||||||
|
second [2]i16 :: [21, 22]
|
||||||
|
rows [2][]i16 :: [first[..], second[..]]
|
||||||
|
entries [2]Entry :: [
|
||||||
|
{ children = rows[..], run = increment, values = [40, 2] },
|
||||||
|
{ children = rows[1..], run = decrement, values = [44, 3] },
|
||||||
|
]
|
||||||
|
return entries[..]
|
||||||
|
}
|
||||||
|
main proc() [8]i16 {
|
||||||
|
entries :: $(make())
|
||||||
|
if (
|
||||||
|
entries.len != 2 or entries[0].children.len != 2
|
||||||
|
or entries[1].children.len != 1 or entries[0].children[0].len != 2
|
||||||
|
) {
|
||||||
|
return [0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
}
|
||||||
|
return [entries[0].children[0][0], entries[0].children[0][1],
|
||||||
|
entries[0].children[1][0], entries[1].children[0][1],
|
||||||
|
entries[0].run(41), entries[1].run(43),
|
||||||
|
entries[0].values[0], entries[1].values[1]]
|
||||||
|
}
|
||||||
|
""", b"[11, 12, 21, 22, 42, 42, 40, 3]\n", None,
|
||||||
|
),
|
||||||
|
"comptime_nonbyte_slices_own_raw_record_views": (
|
||||||
|
"""
|
||||||
|
Entry :: struct { children []i16, run @proc(value i16) i16, tail i16 }
|
||||||
|
Backing :: union { entry Entry }
|
||||||
|
increment proc(value i16) i16 { return value + 1 }
|
||||||
|
make proc() []Entry {
|
||||||
|
children [2]i16 :: [40, 2]
|
||||||
|
backing Backing :: { entry = { children = children[..], run = increment, tail = 7 } }
|
||||||
|
pointer *Entry :: &backing.entry
|
||||||
|
return pointer[..1]
|
||||||
|
}
|
||||||
|
main proc() [4]i16 {
|
||||||
|
entries :: $(make())
|
||||||
|
return [entries[0].children[0], entries[0].children[1],
|
||||||
|
entries[0].run(41), entries[0].tail]
|
||||||
|
}
|
||||||
|
""", b"[40, 2, 42, 7]\n", None,
|
||||||
|
),
|
||||||
|
"comptime_nonbyte_slices_preserve_distinct_slice_identity": (
|
||||||
|
"""
|
||||||
|
Slice :: alias []i16
|
||||||
|
View :: distinct Slice
|
||||||
|
make proc() View {
|
||||||
|
values [2]i16 :: [40, 2]
|
||||||
|
return View(values[..])
|
||||||
|
}
|
||||||
|
exact proc(value View) i16 {
|
||||||
|
values :: Slice(value)
|
||||||
|
return values[0] + values[1]
|
||||||
|
}
|
||||||
|
main proc() i16 { return exact($(make())) }
|
||||||
|
""", b"42\n", None,
|
||||||
|
),
|
||||||
|
"comptime_nonbyte_slices_preserve_empty_and_zero_sized_shapes": (
|
||||||
|
"""
|
||||||
|
Empty :: struct {}
|
||||||
|
empty proc() []i16 {
|
||||||
|
values [0]i16 :: []
|
||||||
|
return values[..]
|
||||||
|
}
|
||||||
|
cells proc() []Empty {
|
||||||
|
values [3]Empty :: [{}, {}, {}]
|
||||||
|
return values[..]
|
||||||
|
}
|
||||||
|
rows proc() [][0]i16 {
|
||||||
|
values [2][0]i16 :: [[], []]
|
||||||
|
return values[..]
|
||||||
|
}
|
||||||
|
main proc() [5]usize {
|
||||||
|
none :: $(empty())
|
||||||
|
items :: $(cells())
|
||||||
|
arrays :: $(rows())
|
||||||
|
saved Empty :: items[2]
|
||||||
|
return [none.len, items.len, arrays.len, arrays[0].len, arrays[1].len]
|
||||||
|
}
|
||||||
|
""", b"[0, 3, 2, 0, 0]\n", None,
|
||||||
|
),
|
||||||
|
"comptime_nonbyte_slices_cross_nested_optional_tagged_and_fallible_channels": (
|
||||||
|
"""
|
||||||
|
Failure :: struct { values []i16 }
|
||||||
|
Choice :: union(enum) { data struct { rows [2]?[]i16 }, empty void }
|
||||||
|
Packet :: struct { choice ??Choice, result []i16 ! Failure }
|
||||||
|
make proc(fail bool) Packet {
|
||||||
|
values [2]i16 :: [-300, 1024]
|
||||||
|
result []i16 ! Failure := values[..]
|
||||||
|
if (fail) result = Failure { values = values[1..] }
|
||||||
|
return {
|
||||||
|
choice = some!(some!(Choice { data = { rows = [values[..1], null] } })),
|
||||||
|
result = result,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main proc() [5]i16 {
|
||||||
|
good :: $(make(false))
|
||||||
|
bad :: $(make(true))
|
||||||
|
choice :: good.choice??
|
||||||
|
if (choice.data.rows[1] != null) return [0, 0, 0, 0, 0]
|
||||||
|
success :: good.result catch { return [1, 1, 1, 1, 1] }
|
||||||
|
failure :: bad.result catch |err| err.values
|
||||||
|
return [choice.data.rows[0]?[0], success[0], success[1],
|
||||||
|
failure[0], i16(failure.len)]
|
||||||
|
}
|
||||||
|
""", b"[-300, -300, 1024, 1024, 1]\n", None,
|
||||||
|
),
|
||||||
|
"comptime_nonbyte_slices_normalize_cache_identity_and_generated_defaults": (
|
||||||
|
"""
|
||||||
|
direct [2]i16 :: [40, 2]
|
||||||
|
make proc(padded bool) []i16 {
|
||||||
|
if (padded) {
|
||||||
|
prefixed [4]i16 :: [9, 40, 2, 8]
|
||||||
|
return prefixed[1..3]
|
||||||
|
}
|
||||||
|
values [2]mut i16 := [40, 2]
|
||||||
|
return values[..]
|
||||||
|
}
|
||||||
|
other proc() []i16 {
|
||||||
|
values [2]i16 :: [40, 3]
|
||||||
|
return values[..]
|
||||||
|
}
|
||||||
|
Box proc($values []i16) type { return struct { values []i16 = values } }
|
||||||
|
Generated proc() type {
|
||||||
|
return struct_type!(.auto, ["values"], [[]i16], [make(false)])
|
||||||
|
}
|
||||||
|
main proc() [4]i16 {
|
||||||
|
first Box(make(false)) :: {}
|
||||||
|
same Box(make(true)) :: first
|
||||||
|
literal Box(direct[..]) :: same
|
||||||
|
generated Generated() :: {}
|
||||||
|
if (Box(make(false)) == Box(other())) return [0, 0, 0, 0]
|
||||||
|
return [literal.values[0], literal.values[1],
|
||||||
|
i16(literal.values.len), generated.values[0] + generated.values[1]]
|
||||||
|
}
|
||||||
|
""", b"[40, 2, 2, 42]\n", None,
|
||||||
|
),
|
||||||
|
"comptime_nonbyte_slice_backing_outlives_runtime_helper_returns": (
|
||||||
|
"""
|
||||||
|
make proc(seed i16) []i16 {
|
||||||
|
values [2]i16 :: [seed, seed + 1]
|
||||||
|
return values[..]
|
||||||
|
}
|
||||||
|
first proc() []i16 { return $(make(40)) }
|
||||||
|
second proc() []i16 { return $(make(100)) }
|
||||||
|
disturb proc(seed i16) i16 {
|
||||||
|
values [8]i16 :: [seed, 2, 3, 4, 5, 6, 7, 8]
|
||||||
|
return values[0] + values[7]
|
||||||
|
}
|
||||||
|
main proc() [5]i16 {
|
||||||
|
left :: first()
|
||||||
|
right :: second()
|
||||||
|
noise :: disturb(200)
|
||||||
|
return [left[0], left[1], right[0], right[1], noise]
|
||||||
|
}
|
||||||
|
""", b"[40, 41, 100, 101, 208]\n", None,
|
||||||
|
),
|
||||||
|
"comptime_nonbyte_slices_reject_uninitialized_elements": (
|
||||||
|
"""
|
||||||
|
make proc() []i16 {
|
||||||
|
values [2]mut i16 := undefined
|
||||||
|
values[0] = 42
|
||||||
|
return values[..]
|
||||||
|
}
|
||||||
|
main proc() usize { return $(make()).len }
|
||||||
|
""", b"", b"local value was read before initialization",
|
||||||
|
),
|
||||||
|
"comptime_nonbyte_slices_reject_oversized_pointer_lengths": (
|
||||||
"""
|
"""
|
||||||
make proc() []i16 {
|
make proc() []i16 {
|
||||||
values [1]i16 :: [42]
|
values [1]i16 :: [42]
|
||||||
|
return values.ptr[..maxval!(usize)]
|
||||||
|
}
|
||||||
|
main proc() usize { return $(make()).len }
|
||||||
|
""", b"", b"compile-time execution exceeds its recursion or step limit",
|
||||||
|
),
|
||||||
|
"comptime_zero_sized_slices_reject_oversized_pointer_lengths": (
|
||||||
|
"""
|
||||||
|
Empty :: struct {}
|
||||||
|
make proc() []Empty {
|
||||||
|
values [1]Empty :: [{}]
|
||||||
|
return values.ptr[..maxval!(usize)]
|
||||||
|
}
|
||||||
|
main proc() usize { return $(make()).len }
|
||||||
|
""", b"", b"compile-time execution exceeds its recursion or step limit",
|
||||||
|
),
|
||||||
|
"comptime_nonbyte_slices_bound_cyclic_retained_storage": (
|
||||||
|
"""
|
||||||
|
Node :: struct { children []Node }
|
||||||
|
make proc() []Node {
|
||||||
|
values [1]mut Node := undefined
|
||||||
|
values[0] = { children = values[..] }
|
||||||
|
return values[..]
|
||||||
|
}
|
||||||
|
main proc() usize { return $(make()).len }
|
||||||
|
""", b"", b"compile-time execution exceeds its recursion or step limit",
|
||||||
|
),
|
||||||
|
"comptime_nonbyte_slices_reject_pointer_lengths_beyond_backing": (
|
||||||
|
"""
|
||||||
|
make proc() []i16 {
|
||||||
|
values [1]i16 :: [42]
|
||||||
|
return values.ptr[..2]
|
||||||
|
}
|
||||||
|
main proc() usize { return $(make()).len }
|
||||||
|
""", b"", b"compile-time result cannot escape evaluation",
|
||||||
|
),
|
||||||
|
"comptime_nonbyte_slices_reject_mutable_escape": (
|
||||||
|
"""
|
||||||
|
make proc() []mut i16 {
|
||||||
|
values [1]mut i16 := [42]
|
||||||
return values[..]
|
return values[..]
|
||||||
}
|
}
|
||||||
main proc() usize { return $(make()).len }
|
main proc() usize { return $(make()).len }
|
||||||
""", b"", b"compile-time result cannot escape evaluation",
|
""", b"", b"compile-time result cannot escape evaluation",
|
||||||
),
|
),
|
||||||
"comptime_aggregate_rejects_nominal_byte_slice_escape": (
|
"comptime_nonbyte_slices_reject_nonnull_pointer_element_escape": (
|
||||||
"""
|
"""
|
||||||
Byte :: distinct u8
|
make proc() []@i16 {
|
||||||
make proc() []Byte {
|
value i16 :: 42
|
||||||
values [1]Byte :: [42]
|
pointers [1]@i16 :: [&value]
|
||||||
return values[..]
|
return pointers[..]
|
||||||
}
|
}
|
||||||
main proc() usize { return $(make()).len }
|
main proc() usize { return $(make()).len }
|
||||||
""", b"", b"compile-time result cannot escape evaluation",
|
""", b"", b"compile-time result cannot escape evaluation",
|
||||||
),
|
),
|
||||||
|
"comptime_nonbyte_sentinel_slices_own_subviews_and_empty_backing": (
|
||||||
|
"""
|
||||||
|
make proc() [;-7]i16 {
|
||||||
|
values [4]i16 :: [9, -300, 1024, -7]
|
||||||
|
return values[1..3;-7]
|
||||||
|
}
|
||||||
|
empty proc() [;1024]i16 {
|
||||||
|
values [1]i16 :: [1024]
|
||||||
|
return values[..0;1024]
|
||||||
|
}
|
||||||
|
main proc() [6]i16 {
|
||||||
|
values :: $(make())
|
||||||
|
none :: $(empty())
|
||||||
|
return [i16(values.len), values[0], values[1], values.ptr[2],
|
||||||
|
i16(none.len), none.ptr[0]]
|
||||||
|
}
|
||||||
|
""", b"[2, -300, 1024, -7, 0, 1024]\n", None,
|
||||||
|
),
|
||||||
|
"comptime_nonbyte_sentinel_slices_recheck_mutated_sentinel": (
|
||||||
|
"""
|
||||||
|
make proc() [;1024]i16 {
|
||||||
|
values [2]mut i16 := [42, 1024]
|
||||||
|
view [;1024]i16 :: values[..1;1024]
|
||||||
|
values[1] = 7
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
main proc() usize { return $(make()).len }
|
||||||
|
""", b"", b"sentinel",
|
||||||
|
),
|
||||||
"comptime_nonzero_sentinel_byte_slices_own_subviews": (
|
"comptime_nonzero_sentinel_byte_slices_own_subviews": (
|
||||||
"""
|
"""
|
||||||
make proc() [;255]u8 {
|
make proc() [;255]u8 {
|
||||||
|
|||||||
Reference in New Issue
Block a user