feat: pass type-valued arrays across compile-time procedure boundaries
This commit is contained in:
@@ -104,7 +104,9 @@ field/index assignments also update initialized, materializable aggregate locals
|
|||||||
|
|
||||||
type-producing bodies also iterate materializable fixed arrays and strings with immutable by-value element captures and optional `usize` indices: `for counts |count, index| { length = length + count + index }` can compute generated storage. nested loops, aggregate captures, empty collections, break/continue and early returns share the type-control execution bound. array places retain their entry-time indices and observe later element updates; temporary arrays and strings are evaluated once. ordinary constant-input procedures additionally support slice and pointer captures through checked IR, provided only materializable results escape. type-producing loop bodies still reject storage-bearing captures rather than retaining temporary interpreter addresses; `inline for` remains open.
|
type-producing bodies also iterate materializable fixed arrays and strings with immutable by-value element captures and optional `usize` indices: `for counts |count, index| { length = length + count + index }` can compute generated storage. nested loops, aggregate captures, empty collections, break/continue and early returns share the type-control execution bound. array places retain their entry-time indices and observe later element updates; temporary arrays and strings are evaluated once. ordinary constant-input procedures additionally support slice and pointer captures through checked IR, provided only materializable results escape. type-producing loop bodies still reject storage-bearing captures rather than retaining temporary interpreter addresses; `inline for` remains open.
|
||||||
|
|
||||||
initialized fixed arrays of `type` also live entirely inside type-producing execution, including nested arrays, explicit/inferred annotations, `.len`, checked `usize` indexing and collection captures. for example, `types [2]mut type := [i8, bool]`, then `types[1] = i16` and `return [types.len]types[1]`, generates `[2]i16` without runtime type metadata. whole-array and nested indexed assignments preserve shape and mutability at every level; copies and loop captures retain earlier values while borrowed array places observe later updates. selected types work in nested producers, signatures, constructors and field defaults through interpreter/native execution, with imported diagnostics and repeatable compilation. uninitialized arrays, type-valued record storage and type-array procedure parameters/results remain open.
|
initialized fixed arrays of `type` live entirely inside compile-time execution, including nested arrays, explicit/inferred annotations, `.len`, checked `usize` indexing and collection captures. for example, `types [2]mut type := [i8, bool]`, then `types[1] = i16` and `return [types.len]types[1]`, generates `[2]i16` without runtime type metadata. whole-array and nested indexed assignments preserve shape and mutability at every level; copies and loop captures retain earlier values while borrowed array places observe later updates.
|
||||||
|
|
||||||
|
procedures returning fixed arrays of `type`, such as `types proc() [2]type { return [i16, bool] }`, also 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. type-producing procedures accept ordinary type-array inputs, and runtime generic procedures accept erased `$types [N]type` parameters. direct projections such as `types()[0]` generate signatures, constructors, storage and defaults; explicit `$()` calls inside producers retain owned array 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 storage, uninitialized arrays, type-valued record storage and reflection remain open.
|
||||||
|
|
||||||
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, but nonnull pointer/slice/callback-bearing payloads 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, but nonnull pointer/slice/callback-bearing payloads retain the constant-materialization escape boundary.
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ captures and `usize` indices through both paths; type-producing bodies iterate
|
|||||||
materializable arrays/strings by value without retaining interpreter addresses.
|
materializable arrays/strings by value without retaining interpreter addresses.
|
||||||
initialized type-valued fixed arrays support nested copies, indexed/whole updates,
|
initialized type-valued fixed arrays support nested copies, indexed/whole updates,
|
||||||
checked projections and by-value collection captures in type-producing bodies.
|
checked projections and by-value collection captures in type-producing bodies.
|
||||||
|
type-array parameters and results cross compile-time procedure boundaries with
|
||||||
|
owned snapshots, cached nominal identity and erased arguments.
|
||||||
general type-valued execution, reflection and generic library acceptance remain open.
|
general type-valued execution, reflection and generic library acceptance remain open.
|
||||||
the driver retains frontend storage and source provenance through
|
the driver retains frontend storage and source provenance through
|
||||||
`@source/compilation`, with compilation-wide AST node identities.
|
`@source/compilation`, with compilation-wide AST node identities.
|
||||||
@@ -85,7 +87,8 @@ test discovery and FFI remain outside these completed slices.
|
|||||||
- [x] optional type-control checkpoint: type-producing bodies execute optional if/while guards with immutable by-value payload captures. ordinary checked IR validates guards; selected scalar/aggregate payloads retain exact types and owned strings across nested captures, generated storage/defaults, loop updates/transfers and early returns. absent optionals skip unselected bodies; invalid guards, capture mutation/shadowing, overflow and execution bounds retain diagnostics. imported producers, dependency provenance, nominal cache identity and repeatable compilation work through interpreter/native execution. null data/function pointers materialize correctly; nonnull storage-bearing payloads retain the constant-materialization escape boundary.
|
- [x] optional type-control checkpoint: type-producing bodies execute optional if/while guards with immutable by-value payload captures. ordinary checked IR validates guards; selected scalar/aggregate payloads retain exact types and owned strings across nested captures, generated storage/defaults, loop updates/transfers and early returns. absent optionals skip unselected bodies; invalid guards, capture mutation/shadowing, overflow and execution bounds retain diagnostics. imported producers, dependency provenance, nominal cache identity and repeatable compilation work through interpreter/native execution. null data/function pointers materialize correctly; nonnull storage-bearing payloads retain the constant-materialization escape boundary.
|
||||||
- [x] aggregate type-update checkpoint: type-producing bodies update initialized, materializable aggregate locals through nested record fields and mutable fixed-array indices. ordinary checked IR validates and executes each assignment, retaining exact value types, mutability (including immutable payload captures), index types/bounds, unknown-field and narrow-overflow diagnostics. new owned constants preserve earlier by-value copies while later loops, generated storage and defaults observe updates. imported producers, dependency provenance, nominal cache identity and repeatable compilation work through interpreter/native execution. pointer/slice-backed mutation, uninitialized aggregate construction and type-valued record storage remain open; type-valued arrays are covered below.
|
- [x] aggregate type-update checkpoint: type-producing bodies update initialized, materializable aggregate locals through nested record fields and mutable fixed-array indices. ordinary checked IR validates and executes each assignment, retaining exact value types, mutability (including immutable payload captures), index types/bounds, unknown-field and narrow-overflow diagnostics. new owned constants preserve earlier by-value copies while later loops, generated storage and defaults observe updates. imported producers, dependency provenance, nominal cache identity and repeatable compilation work through interpreter/native execution. pointer/slice-backed mutation, uninitialized aggregate construction and type-valued record storage remain open; type-valued arrays are covered below.
|
||||||
- [x] collection-iteration checkpoint: arrays, slices, strings and single pointers-to-arrays support immutable exact-type copy captures, storage-qualified pointer captures and optional `usize` indices through interpreter/native execution. iterables/bounds evaluate once, array lvalues remain borrowed and later element updates are visible; temporary arrays, aggregate copy isolation, zero-cell/empty collections, nested loops and cleanup across transfers retain ordinary semantics. ordinary constant-input procedures reuse the same checked IR; type-producing bodies iterate materializable arrays/strings with by-value captures, fixed entry-time place indices, generated storage, nominal cache identity, dependency diagnostics, shared execution bounds and repeatable compilation. storage-bearing type-control captures retain their existing boundary; type-valued elements are covered below.
|
- [x] collection-iteration checkpoint: arrays, slices, strings and single pointers-to-arrays support immutable exact-type copy captures, storage-qualified pointer captures and optional `usize` indices through interpreter/native execution. iterables/bounds evaluate once, array lvalues remain borrowed and later element updates are visible; temporary arrays, aggregate copy isolation, zero-cell/empty collections, nested loops and cleanup across transfers retain ordinary semantics. ordinary constant-input procedures reuse the same checked IR; type-producing bodies iterate materializable arrays/strings with by-value captures, fixed entry-time place indices, generated storage, nominal cache identity, dependency diagnostics, shared execution bounds and repeatable compilation. storage-bearing type-control captures retain their existing boundary; type-valued elements are covered below.
|
||||||
- [x] type-array storage checkpoint: type-producing bodies own initialized fixed arrays of type values, including explicit/inferred nested arrays, `.len`, checked `usize` indexing, whole/indexed updates and immutable by-value collection captures. persistent snapshots preserve copies and exact shape/mutability while borrowed array places observe later updates. selected types generate nested storage, constructors, signatures and defaults through interpreter/native execution, retaining selected-body and index/type/mutability diagnostics, imported provenance, nominal cache identity, shared bounds and repeatable compilation. uninitialized arrays, type-valued record storage, type-array parameters/results and reflection remain open.
|
- [x] type-array storage checkpoint: type-producing bodies own initialized fixed arrays of type values, including explicit/inferred nested arrays, `.len`, checked `usize` indexing, whole/indexed updates and immutable by-value collection captures. persistent snapshots preserve copies and exact shape/mutability while borrowed array places observe later updates. selected types generate nested storage, constructors, signatures and defaults through interpreter/native execution, retaining selected-body and index/type/mutability diagnostics, imported provenance, nominal cache identity, shared bounds and repeatable compilation. uninitialized arrays, type-valued record storage and reflection remain open.
|
||||||
|
- [x] type-array procedure checkpoint: fixed arrays of type values cross compile-time procedure inputs/results, including nested/aliased results, dependent `$N` lengths, empty shapes and erased `$types` parameters in runtime generic procedures. all inputs to type/type-array producers are explicit constants; owned snapshots preserve by-value isolation, exact shape/mutability and canonical/nominal cache identity. projections generate signatures, constructors, storage and defaults through interpreter/native execution. malformed inputs/results, runtime dependencies, imported provenance, shared execution bounds and repeatable compilation retain diagnostics and isolation. runtime type-array storage, uninitialized arrays, type-valued record storage and reflection remain open.
|
||||||
- [ ] remaining gate: general type-valued compile-time execution, inline match/loop expansion and reflection (`typeinfo!`, `field!`, `struct_type!`, generated type/default arrays), broader constant materialization (including untagged unions and storage-bearing values), plus existing-library acceptance. required expression-valued control flow, compound updates, formatting parsing and imported C-binding support may interleave with later gates.
|
- [ ] remaining gate: general type-valued compile-time execution, inline match/loop expansion and reflection (`typeinfo!`, `field!`, `struct_type!`, generated type/default arrays), broader constant materialization (including untagged unions and storage-bearing values), plus existing-library acceptance. required expression-valued control flow, compound updates, formatting parsing and imported C-binding support may interleave with later gates.
|
||||||
- [ ] remaining source constructs: expression-valued control flow and `yield`, compound updates and multiline strings. examples: `source/parser/parse.hon`, `source/llvm/runtime.hon`.
|
- [ ] remaining source constructs: expression-valued control flow and `yield`, compound updates and multiline strings. 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.
|
||||||
|
|||||||
@@ -175,6 +175,26 @@ recompiles_specialized_calls_without_borrowing_previous_ir test {
|
|||||||
` for (empty) |_| return bool
|
` for (empty) |_| return bool
|
||||||
` return struct { values [types.len]types[1][0], bias inferred[0][0] = 2 }
|
` return struct { values [types.len]types[1][0], bias inferred[0][0] = 2 }
|
||||||
`}
|
`}
|
||||||
|
`TypeRows proc() [2]mut [1]mut type {
|
||||||
|
` rows [2]mut [1]mut type := [[i8], [i16]]
|
||||||
|
` (rows[0])[0] = i16
|
||||||
|
` return rows
|
||||||
|
`}
|
||||||
|
`copy_type_rows proc(rows [2]mut [1]mut type) [2]mut [1]mut type {
|
||||||
|
` copied := rows
|
||||||
|
` (copied[0])[0] = u8
|
||||||
|
` if (rows[0][0] != i16) return [[bool], [bool]]
|
||||||
|
` return copied
|
||||||
|
`}
|
||||||
|
`BoundaryType proc(rows [2]mut [1]mut type) type {
|
||||||
|
` original :: rows
|
||||||
|
` changed :: copy_type_rows(rows)
|
||||||
|
` if (changed[0][0] != u8 or rows[0][0] != i16 or original[0][0] != i16) return bool
|
||||||
|
` return struct { values [rows.len]rows[0][0], bias rows[1][0] = 2 }
|
||||||
|
`}
|
||||||
|
`read_type_rows proc($rows [2]mut [1]mut type, value rows[0][0]) rows[1][0] {
|
||||||
|
` return value + rows[1][0](rows.len)
|
||||||
|
`}
|
||||||
`read_selected proc($choice Choice, value i16) i16 {
|
`read_selected proc($choice Choice, value i16) i16 {
|
||||||
` match choice {
|
` match choice {
|
||||||
` .payload |detail|: {
|
` .payload |detail|: {
|
||||||
@@ -210,6 +230,12 @@ recompiles_specialized_calls_without_borrowing_previous_ir test {
|
|||||||
` type_array TypeArray() :: { values = [19, 21] }
|
` type_array TypeArray() :: { values = [19, 21] }
|
||||||
` same_type_array TypeArray() :: type_array
|
` same_type_array TypeArray() :: type_array
|
||||||
` if (same_type_array.values[0] + same_type_array.values[1] + same_type_array.bias != 42) return 0
|
` if (same_type_array.values[0] + same_type_array.values[1] + same_type_array.bias != 42) return 0
|
||||||
|
` boundary BoundaryType(TypeRows()) :: { values = [19, 21] }
|
||||||
|
` same_boundary BoundaryType(TypeRows()) :: boundary
|
||||||
|
` if (same_boundary.values[0] + same_boundary.values[1] + same_boundary.bias != 42) return 0
|
||||||
|
` if (copy_type_rows(TypeRows())[0][0] != u8 or copy_type_rows(TypeRows())[1][0] != i16) return 0
|
||||||
|
` if (TypeRows()[0][0] != i16) return 0
|
||||||
|
` if (read_type_rows(TypeRows(), 40) != 42 or read_type_rows(TypeRows(), 40) != 42) return 0
|
||||||
` payload :: packet.choices[0].payload
|
` payload :: packet.choices[0].payload
|
||||||
` if (payload.values[0] != 40 or payload.values[1] != 2 or payload.text.len != 2) return 0
|
` if (payload.values[0] != 40 or payload.values[1] != 2 or payload.text.len != 2) return 0
|
||||||
` match packet.choices[1] { .empty: {}, .payload: return 0 }
|
` match packet.choices[1] { .empty: {}, .payload: return 0 }
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ Instance :: struct {
|
|||||||
next usize = no_index
|
next usize = no_index
|
||||||
diagnostic diagnostics.DiagnosticId = diagnostics.no_diagnostic
|
diagnostic diagnostics.DiagnosticId = diagnostics.no_diagnostic
|
||||||
signature ast.NodeId = no_node
|
signature ast.NodeId = no_node
|
||||||
|
value ast.NodeId = no_node
|
||||||
}
|
}
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
@@ -121,7 +122,11 @@ elaborate proc(
|
|||||||
if (class == .function and ast.function_generic(&original, id)) needed = true
|
if (class == .function and ast.function_generic(&original, id)) needed = true
|
||||||
if (class == .function) {
|
if (class == .function) {
|
||||||
result_type :: sema.binding(resolution, ast.function_decl(&original, id).return_type)
|
result_type :: sema.binding(resolution, ast.function_decl(&original, id).return_type)
|
||||||
if (result_type.kind == .builtin and result_type.data.builtin == .type) needed = true
|
if (
|
||||||
|
(result_type.kind == .builtin and result_type.data.builtin == .type)
|
||||||
|
or type_array_annotation(&original, resolution.storage.bindings,
|
||||||
|
ast.function_decl(&original, id).return_type)
|
||||||
|
) needed = true
|
||||||
}
|
}
|
||||||
if (needed) break
|
if (needed) break
|
||||||
}
|
}
|
||||||
@@ -245,9 +250,10 @@ expand proc(state @mut State, id ast.NodeId) void ! Error {
|
|||||||
}
|
}
|
||||||
if (class == .expression and ast.expr_kind(&state.tree, id) == .comptime_expr) {
|
if (class == .expression and ast.expr_kind(&state.tree, id) == .comptime_expr) {
|
||||||
operand :: ast.comptime_operand(&state.tree, id)
|
operand :: ast.comptime_operand(&state.tree, id)
|
||||||
try arraylist.append(&state.rewrites.constraints, operand)
|
|
||||||
state.evaluation_diagnostic = diagnostics.no_diagnostic
|
state.evaluation_diagnostic = diagnostics.no_diagnostic
|
||||||
value :: try evaluate(state, operand)
|
value :: try evaluate(state, operand)
|
||||||
|
if (value.annotation == no_node or !type_array_annotation(&state.tree, state.bindings.items, value.annotation))
|
||||||
|
try arraylist.append(&state.rewrites.constraints, operand)
|
||||||
if (value.kind == .invalid) {
|
if (value.kind == .invalid) {
|
||||||
try reject_evaluation(state, id)
|
try reject_evaluation(state, id)
|
||||||
return
|
return
|
||||||
@@ -438,6 +444,36 @@ is_type proc(state @State, id ast.NodeId) bool {
|
|||||||
return value.kind == .builtin and value.data.builtin == .type
|
return value.kind == .builtin and value.data.builtin == .type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# recognize erased result signatures without evaluating dependent lengths or
|
||||||
|
# elaborating their bodies. aliases retain the same compile-time-only contract.
|
||||||
|
@hide:file
|
||||||
|
type_array_annotation proc(tree @ast.Tree, bindings []sema.Binding, input ast.NodeId) bool {
|
||||||
|
current := input
|
||||||
|
array := false
|
||||||
|
for (0..128) |_| {
|
||||||
|
resolved :: bindings[usize(current)]
|
||||||
|
if (resolved.kind == .builtin) return array and resolved.data.builtin == .type
|
||||||
|
if (resolved.kind == .declaration) {
|
||||||
|
declaration :: resolved.data.declaration
|
||||||
|
if (ast.node_class(tree, declaration) != .statement) return false
|
||||||
|
view :: ast.statement_decl(tree, declaration)
|
||||||
|
if (view.mutability != .constant) return false
|
||||||
|
current = view.initializer
|
||||||
|
} else if (ast.expr_kind(tree, current) == .array_type) {
|
||||||
|
array = true
|
||||||
|
current = ast.array_type(tree, current).element
|
||||||
|
} else if (ast.expr_kind(tree, current) == .alias_expr) {
|
||||||
|
current = ast.type_operand(tree, current)
|
||||||
|
} else return false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide:file
|
||||||
|
compiletime_result proc(state @State, annotation ast.NodeId) bool {
|
||||||
|
return is_type(state, annotation) or type_array_annotation(&state.tree, state.bindings.items, annotation)
|
||||||
|
}
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
specialize_call proc(state @mut State, call, function ast.NodeId) void ! Error {
|
specialize_call proc(state @mut State, call, function ast.NodeId) void ! Error {
|
||||||
if (state.depth >= 128 or state.instances.items.len >= 4096) {
|
if (state.depth >= 128 or state.instances.items.len >= 4096) {
|
||||||
@@ -447,8 +483,8 @@ specialize_call proc(state @mut State, call, function ast.NodeId) void ! Error {
|
|||||||
state.depth += 1
|
state.depth += 1
|
||||||
defer state.depth -= 1
|
defer state.depth -= 1
|
||||||
count :: ast.function_param_count(&state.tree, function)
|
count :: ast.function_param_count(&state.tree, function)
|
||||||
# a type result has no runtime call: every input participates in specialization.
|
# type and type-array results have no runtime call: every input is specialized.
|
||||||
type_result :: is_type(state, ast.function_decl(&state.tree, function).return_type)
|
type_result :: compiletime_result(state, ast.function_decl(&state.tree, function).return_type)
|
||||||
runtime := usize(0)
|
runtime := usize(0)
|
||||||
for (0..count) |i| {
|
for (0..count) |i| {
|
||||||
if (!type_result and !ast.param(&state.tree, ast.function_param(&state.tree, function, i)).comptime) runtime += 1
|
if (!type_result and !ast.param(&state.tree, ast.function_param(&state.tree, function, i)).comptime) runtime += 1
|
||||||
@@ -480,7 +516,8 @@ specialize_call proc(state @mut State, call, function ast.NodeId) void ! Error {
|
|||||||
type :: try canonical_type(state, annotation)
|
type :: try canonical_type(state, annotation)
|
||||||
contextual = type != no_node and ast.expr_kind(&state.tree, type) == .fallible_type
|
contextual = type != no_node and ast.expr_kind(&state.tree, type) == .fallible_type
|
||||||
}
|
}
|
||||||
if (contextual) annotation = try argument_annotation(state, function, view.type, arguments)
|
if (contextual or type_array_annotation(&state.tree, state.bindings.items, annotation))
|
||||||
|
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 (arguments[i].kind == .invalid) {
|
if (arguments[i].kind == .invalid) {
|
||||||
try reject_evaluation(state, call)
|
try reject_evaluation(state, call)
|
||||||
@@ -587,7 +624,7 @@ specialize_call proc(state @mut State, call, function ast.NodeId) void ! Error {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
target :: state.instances.items[instance].declaration
|
target :: state.instances.items[instance].declaration
|
||||||
if (target == no_node) {
|
if (target == no_node and state.instances.items[instance].value == no_node) {
|
||||||
try reject(state, call, .specialization_cycle)
|
try reject(state, call, .specialization_cycle)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -596,17 +633,23 @@ specialize_call proc(state @mut State, call, function ast.NodeId) void ! Error {
|
|||||||
for (0..count) |i| {
|
for (0..count) |i| {
|
||||||
parameter :: ast.function_param(&state.tree, signature, i)
|
parameter :: ast.function_param(&state.tree, signature, i)
|
||||||
view :: ast.param(&state.tree, parameter)
|
view :: ast.param(&state.tree, parameter)
|
||||||
if ((!type_result and !view.comptime) or is_type(state, view.type)) continue
|
if (
|
||||||
|
(!type_result and !view.comptime) or is_type(state, view.type)
|
||||||
|
or type_array_annotation(&state.tree, state.bindings.items, view.type)
|
||||||
|
) continue
|
||||||
original :: ast.call_arg(&state.tree, call, i)
|
original :: ast.call_arg(&state.tree, call, i)
|
||||||
constraint :: try ast.add_typed_constant(state.builder, ast.node_span(&state.tree, original), view.type, original)
|
constraint :: try ast.add_typed_constant(state.builder, ast.node_span(&state.tree, original), view.type, original)
|
||||||
try refresh(state)
|
try refresh(state)
|
||||||
try arraylist.append(&state.rewrites.constraints, constraint)
|
try arraylist.append(&state.rewrites.constraints, constraint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
replacement := state.instances.items[instance].value
|
||||||
|
if (replacement == no_node) {
|
||||||
callee :: try reference(state, call, target)
|
callee :: try reference(state, call, target)
|
||||||
replacement := no_node
|
if (state.instances.items[instance].type != no_node)
|
||||||
if (state.instances.items[instance].type != no_node) replacement = try ast.add_type_operand(state.builder, ast.node_span(&state.tree, call), callee, true)
|
replacement = try ast.add_type_operand(state.builder, ast.node_span(&state.tree, call), callee, true)
|
||||||
else replacement = try ast.add_call(state.builder, ast.node_span(&state.tree, call), callee, runtime_arguments.items)
|
else replacement = try ast.add_call(state.builder, ast.node_span(&state.tree, call), callee, runtime_arguments.items)
|
||||||
|
}
|
||||||
try replace(state, call, replacement)
|
try replace(state, call, replacement)
|
||||||
try refresh(state)
|
try refresh(state)
|
||||||
}
|
}
|
||||||
@@ -614,7 +657,7 @@ specialize_call proc(state @mut State, call, function ast.NodeId) void ! Error {
|
|||||||
@hide:file
|
@hide:file
|
||||||
instantiate proc(state @mut State, index usize, call ast.NodeId) void ! Error {
|
instantiate proc(state @mut State, index usize, call ast.NodeId) void ! Error {
|
||||||
function :: state.instances.items[index].function
|
function :: state.instances.items[index].function
|
||||||
type_result :: is_type(state, ast.function_decl(&state.tree, function).return_type)
|
type_result :: compiletime_result(state, ast.function_decl(&state.tree, function).return_type)
|
||||||
count :: state.original_count
|
count :: state.original_count
|
||||||
clone_start :: ast.node_count(&state.tree)
|
clone_start :: ast.node_count(&state.tree)
|
||||||
mapping :: state.mapping
|
mapping :: state.mapping
|
||||||
@@ -670,17 +713,21 @@ instantiate proc(state @mut State, index usize, call ast.NodeId) void ! Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (is_type(state, view.return_type)) {
|
if (type_result) {
|
||||||
if (state.type_execution_depth == 0) state.type_execution_remaining = 4096
|
if (state.type_execution_depth == 0) state.type_execution_remaining = 4096
|
||||||
state.type_execution_depth += 1
|
state.type_execution_depth += 1
|
||||||
defer state.type_execution_depth -= 1
|
defer state.type_execution_depth -= 1
|
||||||
state.evaluation_diagnostic = diagnostics.no_diagnostic
|
state.evaluation_diagnostic = diagnostics.no_diagnostic
|
||||||
constraints_start :: state.rewrites.constraints.items.len
|
constraints_start :: state.rewrites.constraints.items.len
|
||||||
returned :: try return_type_value(state, view.body, ast.NodeId(clone_start))
|
returned :: try return_type_value(state, view.body, ast.NodeId(clone_start), view.return_type)
|
||||||
if (returned == no_node) {
|
if (returned == no_node) {
|
||||||
try reject_type_result(state, call, returned, constraints_start)
|
try reject_type_result(state, call, returned, constraints_start)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (!is_type(state, view.return_type)) {
|
||||||
|
state.instances.items[index].value = returned
|
||||||
|
return
|
||||||
|
}
|
||||||
result := returned
|
result := returned
|
||||||
kind :: ast.expr_kind(&state.tree, returned)
|
kind :: ast.expr_kind(&state.tree, returned)
|
||||||
if (kind != .record_type and kind != .union_type and kind != .enum_type and kind != .distinct_type) {
|
if (kind != .record_type and kind != .union_type and kind != .enum_type and kind != .distinct_type) {
|
||||||
@@ -816,6 +863,12 @@ argument_annotation proc(
|
|||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
evaluate_expected proc(state @mut State, id, annotation ast.NodeId) Value ! Error {
|
evaluate_expected proc(state @mut State, id, annotation ast.NodeId) Value ! Error {
|
||||||
|
if (type_array_annotation(&state.tree, state.bindings.items, annotation)) {
|
||||||
|
constant :: try type_execution_value(state, id, annotation)
|
||||||
|
if (constant == no_node) return Value{}
|
||||||
|
value :: ast.typed_constant(&state.tree, constant)
|
||||||
|
return Value{ kind = .aggregate, node = value.value, annotation = value.type }
|
||||||
|
}
|
||||||
if (!is_type(state, annotation)) {
|
if (!is_type(state, annotation)) {
|
||||||
type :: try canonical_type(state, annotation)
|
type :: try canonical_type(state, annotation)
|
||||||
if (type != no_node and (contextual_literal(state, id) or ast.expr_kind(&state.tree, type) == .fallible_type))
|
if (type != no_node and (contextual_literal(state, id) or ast.expr_kind(&state.tree, type) == .fallible_type))
|
||||||
@@ -1112,6 +1165,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
|
||||||
|
result_type ast.NodeId
|
||||||
loop_depth usize = 0
|
loop_depth usize = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1121,13 +1175,14 @@ TypeFlow :: enum { next, returned, break, continue, invalid }
|
|||||||
# type values cannot enter runtime IR. execute their control flow here, substituting
|
# type values cannot enter runtime IR. execute their control flow here, substituting
|
||||||
# owned constants into snapshots; ordinary value operations still use checked IR.
|
# owned constants into snapshots; ordinary value operations still use checked IR.
|
||||||
@hide:file
|
@hide:file
|
||||||
return_type_value proc(state @mut State, block, first_owned ast.NodeId) ast.NodeId ! Error {
|
return_type_value proc(state @mut State, block, first_owned, result_type ast.NodeId) ast.NodeId ! Error {
|
||||||
# ponytail: snapshots recheck scalar IR; cap type-control work at 4096 steps
|
# ponytail: snapshots recheck scalar IR; cap type-control work at 4096 steps
|
||||||
# until type values can share one persistent checked execution frame.
|
# until type values can share one persistent checked execution frame.
|
||||||
execution := TypeExecution{
|
execution := TypeExecution{
|
||||||
locals = arraylist.init(state.allocator),
|
locals = arraylist.init(state.allocator),
|
||||||
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 = first_owned,
|
first_owned = first_owned,
|
||||||
|
result_type = result_type,
|
||||||
}
|
}
|
||||||
defer arraylist.deinit(&execution.locals)
|
defer arraylist.deinit(&execution.locals)
|
||||||
defer mem.free(state.allocator, execution.mapping)
|
defer mem.free(state.allocator, execution.mapping)
|
||||||
@@ -1291,6 +1346,7 @@ type_match_body proc(state @mut State, id ast.NodeId, arm ast.MatchArmView) ast.
|
|||||||
locals = arraylist.init(state.allocator),
|
locals = arraylist.init(state.allocator),
|
||||||
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),
|
first_owned = ast.NodeId(0),
|
||||||
|
result_type = no_node,
|
||||||
}
|
}
|
||||||
defer arraylist.deinit(&execution.locals)
|
defer arraylist.deinit(&execution.locals)
|
||||||
defer mem.free(state.allocator, execution.mapping)
|
defer mem.free(state.allocator, execution.mapping)
|
||||||
@@ -1341,6 +1397,11 @@ type_array_projection proc(state @mut State, id ast.NodeId) bool ! Error {
|
|||||||
kind :: ast.expr_kind(&state.tree, id)
|
kind :: ast.expr_kind(&state.tree, id)
|
||||||
if (kind == .index or kind == .selection) {
|
if (kind == .index or kind == .selection) {
|
||||||
base :: if (kind == .index) ast.index_expr(&state.tree, id).base else ast.selection_expr(&state.tree, id).base
|
base :: if (kind == .index) ast.index_expr(&state.tree, id).base else ast.selection_expr(&state.tree, id).base
|
||||||
|
base_binding :: binding(state, base)
|
||||||
|
if (base_binding.kind == .invalid) {
|
||||||
|
state.evaluation_diagnostic = base_binding.data.diagnostic
|
||||||
|
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 (!try type_array_storage(state, array.type)) return true
|
if (!try type_array_storage(state, array.type)) return true
|
||||||
@@ -1553,13 +1614,13 @@ type_execution_value proc(state @mut State, input, annotation ast.NodeId) ast.No
|
|||||||
return concrete.node
|
return concrete.node
|
||||||
}
|
}
|
||||||
if (!try expand_type_matches(state, expression)) return no_node
|
if (!try expand_type_matches(state, expression)) return no_node
|
||||||
if (try type_array_storage(state, annotation)) return try type_array_value(state, expression, annotation)
|
|
||||||
try expand(state, expression)
|
try expand(state, expression)
|
||||||
resolved :: binding(state, expression)
|
resolved :: binding(state, expression)
|
||||||
if (resolved.kind == .invalid) {
|
if (resolved.kind == .invalid) {
|
||||||
state.evaluation_diagnostic = resolved.data.diagnostic
|
state.evaluation_diagnostic = resolved.data.diagnostic
|
||||||
return no_node
|
return no_node
|
||||||
}
|
}
|
||||||
|
if (try type_array_storage(state, annotation)) return try type_array_value(state, expression, annotation)
|
||||||
if (is_type(state, annotation)) {
|
if (is_type(state, annotation)) {
|
||||||
value :: try evaluate(state, expression)
|
value :: try evaluate(state, expression)
|
||||||
if (value.kind != .type) {
|
if (value.kind != .type) {
|
||||||
@@ -1662,7 +1723,7 @@ execute_type_statement proc(state @mut State, execution @mut TypeExecution, stat
|
|||||||
.return_stmt: {
|
.return_stmt: {
|
||||||
if (ast.return_stmt(&state.tree, statement).value) |value| {
|
if (ast.return_stmt(&state.tree, statement).value) |value| {
|
||||||
expression :: try type_snapshot(state, execution, value)
|
expression :: try type_snapshot(state, execution, value)
|
||||||
execution.returned = try type_execution_value(state, expression, try builtin(state, expression, .type))
|
execution.returned = try type_execution_value(state, expression, execution.result_type)
|
||||||
if (execution.returned != no_node) return .returned
|
if (execution.returned != no_node) return .returned
|
||||||
}
|
}
|
||||||
return .invalid
|
return .invalid
|
||||||
@@ -2211,6 +2272,8 @@ canonical_type proc(state @mut State, id ast.NodeId) ast.NodeId ! Error {
|
|||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
same_type proc(state @mut State, left, right ast.NodeId) bool ! Error {
|
same_type proc(state @mut State, left, right ast.NodeId) bool ! Error {
|
||||||
|
if (is_type(state, left) or is_type(state, right))
|
||||||
|
return is_type(state, left) and is_type(state, right)
|
||||||
a :: try canonical_type(state, left)
|
a :: try canonical_type(state, left)
|
||||||
b :: try canonical_type(state, right)
|
b :: try canonical_type(state, right)
|
||||||
if (a == no_node or b == no_node) return false
|
if (a == no_node or b == no_node) return false
|
||||||
@@ -2833,5 +2896,5 @@ flexible proc(state @State, id ast.NodeId) bool {
|
|||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
template proc(state @State, id ast.NodeId) bool {
|
template proc(state @State, id ast.NodeId) bool {
|
||||||
return ast.function_generic(&state.tree, id) or is_type(state, ast.function_decl(&state.tree, id).return_type)
|
return ast.function_generic(&state.tree, id) or compiletime_result(state, ast.function_decl(&state.tree, id).return_type)
|
||||||
}
|
}
|
||||||
|
|||||||
+212
-1
@@ -1328,6 +1328,186 @@ CASES = {
|
|||||||
main proc() Bad() { return 42 }
|
main proc() Bad() { return 42 }
|
||||||
""", b"", b"array length must be a nonnegative supported compile-time integer",
|
""", b"", b"array length must be a nonnegative supported compile-time integer",
|
||||||
),
|
),
|
||||||
|
"type_array_procedure_nested_results_in_signatures_storage_and_defaults": (
|
||||||
|
"""
|
||||||
|
Row :: alias [2]type
|
||||||
|
row proc(wide bool) Row {
|
||||||
|
if (wide) return [i16, [2]i16]
|
||||||
|
return [i8, [2]i8]
|
||||||
|
}
|
||||||
|
rows proc() [2]Row { return [row(true), row(false)] }
|
||||||
|
Storage :: struct { values rows()[0][1], bias rows()[0][0] = 2 }
|
||||||
|
read proc(value rows()[0][0]) rows()[0][0] { return value }
|
||||||
|
main proc() i16 {
|
||||||
|
value Storage :: { values = [19, 21] }
|
||||||
|
small rows()[1][1] :: [0, 0]
|
||||||
|
return read(value.values[0]) + value.values[1] + value.bias + i16(small[0])
|
||||||
|
}
|
||||||
|
""", b"42\n", None,
|
||||||
|
),
|
||||||
|
"type_array_procedure_dependent_lengths_and_ordinary_array_inputs": (
|
||||||
|
"""
|
||||||
|
identity proc($N usize, types [N]type) [N]type { return types }
|
||||||
|
row proc() [2]type { return identity(2, identity(1 + 1, [i16, bool])) }
|
||||||
|
Box proc($N usize, types [N]type) type {
|
||||||
|
return struct { value types[0], enabled types[1] = true }
|
||||||
|
}
|
||||||
|
main proc() i16 {
|
||||||
|
value Box(2, row()) :: { value = 42 }
|
||||||
|
same Box(1 + 1, [i16, bool]) :: value
|
||||||
|
if (same.enabled) return same.value
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
""", b"42\n", None,
|
||||||
|
),
|
||||||
|
"type_array_procedure_erased_runtime_generic_parameters": (
|
||||||
|
"""
|
||||||
|
row proc() [2]type { return [i16, bool] }
|
||||||
|
add proc($types [2]type, left types[0], right types[0]) types[0] {
|
||||||
|
enabled types[1] :: true
|
||||||
|
if (enabled) return left + right
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
forward proc(value i16) i16 { return add(row(), value, 2) }
|
||||||
|
main proc() i16 { return forward(40) }
|
||||||
|
""", b"42\n", None,
|
||||||
|
),
|
||||||
|
"type_array_procedure_inputs_results_and_cache_are_by_value": (
|
||||||
|
"""
|
||||||
|
change proc(types [1]mut [2]mut type) [1]mut [2]mut type {
|
||||||
|
copied := types
|
||||||
|
(copied[0])[0] = bool
|
||||||
|
return copied
|
||||||
|
}
|
||||||
|
seed proc() [1]mut [2]mut type { return [[i16, i8]] }
|
||||||
|
Selected proc() type {
|
||||||
|
original [1]mut [2]mut type := $(seed())
|
||||||
|
changed := change(original)
|
||||||
|
(changed[0])[1] = u8
|
||||||
|
cached := change(seed())
|
||||||
|
fresh := seed()
|
||||||
|
if (original[0][0] != i16 or fresh[0][0] != i16) return bool
|
||||||
|
if (cached[0][0] != bool or cached[0][1] != i8) return bool
|
||||||
|
if (changed[0][1] != u8) return bool
|
||||||
|
return original[0][0]
|
||||||
|
}
|
||||||
|
main proc() Selected() { return 42 }
|
||||||
|
""", b"42\n", None,
|
||||||
|
),
|
||||||
|
"type_array_procedure_cache_canonical_leaves_and_nominal_identity": (
|
||||||
|
"""
|
||||||
|
Original :: struct { value i16 }
|
||||||
|
Same :: alias Original
|
||||||
|
leaf proc() type { return Original }
|
||||||
|
identity proc(types [2]type) [2]type { return types }
|
||||||
|
Box proc(types [2]type) type { return struct { first types[0], second types[1] } }
|
||||||
|
Selected proc() type {
|
||||||
|
leaves :: identity([struct { value i16 }, struct { value i16 }])
|
||||||
|
copied :: identity(leaves)
|
||||||
|
if (leaves[0] == leaves[1] or leaves[0] != copied[0]) return bool
|
||||||
|
if (Box([leaves[0], leaves[0]]) == Box([leaves[1], leaves[1]])) return bool
|
||||||
|
return Box([copied[0], leaves[0]])
|
||||||
|
}
|
||||||
|
main proc() i16 {
|
||||||
|
canonical Box(identity([Same, leaf()])) :: {
|
||||||
|
first = { value = 19 }, second = { value = 21 },
|
||||||
|
}
|
||||||
|
cached Box([Original, Original]) :: canonical
|
||||||
|
nominal Selected() :: { first = { value = 1 }, second = { value = 1 } }
|
||||||
|
copy := nominal.first
|
||||||
|
copy = nominal.second
|
||||||
|
return cached.first.value + cached.second.value + nominal.first.value + copy.value
|
||||||
|
}
|
||||||
|
""", b"42\n", None,
|
||||||
|
),
|
||||||
|
"type_array_procedure_empty_nested_shapes": (
|
||||||
|
"""
|
||||||
|
empty proc() [0]type { return [] }
|
||||||
|
rows proc(types [0]type) [2][0]type { return [types, empty()] }
|
||||||
|
outer proc() [0][2]type { return [] }
|
||||||
|
Selected proc(types [2][0]type, absent [0][2]type) type {
|
||||||
|
if (types.len != 2 or types[0].len != 0 or absent.len != 0) return bool
|
||||||
|
for types |row| { for row |_| { return bool } }
|
||||||
|
return i16
|
||||||
|
}
|
||||||
|
main proc() Selected(rows(empty()), outer()) { return 42 }
|
||||||
|
""", b"42\n", None,
|
||||||
|
),
|
||||||
|
"type_array_procedure_rejects_input_shape": (
|
||||||
|
"""
|
||||||
|
first proc(types [2]type) type { return types[0] }
|
||||||
|
main proc() first([i16]) { return 42 }
|
||||||
|
""", b"", b"array literal length does not match its type",
|
||||||
|
),
|
||||||
|
"type_array_procedure_rejects_input_leaf": (
|
||||||
|
"""
|
||||||
|
first proc(types [1]type) type { return types[0] }
|
||||||
|
main proc() first([42]) { return 42 }
|
||||||
|
""", b"", b"incompatible value type",
|
||||||
|
),
|
||||||
|
"type_array_procedure_rejects_parameter_mutability_mismatch": (
|
||||||
|
"""
|
||||||
|
row proc() [1]mut type { return [i16] }
|
||||||
|
first proc(types [1]type) type { return types[0] }
|
||||||
|
main proc() first(row()) { return 42 }
|
||||||
|
""", b"", b"incompatible value type",
|
||||||
|
),
|
||||||
|
"type_array_procedure_rejects_result_shape": (
|
||||||
|
"""
|
||||||
|
bad proc() [1][2]type { return [[i16]] }
|
||||||
|
main proc() bad()[0][0] { return 42 }
|
||||||
|
""", b"", b"array literal length does not match its type",
|
||||||
|
),
|
||||||
|
"type_array_procedure_rejects_result_leaf": (
|
||||||
|
"""
|
||||||
|
bad proc() [1]type { return [42] }
|
||||||
|
main proc() bad()[0] { return 42 }
|
||||||
|
""", b"", b"incompatible value type",
|
||||||
|
),
|
||||||
|
"type_array_procedure_result_index_checks_bounds": (
|
||||||
|
"""
|
||||||
|
row proc() [1]type { return [i16] }
|
||||||
|
main proc() row()[1] { return 42 }
|
||||||
|
""", b"", b"index out of bounds",
|
||||||
|
),
|
||||||
|
"type_array_procedure_result_index_requires_usize": (
|
||||||
|
"""
|
||||||
|
row proc() [1]type { return [i16] }
|
||||||
|
main proc() row()[u8(0)] { return 42 }
|
||||||
|
""", b"", b"array index must have type usize",
|
||||||
|
),
|
||||||
|
"type_array_procedure_rejects_runtime_dependency": (
|
||||||
|
"""
|
||||||
|
row proc(wide bool) [1]type {
|
||||||
|
if (wide) return [i16]
|
||||||
|
return [i8]
|
||||||
|
}
|
||||||
|
read proc(wide bool) i16 {
|
||||||
|
value row(wide)[0] :: 42
|
||||||
|
return i16(value)
|
||||||
|
}
|
||||||
|
main proc() i16 { return read(true) }
|
||||||
|
""", b"", b"generic argument must be a compile-time value",
|
||||||
|
),
|
||||||
|
"type_array_procedure_requires_all_ordinary_arguments": (
|
||||||
|
"""
|
||||||
|
row proc(ignored usize) [1]type { return [i16] }
|
||||||
|
main proc() row()[0] { return 42 }
|
||||||
|
""", b"", b"argument count does not match",
|
||||||
|
),
|
||||||
|
"type_array_procedure_nested_execution_shares_step_limit": (
|
||||||
|
"""
|
||||||
|
row proc(count usize) [1]type {
|
||||||
|
for (0..count) |_| { }
|
||||||
|
return [i16]
|
||||||
|
}
|
||||||
|
Selected proc() type {
|
||||||
|
for (0..usize(100)) |index| { types :: row(index + 100) }
|
||||||
|
return i16
|
||||||
|
}
|
||||||
|
main proc() Selected() { return 42 }
|
||||||
|
""", b"", b"compile-time execution exceeds its recursion or step limit",
|
||||||
|
),
|
||||||
"type_execution_mutable_types_and_nested_producers": (
|
"type_execution_mutable_types_and_nested_producers": (
|
||||||
"""
|
"""
|
||||||
Nest proc(T type, depth usize) type {
|
Nest proc(T type, depth usize) type {
|
||||||
@@ -4977,7 +5157,38 @@ 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()}:3:".encode()), outcome(interpreted)
|
assert interpreted.stderr.startswith(f"{sizing.resolve()}:3:".encode()), outcome(interpreted)
|
||||||
assert len(interpreted.stderr.splitlines()) == 1, outcome(interpreted)
|
assert len(interpreted.stderr.splitlines()) == 1, outcome(interpreted)
|
||||||
return 14
|
sizing.write_text(textwrap.dedent("""
|
||||||
|
row proc($N usize, types [N]type) [N]type { return types }
|
||||||
|
rows proc() [1][2]type { return [row(2, [i16, bool])] }
|
||||||
|
FromTypes proc(types [2]type) type {
|
||||||
|
return struct { value types[0], enabled types[1] = true }
|
||||||
|
}
|
||||||
|
add proc($types [2]type, value types[0]) types[0] { return value + 2 }
|
||||||
|
""").strip() + "\n")
|
||||||
|
source.write_text(textwrap.dedent("""
|
||||||
|
lib :: import "../library"
|
||||||
|
Imported :: alias lib.FromTypes(lib.rows()[0])
|
||||||
|
read proc(value lib.rows()[0][0]) i16 {
|
||||||
|
return lib.add(lib.row(2, [i16, bool]), value)
|
||||||
|
}
|
||||||
|
main proc() i16 {
|
||||||
|
value Imported :: { value = 40 }
|
||||||
|
same lib.FromTypes([i16, bool]) :: value
|
||||||
|
if (same.enabled) return read(same.value)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
""").strip() + "\n")
|
||||||
|
check(app, executable, (b"42\n", None))
|
||||||
|
sizing.write_text(textwrap.dedent("""
|
||||||
|
row proc() [1]type { return [i16] }
|
||||||
|
bad_rows proc() [1][1]type { return [[row()[1]]] }
|
||||||
|
""").strip() + "\n")
|
||||||
|
source.write_text('lib :: import "../library"\nmain proc() lib.bad_rows()[0][0] { return 42 }\n')
|
||||||
|
check(app, executable, (b"", b"index out of bounds"))
|
||||||
|
interpreted = run(str(HONEY), "run", str(app))
|
||||||
|
assert interpreted.stderr.startswith(f"{sizing.resolve()}:2:".encode()), outcome(interpreted)
|
||||||
|
assert len(interpreted.stderr.splitlines()) == 1, outcome(interpreted)
|
||||||
|
return 16
|
||||||
|
|
||||||
|
|
||||||
def check_dependency_provenance(temporary, executable):
|
def check_dependency_provenance(temporary, executable):
|
||||||
|
|||||||
Reference in New Issue
Block a user