feat: support mutable metadata slice borrowing
This commit is contained in:
@@ -132,7 +132,7 @@ 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.
|
||||||
|
|
||||||
ordinary parameters containing `type` also make a procedure compile-time-only, even when its result is a runtime value. for example, `count proc(types []type) usize { return types.len }` computes `count([i16, bool])` without runtime metadata. every input must be explicitly supplied and constant; use `$` parameters when later signature annotations depend on them. helpers can return owned scalar, aggregate and string values, recurse over metadata and use checked `try`/`catch` with scoped cleanup. plain and fallible `void` support bare returns and successful fallthrough. ordinary runtime dependencies and incomplete metadata arguments remain diagnostic; mutable metadata borrowing and general type-valued execution remain open.
|
ordinary parameters containing `type` also make a procedure compile-time-only, even when its result is a runtime value. for example, `count proc(types []type) usize { return types.len }` computes `count([i16, bool])` without runtime metadata. every input must be explicitly supplied and constant; use `$` parameters when later signature annotations depend on them. helpers can return owned scalar, aggregate and string values, recurse over metadata and use checked `try`/`catch` with scoped cleanup. plain and fallible `void` support bare returns and successful fallthrough. ordinary runtime dependencies and incomplete owned metadata arguments remain diagnostic; mutable slice parameters may borrow partially initialized backing to fill it.
|
||||||
|
|
||||||
local metadata storage uses owned compile-time snapshots alongside ordinary runtime parameters and effects. explicit annotations (`T type := i16`), inferred mutable type bindings (`T := i16`), metadata arrays/records/tuples, helper results and checked projections are erased before runtime checking. sequential updates, saved copies, static branches, bounded construction loops and scoped metadata cleanup select concrete runtime storage without emitting type values. runtime branches and loops may create independent local metadata; they cannot conditionally update an enclosing compile-time snapshot. runtime optional/collection captures and ordinary effects keep their execution semantics.
|
local metadata storage uses owned compile-time snapshots alongside ordinary runtime parameters and effects. explicit annotations (`T type := i16`), inferred mutable type bindings (`T := i16`), metadata arrays/records/tuples, helper results and checked projections are erased before runtime checking. sequential updates, saved copies, static branches, bounded construction loops and scoped metadata cleanup select concrete runtime storage without emitting type values. runtime branches and loops may create independent local metadata; they cannot conditionally update an enclosing compile-time snapshot. runtime optional/collection captures and ordinary effects keep their execution semantics.
|
||||||
|
|
||||||
@@ -145,9 +145,11 @@ compute proc(value i16) i16 {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
procedures whose metadata depends on ordinary parameters retain fully constant-input execution; every input to those calls remains explicitly supplied and constant. metadata parameters/results also retain their existing compile-time-only contract. nested storage, partial initialization, checked updates, recursion and fallible cleanup reuse type-control execution and its shared 4096-step bound. inferred arrays retain readonly elements unless their annotation permits updates; mutable bindings still permit whole-value replacement. ordinary local aliases and scalar metadata projections keep their runtime-call contract. mutable metadata borrowing remains open.
|
procedures whose metadata depends on ordinary parameters retain fully constant-input execution; every input to those calls remains explicitly supplied and constant. metadata parameters/results also retain their existing compile-time-only contract. nested storage, partial initialization, checked updates, recursion and fallible cleanup reuse type-control execution and its shared 4096-step bound. inferred arrays retain readonly elements unless their annotation permits updates; mutable bindings still permit whole-value replacement. ordinary local aliases and scalar metadata projections keep their runtime-call contract.
|
||||||
|
|
||||||
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.
|
readonly type-valued slices (`[]type` and slices of metadata records/arrays) cross compile-time procedure boundaries as owned snapshots. contextual array literals, including `[]`, construct them. local array/slice views retain live aliases with checked `usize` bounds, frozen subview offsets, indexing and `.len`; later backing writes or whole-array replacement remain visible, while by-value array/record copies stay independent. nested slices, ordinary/inline by-value iteration, erased `$` inputs and optional/fallible channels retain exact nominal leaves and content-based specialization identity.
|
||||||
|
|
||||||
|
mutable metadata slice views (`[]mut type` and slices of metadata records/arrays) borrow writable array places, including nested fields/indices. immutable slice bindings may update their mutable elements; readonly backing and enclosing places never gain write access. compile-time helpers share aliases and commit writes to caller storage, including initialization and scoped `defer`/`errdefer` cleanup. calls with mutable borrowed arguments execute afresh rather than reusing cached effects; readonly-only inputs snapshot at call entry and retain content-based nominal caching. readonly results snapshot before return cleanup, and owned arguments/results require initialized leaves. expired local views, bounds errors and runtime-controlled metadata mutation diagnose. mutable results, metadata sentinels, data-pointer borrowing and pointer captures remain unsupported; metadata has no runtime storage.
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
|
|||||||
@@ -54,8 +54,9 @@ tagged type-valued unions own active payloads across compile-time calls and eras
|
|||||||
arguments; checked payload updates preserve saved copies, and whole-value assignments
|
arguments; checked payload updates preserve saved copies, and whole-value assignments
|
||||||
initialize undefined storage or select another variant.
|
initialize undefined storage or select another variant.
|
||||||
readonly type-valued slices own nested metadata leaves across compile-time calls,
|
readonly type-valued slices own nested metadata leaves across compile-time calls,
|
||||||
checked immutable-array/slice views, by-value iteration and erased arguments;
|
by-value iteration and erased arguments. local metadata slice views borrow live
|
||||||
mutable metadata borrowing remains open.
|
array places; mutable views and helper parameters update shared backing, while
|
||||||
|
readonly results snapshot before cleanup. metadata data-pointer borrowing remains open.
|
||||||
ordinary metadata-input procedures also compute owned runtime values, including
|
ordinary metadata-input procedures also compute owned runtime values, including
|
||||||
reflected field names/counts, recursive helpers and fallible cleanup.
|
reflected field names/counts, recursive helpers and fallible cleanup.
|
||||||
explicit and inferred local metadata storage also coexist with ordinary runtime
|
explicit and inferred local metadata storage also coexist with ordinary runtime
|
||||||
@@ -71,7 +72,7 @@ with static tags and exact by-value payloads, preserving runtime dispatch and sc
|
|||||||
`typeinfo!` exposes owned compile-time array/distinct/record/enum metadata, including
|
`typeinfo!` exposes owned compile-time array/distinct/record/enum metadata, including
|
||||||
readonly field collections, exact nominal types and reflected generated storage/defaults.
|
readonly field collections, exact nominal types and reflected generated storage/defaults.
|
||||||
the existing `meta.EnumFieldStruct` and `enummap.EnumMap`, `init` and `get` work;
|
the existing `meta.EnumFieldStruct` and `enummap.EnumMap`, `init` and `get` work;
|
||||||
mutable metadata borrowing, broader materialization and full library acceptance remain open.
|
metadata data-pointer borrowing, broader materialization and full library acceptance remain open.
|
||||||
`some!` preserves nested optional presence; ordinary procedures initialize concrete
|
`some!` preserves nested optional presence; ordinary procedures initialize concrete
|
||||||
runtime `undefined` locals and compute fully initialized materializable constants.
|
runtime `undefined` locals and compute fully initialized materializable constants.
|
||||||
the existing `ArrayList` uses nested generic inference, file-local helpers and
|
the existing `ArrayList` uses nested generic inference, file-local helpers and
|
||||||
@@ -184,7 +185,8 @@ test discovery and FFI remain outside these completed slices.
|
|||||||
- [x] inferred metadata-local execution checkpoint: inferred mutable type bindings and metadata array/record/tuple locals support constant-input execution without metadata inputs, results or local annotations. literal/helper results, checked field/index/optional projections, yielding blocks and conditional/fallible selection preserve owned copies, nominal leaves, whole-value replacement, loops, recursion and scoped cleanup through interpreter/native execution. inferred array elements remain readonly; ordinary local aliases and scalar metadata projections retain runtime parameters. ordinary fallible success/error snapshots use checked channel discrimination rather than assuming every payload carries a nominal type. imported private types, source/session disposal and repeat compilation preserve ownership. independent runtime parameters and metadata-local entry points are supported below.
|
- [x] inferred metadata-local execution checkpoint: inferred mutable type bindings and metadata array/record/tuple locals support constant-input execution without metadata inputs, results or local annotations. literal/helper results, checked field/index/optional projections, yielding blocks and conditional/fallible selection preserve owned copies, nominal leaves, whole-value replacement, loops, recursion and scoped cleanup through interpreter/native execution. inferred array elements remain readonly; ordinary local aliases and scalar metadata projections retain runtime parameters. ordinary fallible success/error snapshots use checked channel discrimination rather than assuming every payload carries a nominal type. imported private types, source/session disposal and repeat compilation preserve ownership. independent runtime parameters and metadata-local entry points are supported below.
|
||||||
- [x] formatted-IO acceptance checkpoint: existing `std/io.print` parses constant formats and expands tuple fields through interpreter/native execution without replacement or adapted library code. escaped braces, all integer bases/boundaries, booleans, strings/arrays, sparse/aliased enums, nested scalar distinct values, runtime arguments and computed formats retain exact output; short writes, zero progress, invalid callback counts and write errors retain checked propagation. malformed formats, wrong tuple/count/type inputs and unsupported specifiers diagnose. native `f64` default/decimal/scientific formatting uses imported variadic `snprintf` at `-O0`/`-O2`, while interpreter FFI remains a demanded trap. character literals preserve Unicode code points and byte escapes; newline-separated conditional branches and package-local visibility match Bro. partial compile-time prefixes infer remaining arguments without overriding explicit values; pure type builtins and effect-free bound type helpers specialize guards. `rem!` preserves exact numeric types, signed-minimum remainder and checked zero divisors. immutable C byte-pointer coercions, borrowed pointer-to-array slices and optional presence retain mutability/nominal checks. inline iterables propagate constant demand through original parser calls, metadata indexing and borrowed initialization; guarded errors follow reached control flow, owned snapshots commit local mutation, contextual constant numeric locals preserve rigid boundaries, and runtime bodies/effects remain runtime. general type-valued execution, broader materialization and broader `meta` acceptance remain open.
|
- [x] formatted-IO acceptance checkpoint: existing `std/io.print` parses constant formats and expands tuple fields through interpreter/native execution without replacement or adapted library code. escaped braces, all integer bases/boundaries, booleans, strings/arrays, sparse/aliased enums, nested scalar distinct values, runtime arguments and computed formats retain exact output; short writes, zero progress, invalid callback counts and write errors retain checked propagation. malformed formats, wrong tuple/count/type inputs and unsupported specifiers diagnose. native `f64` default/decimal/scientific formatting uses imported variadic `snprintf` at `-O0`/`-O2`, while interpreter FFI remains a demanded trap. character literals preserve Unicode code points and byte escapes; newline-separated conditional branches and package-local visibility match Bro. partial compile-time prefixes infer remaining arguments without overriding explicit values; pure type builtins and effect-free bound type helpers specialize guards. `rem!` preserves exact numeric types, signed-minimum remainder and checked zero divisors. immutable C byte-pointer coercions, borrowed pointer-to-array slices and optional presence retain mutability/nominal checks. inline iterables propagate constant demand through original parser calls, metadata indexing and borrowed initialization; guarded errors follow reached control flow, owned snapshots commit local mutation, contextual constant numeric locals preserve rigid boundaries, and runtime bodies/effects remain runtime. general type-valued execution, broader materialization and broader `meta` acceptance remain open.
|
||||||
- [x] mixed metadata-local execution checkpoint: ordinary and generic procedures retain runtime scalar/pointer inputs and effects alongside erased explicit/inferred type, array and record locals. owned snapshots preserve sequential field/index/whole updates, saved copies, generated runtime storage, constant branches, bounded construction loops, static exits and scoped metadata cleanup. runtime branches/loops create independent metadata locals; optional/collection captures keep their runtime bindings. constant tagged/optional metadata selection specializes only the selected body. runtime-dependent metadata still requires fully constant-input execution, and runtime paths cannot mutate enclosing metadata snapshots. interpreter/native `-O0`/`-O2`, imported private nominal leaves, source/session disposal and repeat compilation retain ownership and diagnostics. mutable metadata borrowing and broader constant materialization remain open.
|
- [x] mixed metadata-local execution checkpoint: ordinary and generic procedures retain runtime scalar/pointer inputs and effects alongside erased explicit/inferred type, array and record locals. owned snapshots preserve sequential field/index/whole updates, saved copies, generated runtime storage, constant branches, bounded construction loops, static exits and scoped metadata cleanup. runtime branches/loops create independent metadata locals; optional/collection captures keep their runtime bindings. constant tagged/optional metadata selection specializes only the selected body. runtime-dependent metadata still requires fully constant-input execution, and runtime paths cannot mutate enclosing metadata snapshots. interpreter/native `-O0`/`-O2`, imported private nominal leaves, source/session disposal and repeat compilation retain ownership and diagnostics. mutable metadata borrowing and broader constant materialization remain open.
|
||||||
- [ ] remaining gate: mutable metadata borrowing, broader constant materialization (including address-bearing untagged unions, nonnull data pointers and mutable slices), plus remaining existing-library acceptance for broader `meta` operations. broader imported C-binding support may interleave with later gates.
|
- [x] mutable metadata slice-borrowing checkpoint: local type-valued array/slice views retain live aliases, frozen subview bounds and exact nominal leaves. mutable views update nested metadata fields/indices through immutable slice bindings, share helper parameters, initialize undefined backing and run scoped `defer`/`errdefer`; ordinary array/record copies remain independent. readonly results snapshot before return cleanup, readonly-only calls retain content-based specialization identity, and mutable borrowed calls never reuse cached effects. mixed runtime parameters/effects remain runtime; runtime-controlled metadata mutation, readonly reversal, expired places, incomplete owned snapshots and bounds/index failures diagnose. imported private leaves and repeat compilation/source disposal retain ownership through interpreter/native execution. metadata data pointers, pointer captures, mutable result materialization and sentinels remain open.
|
||||||
|
- [ ] remaining gate: metadata data-pointer borrowing and pointer captures, broader constant materialization (including address-bearing untagged unions, nonnull data pointers and mutable slices), plus remaining existing-library acceptance for broader `meta` operations. 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.
|
||||||
|
|||||||
@@ -1318,6 +1318,7 @@ readonly_type_slices_recompile_owned_private_nominal_payloads test {
|
|||||||
` saved :: $(lib.make())
|
` saved :: $(lib.make())
|
||||||
` if (lib.Box(saved) != lib.Box(lib.make())) return bool
|
` if (lib.Box(saved) != lib.Box(lib.make())) return bool
|
||||||
` if (lib.Box(saved) != lib.Box(lib.literal())) return bool
|
` if (lib.Box(saved) != lib.Box(lib.literal())) return bool
|
||||||
|
` if (lib.Box(saved) != lib.Box(lib.borrowed())) return bool
|
||||||
` Selected :: lib.types()[0]
|
` Selected :: lib.types()[0]
|
||||||
` if (Selected == Child or saved[0].child != Selected) return bool
|
` if (Selected == Child or saved[0].child != Selected) return bool
|
||||||
` return lib.Box(saved)
|
` return lib.Box(saved)
|
||||||
@@ -1357,6 +1358,16 @@ readonly_type_slices_recompile_owned_private_nominal_payloads test {
|
|||||||
` ]
|
` ]
|
||||||
` return saved
|
` return saved
|
||||||
`}
|
`}
|
||||||
|
`fill proc(values []mut Config) void {
|
||||||
|
` for values |_, index| { values[index] = { child = Child, count = 2 } }
|
||||||
|
`}
|
||||||
|
`borrowed proc() []Config {
|
||||||
|
` rows [2]mut Config := undefined
|
||||||
|
` view :: rows[..]
|
||||||
|
` fill(view)
|
||||||
|
` defer view[0].child = bool
|
||||||
|
` return view
|
||||||
|
`}
|
||||||
`Box proc(input []Config) type {
|
`Box proc(input []Config) type {
|
||||||
` return struct { values [input[0].count]input[0].child, bias i16 = i16(input[1].count) }
|
` return struct { values [input[0].count]input[0].child, bias i16 = i16(input[1].count) }
|
||||||
`}
|
`}
|
||||||
|
|||||||
@@ -108,6 +108,9 @@ State :: struct {
|
|||||||
demanded_functions []mut u8
|
demanded_functions []mut u8
|
||||||
execution_functions []mut u8
|
execution_functions []mut u8
|
||||||
execution_visited arraylist.ArrayList(ast.NodeId)
|
execution_visited arraylist.ArrayList(ast.NodeId)
|
||||||
|
metadata_views arraylist.ArrayList(MetadataView)
|
||||||
|
metadata_view_indices hashmap.AutoHashMap(ast.NodeId, usize)
|
||||||
|
metadata_runtime_depth usize = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
#! elaborates templates into cached concrete declarations in the caller-owned `builder` arena.
|
#! elaborates templates into cached concrete declarations in the caller-owned `builder` arena.
|
||||||
@@ -185,6 +188,8 @@ elaborate proc(
|
|||||||
demanded_functions = demanded_functions,
|
demanded_functions = demanded_functions,
|
||||||
execution_functions = execution_functions,
|
execution_functions = execution_functions,
|
||||||
execution_visited = arraylist.init(allocator),
|
execution_visited = arraylist.init(allocator),
|
||||||
|
metadata_views = arraylist.init(allocator),
|
||||||
|
metadata_view_indices = hashmap.init(allocator),
|
||||||
}
|
}
|
||||||
defer arraylist.deinit(&state.bindings)
|
defer arraylist.deinit(&state.bindings)
|
||||||
defer arraylist.deinit(&state.declarations)
|
defer arraylist.deinit(&state.declarations)
|
||||||
@@ -192,6 +197,8 @@ elaborate proc(
|
|||||||
defer hashmap.deinit(&state.cache)
|
defer hashmap.deinit(&state.cache)
|
||||||
defer hashmap.deinit(&state.capture_types)
|
defer hashmap.deinit(&state.capture_types)
|
||||||
defer arraylist.deinit(&state.execution_visited)
|
defer arraylist.deinit(&state.execution_visited)
|
||||||
|
defer arraylist.deinit(&state.metadata_views)
|
||||||
|
defer hashmap.deinit(&state.metadata_view_indices)
|
||||||
defer {
|
defer {
|
||||||
for (state.instances.items) |instance| mem.free(allocator, instance.arguments)
|
for (state.instances.items) |instance| mem.free(allocator, instance.arguments)
|
||||||
arraylist.deinit(&state.instances)
|
arraylist.deinit(&state.instances)
|
||||||
@@ -1759,7 +1766,7 @@ expand_inline_match proc(state @mut State, id, subject_type ast.NodeId) void ! E
|
|||||||
mapping = try mem.alloc(ast.NodeId, state.allocator, ast.node_count(&state.tree)),
|
mapping = try mem.alloc(ast.NodeId, state.allocator, ast.node_count(&state.tree)),
|
||||||
first_owned = ast.NodeId(0), result_type = no_node,
|
first_owned = ast.NodeId(0), result_type = no_node,
|
||||||
}
|
}
|
||||||
defer arraylist.deinit(&execution.locals)
|
defer type_execution_deinit(state, &execution)
|
||||||
defer mem.free(state.allocator, execution.mapping)
|
defer mem.free(state.allocator, execution.mapping)
|
||||||
span :: ast.node_span(&state.tree, inline_arm)
|
span :: ast.node_span(&state.tree, inline_arm)
|
||||||
static_capture :: if (tagged) arm.tag_capture else arm.capture
|
static_capture :: if (tagged) arm.tag_capture else arm.capture
|
||||||
@@ -2583,6 +2590,18 @@ specialize_call proc(state @mut State, call, function, expected ast.NodeId) void
|
|||||||
if (contextual or type_record_storage(state, annotation, 0))
|
if (contextual or type_record_storage(state, annotation, 0))
|
||||||
annotation = try argument_annotation(state, function, view.type, arguments)
|
annotation = try argument_annotation(state, function, view.type, arguments)
|
||||||
arguments[i] = try evaluate_expected(state, argument, annotation)
|
arguments[i] = try evaluate_expected(state, argument, annotation)
|
||||||
|
if ((view.comptime or !metadata_result) and arguments[i].kind == .aggregate
|
||||||
|
and metadata_has_view(state, arguments[i].node)) {
|
||||||
|
wrapped :: try ast.add_typed_constant(state.builder, ast.node_span(&state.tree, argument),
|
||||||
|
arguments[i].annotation, arguments[i].node)
|
||||||
|
try refresh(state)
|
||||||
|
owned :: try metadata_own(state, wrapped, argument)
|
||||||
|
if (owned == no_node or !try type_array_initialized(state, owned, argument)) {
|
||||||
|
try reject_evaluation(state, call)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
arguments[i].node = ast.typed_constant(&state.tree, owned).value
|
||||||
|
}
|
||||||
if (arguments[i].kind == .invalid) {
|
if (arguments[i].kind == .invalid) {
|
||||||
try reject_evaluation(state, call)
|
try reject_evaluation(state, call)
|
||||||
return
|
return
|
||||||
@@ -2648,13 +2667,41 @@ specialize_call proc(state @mut State, call, function, expected ast.NodeId) void
|
|||||||
annotation :: try argument_annotation(state, function, view.type, arguments)
|
annotation :: try argument_annotation(state, function, view.type, arguments)
|
||||||
try expand_expected(state, argument, annotation)
|
try expand_expected(state, argument, annotation)
|
||||||
}
|
}
|
||||||
|
borrowed := false
|
||||||
|
for arguments |argument| {
|
||||||
|
if (argument.kind != .invalid and metadata_has_view(state, argument.node)) borrowed = true
|
||||||
|
}
|
||||||
|
if (borrowed) {
|
||||||
|
readonly := true
|
||||||
|
for arguments |argument| {
|
||||||
|
if (argument.kind != .invalid and !try metadata_readonly_views(state, argument.node, argument.annotation))
|
||||||
|
readonly = false
|
||||||
|
}
|
||||||
|
if (readonly) {
|
||||||
|
for arguments |@argument| {
|
||||||
|
if (argument.kind == .invalid or !metadata_has_view(state, argument.node)) continue
|
||||||
|
wrapped :: try ast.add_typed_constant(state.builder, ast.node_span(&state.tree, argument.node),
|
||||||
|
argument.annotation, argument.node)
|
||||||
|
try refresh(state)
|
||||||
|
owned :: try metadata_own(state, wrapped, call)
|
||||||
|
if (owned == no_node or !try type_array_initialized(state, owned, call)) {
|
||||||
|
try reject_evaluation(state, call)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
argument.node = ast.typed_constant(&state.tree, owned).value
|
||||||
|
}
|
||||||
|
borrowed = false
|
||||||
|
}
|
||||||
|
}
|
||||||
demand_key :: if (demanded) usize(1) else usize(0)
|
demand_key :: if (demanded) usize(1) else usize(0)
|
||||||
key := mix(usize(function), demand_key)
|
key := mix(usize(function), demand_key)
|
||||||
for arguments |argument| {
|
for arguments |argument| {
|
||||||
if (argument.kind != .invalid) key = mix(key, try value_hash(state, argument))
|
if (argument.kind != .invalid) key = mix(key, try value_hash(state, argument))
|
||||||
}
|
}
|
||||||
head := no_index
|
head := no_index
|
||||||
|
if (!borrowed) {
|
||||||
if (hashmap.get(&state.cache, key)) |found| head = found
|
if (hashmap.get(&state.cache, key)) |found| head = found
|
||||||
|
}
|
||||||
current := head
|
current := head
|
||||||
instance := no_index
|
instance := no_index
|
||||||
while (current != no_index) {
|
while (current != no_index) {
|
||||||
@@ -2688,7 +2735,9 @@ specialize_call proc(state @mut State, call, function, expected ast.NodeId) void
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
# cache heads are replaced without reallocating a second entry for the same hash.
|
# cache heads are replaced without reallocating a second entry for the same hash.
|
||||||
if (head != no_index) {
|
if (borrowed) {
|
||||||
|
# live places carry effects and must never reuse a cached execution.
|
||||||
|
} else if (head != no_index) {
|
||||||
for (state.cache.entries) |@entry| {
|
for (state.cache.entries) |@entry| {
|
||||||
if (entry.hash != 0 and entry.key == key) {
|
if (entry.hash != 0 and entry.key == key) {
|
||||||
entry.value = instance
|
entry.value = instance
|
||||||
@@ -3640,6 +3689,7 @@ TypeExecution :: struct {
|
|||||||
mapping []mut ast.NodeId
|
mapping []mut ast.NodeId
|
||||||
first_owned ast.NodeId
|
first_owned ast.NodeId
|
||||||
returned ast.NodeId = no_node
|
returned ast.NodeId = no_node
|
||||||
|
view_identity ast.NodeId = no_node
|
||||||
result_type ast.NodeId
|
result_type ast.NodeId
|
||||||
failed bool = false
|
failed bool = false
|
||||||
yielded ast.NodeId = no_node
|
yielded ast.NodeId = no_node
|
||||||
@@ -3649,6 +3699,8 @@ TypeExecution :: struct {
|
|||||||
cleanup_depth usize = 0
|
cleanup_depth usize = 0
|
||||||
cleanup_loop_depth usize = 0
|
cleanup_loop_depth usize = 0
|
||||||
evaluate_values bool = false
|
evaluate_values bool = false
|
||||||
|
# dispatch snapshots must retain match shape until ordinary arm validation.
|
||||||
|
resolve_views bool = true
|
||||||
pending_flow TypeFlow = .next
|
pending_flow TypeFlow = .next
|
||||||
loop_depth usize = 0
|
loop_depth usize = 0
|
||||||
inline_body ast.NodeId = no_node
|
inline_body ast.NodeId = no_node
|
||||||
@@ -3659,6 +3711,442 @@ TypeExecution :: struct {
|
|||||||
residual_flow TypeFlow = .next
|
residual_flow TypeFlow = .next
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# unique empty AST literals identify views in metadata_view_indices. owner teardown
|
||||||
|
# invalidates handles before its stack frame disappears; only owned leaves reach IR.
|
||||||
|
@hide:file
|
||||||
|
MetadataView :: struct {
|
||||||
|
owner @mut TypeExecution
|
||||||
|
owner_identity ast.NodeId = no_node
|
||||||
|
place ast.NodeId
|
||||||
|
start usize
|
||||||
|
count usize
|
||||||
|
active bool = true
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide:file
|
||||||
|
metadata_view_index proc(state @State, id ast.NodeId) usize {
|
||||||
|
if (id == no_node) return no_index
|
||||||
|
if (ast.node_class(&state.tree, id) != .expression) return no_index
|
||||||
|
marker :: if (ast.expr_kind(&state.tree, id) == .typed_constant)
|
||||||
|
ast.typed_constant(&state.tree, id).value else id
|
||||||
|
if (hashmap.get(&state.metadata_view_indices, marker)) |index| return index
|
||||||
|
return no_index
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide:file
|
||||||
|
metadata_has_view proc(state @State, id ast.NodeId) bool {
|
||||||
|
if (id == no_node or state.metadata_views.items.len == 0) return false
|
||||||
|
if (metadata_view_index(state, id) != no_index) return true
|
||||||
|
for (0..ast.child_count(&state.tree, id)) |i| {
|
||||||
|
if (metadata_has_view(state, ast.child(&state.tree, id, i))) return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide:file
|
||||||
|
metadata_uses_views proc(state @State, execution @TypeExecution, id ast.NodeId) bool {
|
||||||
|
if (type_call_metadata_borrows(state, execution, id)) return true
|
||||||
|
for (execution.locals.items) |local| {
|
||||||
|
if (metadata_has_view(state, local.value) and metadata_reads(state, id, local.declaration, 0))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide:file
|
||||||
|
type_execution_deinit proc(state @mut State, execution @mut TypeExecution) void {
|
||||||
|
if (execution.view_identity != no_node) {
|
||||||
|
for (state.metadata_views.items) |@view| {
|
||||||
|
if (view.owner_identity == execution.view_identity) view.active = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arraylist.deinit(&execution.locals)
|
||||||
|
}
|
||||||
|
|
||||||
|
# handles refer to live type-control places, never to temporary interpreter storage.
|
||||||
|
@hide:file
|
||||||
|
metadata_view_read proc(state @mut State, index usize, at ast.NodeId) ast.NodeId ! Error {
|
||||||
|
view :: state.metadata_views.items[index]
|
||||||
|
if (view.active) {
|
||||||
|
root :: assignment_root(state, view.place)
|
||||||
|
for (view.owner.locals.items) |local| {
|
||||||
|
if (local.declaration != root) continue
|
||||||
|
result :: try type_collection_read(state, view.owner, view.place)
|
||||||
|
if (!try expand_type_matches(state, result)) return no_node
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
||||||
|
.comptime_value_escape, ast.node_span(&state.tree, at))
|
||||||
|
return no_node
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide:file
|
||||||
|
metadata_view_snapshot proc(state @mut State, value, at ast.NodeId) ast.NodeId ! Error {
|
||||||
|
index :: metadata_view_index(state, value)
|
||||||
|
if (index == no_index) return value
|
||||||
|
view :: state.metadata_views.items[index]
|
||||||
|
annotation :: ast.typed_constant(&state.tree, value).type
|
||||||
|
span :: ast.node_span(&state.tree, at)
|
||||||
|
source :: try metadata_view_read(state, index, at)
|
||||||
|
if (source == no_node) return no_node
|
||||||
|
array :: ast.typed_constant(&state.tree, source).value
|
||||||
|
count :: ast.array_element_count(&state.tree, array)
|
||||||
|
if (view.start > count or view.count > count - view.start) {
|
||||||
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
||||||
|
.invalid_slice_bounds, ast.node_span(&state.tree, at))
|
||||||
|
return no_node
|
||||||
|
}
|
||||||
|
elements := arraylist.init(ast.NodeId, state.allocator)
|
||||||
|
defer arraylist.deinit(&elements)
|
||||||
|
for (view.start..(view.start + view.count)) |i| {
|
||||||
|
if (!try type_step(state, at)) return no_node
|
||||||
|
try arraylist.append(&elements, ast.array_element(&state.tree, array, i))
|
||||||
|
}
|
||||||
|
literal :: try ast.add_array_literal(state.builder, span, elements.items)
|
||||||
|
result :: try ast.add_typed_constant(state.builder, span, annotation, literal)
|
||||||
|
try refresh(state)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
# readonly boundary values own their leaves before cleanup invalidates local places.
|
||||||
|
@hide:file
|
||||||
|
metadata_own proc(state @mut State, input, at ast.NodeId) ast.NodeId ! Error {
|
||||||
|
value := input
|
||||||
|
if (!metadata_has_view(state, value)) return value
|
||||||
|
if (metadata_view_index(state, value) != no_index) {
|
||||||
|
if (ast.expr_kind(&state.tree, value) != .typed_constant) return no_node
|
||||||
|
type :: try canonical_type(state, ast.typed_constant(&state.tree, value).type)
|
||||||
|
if (ast.slice_type(&state.tree, type).mutable) {
|
||||||
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
||||||
|
.comptime_value_escape, ast.node_span(&state.tree, at))
|
||||||
|
return no_node
|
||||||
|
}
|
||||||
|
value = try metadata_view_snapshot(state, value, at)
|
||||||
|
if (value == no_node) return no_node
|
||||||
|
if (!metadata_has_view(state, value)) return value
|
||||||
|
}
|
||||||
|
mapping :: try mem.alloc(ast.NodeId, state.allocator, ast.node_count(&state.tree))
|
||||||
|
defer mem.free(state.allocator, mapping)
|
||||||
|
mem.set(mapping, no_node)
|
||||||
|
for (0..ast.child_count(&state.tree, value)) |i| {
|
||||||
|
child :: ast.child(&state.tree, value, i)
|
||||||
|
owned :: if (metadata_has_view(state, child)) try metadata_own(state, child, at) else child
|
||||||
|
if (owned == no_node) return no_node
|
||||||
|
mapping[usize(child)] = owned
|
||||||
|
}
|
||||||
|
result :: try ast.clone_subtree(state.builder, value, mapping)
|
||||||
|
try refresh(state)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide:file
|
||||||
|
metadata_readonly_views proc(state @mut State, id, annotation ast.NodeId) bool ! Error {
|
||||||
|
if (!metadata_has_view(state, id)) return true
|
||||||
|
if (metadata_view_index(state, id) != no_index) {
|
||||||
|
type := annotation
|
||||||
|
if (ast.node_class(&state.tree, id) == .expression
|
||||||
|
and ast.expr_kind(&state.tree, id) == .typed_constant)
|
||||||
|
type = ast.typed_constant(&state.tree, id).type
|
||||||
|
type = try canonical_type(state, type)
|
||||||
|
return (type != no_node and ast.expr_kind(&state.tree, type) == .slice_type
|
||||||
|
and !ast.slice_type(&state.tree, type).mutable)
|
||||||
|
}
|
||||||
|
for (0..ast.child_count(&state.tree, id)) |i| {
|
||||||
|
if (!try metadata_readonly_views(state, ast.child(&state.tree, id, i), no_node)) return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide:file
|
||||||
|
MetadataPlace :: struct {
|
||||||
|
owner @mut TypeExecution
|
||||||
|
place ast.NodeId
|
||||||
|
mutable bool
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide:file
|
||||||
|
metadata_place proc(
|
||||||
|
state @mut State, execution @mut TypeExecution, place ast.NodeId,
|
||||||
|
) MetadataPlace ! Error {
|
||||||
|
kind :: ast.expr_kind(&state.tree, place)
|
||||||
|
root :: assignment_root(state, place)
|
||||||
|
if (kind != .selection and kind != .index) {
|
||||||
|
return MetadataPlace{ owner = execution, place = place,
|
||||||
|
mutable = (root != no_node and ast.node_class(&state.tree, root) == .statement
|
||||||
|
and ast.statement_decl(&state.tree, root).mutability == .variable) }
|
||||||
|
}
|
||||||
|
parent :: if (kind == .index) ast.index_expr(&state.tree, place).base
|
||||||
|
else ast.selection_expr(&state.tree, place).base
|
||||||
|
base :: try type_collection_read(state, execution, parent)
|
||||||
|
if (!try expand_type_matches(state, base))
|
||||||
|
return MetadataPlace{ owner = execution, place = no_node, mutable = false }
|
||||||
|
borrowed :: metadata_view_index(state, base)
|
||||||
|
if (kind == .index and borrowed != no_index) {
|
||||||
|
view :: state.metadata_views.items[borrowed]
|
||||||
|
if (try metadata_view_read(state, borrowed, place) == no_node)
|
||||||
|
return MetadataPlace{ owner = execution, place = no_node, mutable = false }
|
||||||
|
offset :: try type_array_index(state, ast.index_expr(&state.tree, place).index, view.count, false)
|
||||||
|
if (offset == no_index)
|
||||||
|
return MetadataPlace{ owner = execution, place = no_node, mutable = false }
|
||||||
|
span :: ast.node_span(&state.tree, place)
|
||||||
|
type :: try canonical_type(state, ast.typed_constant(&state.tree, base).type)
|
||||||
|
mutable :: ast.slice_type(&state.tree, type).mutable
|
||||||
|
index :: try ast.add_integer(state.builder, span, u64(view.start + offset))
|
||||||
|
result :: try ast.add_index(state.builder, span, view.place, index)
|
||||||
|
try refresh(state)
|
||||||
|
return MetadataPlace{ owner = view.owner, place = result, mutable = mutable }
|
||||||
|
}
|
||||||
|
result := try metadata_place(state, execution, parent)
|
||||||
|
if (result.place == no_node) return result
|
||||||
|
if (kind == .index) {
|
||||||
|
type :: try canonical_type(state, try expression_type(state, base, 0))
|
||||||
|
if (type == no_node or ast.expr_kind(&state.tree, type) != .array_type) {
|
||||||
|
result.mutable = false
|
||||||
|
} else result.mutable = result.mutable and ast.array_type(&state.tree, type).mutable
|
||||||
|
result.place = try ast.add_index(state.builder, ast.node_span(&state.tree, place),
|
||||||
|
result.place, ast.index_expr(&state.tree, place).index)
|
||||||
|
} else result.place = try ast.add_selection(state.builder, ast.node_span(&state.tree, place),
|
||||||
|
result.place, ast.selection_expr(&state.tree, place).member)
|
||||||
|
try refresh(state)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide:file
|
||||||
|
metadata_view_create proc(
|
||||||
|
state @mut State, execution @mut TypeExecution, id ast.NodeId,
|
||||||
|
) ast.NodeId ! Error {
|
||||||
|
view :: ast.slice_expr(&state.tree, id)
|
||||||
|
normalized :: try type_assignment_target(state, execution, view.base)
|
||||||
|
if (normalized == no_node) return no_node
|
||||||
|
place :: try type_collection_place(state, execution, normalized)
|
||||||
|
source := no_node
|
||||||
|
if (place != no_node) source = try type_collection_read(state, execution, place)
|
||||||
|
else source = try type_snapshot(state, execution, normalized)
|
||||||
|
if (source == no_node or !try expand_type_matches(state, source)) return no_node
|
||||||
|
annotation :: try expression_type(state, source, 0)
|
||||||
|
if (!type_record_storage(state, annotation, 0)) return no_node
|
||||||
|
if (view.sentinel != null) {
|
||||||
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
||||||
|
.invalid_operand, ast.node_span(&state.tree, id))
|
||||||
|
return no_node
|
||||||
|
}
|
||||||
|
type :: try canonical_type(state, annotation)
|
||||||
|
kind :: ast.expr_kind(&state.tree, type)
|
||||||
|
if (kind != .array_type and kind != .slice_type) return no_node
|
||||||
|
previous :: metadata_view_index(state, source)
|
||||||
|
if (place == no_node and previous == no_index) return no_node
|
||||||
|
base := MetadataView{ owner = execution, place = place, start = 0, count = 0 }
|
||||||
|
mutable := false
|
||||||
|
element := no_node
|
||||||
|
if (previous != no_index) {
|
||||||
|
base = state.metadata_views.items[previous]
|
||||||
|
mutable = ast.slice_type(&state.tree, type).mutable
|
||||||
|
element = ast.slice_type(&state.tree, type).element
|
||||||
|
} else if (kind == .array_type) {
|
||||||
|
descriptor :: ast.array_type(&state.tree, type)
|
||||||
|
storage :: try metadata_place(state, execution, place)
|
||||||
|
if (storage.place == no_node) return no_node
|
||||||
|
base.owner = storage.owner
|
||||||
|
base.place = storage.place
|
||||||
|
mutable = descriptor.mutable and storage.mutable
|
||||||
|
element = descriptor.element
|
||||||
|
base.count = ast.array_element_count(&state.tree, ast.typed_constant(&state.tree, source).value)
|
||||||
|
} else return no_node
|
||||||
|
start := usize(0)
|
||||||
|
end := base.count
|
||||||
|
if (view.start) |bound| {
|
||||||
|
snapshot :: try type_snapshot(state, execution, bound)
|
||||||
|
if (snapshot == no_node) return no_node
|
||||||
|
start = try type_array_index(state, snapshot, base.count, true)
|
||||||
|
}
|
||||||
|
if (start == no_index) return no_node
|
||||||
|
if (view.end) |bound| {
|
||||||
|
snapshot :: try type_snapshot(state, execution, bound)
|
||||||
|
if (snapshot == no_node) return no_node
|
||||||
|
end = try type_array_index(state, snapshot, base.count, true)
|
||||||
|
}
|
||||||
|
if (end == no_index) return no_node
|
||||||
|
if (start > end) {
|
||||||
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
||||||
|
.invalid_slice_bounds, ast.node_span(&state.tree, id))
|
||||||
|
return no_node
|
||||||
|
}
|
||||||
|
span :: ast.node_span(&state.tree, id)
|
||||||
|
slice :: try ast.add_slice_type(state.builder, span, element, mutable, null)
|
||||||
|
empty [0]ast.NodeId :: []
|
||||||
|
marker :: try ast.add_array_literal(state.builder, span, empty[..])
|
||||||
|
result :: try ast.add_typed_constant(state.builder, span, slice, marker)
|
||||||
|
try refresh(state)
|
||||||
|
if (base.owner.view_identity == no_node) base.owner.view_identity = marker
|
||||||
|
if (previous == no_index) base.owner_identity = base.owner.view_identity
|
||||||
|
base.start += start
|
||||||
|
base.count = end - start
|
||||||
|
index :: state.metadata_views.items.len
|
||||||
|
try arraylist.append(&state.metadata_views, base)
|
||||||
|
hashmap.put(&state.metadata_view_indices, marker, index) catch |err| {
|
||||||
|
match err {
|
||||||
|
.key_exists: unreachable
|
||||||
|
else: return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide:file
|
||||||
|
metadata_resolve_views proc(state @mut State, id ast.NodeId) bool ! Error {
|
||||||
|
if (!metadata_has_view(state, id)) return true
|
||||||
|
if (ast.node_class(&state.tree, id) == .expression
|
||||||
|
and ast.expr_kind(&state.tree, id) == .typed_constant) return true
|
||||||
|
if (ast.node_class(&state.tree, id) == .expression
|
||||||
|
and ast.expr_kind(&state.tree, id) == .binary) {
|
||||||
|
binary :: ast.binary_expr(&state.tree, id)
|
||||||
|
if (binary.operator == .logical_and or binary.operator == .logical_or) {
|
||||||
|
if (!try metadata_resolve_views(state, binary.left)) return false
|
||||||
|
if (branch_constant(state, binary.left, 0)) {
|
||||||
|
value :: try evaluate_ir(state, binary.left, try builtin(state, id, .bool))
|
||||||
|
if (value.kind != .boolean) return false
|
||||||
|
if (value.boolean == (binary.operator == .logical_or)) {
|
||||||
|
# inspect the skipped call's signature without executing its body.
|
||||||
|
right :: storage_expression_annotation(&state.tree, state.bindings.items, binary.right, 0)
|
||||||
|
if (!try same_type(state, right, try builtin(state, id, .bool))) {
|
||||||
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
||||||
|
.invalid_operand, ast.node_span(&state.tree, binary.right))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
result :: try ast.add_boolean(state.builder, ast.node_span(&state.tree, id), value.boolean)
|
||||||
|
try replace(state, id, result)
|
||||||
|
try refresh(state)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return try metadata_resolve_views(state, binary.right)
|
||||||
|
}
|
||||||
|
state.metadata_runtime_depth += 1
|
||||||
|
defer state.metadata_runtime_depth -= 1
|
||||||
|
return try metadata_resolve_views(state, binary.right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ast.node_class(&state.tree, id) == .expression
|
||||||
|
and ast.expr_kind(&state.tree, id) == .match_expr) {
|
||||||
|
subject :: ast.match_expr(&state.tree, id).subject
|
||||||
|
if (!try metadata_resolve_views(state, subject)) return false
|
||||||
|
if (try specialize_match(state, id, no_node, no_node)) {
|
||||||
|
if (binding(state, id).kind == .invalid) return false
|
||||||
|
return try metadata_resolve_views(state, id)
|
||||||
|
}
|
||||||
|
state.metadata_runtime_depth += 1
|
||||||
|
defer state.metadata_runtime_depth -= 1
|
||||||
|
for (0..ast.match_arm_count(&state.tree, id)) |i| {
|
||||||
|
if (!try metadata_resolve_views(state, ast.match_arm(&state.tree, id, i))) return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for (0..ast.child_count(&state.tree, id)) |i| {
|
||||||
|
if (!try metadata_resolve_views(state, ast.child(&state.tree, id, i))) return false
|
||||||
|
}
|
||||||
|
if (ast.node_class(&state.tree, id) != .expression) return true
|
||||||
|
if (ast.expr_kind(&state.tree, id) == .call) {
|
||||||
|
try expand(state, id)
|
||||||
|
if (binding(state, id).kind == .invalid) {
|
||||||
|
state.evaluation_diagnostic = binding(state, id).data.diagnostic
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return try type_array_projection(state, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide:file
|
||||||
|
metadata_rebase_place proc(
|
||||||
|
state @mut State, execution @mut TypeExecution, target, first, replacement ast.NodeId,
|
||||||
|
) ast.NodeId ! Error {
|
||||||
|
if (target == first) return replacement
|
||||||
|
kind :: ast.expr_kind(&state.tree, target)
|
||||||
|
base :: if (kind == .index) ast.index_expr(&state.tree, target).base
|
||||||
|
else ast.selection_expr(&state.tree, target).base
|
||||||
|
rebased :: try metadata_rebase_place(state, execution, base, first, replacement)
|
||||||
|
result := no_node
|
||||||
|
span :: ast.node_span(&state.tree, target)
|
||||||
|
if (kind == .selection)
|
||||||
|
result = try ast.add_selection(state.builder, span, rebased, ast.selection_expr(&state.tree, target).member)
|
||||||
|
else {
|
||||||
|
index :: try type_snapshot(state, execution, ast.index_expr(&state.tree, target).index)
|
||||||
|
if (index == no_node) return no_node
|
||||||
|
frozen :: try type_execution_value(state, index, try builtin(state, index, .usize))
|
||||||
|
if (frozen == no_node) return no_node
|
||||||
|
result = try ast.add_index(state.builder, span, rebased, frozen)
|
||||||
|
}
|
||||||
|
try refresh(state)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide:file
|
||||||
|
type_place_root proc(state @State, id ast.NodeId) ast.NodeId {
|
||||||
|
kind :: ast.expr_kind(&state.tree, id)
|
||||||
|
if (kind == .selection) return type_place_root(state, ast.selection_expr(&state.tree, id).base)
|
||||||
|
if (kind == .index) return type_place_root(state, ast.index_expr(&state.tree, id).base)
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide:file
|
||||||
|
metadata_assignment proc(
|
||||||
|
state @mut State, execution @mut TypeExecution, target, input ast.NodeId,
|
||||||
|
) bool ! Error {
|
||||||
|
first := target
|
||||||
|
while (true) {
|
||||||
|
kind :: ast.expr_kind(&state.tree, first)
|
||||||
|
if (kind != .selection and kind != .index) return false
|
||||||
|
base :: if (kind == .index) ast.index_expr(&state.tree, first).base
|
||||||
|
else ast.selection_expr(&state.tree, first).base
|
||||||
|
if (kind == .index) {
|
||||||
|
value :: try type_snapshot(state, execution, base)
|
||||||
|
if (value == no_node or !try expand_type_matches(state, value)) return true
|
||||||
|
index :: metadata_view_index(state, value)
|
||||||
|
if (index != no_index) {
|
||||||
|
view :: state.metadata_views.items[index]
|
||||||
|
type :: try canonical_type(state, ast.typed_constant(&state.tree, value).type)
|
||||||
|
if (!ast.slice_type(&state.tree, type).mutable) {
|
||||||
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
||||||
|
.invalid_index_assignment, ast.node_span(&state.tree, target))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (try metadata_view_read(state, index, target) == no_node) return true
|
||||||
|
if (state.metadata_runtime_depth > 0) {
|
||||||
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
||||||
|
.comptime_argument_required, ast.node_span(&state.tree, target))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
snapshot :: try type_snapshot(state, execution, ast.index_expr(&state.tree, first).index)
|
||||||
|
if (snapshot == no_node) return true
|
||||||
|
offset :: try type_array_index(state, snapshot, view.count, false)
|
||||||
|
if (offset == no_index) return true
|
||||||
|
span :: ast.node_span(&state.tree, first)
|
||||||
|
frozen :: try ast.add_integer(state.builder, span, u64(view.start + offset))
|
||||||
|
place :: try ast.add_index(state.builder, span, view.place, frozen)
|
||||||
|
try refresh(state)
|
||||||
|
rebased :: try metadata_rebase_place(state, execution, target, first, place)
|
||||||
|
if (rebased == no_node) return true
|
||||||
|
expression :: try type_snapshot(state, execution, input)
|
||||||
|
if (expression == no_node or !try expand_type_matches(state, expression)) return true
|
||||||
|
root :: assignment_root(state, view.place)
|
||||||
|
for (view.owner.locals.items) |local, i| {
|
||||||
|
if (local.declaration != root) continue
|
||||||
|
if (view.owner.residual != null and i < view.owner.residual_boundary) {
|
||||||
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
||||||
|
.comptime_argument_required, ast.node_span(&state.tree, target))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
updated :: try type_array_update(state, view.owner, rebased,
|
||||||
|
type_place_root(state, view.place), local.value, expression)
|
||||||
|
if (updated != no_node) view.owner.locals.items[i].value = updated
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
first = base
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
TypeFlow :: enum { next, returned, yielded, break, continue, invalid }
|
TypeFlow :: enum { next, returned, yielded, break, continue, invalid }
|
||||||
|
|
||||||
@@ -3900,7 +4388,7 @@ expand_runtime_metadata proc(state @mut State, function ast.NodeId) void ! Error
|
|||||||
mapping = try mem.alloc(ast.NodeId, state.allocator, ast.node_count(&state.tree)),
|
mapping = try mem.alloc(ast.NodeId, state.allocator, ast.node_count(&state.tree)),
|
||||||
first_owned = ast.NodeId(0), result_type = view.return_type, residual_body = view.body,
|
first_owned = ast.NodeId(0), result_type = view.return_type, residual_body = view.body,
|
||||||
}
|
}
|
||||||
defer arraylist.deinit(&execution.locals)
|
defer type_execution_deinit(state, &execution)
|
||||||
defer mem.free(state.allocator, execution.mapping)
|
defer mem.free(state.allocator, execution.mapping)
|
||||||
mem.set(execution.mapping, no_node)
|
mem.set(execution.mapping, no_node)
|
||||||
body := try residual_block(state, &execution, view.body, 0)
|
body := try residual_block(state, &execution, view.body, 0)
|
||||||
@@ -3933,7 +4421,7 @@ return_type_value proc(state @mut State, block, first_owned, result_type ast.Nod
|
|||||||
result_type = result_type,
|
result_type = result_type,
|
||||||
evaluate_values = true,
|
evaluate_values = true,
|
||||||
}
|
}
|
||||||
defer arraylist.deinit(&execution.locals)
|
defer type_execution_deinit(state, &execution)
|
||||||
defer mem.free(state.allocator, execution.mapping)
|
defer mem.free(state.allocator, execution.mapping)
|
||||||
mem.set(execution.mapping, no_node)
|
mem.set(execution.mapping, no_node)
|
||||||
flow :: try execute_type_block(state, &execution, block)
|
flow :: try execute_type_block(state, &execution, block)
|
||||||
@@ -3967,7 +4455,7 @@ evaluate_type_value_block proc(state @mut State, expression, annotation ast.Node
|
|||||||
result_type = no_node,
|
result_type = no_node,
|
||||||
evaluate_values = true,
|
evaluate_values = true,
|
||||||
}
|
}
|
||||||
defer arraylist.deinit(&execution.locals)
|
defer type_execution_deinit(state, &execution)
|
||||||
defer mem.free(state.allocator, execution.mapping)
|
defer mem.free(state.allocator, execution.mapping)
|
||||||
mem.set(execution.mapping, no_node)
|
mem.set(execution.mapping, no_node)
|
||||||
return try execute_type_value_block(state, &execution, ast.block_expr(&state.tree, expression), annotation)
|
return try execute_type_value_block(state, &execution, ast.block_expr(&state.tree, expression), annotation)
|
||||||
@@ -4079,41 +4567,75 @@ prepare_type_snapshot proc(state @mut State, execution @mut TypeExecution, id as
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (execution.evaluate_values and ast.node_class(&state.tree, id) == .expression) {
|
if (execution.residual != null and !execution.evaluate_values
|
||||||
|
and ast.node_class(&state.tree, id) == .expression and metadata_uses_views(state, execution, id)) {
|
||||||
kind :: ast.expr_kind(&state.tree, id)
|
kind :: ast.expr_kind(&state.tree, id)
|
||||||
if (kind == .call and type_call_borrows(state, execution, id)) {
|
if (kind == .binary) {
|
||||||
execution.mapping[usize(id)] = try type_borrow_call(state, execution, id)
|
binary :: ast.binary_expr(&state.tree, id)
|
||||||
if (execution.mapping[usize(id)] == no_node) execution.pending_flow = .invalid
|
if (binary.operator == .logical_and or binary.operator == .logical_or) {
|
||||||
|
left :: try type_snapshot(state, execution, binary.left)
|
||||||
|
if (left == no_node) return
|
||||||
|
known :: branch_constant(state, left, 0)
|
||||||
|
if (known) {
|
||||||
|
value :: try evaluate_ir(state, left, try builtin(state, id, .bool))
|
||||||
|
if (value.kind != .boolean) {
|
||||||
|
execution.pending_flow = .invalid
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (kind == .slice) {
|
if (value.boolean == (binary.operator == .logical_or)) {
|
||||||
base :: ast.slice_expr(&state.tree, id).base
|
right :: storage_expression_annotation(&state.tree, state.bindings.items, binary.right, 0)
|
||||||
root := base
|
if (!try same_type(state, right, try builtin(state, id, .bool))) {
|
||||||
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,
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
||||||
.comptime_value_escape, ast.node_span(&state.tree, id))
|
.invalid_operand, ast.node_span(&state.tree, binary.right))
|
||||||
|
execution.pending_flow = .invalid
|
||||||
|
return
|
||||||
|
}
|
||||||
|
execution.mapping[usize(id)] = try ast.add_boolean(state.builder,
|
||||||
|
ast.node_span(&state.tree, id), value.boolean)
|
||||||
|
try refresh(state)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!known) state.metadata_runtime_depth += 1
|
||||||
|
defer if (!known) state.metadata_runtime_depth -= 1
|
||||||
|
right :: try type_snapshot(state, execution, binary.right)
|
||||||
|
execution.mapping[usize(binary.left)] = left
|
||||||
|
execution.mapping[usize(binary.right)] = right
|
||||||
|
if (right == no_node) execution.pending_flow = .invalid
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (kind == .match_expr) {
|
||||||
|
subject :: try type_snapshot(state, execution, ast.match_expr(&state.tree, id).subject)
|
||||||
|
if (subject == no_node) return
|
||||||
|
if (!branch_constant(state, subject, 0)) {
|
||||||
|
state.metadata_runtime_depth += 1
|
||||||
|
defer state.metadata_runtime_depth -= 1
|
||||||
|
for (0..ast.child_count(&state.tree, id)) |i|
|
||||||
|
try prepare_type_snapshot(state, execution, ast.child(&state.tree, id, i))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((execution.evaluate_values or execution.residual != null)
|
||||||
|
and ast.node_class(&state.tree, id) == .expression and ast.expr_kind(&state.tree, id) == .slice) {
|
||||||
|
result :: try metadata_view_create(state, execution, id)
|
||||||
|
if (result != no_node) {
|
||||||
|
execution.mapping[usize(id)] = result
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (state.evaluation_diagnostic != diagnostics.no_diagnostic) {
|
||||||
execution.pending_flow = .invalid
|
execution.pending_flow = .invalid
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (execution.evaluate_values and ast.node_class(&state.tree, id) == .expression) {
|
||||||
|
kind :: ast.expr_kind(&state.tree, id)
|
||||||
|
if (kind == .call and type_call_borrows(state, execution, id)
|
||||||
|
and !type_call_metadata_borrows(state, execution, id)) {
|
||||||
|
execution.mapping[usize(id)] = try type_borrow_call(state, execution, id)
|
||||||
|
if (execution.mapping[usize(id)] == no_node) 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,
|
||||||
@@ -4141,6 +4663,19 @@ prepare_type_snapshot proc(state @mut State, execution @mut TypeExecution, id as
|
|||||||
}
|
}
|
||||||
right := no_node
|
right := no_node
|
||||||
if (value.boolean == (view.operator == .logical_or)) {
|
if (value.boolean == (view.operator == .logical_or)) {
|
||||||
|
if (metadata_uses_views(state, execution, view.right)) {
|
||||||
|
annotation :: storage_expression_annotation(&state.tree, state.bindings.items, view.right, 0)
|
||||||
|
if (!try same_type(state, annotation, boolean)) {
|
||||||
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
||||||
|
.invalid_operand, ast.node_span(&state.tree, view.right))
|
||||||
|
execution.pending_flow = .invalid
|
||||||
|
return
|
||||||
|
}
|
||||||
|
execution.mapping[usize(id)] = try ast.add_boolean(state.builder,
|
||||||
|
ast.node_span(&state.tree, id), value.boolean)
|
||||||
|
try refresh(state)
|
||||||
|
return
|
||||||
|
}
|
||||||
saved :: execution.evaluate_values
|
saved :: execution.evaluate_values
|
||||||
execution.evaluate_values = false
|
execution.evaluate_values = false
|
||||||
right = try type_snapshot(state, execution, view.right)
|
right = try type_snapshot(state, execution, view.right)
|
||||||
@@ -4239,6 +4774,7 @@ type_snapshot proc(state @mut State, execution @mut TypeExecution, id ast.NodeId
|
|||||||
result :: try ast.clone_subtree(state.builder, id, execution.mapping)
|
result :: try ast.clone_subtree(state.builder, id, execution.mapping)
|
||||||
try refresh(state)
|
try refresh(state)
|
||||||
try bind_type_snapshot(state, execution, id, start)
|
try bind_type_snapshot(state, execution, id, start)
|
||||||
|
if (execution.resolve_views and !try metadata_resolve_views(state, result)) return no_node
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4354,7 +4890,7 @@ specialize_type_catch proc(state @mut State, id, annotation, expected ast.NodeId
|
|||||||
first_owned = ast.NodeId(0),
|
first_owned = ast.NodeId(0),
|
||||||
result_type = no_node,
|
result_type = no_node,
|
||||||
}
|
}
|
||||||
defer arraylist.deinit(&execution.locals)
|
defer type_execution_deinit(state, &execution)
|
||||||
defer mem.free(state.allocator, execution.mapping)
|
defer mem.free(state.allocator, execution.mapping)
|
||||||
mem.set(execution.mapping, no_node)
|
mem.set(execution.mapping, no_node)
|
||||||
if (view.capture) |capture| {
|
if (view.capture) |capture| {
|
||||||
@@ -4461,6 +4997,9 @@ type_dispatch_snapshot proc(state @mut State, execution @mut TypeExecution, id a
|
|||||||
try expand_inline_match(state, id, subject_type)
|
try expand_inline_match(state, id, subject_type)
|
||||||
if (ast.expr_kind(&state.tree, id) != .match_expr) return no_node
|
if (ast.expr_kind(&state.tree, id) != .match_expr) return no_node
|
||||||
saved :: execution.evaluate_values
|
saved :: execution.evaluate_values
|
||||||
|
saved_views :: execution.resolve_views
|
||||||
|
execution.resolve_views = false
|
||||||
|
defer execution.resolve_views = saved_views
|
||||||
execution.evaluate_values = false
|
execution.evaluate_values = false
|
||||||
defer execution.evaluate_values = saved
|
defer execution.evaluate_values = saved
|
||||||
snapshot :: try type_snapshot(state, execution, id)
|
snapshot :: try type_snapshot(state, execution, id)
|
||||||
@@ -4634,7 +5173,7 @@ expand_inline_for proc(state @mut State, id ast.NodeId) void ! Error {
|
|||||||
first_owned = ast.NodeId(0), result_type = no_node,
|
first_owned = ast.NodeId(0), result_type = no_node,
|
||||||
inline_body = loop.body, inline_blocks = &blocks,
|
inline_body = loop.body, inline_blocks = &blocks,
|
||||||
}
|
}
|
||||||
defer arraylist.deinit(&execution.locals)
|
defer type_execution_deinit(state, &execution)
|
||||||
defer mem.free(state.allocator, execution.mapping)
|
defer mem.free(state.allocator, execution.mapping)
|
||||||
mem.set(execution.mapping, no_node)
|
mem.set(execution.mapping, no_node)
|
||||||
for (0..usize(2)) |i| {
|
for (0..usize(2)) |i| {
|
||||||
@@ -4825,7 +5364,7 @@ type_match_body proc(state @mut State, id ast.NodeId, arm ast.MatchArmView) ast.
|
|||||||
first_owned = ast.NodeId(0),
|
first_owned = ast.NodeId(0),
|
||||||
result_type = no_node,
|
result_type = no_node,
|
||||||
}
|
}
|
||||||
defer arraylist.deinit(&execution.locals)
|
defer type_execution_deinit(state, &execution)
|
||||||
defer mem.free(state.allocator, execution.mapping)
|
defer mem.free(state.allocator, execution.mapping)
|
||||||
mem.set(execution.mapping, no_node)
|
mem.set(execution.mapping, no_node)
|
||||||
captured :: if (is_type(state, snapshot.type)) snapshot.value else value
|
captured :: if (is_type(state, snapshot.type)) snapshot.value else value
|
||||||
@@ -4923,7 +5462,12 @@ type_array_projection proc(state @mut State, id ast.NodeId) bool ! Error {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (ast.expr_kind(&state.tree, base) != .typed_constant) return true
|
if (ast.expr_kind(&state.tree, base) != .typed_constant) return true
|
||||||
array :: ast.typed_constant(&state.tree, base)
|
array := ast.typed_constant(&state.tree, base)
|
||||||
|
if (metadata_view_index(state, base) != no_index) {
|
||||||
|
snapshot :: try metadata_view_snapshot(state, base, id)
|
||||||
|
if (snapshot == no_node) return false
|
||||||
|
array = ast.typed_constant(&state.tree, snapshot)
|
||||||
|
}
|
||||||
type :: try canonical_type(state, array.type)
|
type :: try canonical_type(state, array.type)
|
||||||
record :: (type != no_node and (ast.expr_kind(&state.tree, type) == .record_type
|
record :: (type != no_node and (ast.expr_kind(&state.tree, type) == .record_type
|
||||||
or (type_record_storage(state, type, 0) and ast.expr_kind(&state.tree, type) == .union_type)))
|
or (type_record_storage(state, type, 0) and ast.expr_kind(&state.tree, type) == .union_type)))
|
||||||
@@ -5184,8 +5728,8 @@ type_record_check proc(state @mut State, annotation ast.NodeId) bool ! Error {
|
|||||||
}
|
}
|
||||||
if (ast.expr_kind(&state.tree, type) == .slice_type) {
|
if (ast.expr_kind(&state.tree, type) == .slice_type) {
|
||||||
descriptor :: ast.slice_type(&state.tree, type)
|
descriptor :: ast.slice_type(&state.tree, type)
|
||||||
if (descriptor.mutable or descriptor.sentinel != null) {
|
if (descriptor.sentinel != null) {
|
||||||
code diagnostics.Code :: if (descriptor.mutable) .comptime_value_escape else .invalid_operand
|
code diagnostics.Code :: .invalid_operand
|
||||||
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, code, ast.node_span(&state.tree, type))
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, code, ast.node_span(&state.tree, type))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -5555,8 +6099,7 @@ 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,
|
# local handles retain borrowing; owned readonly literals cross execution boundaries.
|
||||||
# not a snapshot that silently stops observing later element writes.
|
|
||||||
@hide:file
|
@hide:file
|
||||||
type_slice_value proc(state @mut State, expression, annotation ast.NodeId) ast.NodeId ! Error {
|
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
|
if (state.type_execution_depth == 0) state.type_execution_remaining = 4096
|
||||||
@@ -5575,8 +6118,27 @@ type_slice_value proc(state @mut State, expression, annotation ast.NodeId) ast.N
|
|||||||
if (kind == .typed_constant) {
|
if (kind == .typed_constant) {
|
||||||
actual :: ast.typed_constant(&state.tree, expression)
|
actual :: ast.typed_constant(&state.tree, expression)
|
||||||
if (try same_type(state, type, actual.type)) return expression
|
if (try same_type(state, type, actual.type)) return expression
|
||||||
|
index :: metadata_view_index(state, expression)
|
||||||
|
if (index != no_index) {
|
||||||
|
actual_type :: try canonical_type(state, actual.type)
|
||||||
|
if (ast.expr_kind(&state.tree, actual_type) == .slice_type) {
|
||||||
|
source :: ast.slice_type(&state.tree, actual_type)
|
||||||
|
if ((!descriptor.mutable or source.mutable)
|
||||||
|
and try same_type(state, descriptor.element, source.element)) {
|
||||||
|
result :: try ast.add_typed_constant(state.builder, span, type, actual.value)
|
||||||
|
try refresh(state)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, .type_mismatch, span)
|
||||||
|
return no_node
|
||||||
|
}
|
||||||
}
|
}
|
||||||
source := expression
|
source := expression
|
||||||
|
if (descriptor.mutable) {
|
||||||
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, .comptime_value_escape, span)
|
||||||
|
return no_node
|
||||||
|
}
|
||||||
start := usize(0)
|
start := usize(0)
|
||||||
end := no_index
|
end := no_index
|
||||||
if (kind == .slice) {
|
if (kind == .slice) {
|
||||||
@@ -6114,6 +6676,15 @@ type_call_borrows proc(state @State, execution @TypeExecution, call ast.NodeId)
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@hide:file
|
||||||
|
type_call_metadata_borrows proc(state @State, execution @TypeExecution, call ast.NodeId) bool {
|
||||||
|
for (execution.locals.items) |local| {
|
||||||
|
if (type_record_storage(state, local.annotation, 0)
|
||||||
|
and type_borrowed_place(state, call, local.declaration, false)) return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
TypeBorrow :: struct { index usize, previous ast.NodeId, temporary ast.NodeId }
|
TypeBorrow :: struct { index usize, previous ast.NodeId, temporary ast.NodeId }
|
||||||
|
|
||||||
@@ -6431,6 +7002,8 @@ execute_type_statement_body proc(state @mut State, execution @mut TypeExecution,
|
|||||||
expression :: try type_snapshot_expected(state, execution, value, execution.result_type)
|
expression :: try type_snapshot_expected(state, execution, value, execution.result_type)
|
||||||
if (expression == no_node) return .invalid
|
if (expression == no_node) return .invalid
|
||||||
execution.returned = try type_execution_value(state, expression, execution.result_type)
|
execution.returned = try type_execution_value(state, expression, execution.result_type)
|
||||||
|
if (execution.returned != no_node)
|
||||||
|
execution.returned = try metadata_own(state, execution.returned, value)
|
||||||
if (execution.returned != no_node
|
if (execution.returned != no_node
|
||||||
and !try type_array_initialized(state, execution.returned, value)) return .invalid
|
and !try type_array_initialized(state, execution.returned, value)) return .invalid
|
||||||
if (execution.returned != no_node) {
|
if (execution.returned != no_node) {
|
||||||
@@ -6491,6 +7064,11 @@ execute_type_statement_body proc(state @mut State, execution @mut TypeExecution,
|
|||||||
mem.set(execution.mapping[previous..], no_node)
|
mem.set(execution.mapping[previous..], no_node)
|
||||||
return try execute_type_statement(state, execution, normalized)
|
return try execute_type_statement(state, execution, normalized)
|
||||||
}
|
}
|
||||||
|
if (try metadata_assignment(state, execution, target, assignment.value)) {
|
||||||
|
if (state.evaluation_diagnostic != diagnostics.no_diagnostic or execution.pending_flow != .next)
|
||||||
|
return .invalid
|
||||||
|
return .next
|
||||||
|
}
|
||||||
root := assignment.target
|
root := assignment.target
|
||||||
indexed := true
|
indexed := true
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -6768,7 +7346,10 @@ execute_type_collection proc(state @mut State, execution @mut TypeExecution, sta
|
|||||||
collection :: try type_execution_value(state, snapshot, annotation)
|
collection :: try type_execution_value(state, snapshot, annotation)
|
||||||
if (collection == no_node) return .invalid
|
if (collection == no_node) return .invalid
|
||||||
count := Value{}
|
count := Value{}
|
||||||
if tuple {
|
borrowed :: metadata_view_index(state, collection)
|
||||||
|
if (borrowed != no_index) {
|
||||||
|
count = Value{ kind = .integer, magnitude = u64(state.metadata_views.items[borrowed].count) }
|
||||||
|
} else if tuple {
|
||||||
count = Value{ kind = .integer, magnitude = u64(ast.record_field_count(&state.tree, annotation)) }
|
count = Value{ kind = .integer, magnitude = u64(ast.record_field_count(&state.tree, annotation)) }
|
||||||
} else if (try execution_array_storage(state, annotation) or try metadata_sequence(state, annotation)) {
|
} else if (try execution_array_storage(state, annotation) or try metadata_sequence(state, annotation)) {
|
||||||
count = Value{ kind = .integer,
|
count = Value{ kind = .integer,
|
||||||
|
|||||||
Binary file not shown.
+291
-7
@@ -3375,31 +3375,301 @@ CASES = {
|
|||||||
main proc() Storage() { return [40, 2] }
|
main proc() Storage() { return [40, 2] }
|
||||||
""", b"[40, 2]\n", None,
|
""", b"[40, 2]\n", None,
|
||||||
),
|
),
|
||||||
|
"metadata_slices_live_aliases_saved_bounds_rebinding_and_array_copies": (
|
||||||
|
"""
|
||||||
|
Storage proc() type {
|
||||||
|
backing [3]mut type := [i8, bool, u8]
|
||||||
|
copied :: backing
|
||||||
|
start := usize(1)
|
||||||
|
finish := usize(2)
|
||||||
|
view []mut type := backing[..]
|
||||||
|
saved :: view[start..finish]
|
||||||
|
shared :: saved
|
||||||
|
start = 0
|
||||||
|
finish = 3
|
||||||
|
saved[0] = i16
|
||||||
|
if (backing[1] != i16 or copied[1] != bool) return bool
|
||||||
|
backing = [bool, i8, bool]
|
||||||
|
if (shared.len != 1 or shared[0] != i8) return bool
|
||||||
|
other [1]mut type := [u8]
|
||||||
|
view = other[..]
|
||||||
|
view[0] = i16
|
||||||
|
shared[0] = i16
|
||||||
|
if (other[0] != i16 or backing[1] != i16) return bool
|
||||||
|
return shared[0]
|
||||||
|
}
|
||||||
|
main proc() Storage() { return 42 }
|
||||||
|
""", b"42\n", None,
|
||||||
|
),
|
||||||
|
"metadata_record_slices_borrow_helpers_nested_writes_and_uncached_effects": (
|
||||||
|
"""
|
||||||
|
Info :: struct { child type, nested struct { children [2]mut type, count usize } }
|
||||||
|
update proc(items []mut Info) void {
|
||||||
|
items[0].child = i16
|
||||||
|
items[0].nested.children[1] = i16
|
||||||
|
items[0].nested.count += 1
|
||||||
|
}
|
||||||
|
read proc(items []Info) usize { return items[0].nested.count }
|
||||||
|
Storage proc() type {
|
||||||
|
backing [1]mut Info := [{ child = bool, nested = { children = [i8, bool], count = 0 } }]
|
||||||
|
copied :: backing
|
||||||
|
view :: backing[..]
|
||||||
|
update(view)
|
||||||
|
if (read(view) != 1) return bool
|
||||||
|
backing[0] = copied[0]
|
||||||
|
update(view)
|
||||||
|
if (read(view) != 1) return bool
|
||||||
|
update(view)
|
||||||
|
if (read(view) != 2 or copied[0].nested.count != 0) return bool
|
||||||
|
if (view[0].child != i16 or view[0].nested.children[1] != i16) return bool
|
||||||
|
if (copied[0].child != bool or copied[0].nested.children[1] != bool) return bool
|
||||||
|
return [read(view)]view[0].child
|
||||||
|
}
|
||||||
|
main proc() Storage() { return [40, 2] }
|
||||||
|
""", b"[40, 2]\n", None,
|
||||||
|
),
|
||||||
|
"metadata_slices_borrow_cleanup_initialization_and_return_snapshot": (
|
||||||
|
"""
|
||||||
|
Error :: enum { failed }
|
||||||
|
initialize proc(items []mut type) void {
|
||||||
|
defer items[1] = i16
|
||||||
|
items[0] = bool
|
||||||
|
}
|
||||||
|
fail proc(items []mut type) void ! Error {
|
||||||
|
errdefer items[0] = i16
|
||||||
|
return .failed
|
||||||
|
}
|
||||||
|
types proc() []type {
|
||||||
|
backing [2]mut type := undefined
|
||||||
|
view :: backing[..]
|
||||||
|
initialize(view)
|
||||||
|
fail(view) catch {}
|
||||||
|
defer view[0] = bool
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
main proc() [2]types()[0] { return [40, 2] }
|
||||||
|
""", b"[40, 2]\n", None,
|
||||||
|
),
|
||||||
|
"metadata_slices_reject_immutable_backing_write": (
|
||||||
|
"""
|
||||||
|
Storage proc() type {
|
||||||
|
backing [1]mut type :: [i16]
|
||||||
|
view :: backing[..]
|
||||||
|
view[0] = bool
|
||||||
|
return view[0]
|
||||||
|
}
|
||||||
|
main proc() Storage() { return 42 }
|
||||||
|
""", b"", b"writable",
|
||||||
|
),
|
||||||
|
"metadata_slices_reject_readonly_array_to_mutable_view": (
|
||||||
|
"""
|
||||||
|
Storage proc() type {
|
||||||
|
backing [1]type := [i16]
|
||||||
|
view []mut type :: backing[..]
|
||||||
|
view[0] = bool
|
||||||
|
return view[0]
|
||||||
|
}
|
||||||
|
main proc() Storage() { return 42 }
|
||||||
|
""", b"", b"incompatible",
|
||||||
|
),
|
||||||
|
"metadata_slices_reject_incomplete_borrow_snapshot": (
|
||||||
|
"""
|
||||||
|
types proc() []type {
|
||||||
|
backing [2]mut type := undefined
|
||||||
|
view :: backing[..]
|
||||||
|
view[0] = i16
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
main proc() types()[0] { return 42 }
|
||||||
|
""", b"", b"read before initialization",
|
||||||
|
),
|
||||||
|
"metadata_slices_reject_runtime_branch_borrow_mutation": (
|
||||||
|
"""
|
||||||
|
update proc(items []mut type) void { items[0] = i16 }
|
||||||
|
compute proc(take bool) usize {
|
||||||
|
backing [1]mut type := [i8]
|
||||||
|
view :: backing[..]
|
||||||
|
if (take) update(view)
|
||||||
|
return sizeof!(view[0])
|
||||||
|
}
|
||||||
|
main proc() usize { return compute(true) }
|
||||||
|
""", b"", b"compile-time value",
|
||||||
|
),
|
||||||
|
"metadata_slices_reject_mutable_view_index_type": (
|
||||||
|
"""
|
||||||
|
Storage proc() type {
|
||||||
|
backing [1]mut type := [i16]
|
||||||
|
view :: backing[..]
|
||||||
|
view[u8(0)] = bool
|
||||||
|
return i16
|
||||||
|
}
|
||||||
|
main proc() Storage() { return 42 }
|
||||||
|
""", b"", b"usize",
|
||||||
|
),
|
||||||
|
"metadata_slices_reject_mutable_subview_write_at_length": (
|
||||||
|
"""
|
||||||
|
Storage proc() type {
|
||||||
|
backing [2]mut type := [i16, bool]
|
||||||
|
view :: backing[1..2]
|
||||||
|
view[1] = i8
|
||||||
|
return i16
|
||||||
|
}
|
||||||
|
main proc() Storage() { return 42 }
|
||||||
|
""", b"", b"index out of bounds",
|
||||||
|
),
|
||||||
|
"metadata_slices_nested_places_iteration_optional_aliases_and_empty_shapes": (
|
||||||
|
"""
|
||||||
|
Info :: struct { children [2]mut type }
|
||||||
|
Choice :: union(enum) { types []mut type, empty void }
|
||||||
|
Storage proc() type {
|
||||||
|
first [1]mut Info := [{ children = [i8, bool] }]
|
||||||
|
second [1]mut Info := [{ children = [bool, u8] }]
|
||||||
|
rows := first[..]
|
||||||
|
children :: field!(rows[0], "children")[..]
|
||||||
|
rows = second[..]
|
||||||
|
selected ?[]mut type :: some!(children)
|
||||||
|
if selected |chosen| { chosen[0] = i16 }
|
||||||
|
choice Choice :: .types {children}
|
||||||
|
match choice { .types |captured|: captured[1] = i16, .empty: {} }
|
||||||
|
if (children[1] != i16) return bool
|
||||||
|
for children |T, index| {
|
||||||
|
if (index == 0) children[1] = T
|
||||||
|
if (index == 1 and T != i16) return bool
|
||||||
|
}
|
||||||
|
if (second[0].children[0] != bool) return bool
|
||||||
|
empty [0]mut type := []
|
||||||
|
view :: empty[..]
|
||||||
|
for view |T| { return T }
|
||||||
|
if (view[..].len != 0) return bool
|
||||||
|
return first[0].children[1]
|
||||||
|
}
|
||||||
|
main proc() Storage() { return 42 }
|
||||||
|
""", b"42\n", None,
|
||||||
|
),
|
||||||
|
"metadata_slices_local_fallible_captures_preserve_aliases": (
|
||||||
|
"""
|
||||||
|
Failure :: struct { types []mut type }
|
||||||
|
Storage proc() type {
|
||||||
|
backing [1]mut type := [i8]
|
||||||
|
value void ! Failure :: Failure { types = backing[..] }
|
||||||
|
value catch |failure| { failure.types[0] = i16 }
|
||||||
|
return backing[0]
|
||||||
|
}
|
||||||
|
main proc() Storage() { return 42 }
|
||||||
|
""", b"42\n", None,
|
||||||
|
),
|
||||||
|
"metadata_slices_reject_readonly_enclosing_array_place": (
|
||||||
|
"""
|
||||||
|
Info :: struct { children [1]mut type }
|
||||||
|
Storage proc() type {
|
||||||
|
rows [1]Info := [{ children = [i8] }]
|
||||||
|
children :: rows[0].children[..]
|
||||||
|
children[0] = i16
|
||||||
|
return rows[0].children[0]
|
||||||
|
}
|
||||||
|
main proc() Storage() { return 42 }
|
||||||
|
""", b"", b"writable",
|
||||||
|
),
|
||||||
|
"metadata_slices_mixed_execution_and_readonly_generator_cache": (
|
||||||
|
"""
|
||||||
|
Token proc(types []type) type { return struct {} }
|
||||||
|
update proc(items []mut type) void { items[0] = i16 }
|
||||||
|
compute proc(value i16) i16 {
|
||||||
|
backing [1]mut type := [i8]
|
||||||
|
update(backing[..])
|
||||||
|
view :: backing[..]
|
||||||
|
if (Token(view) != Token([i16])) return -1
|
||||||
|
local view[0] :: value
|
||||||
|
return local
|
||||||
|
}
|
||||||
|
main proc() i16 { return compute(42) }
|
||||||
|
""", b"42\n", None,
|
||||||
|
),
|
||||||
|
"metadata_slices_reject_dangling_nested_scope": (
|
||||||
|
"""
|
||||||
|
Storage proc() type {
|
||||||
|
backing [1]mut type := [i16]
|
||||||
|
view []type := backing[..]
|
||||||
|
{
|
||||||
|
inner [1]mut type := [bool]
|
||||||
|
view = inner[..]
|
||||||
|
}
|
||||||
|
return view[0]
|
||||||
|
}
|
||||||
|
main proc() Storage() { return 42 }
|
||||||
|
""", b"", b"compile-time result cannot escape evaluation as a source value",
|
||||||
|
),
|
||||||
|
"metadata_slices_reject_runtime_short_circuit_mutation": (
|
||||||
|
"""
|
||||||
|
update proc(items []mut type) bool { items[0] = i16
|
||||||
|
return true }
|
||||||
|
compute proc(take bool) usize {
|
||||||
|
backing [1]mut type := [i8]
|
||||||
|
view :: backing[..]
|
||||||
|
if (take and update(view)) return sizeof!(view[0])
|
||||||
|
return sizeof!(view[0])
|
||||||
|
}
|
||||||
|
main proc() usize { return compute(false) }
|
||||||
|
""", b"", b"compile-time value",
|
||||||
|
),
|
||||||
|
"metadata_slices_reject_runtime_conditional_expression_mutation": (
|
||||||
|
"""
|
||||||
|
update proc(items []mut type) bool { items[0] = i16
|
||||||
|
return true }
|
||||||
|
compute proc(take bool) usize {
|
||||||
|
backing [1]mut type := [i8]
|
||||||
|
view :: backing[..]
|
||||||
|
selected :: if (take) update(view) else false
|
||||||
|
if (selected) return sizeof!(view[0])
|
||||||
|
return sizeof!(view[0])
|
||||||
|
}
|
||||||
|
main proc() usize { return compute(false) }
|
||||||
|
""", b"", b"compile-time value",
|
||||||
|
),
|
||||||
|
"metadata_slices_skip_short_circuited_borrowed_effects": (
|
||||||
|
"""
|
||||||
|
update proc(items []mut type) bool { items[0] = i16
|
||||||
|
return true }
|
||||||
|
Storage proc() type {
|
||||||
|
backing [1]mut type := [i8]
|
||||||
|
view :: backing[..]
|
||||||
|
if (false and update(view)) return bool
|
||||||
|
return view[0]
|
||||||
|
}
|
||||||
|
compute proc(value Storage()) i16 {
|
||||||
|
backing [1]mut type := [i8]
|
||||||
|
view :: backing[..]
|
||||||
|
if (true or update(view)) return i16(value)
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
main proc() i16 { return compute(42) }
|
||||||
|
""", b"42\n", None,
|
||||||
|
),
|
||||||
"metadata_slices_reject_mutable_slice_annotation": (
|
"metadata_slices_reject_mutable_slice_annotation": (
|
||||||
"""
|
"""
|
||||||
types proc() []mut type { return [i16] }
|
types proc() []mut type { return [i16] }
|
||||||
main proc() types()[0] { return 42 }
|
main proc() types()[0] { return 42 }
|
||||||
""", b"", b"compile-time result cannot escape evaluation as a source value",
|
""", b"", b"compile-time result cannot escape evaluation as a source value",
|
||||||
),
|
),
|
||||||
"metadata_slices_reject_mutable_array_backing": (
|
"metadata_slices_snapshot_mutable_array_backing": (
|
||||||
"""
|
"""
|
||||||
types proc() []type {
|
types proc() []type {
|
||||||
backing [1]mut type := [i16]
|
backing [1]mut type := [i16]
|
||||||
return backing[..]
|
return backing[..]
|
||||||
}
|
}
|
||||||
main proc() types()[0] { return 42 }
|
main proc() types()[0] { return 42 }
|
||||||
""", b"", b"compile-time result cannot escape evaluation as a source value",
|
""", b"42\n", None,
|
||||||
),
|
),
|
||||||
"metadata_slices_reject_mutable_binding_with_readonly_array_elements": (
|
"metadata_slices_snapshot_mutable_binding_with_readonly_array_elements": (
|
||||||
"""
|
"""
|
||||||
types proc() []type {
|
types proc() []type {
|
||||||
backing [1]type := [i16]
|
backing [1]type := [i16]
|
||||||
return backing[..]
|
return backing[..]
|
||||||
}
|
}
|
||||||
main proc() types()[0] { return 42 }
|
main proc() types()[0] { return 42 }
|
||||||
""", b"", b"compile-time result cannot escape evaluation as a source value",
|
""", b"42\n", None,
|
||||||
),
|
),
|
||||||
"metadata_record_slices_reject_mutable_array_backing": (
|
"metadata_record_slices_snapshot_mutable_array_backing": (
|
||||||
"""
|
"""
|
||||||
Info :: struct { child type, count usize }
|
Info :: struct { child type, count usize }
|
||||||
fields proc() []Info {
|
fields proc() []Info {
|
||||||
@@ -3407,7 +3677,7 @@ CASES = {
|
|||||||
return backing[..]
|
return backing[..]
|
||||||
}
|
}
|
||||||
main proc() fields()[0].child { return 42 }
|
main proc() fields()[0].child { return 42 }
|
||||||
""", b"", b"compile-time result cannot escape evaluation as a source value",
|
""", b"42\n", None,
|
||||||
),
|
),
|
||||||
"metadata_slices_reject_sentinel_annotation": (
|
"metadata_slices_reject_sentinel_annotation": (
|
||||||
"""
|
"""
|
||||||
@@ -11412,6 +11682,16 @@ def check_specialization_packages(temporary, executable):
|
|||||||
return struct { values [input.counts[1]]input.child, bias i16 = input.bias }
|
return struct { values [input.counts[1]]input.child, bias i16 = input.bias }
|
||||||
}
|
}
|
||||||
add proc($input Config, value input.child) input.child { return value + input.bias }
|
add proc($input Config, value input.child) input.child { return value + input.bias }
|
||||||
|
@hide:file
|
||||||
|
Private :: distinct i16
|
||||||
|
@hide:file
|
||||||
|
replace proc(items []mut type) void { items[0] = Private }
|
||||||
|
borrowed_types proc() []type {
|
||||||
|
backing [1]mut type := [bool]
|
||||||
|
replace(backing[..])
|
||||||
|
return backing[..]
|
||||||
|
}
|
||||||
|
FromBorrow proc() type { return struct { value borrowed_types()[0] } }
|
||||||
NominalConfig :: struct { child type }
|
NominalConfig :: struct { child type }
|
||||||
nominal_config proc() NominalConfig {
|
nominal_config proc() NominalConfig {
|
||||||
return { child = struct { value i16 } }
|
return { child = struct { value i16 } }
|
||||||
@@ -11426,11 +11706,15 @@ def check_specialization_packages(temporary, executable):
|
|||||||
same lib.FromRecord({ child = i16, counts = [1, 2] }) :: value
|
same lib.FromRecord({ child = i16, counts = [1, 2] }) :: value
|
||||||
nominal lib.FromNominal(lib.nominal_config()) :: { value = { value = 40 } }
|
nominal lib.FromNominal(lib.nominal_config()) :: { value = { value = 40 } }
|
||||||
same_nominal lib.FromNominal(lib.nominal_config()) :: nominal
|
same_nominal lib.FromNominal(lib.nominal_config()) :: nominal
|
||||||
|
borrowed lib.FromBorrow() :: { value = 40 }
|
||||||
|
same_borrowed lib.FromBorrow() :: borrowed
|
||||||
|
if (i16(same_borrowed.value) != 40) return 0
|
||||||
if (same_nominal.value.value != 40) return 0
|
if (same_nominal.value.value != 40) return 0
|
||||||
return lib.add(lib.record_config(), same.values[0] + same.values[1])
|
return lib.add(lib.record_config(), same.values[0] + same.values[1])
|
||||||
}
|
}
|
||||||
""").strip() + "\n")
|
""").strip() + "\n")
|
||||||
check(app, executable, (b"42\n", None))
|
check(app, executable, (b"42\n", None))
|
||||||
|
check(app, executable, (b"42\n", None))
|
||||||
sizing.write_text(textwrap.dedent("""
|
sizing.write_text(textwrap.dedent("""
|
||||||
Config :: struct { child type, rows [1]mut [1]mut type }
|
Config :: struct { child type, rows [1]mut [1]mut type }
|
||||||
record_config proc() Config { return { child = i16, rows = [[i16]] } }
|
record_config proc() Config { return { child = i16, rows = [[i16]] } }
|
||||||
@@ -11564,7 +11848,7 @@ def check_specialization_packages(temporary, executable):
|
|||||||
interpreted = run(str(HONEY), "run", str(app))
|
interpreted = run(str(HONEY), "run", str(app))
|
||||||
assert interpreted.stderr.startswith(f"{sizing.resolve()}:4:".encode()), outcome(interpreted)
|
assert interpreted.stderr.startswith(f"{sizing.resolve()}:4:".encode()), outcome(interpreted)
|
||||||
assert len(interpreted.stderr.splitlines()) == 1, outcome(interpreted)
|
assert len(interpreted.stderr.splitlines()) == 1, outcome(interpreted)
|
||||||
return 26
|
return 27
|
||||||
|
|
||||||
|
|
||||||
def check_dependency_provenance(temporary, executable):
|
def check_dependency_provenance(temporary, executable):
|
||||||
|
|||||||
Reference in New Issue
Block a user