feat: support existing enum map initialization and lookup
This commit is contained in:
@@ -114,7 +114,11 @@ initialized records/tuples containing `type` fields live inside compile-time pro
|
||||
|
||||
fixed type arrays may also start with `undefined` inside type/type-array producers: `types [2]mut type := undefined`, then fill `types[index] = i16` in a loop. nested and whole-array updates preserve shape, mutability and independent initialization state in local copies; `.len` is available before elements initialize. reading an uninitialized type leaf, returning an incomplete array or passing one to a compile-time procedure diagnoses a read before initialization. empty shapes are valid, but every dimension must be explicit and nonnegative. construction charges each expanded cell against the shared execution bound, including nested zero-leaf shapes.
|
||||
|
||||
ordinary fixed value arrays use the same construction inside type/type-array producers: `counts [2]mut usize := undefined`, then fill indexed elements or replace whole rows/arrays. initialized leaves retain exact scalar, string, optional, record and tagged-union values; local copies retain independent partial initialization state. generated lengths/defaults and compile-time arguments can use initialized elements, while incomplete arrays cannot enter checked IR or procedure arguments. array `.len` and empty shapes do not read leaves. this does not enable runtime `undefined` storage, partial record-field initialization, uninitialized arrays in ordinary value-returning procedures, or storage-bearing constant materialization such as nonnull pointers/slices.
|
||||
ordinary fixed value arrays use the same construction inside type/type-array producers: `counts [2]mut usize := undefined`, then fill indexed elements or replace whole rows/arrays. initialized leaves retain exact scalar, string, optional, record and tagged-union values; local copies retain independent partial initialization state. generated lengths/defaults and compile-time arguments can use initialized elements, while incomplete arrays cannot enter checked IR or procedure arguments. array `.len` and empty shapes do not read leaves. partial type-valued record construction and storage-bearing constant materialization such as nonnull pointers/slices remain unsupported.
|
||||
|
||||
ordinary procedures also accept `undefined` as the initializer of a mutable local with a concrete runtime type, including nested records and fixed arrays. field/index writes initialize storage without reading its previous contents; aggregate copies retain independent contents and initialization state in the interpreter. ordinary compile-time calls can fill this storage and return materializable values, but observing or materializing an uninitialized leaf is diagnosed. native storage uses LLVM poison, not zero initialization: assign every leaf before observing it, and do not rely on native read-before-initialization traps for `undefined` storage. immutable bindings, globals, inferred array lengths and other expression uses remain invalid.
|
||||
|
||||
`some!(value)` constructs the present branch of an expected optional, evaluating its payload once and retaining exact payload typing. `value ??i16 :: some!(null)` is present with an absent inner optional; it differs from outer `null`. nested presence survives compile-time results, generic cache keys and generated defaults. optional-result procedures still require explicit returns on every path.
|
||||
|
||||
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.
|
||||
|
||||
@@ -140,7 +144,7 @@ type `==` and `!=` comparisons in compile-time expressions and specializations p
|
||||
|
||||
`typeinfo!(T)` returns the compile-time-only `TypeInfo` declared by `@std/meta`; import that exact path, optionally aliased, in the calling package. arrays expose `child` and `len`, distinct types expose their immediate backing type, and records/enums expose owned readonly `fields` collections. each field has `name`, exact `type` and ordinal `index`; enum indices follow declaration order, not numeric discriminants, and enum field types retain the reflected enum or composed error-set identity. records also expose their original optional `name`, `is_tuple` and `.auto`/`.c` layout; tuple names are canonical decimal ordinals. aliases preserve underlying names and nominal identities.
|
||||
|
||||
ordinary and inline metadata matches select bodies before runtime checking, including in nongeneric procedures. metadata collections support `.len`, checked indexing, by-value iteration and compile-time procedure inputs/results; copied field records can change without mutating the readonly collection. field names/types drive `field!`, generic calls, generated storage and defaults through interpreter/native execution, without runtime reflection objects or borrowed interpreter addresses. construction and expansion share the type-control bound. the unchanged `std/meta.EnumFieldStruct` and `std/enummap.EnumMap` type producer are verified; complete `enummap.init/get` acceptance, general storage-bearing constant materialization and runtime metadata storage are not included.
|
||||
ordinary and inline metadata matches select bodies before runtime checking, including in nongeneric procedures. metadata collections support `.len`, checked indexing, by-value iteration and compile-time procedure inputs/results; copied field records can change without mutating the readonly collection. field names/types drive `field!`, generic calls, generated storage and defaults through interpreter/native execution, without runtime reflection objects or borrowed interpreter addresses. construction and expansion share the type-control bound. the existing `std/meta.EnumFieldStruct` and `std/enummap.EnumMap`, `init` and `get` work through interpreter/native execution, including sparse/aliased/composed enum keys, absent entries and aggregate copy isolation. generated-map arguments infer key/value types before checking contextual `.tag` arguments; conflicting rigid arguments and unresolved generics still diagnose. `get` explicitly returns `null` after its lookup. general storage-bearing constant materialization and runtime metadata storage remain unsupported.
|
||||
|
||||
`struct_type!(layout, names, types, defaults)` generates an ordinary record from constant fixed arrays or tuples of equal length. for example, `struct_type!(.auto, ["value", "bias"], [i16, i8], [null, 2])` requires a `value` initializer and supplies an `i8` default for `bias`. literal collections retain each field's context, including heterogeneous types and defaults; computed collections can come from helpers and loop-filled producer locals. names must be unique ASCII identifiers. each default is checked as `?Field`: absent means required, present supplies an owned field value. generated fields retain ordinary exact typing, aggregate copy isolation, field selection and checked updates through interpreter/native execution. producer specializations cache nominal identity; imported producers and repeated compilation retain source provenance and fresh owned values. incomplete arrays and runtime dependencies are rejected, and construction shares the type-control execution bound.
|
||||
|
||||
|
||||
@@ -49,8 +49,10 @@ final inline match arms expand nominal enums, tagged unions and composed error s
|
||||
with static tags and exact by-value payloads, preserving runtime dispatch and scoped control flow.
|
||||
`typeinfo!` exposes owned compile-time array/distinct/record/enum metadata, including
|
||||
readonly field collections, exact nominal types and reflected generated storage/defaults.
|
||||
the existing `meta.EnumFieldStruct` and `enummap.EnumMap` type producer work;
|
||||
the existing `meta.EnumFieldStruct` and `enummap.EnumMap`, `init` and `get` work;
|
||||
general type-valued execution, broader materialization and full library acceptance remain open.
|
||||
`some!` preserves nested optional presence; ordinary procedures initialize concrete
|
||||
runtime `undefined` locals and compute fully initialized materializable constants.
|
||||
the driver retains frontend storage and source provenance through
|
||||
`@source/compilation`, with compilation-wide AST node identities.
|
||||
file commands retain single-file behavior; directory commands compile the
|
||||
@@ -104,8 +106,8 @@ test discovery and FFI remain outside these completed slices.
|
||||
- [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 type arrays and local type-valued records are covered below; reflection remains 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, type-valued record procedure boundaries and reflection remain open.
|
||||
- [x] field-selection reflection checkpoint: `field!(base, name)` evaluates/erases a constant string name and reuses ordinary selection through interpreter/native execution. records/tuples, enum/error-set members, tagged payloads, callbacks, pointer-qualified places and sequence `.len`/`.ptr` retain exact types, single base evaluation, address-taking, mutability and active-field checks. type-producing bodies snapshot changing selector names for nested aggregate updates without losing by-value isolation. runtime/nonstring names, malformed calls, missing members and noncanonical/overflowing tuple ordinals retain diagnostics; full metadata reflection and inline expansion remain open.
|
||||
- [x] uninitialized type-array checkpoint: fixed type arrays in type/type-array producers start with `undefined` and fill through loop-driven indexed, nested and whole-array updates. persistent local copies retain independent initialization state; `.len` works before leaves initialize. premature leaf reads and incomplete procedure arguments/results diagnose read-before-initialization without caching invalid values. explicit nonnegative dimensions, empty shapes, ordinary mutability/shape checks, nominal identity and repeatable compilation work through interpreter/native execution. construction shares the type-control bound per expanded cell, including nested zero-leaf shapes. ordinary value-array construction and local type-valued records are covered below; runtime undefined storage and metadata reflection remain open.
|
||||
- [x] uninitialized value-array checkpoint: ordinary fixed arrays in type/type-array producers start with `undefined`, fill nested indices or replace whole arrays/rows, and generate storage/defaults through interpreter/native execution. owned snapshots preserve independent partial initialization in local copies; initialized scalar/string/optional/record/tagged-union elements retain exact types and ordinary checked operations. premature leaf reads and incomplete procedure arguments diagnose read-before-initialization. empty shapes, `.len` before initialization, mutability/type/index/bounds/overflow diagnostics, per-cell construction limits and repeatable compilation are verified. runtime undefined storage, uninitialized arrays in ordinary value-returning procedures, partial record construction and storage-bearing constant materialization remain open.
|
||||
- [x] uninitialized type-array checkpoint: fixed type arrays in type/type-array producers start with `undefined` and fill through loop-driven indexed, nested and whole-array updates. persistent local copies retain independent initialization state; `.len` works before leaves initialize. premature leaf reads and incomplete procedure arguments/results diagnose read-before-initialization without caching invalid values. explicit nonnegative dimensions, empty shapes, ordinary mutability/shape checks, nominal identity and repeatable compilation work through interpreter/native execution. construction shares the type-control bound per expanded cell, including nested zero-leaf shapes. ordinary value-array construction, local type-valued records, runtime undefined storage and metadata reflection are covered below.
|
||||
- [x] uninitialized value-array checkpoint: ordinary fixed arrays in type/type-array producers start with `undefined`, fill nested indices or replace whole arrays/rows, and generate storage/defaults through interpreter/native execution. owned snapshots preserve independent partial initialization in local copies; initialized scalar/string/optional/record/tagged-union elements retain exact types and ordinary checked operations. premature leaf reads and incomplete procedure arguments diagnose read-before-initialization. empty shapes, `.len` before initialization, mutability/type/index/bounds/overflow diagnostics, per-cell construction limits and repeatable compilation are verified. runtime undefined storage and ordinary value-returning procedures are covered below; partial type-valued record construction and storage-bearing constant materialization remain open.
|
||||
- [x] type-record storage checkpoint: initialized records/tuples containing type values live inside type/type-array producers, including local schemas/defaults, typed/contextual/inferred construction, nested records/arrays, checked field/index reads and updates, whole-value replacement and immutable by-value collection captures. persistent snapshots isolate copies/captures while borrowed array places observe later updates; type leaves retain nominal identity and ordinary value leaves reuse checked IR. generated storage/defaults, invalid field/type/default diagnostics, readonly places, narrow overflow, imported producers/provenance, cached nominal results and repeatable compilation work through interpreter/native execution. field validation/construction shares the type-control bound. type-record procedure inputs/results are covered below; runtime type-valued records, partial record initialization, metadata reflection and inline expansion remain open.
|
||||
- [x] type-record procedure checkpoint: records/tuples containing type values cross compile-time procedure inputs/results, including nested arrays/records, aliases, defaults and dependent annotations. compile-time producers accept ordinary constant inputs; runtime generics accept erased `$` metadata parameters. owned snapshots isolate argument/result copies, exact type/value leaves retain checked diagnostics, and normalized cache keys preserve nominal generated types. generated storage/defaults, imported producers/provenance, shared execution bounds and repeatable compilation work through interpreter/native execution. runtime metadata storage, partial record initialization, reflection and inline expansion remain open.
|
||||
- [x] generated-record checkpoint: `struct_type!(layout, names, types, defaults)` builds ordinary records from constant fixed arrays or tuples with unique ASCII field names, concrete types and contextual optional defaults. literal heterogeneous collections, helper results and loop-filled producer arrays preserve exact types, owned defaults/copies and cached nominal identity through interpreter/native execution. `.auto` permits empty records; `.c` requires nonempty C-compatible fields and retains native field order/layout. malformed inputs, incomplete arrays, runtime dependencies, field/default validation, imported provenance, shared construction bounds and repeatable compilation retain diagnostics and isolation. metadata inspection, inline expansion, aggregate C calling conventions and storage-bearing defaults remain open.
|
||||
@@ -113,8 +115,9 @@ test discovery and FFI remain outside these completed slices.
|
||||
- [x] heterogeneous inline-tuple checkpoint: `inline for` iterates constant tuples with exact per-field value/type captures and `usize` indices through interpreter/native execution. literal/helper/aliased tuples, erased generic inputs, nested tuple projections, generated storage, empty shapes, static exits, cleanup and aggregate copy isolation retain existing semantics. type-producing bodies borrow tuple places so later fields observe updates while saved copies remain isolated. runtime dependencies and storage-bearing escapes are rejected before iteration, including discarded captures and early exits; named records and ordinary tuple loops remain invalid. imported producers, nominal type leaves and repeatable compilation are verified. inline-match syntax is covered below; metadata collections remain open.
|
||||
- [x] nominal inline-match checkpoint: final `inline |tag|:` enum arms and `inline |payload[, tag]|:` tagged-union arms expand remaining variants through interpreter/native execution. runtime subjects evaluate once; immutable static enum/tag captures specialize nested conditions, matches and generic calls while exact by-value scalar/aggregate/string/void payloads retain runtime semantics and copy isolation. statement/expression arms, ordinary loop/optional/match captures, scoped cleanup and return/break/continue work; type-producing bodies select generated storage and update outer type/value locals, including collection captures. invalid subjects/captures, redundant/nonfinal arms, ordinary pattern checks, imported provenance, shared expansion bounds and repeatable compilation retain diagnostics and ownership. composed error-set inline arms are covered below; pointer captures and metadata reflection remain open.
|
||||
- [x] composed inline-match checkpoint: composed error sets expand remaining variants through interpreter/native execution, including nested/repeated/aliased enum, tagged-union and named struct members. all-void sets use one static tag capture; payload-bearing sets use exact immutable by-value payloads and optional static tags, including void members. catch captures, nested inline tag matches, generic calls, owned aggregate copies, deferred cleanup and error returns retain ordinary semantics. constant inputs and type-producing bodies select generated storage with cached nominal identity; struct variant names remain identifiers in generated constants rather than embedding declarations. invalid compositions/captures, narrow payload types, redundant arms, imported dependencies and repeatable compilation retain diagnostics and ownership. member discovery shares the type-control bound; pointer captures and metadata reflection remain open.
|
||||
- [x] type-metadata reflection checkpoint: `typeinfo!(T)` uses the actual `@std/meta` ABI to classify supported types and expose array child/length, immediate distinct backing, record names/tuple/layout and enum/record fields through interpreter/native execution. readonly owned metadata collections retain exact nominal/type leaves, ordinal indices, composed enum identity, checked projections, by-value iteration/copies and compile-time procedure boundaries. ordinary/inline matches select runtime bodies; reflected fields drive `field!`, generics and generated storage/defaults. arity/type/import/ABI failures, match/capture validation, bounds, readonly updates, runtime escape rejection, imported provenance, shared construction bounds and repeatable compilation retain diagnostics and ownership. existing `meta.EnumFieldStruct` and `enummap.EnumMap` generated storage are verified without replacement library code; full `enummap.init/get`, general type-valued execution and broader constant materialization remain open.
|
||||
- [ ] remaining gate: general type-valued compile-time execution, broader constant materialization (including untagged unions and storage-bearing values), plus full existing-library acceptance. `enummap.init/get` still expose generic-inference/fallthrough diagnostics; optional construction and runtime undefined storage also remain acceptance dependencies. required expression-valued control flow, compound updates, formatting parsing and imported C-binding support may interleave with later gates.
|
||||
- [x] type-metadata reflection checkpoint: `typeinfo!(T)` uses the actual `@std/meta` ABI to classify supported types and expose array child/length, immediate distinct backing, record names/tuple/layout and enum/record fields through interpreter/native execution. readonly owned metadata collections retain exact nominal/type leaves, ordinal indices, composed enum identity, checked projections, by-value iteration/copies and compile-time procedure boundaries. ordinary/inline matches select runtime bodies; reflected fields drive `field!`, generics and generated storage/defaults. arity/type/import/ABI failures, match/capture validation, bounds, readonly updates, runtime escape rejection, imported provenance, shared construction bounds and repeatable compilation retain diagnostics and ownership. existing `meta.EnumFieldStruct` and `enummap.EnumMap` generated storage are verified without replacement library code; full `enummap.init/get` is covered below, while general type-valued execution and broader constant materialization remain open.
|
||||
- [x] enum-map acceptance checkpoint: existing `enummap.init/get` run through interpreter/native execution with sparse/aliased/composed enum keys, absent entries and owned aggregate values. contextual `.tag` and aggregate arguments reuse key/value types inferred from rigid generated-map arguments; unresolved generics and rigid conflicts still diagnose. `some!` constructs exact, single-evaluation optional payloads and preserves nested present-null values through compile-time results, cache keys and generated defaults. concrete mutable runtime locals initialize from `undefined`, including nested record/array updates, pointer access and by-value copies; ordinary compile-time procedures materialize fully initialized storage and reject incomplete snapshots. native undefined storage is poison, not zeroed or per-leaf checked. `get` now explicitly returns `null` on lookup fallthrough, preserving the ordinary nonvoid return contract.
|
||||
- [ ] remaining gate: general type-valued compile-time execution, broader constant materialization (including untagged unions and storage-bearing values), plus full existing-library acceptance for `ArrayList`, hash maps, broader `meta` operations and `io.print`. 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`.
|
||||
- [ ] 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.
|
||||
|
||||
@@ -121,6 +121,7 @@ Code :: enum {
|
||||
invalid_c_record_field_type
|
||||
missing_metadata_import
|
||||
invalid_metadata_abi
|
||||
invalid_undefined
|
||||
}
|
||||
|
||||
Diagnostic :: struct {
|
||||
@@ -263,5 +264,6 @@ messages :: $enummap.init(Code, []u8, {
|
||||
invalid_c_record_field_type = "record field is not a supported C-layout value type",
|
||||
missing_metadata_import = "typeinfo! requires importing @std/meta",
|
||||
invalid_metadata_abi = "@std/meta declarations do not match the compiler reflection ABI",
|
||||
invalid_undefined = "undefined requires a concrete mutable local declaration",
|
||||
})
|
||||
|
||||
|
||||
@@ -229,6 +229,13 @@ execute proc(
|
||||
value := ir.Value{}
|
||||
match instruction.op {
|
||||
.constant: value = instruction.constant
|
||||
.undefined_value: {
|
||||
destination :: &frames.items[frame_index].values[usize(instruction.destination)]
|
||||
if (ir.aggregate(instruction.type)) {
|
||||
mem.set(destination.data.elements, ir.Value{ type = .invalid, uninitialized = true })
|
||||
} else destination^ = ir.Value{ type = instruction.type, uninitialized = true }
|
||||
continue
|
||||
}
|
||||
.load_local: {
|
||||
local :: &frames.items[frame_index].locals[usize(instruction.slot)]
|
||||
if (!local.initialized) return try runtime_trap(diagnostic_store, .read_before_initialization, instruction.span)
|
||||
@@ -663,6 +670,7 @@ execute proc(
|
||||
value = ir.Value{ type = .bool, data = ir.ValueData{ boolean = result } }
|
||||
}
|
||||
}
|
||||
if (value.uninitialized) return try runtime_trap(diagnostic_store, .read_before_initialization, instruction.span)
|
||||
if (instruction.destination != ir.no_value) copy_value(module, &frames.items[frame_index].values[usize(instruction.destination)], value)
|
||||
}
|
||||
unreachable
|
||||
|
||||
+8
-1
@@ -88,6 +88,8 @@ Value :: struct {
|
||||
type Type = .void
|
||||
array_type ArrayId = no_array
|
||||
raw bool = false
|
||||
# interpreter poison survives copies; native storage uses LLVM poison directly.
|
||||
uninitialized bool = false
|
||||
data ValueData = ValueData{ integer = 0 }
|
||||
}
|
||||
|
||||
@@ -126,6 +128,7 @@ ArrayStorage :: struct {
|
||||
|
||||
Op :: enum {
|
||||
constant
|
||||
undefined_value
|
||||
load_local
|
||||
store_local
|
||||
assign_local
|
||||
@@ -372,6 +375,7 @@ aggregate_item proc(module @Module, value Value, index usize) Value {
|
||||
raw :: value.raw or shape.is_union
|
||||
if (!aggregate(type)) {
|
||||
if (raw) return read_storage(module, type, child, value.data.elements[start..], true)
|
||||
if (value.data.elements[start].uninitialized) return Value{ type = type, uninitialized = true }
|
||||
return value.data.elements[start]
|
||||
}
|
||||
width = if (raw) storage_size(module, type, child) else module.arrays.items[usize(child)].cells
|
||||
@@ -438,6 +442,9 @@ read_storage proc(module @Module, type Type, shape ArrayId, cells []mut Value, r
|
||||
bits u64 := 0
|
||||
size :: storage_size(module, type, shape)
|
||||
count :: if (size > 8) usize(8) else size
|
||||
for (0..count) |i| {
|
||||
if (cells[i].uninitialized) return Value{ type = type, uninitialized = true }
|
||||
}
|
||||
for (0..count) |i| bits += cells[i].data.unsigned << u64(i * 8)
|
||||
data := ValueData{ unsigned = bits }
|
||||
match type {
|
||||
@@ -485,7 +492,7 @@ write_storage proc(module @Module, cells []mut Value, value Value) void {
|
||||
byte u64 := 0
|
||||
if (i < 8) byte = (bits >> u64(i * 8)) & 255
|
||||
else byte = (u64(string_bytes(module, value.data.string).len) >> u64((i - 8) * 8)) & 255
|
||||
cells[i] = Value{ type = .u8, data = ValueData{ unsigned = byte } }
|
||||
cells[i] = Value{ type = .u8, uninitialized = value.uninitialized, data = ValueData{ unsigned = byte } }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,12 @@ emit_instruction proc(state @mut State, block @ir.Block, instruction ir.Instruct
|
||||
try io.print(", ptr %v{}\n", output, {u32(instruction.destination)})
|
||||
return
|
||||
}
|
||||
.undefined_value: {
|
||||
try io.write_all(output, " store ")
|
||||
try write_type(output, state.module, type)
|
||||
try io.print(" poison, ptr %v{}\n", output, {u32(instruction.destination)})
|
||||
return
|
||||
}
|
||||
.load_local: {
|
||||
try check_local(state, instruction.slot, true, instruction.span)
|
||||
try io.print(" %r{} = load ", output, {result})
|
||||
|
||||
+18
-2
@@ -1725,8 +1725,16 @@ lower_slice proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocError |
|
||||
lower_builtin proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocError | diagnostics.CapacityError) {
|
||||
tree :: state.tree
|
||||
span :: ast.node_span(tree, id)
|
||||
name :: ast.source_text(tree, ast.node_span(tree, ast.builtin_call(tree, id).name))
|
||||
name :: ast.literal_text(tree, ast.builtin_call(tree, id).name)
|
||||
if (trap_type(state, id)) return ir.no_value
|
||||
if (mem.eql(name, "some")) {
|
||||
type :: typecheck.type_of(state.checked, id)
|
||||
child :: typecheck.array_type(state.checked, type.array_type).element
|
||||
payload :: try lower_as(state, ast.call_arg(tree, id, 0), child)
|
||||
if (state.terminated or child.kind == .pointer or child.kind == .function) return payload
|
||||
present :: try constant(state, ir.Value{ type = .bool, data = ir.ValueData{ boolean = true } }, span)
|
||||
return try record_pair(state, try array_shape(state, type), present, payload, span)
|
||||
}
|
||||
if (mem.eql(name, "sizeof") or mem.eql(name, "alignof")) {
|
||||
checked :: typecheck.type_of(state.checked, ast.call_arg(tree, id, 0))
|
||||
type :: lower_type(state, checked)
|
||||
@@ -1893,7 +1901,15 @@ lower_expression proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocEr
|
||||
if (!trap_type(state, id)) trap(state, try diagnostics.add(state.diagnostics, .type_not_value, span), span)
|
||||
}
|
||||
.alias_expr: return try lower_expression(state, ast.type_operand(tree, id))
|
||||
.enum_literal, .literal_undefined: {
|
||||
.literal_undefined: {
|
||||
if (trap_type(state, id)) return ir.no_value
|
||||
checked :: typecheck.type_of(state.checked, id)
|
||||
return try emit(state, ir.Instruction{
|
||||
op = .undefined_value, type = lower_type(state, checked),
|
||||
array_type = try array_shape(state, checked), span = span,
|
||||
})
|
||||
}
|
||||
.enum_literal: {
|
||||
if (!trap_type(state, id)) unreachable
|
||||
}
|
||||
.match_expr: return try lower_match(state, id)
|
||||
|
||||
@@ -174,6 +174,66 @@ supports_bare_void_value_returns_and_fallthrough test {
|
||||
}
|
||||
}
|
||||
|
||||
constructs_present_optionals_without_losing_nested_null_or_repeating_values test {
|
||||
diagnostic_store := diagnostics.init(mem.c_allocator)
|
||||
defer diagnostics.deinit(&diagnostic_store)
|
||||
source ::
|
||||
`next proc(calls @mut i16) i16 {
|
||||
` calls^ = calls^ + 1
|
||||
` return 42
|
||||
`}
|
||||
`nested proc() ??i16 { return some!(null) }
|
||||
`Box :: struct { values [2]i16 }
|
||||
`main proc() bool {
|
||||
` calls i16 := 0
|
||||
` absent ??i16 :: null
|
||||
` present ??i16 :: nested()
|
||||
` full ??i16 :: some!(some!(42))
|
||||
` exact i16 :: 42
|
||||
` value ?i16 :: some!(exact)
|
||||
` once ?i16 :: some!(next(&calls))
|
||||
` boxed ?Box :: some!(Box{ values = [20, 22] })
|
||||
` pointer ?@i16 :: some!(&exact)
|
||||
` nested_pointer ??@i16 :: some!(null)
|
||||
` return (
|
||||
` absent == null and present != null and present? == null
|
||||
` and full != null and full? != null and full?? == 42
|
||||
` and value? == 42 and once? == 42 and calls == 1
|
||||
` and boxed?.values[0] + boxed?.values[1] == 42
|
||||
` and pointer?^ == 42 and nested_pointer != null and nested_pointer? == null
|
||||
` )
|
||||
`}
|
||||
module := try compile_owned(source, &diagnostic_store)
|
||||
defer ir.deinit(&module)
|
||||
try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
|
||||
outcome := try interpreter.run(mem.c_allocator, &module, &diagnostic_store)
|
||||
defer interpreter.deinit(&outcome)
|
||||
try testing.expect(outcome.trap == null)
|
||||
try testing.expect_equal(ir.Type.bool, outcome.value.type)
|
||||
try testing.expect(outcome.value.data.boolean)
|
||||
}
|
||||
|
||||
rejects_some_without_optional_context_wrong_arity_and_inexact_payloads test {
|
||||
sources [6][]u8 :: [
|
||||
"main proc() ?i16 { return some!() }\n",
|
||||
"main proc() ?i16 { return some!(1, 2) }\n",
|
||||
"main proc() bool { return some!(true) }\n",
|
||||
"main proc() ?i16 { return some!(true) }\n",
|
||||
"main proc() ?i16 { value i32 :: 42\nreturn some!(value) }\n",
|
||||
"main proc() ?i16 { return some!(null) }\n",
|
||||
]
|
||||
for (sources) |source, i| {
|
||||
diagnostic_store := diagnostics.init(mem.c_allocator)
|
||||
defer diagnostics.deinit(&diagnostic_store)
|
||||
module := try compile_owned(source, &diagnostic_store)
|
||||
defer ir.deinit(&module)
|
||||
outcome := try interpreter.run(mem.c_allocator, &module, &diagnostic_store)
|
||||
defer interpreter.deinit(&outcome)
|
||||
code diagnostics.Code :: if (i < 2) .argument_count_mismatch else if (i == 2) .invalid_operand else .type_mismatch
|
||||
try expect_trap(outcome, &diagnostic_store, code)
|
||||
}
|
||||
}
|
||||
|
||||
preserves_demanded_diagnostic_identity_and_source_location test {
|
||||
diagnostic_store := diagnostics.init(mem.c_allocator)
|
||||
defer diagnostics.deinit(&diagnostic_store)
|
||||
|
||||
@@ -924,6 +924,20 @@ collect_declaration proc(state @mut State, id ast.NodeId) void ! (mem.AllocError
|
||||
state.cells[usize(id)].fixed = true
|
||||
state.cells[usize(id)].type = invalid_type(binding.data.diagnostic)
|
||||
}
|
||||
if (ast.expr_kind(state.tree, declaration.initializer) == .literal_undefined) {
|
||||
type :: state.cells[usize(id)].type
|
||||
if (
|
||||
!state.cells[usize(id)].local or declaration.mutability != .variable
|
||||
or type.kind == .none or type.kind == .type_value or has_inferred_count(state, type)
|
||||
) {
|
||||
try fail(state, declaration.initializer, .invalid_undefined, ast.node_span(state.tree, declaration.initializer))
|
||||
} else {
|
||||
state.cells[usize(declaration.initializer)].role = .expression
|
||||
seed(state, declaration.initializer, type)
|
||||
}
|
||||
try depend(state, declaration.initializer, id)
|
||||
return
|
||||
}
|
||||
try collect_expression(state, declaration.initializer)
|
||||
if (
|
||||
type_syntax(ast.expr_kind(state.tree, declaration.initializer))
|
||||
@@ -1201,7 +1215,7 @@ collect_expression proc(state @mut State, id ast.NodeId) void ! (mem.AllocError
|
||||
.literal_string: state.cells[usize(id)].type = Type{ kind = .string }
|
||||
.literal_null: state.cells[usize(id)].type = Type{ kind = .null }
|
||||
.literal_void: state.cells[usize(id)].type = Type{ kind = .void }
|
||||
.literal_undefined: try fail(state, id, .comptime_argument_required, ast.node_span(tree, id))
|
||||
.literal_undefined: try fail(state, id, .invalid_undefined, ast.node_span(tree, id))
|
||||
.builtin_call: try collect_builtin(state, id)
|
||||
.slice: {
|
||||
view :: ast.slice_expr(tree, id)
|
||||
@@ -1550,6 +1564,17 @@ demand proc(state @mut State, id ast.NodeId, expected Type, code diagnostics.Cod
|
||||
seed(state, id, expected)
|
||||
return
|
||||
}
|
||||
if (mem.eql(name, "some") and expected.kind != .fallible) {
|
||||
state.cells[index].expected = expected
|
||||
if (expected.kind == .optional) {
|
||||
seed(state, id, expected)
|
||||
if (ast.call_arg_count(state.tree, id) == 1) {
|
||||
try demand(state, ast.call_arg(state.tree, id, 0),
|
||||
state.arrays.items[usize(expected.array_type)].element, code)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
if (expected.kind == .fallible) {
|
||||
descriptor :: state.arrays.items[usize(expected.array_type)]
|
||||
@@ -2368,7 +2393,7 @@ c_abi_type proc(state @State, type Type) bool {
|
||||
|
||||
@hide:file
|
||||
builtin_name proc(state @State, id ast.NodeId) []u8 {
|
||||
return ast.source_text(state.tree, ast.node_span(state.tree, ast.builtin_call(state.tree, id).name))
|
||||
return ast.literal_text(state.tree, ast.builtin_call(state.tree, id).name)
|
||||
}
|
||||
|
||||
@hide:file
|
||||
@@ -2460,13 +2485,31 @@ infer_builtin proc(state @mut State, id ast.NodeId, defaults bool) Type ! (mem.A
|
||||
layout :: mem.eql(name, "sizeof") or mem.eql(name, "alignof")
|
||||
cast :: mem.eql(name, "ptrcast") or mem.eql(name, "constcast")
|
||||
memory :: mem.eql(name, "memcopy") or mem.eql(name, "memset")
|
||||
valid_count :: (layout and count == 1) or (cast and (count == 1 or count == 2)) or (memory and count == 2)
|
||||
some :: mem.eql(name, "some")
|
||||
valid_count :: (layout and count == 1) or (cast and (count == 1 or count == 2)) or (memory and count == 2) or (some and count == 1)
|
||||
if (!valid_count) {
|
||||
code diagnostics.Code :: if (layout or cast or memory) .argument_count_mismatch else .invalid_operand
|
||||
code diagnostics.Code :: if (layout or cast or memory or some) .argument_count_mismatch else .invalid_operand
|
||||
if (defaults) try fail(state, id, code, span)
|
||||
return group_type(state, id)
|
||||
}
|
||||
if (layout) return group_type(state, id)
|
||||
if (some) {
|
||||
result :: state.cells[root(state, usize(id))].expected
|
||||
if (result.kind != .optional) {
|
||||
if (defaults) try fail(state, id, .invalid_operand, span)
|
||||
return group_type(state, id)
|
||||
}
|
||||
child :: state.arrays.items[usize(result.array_type)].element
|
||||
operand :: ast.call_arg(state.tree, id, 0)
|
||||
try demand(state, operand, child, .type_mismatch)
|
||||
actual :: try infer_shape(state, operand, defaults)
|
||||
if (
|
||||
defaults and actual.kind != .none and actual.kind != .invalid
|
||||
and !assignable_value(state, operand, actual, child)
|
||||
) try fail(state, operand, .type_mismatch, ast.node_span(state.tree, operand))
|
||||
seed(state, id, result)
|
||||
return group_type(state, id)
|
||||
}
|
||||
if (cast) {
|
||||
result := state.cells[root(state, usize(id))].expected
|
||||
if (count == 2) result = state.types[usize(ast.call_arg(state.tree, id, 0))]
|
||||
|
||||
@@ -1571,10 +1571,19 @@ specialize_call proc(state @mut State, call, function ast.NodeId) void ! Error {
|
||||
if (view.comptime) continue
|
||||
argument :: runtime_arguments.items[position]
|
||||
position += 1
|
||||
contextual :: flexible(state, argument)
|
||||
contextual := flexible(state, argument) or contextual_literal(state, argument)
|
||||
if (ast.expr_kind(&state.tree, argument) == .record_literal) {
|
||||
annotation :: ast.record_literal(&state.tree, argument).type
|
||||
contextual = annotation == null or ast.expr_kind(&state.tree, annotation?) == .enum_literal
|
||||
}
|
||||
if (contextual != (phase == 1)) continue
|
||||
actual :: try expression_type(state, argument, 0)
|
||||
if (actual == no_node or !try infer(state, function, view.type, actual, arguments, contextual)) {
|
||||
# contextual literals need not have an independent type; the rewritten
|
||||
# call checks them against the substituted signature after inference.
|
||||
if (
|
||||
(actual == no_node or !try infer(state, function, view.type, actual, arguments, contextual))
|
||||
and !contextual
|
||||
) {
|
||||
try reject(state, call, .cannot_infer_generic)
|
||||
return
|
||||
}
|
||||
@@ -1872,6 +1881,8 @@ evaluation_owned proc(state @State, id ast.NodeId, flags []mut u8) void {
|
||||
@hide:file
|
||||
contextual_literal proc(state @State, id ast.NodeId) bool {
|
||||
kind :: ast.expr_kind(&state.tree, id)
|
||||
if (kind == .builtin_call)
|
||||
return mem.eql(ast.literal_text(&state.tree, ast.builtin_call(&state.tree, id).name), "some")
|
||||
return kind == .record_literal or kind == .array_literal or kind == .enum_literal
|
||||
}
|
||||
|
||||
@@ -1937,6 +1948,17 @@ serialize_value proc(
|
||||
state @mut State, module @ir.Module, checked @typecheck.Result,
|
||||
type typecheck.Type, value ir.Value, span ast.Span,
|
||||
) ast.NodeId ! Error {
|
||||
uninitialized := value.uninitialized
|
||||
if (ir.aggregate(value.type)) {
|
||||
if (module.arrays.items[usize(value.array_type)].is_union)
|
||||
uninitialized = ir.union_tag(module, value).uninitialized
|
||||
else if (type.kind == .optional)
|
||||
uninitialized = ir.aggregate_item(module, value, 0).uninitialized
|
||||
}
|
||||
if (uninitialized) {
|
||||
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, .read_before_initialization, span)
|
||||
return no_node
|
||||
}
|
||||
result := no_node
|
||||
if (type.kind == .array) {
|
||||
array :: typecheck.array_type(checked, type.array_type)
|
||||
@@ -2013,7 +2035,12 @@ serialize_value proc(
|
||||
result = try ast.add_literal(state.builder, .null, span)
|
||||
} else {
|
||||
child :: ir.aggregate_item(module, value, 1)
|
||||
return try serialize_value(state, module, checked, container.element, child, span)
|
||||
payload :: try serialize_value(state, module, checked, container.element, child, span)
|
||||
if (payload == no_node) return no_node
|
||||
name :: try ast.add_generated_identifier(state.builder, span,
|
||||
try strpool.intern(state.strings, "some"), "some")
|
||||
arguments [1]ast.NodeId :: [payload]
|
||||
result = try ast.add_builtin_call(state.builder, span, name, arguments[..])
|
||||
}
|
||||
} else if (ir.integer_bits(value.type) != 0 and type.kind != .pointer) {
|
||||
negative :: !ir.unsigned_integer(value.type) and value.data.integer < 0
|
||||
@@ -2063,6 +2090,9 @@ constant_equal proc(state @mut State, left, right ast.NodeId) bool ! Error {
|
||||
}
|
||||
return true
|
||||
}
|
||||
if (kind == .builtin_call and contextual_literal(state, left) and contextual_literal(state, right)) {
|
||||
return try constant_equal(state, ast.call_arg(&state.tree, left, 0), ast.call_arg(&state.tree, right, 0))
|
||||
}
|
||||
if (kind == .enum_literal)
|
||||
return ast.name_id(&state.tree, ast.enum_literal(&state.tree, left)) == ast.name_id(&state.tree, ast.enum_literal(&state.tree, right))
|
||||
return try value_equal(state, try evaluate(state, left), try evaluate(state, right))
|
||||
@@ -2092,6 +2122,8 @@ constant_hash proc(state @mut State, id ast.NodeId) usize ! Error {
|
||||
}
|
||||
return result
|
||||
}
|
||||
if (kind == .builtin_call and contextual_literal(state, id))
|
||||
return mix(hash, try constant_hash(state, ast.call_arg(&state.tree, id, 0)))
|
||||
if (kind == .enum_literal) return mix(hash, usize(ast.name_id(&state.tree, ast.enum_literal(&state.tree, id))))
|
||||
return try value_hash(state, try evaluate(state, id))
|
||||
}
|
||||
@@ -2199,6 +2231,7 @@ evaluate_ir_body proc(state @mut State, at, body, result_type ast.NodeId) Value
|
||||
source_type :: typecheck.type_of(&checked, result_type)
|
||||
node :: try serialize_value(state, &module, &checked, source_type, value, span)
|
||||
if (node == no_node) {
|
||||
if (state.evaluation_diagnostic == diagnostics.no_diagnostic)
|
||||
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, .comptime_value_escape, span)
|
||||
return Value{}
|
||||
}
|
||||
|
||||
@@ -44,4 +44,5 @@ get proc($E, $V type, map @EnumMap(E, V), key E) ?V {
|
||||
}
|
||||
else: compile_error!("EnumMap key must be an enum")
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
+208
-9
@@ -11,6 +11,165 @@ HONEY = ROOT / "build/honey"
|
||||
|
||||
# Explicit expectations guard against frontend bugs shared by both execution paths.
|
||||
CASES = {
|
||||
"runtime_undefined_nested_storage_and_copy_isolation": (
|
||||
"""
|
||||
Item :: struct { values [2]mut i16, label string }
|
||||
fill proc() Item {
|
||||
item Item := undefined
|
||||
for (usize(0)..item.values.len) |i| { item.values[i] = i16(i) + 19 }
|
||||
item.label = "yes"
|
||||
return item
|
||||
}
|
||||
main proc() i16 {
|
||||
item := fill()
|
||||
saved :: item
|
||||
pointer :: &item
|
||||
pointer.values[0] = 0
|
||||
return saved.values[0] + saved.values[1] + i16(saved.label.len)
|
||||
}
|
||||
""", b"42\n", None,
|
||||
),
|
||||
"ordinary_constant_procedures_fill_undefined_arrays": (
|
||||
"""
|
||||
values proc() [2]mut i16 {
|
||||
result [2]mut i16 := undefined
|
||||
for (usize(0)..result.len) |i| { result[i] = i16(i) + 20 }
|
||||
return result
|
||||
}
|
||||
main proc() i16 { result :: $(values())
|
||||
return result[0] + result[1] + 1 }
|
||||
""", b"42\n", None,
|
||||
),
|
||||
"ordinary_constants_reject_partial_undefined_snapshots": (
|
||||
"""
|
||||
values proc() [2]mut i16 {
|
||||
result [2]mut i16 := undefined
|
||||
result[0] = 42
|
||||
saved :: result
|
||||
result[1] = 0
|
||||
return saved
|
||||
}
|
||||
main proc() [2]mut i16 { return $(values()) }
|
||||
""", b"", b"read before initialization",
|
||||
),
|
||||
"ordinary_constants_reject_undefined_optional_presence": (
|
||||
"""
|
||||
missing proc() ?i16 { value ?i16 := undefined
|
||||
return value }
|
||||
main proc() ?i16 { return $(missing()) }
|
||||
""", b"", b"read before initialization",
|
||||
),
|
||||
"ordinary_constants_reject_undefined_union_tags": (
|
||||
"""
|
||||
Choice :: union(enum) { value i16, empty void }
|
||||
missing proc() Choice { value Choice := undefined
|
||||
return value }
|
||||
main proc() Choice { return $(missing()) }
|
||||
""", b"", b"read before initialization",
|
||||
),
|
||||
"undefined_zero_leaf_shapes_cross_runtime_and_constant_calls": (
|
||||
"""
|
||||
empty proc() [2]mut [0]i16 {
|
||||
rows [2]mut [0]i16 := undefined
|
||||
return rows
|
||||
}
|
||||
main proc() usize {
|
||||
runtime :: empty()
|
||||
constant :: $(empty())
|
||||
return runtime.len + constant.len
|
||||
}
|
||||
""", b"4\n", None,
|
||||
),
|
||||
"some_nested_present_null_survives_constant_roundtrip": (
|
||||
"""
|
||||
nested proc() ??i16 { return some!(null) }
|
||||
present proc() ??i16 { return some!(some!(42)) }
|
||||
main proc() i16 {
|
||||
first ??i16 :: $(nested())
|
||||
second ??i16 :: $(present())
|
||||
absent ??i16 :: null
|
||||
if (first == null or first? != null or absent != null) return -1
|
||||
return second??
|
||||
}
|
||||
""", b"42\n", None,
|
||||
),
|
||||
"some_optional_specialization_keys_preserve_every_presence_layer": (
|
||||
"""
|
||||
Box proc($value ??i16) type { return struct { value ??i16 = value } }
|
||||
main proc() i16 {
|
||||
empty Box(null) :: {}
|
||||
nested Box(some!(null)) :: {}
|
||||
present Box(some!(some!(42))) :: {}
|
||||
same Box(some!(some!(42))) :: present
|
||||
if (empty.value != null or nested.value == null or nested.value? != null) return -1
|
||||
if (Box(null) == Box(some!(null))) return -2
|
||||
if (Box(some!(some!(42))) == Box(some!(some!(41)))) return -3
|
||||
return same.value??
|
||||
}
|
||||
""", b"42\n", None,
|
||||
),
|
||||
"generic_contextual_literals_use_rigid_arguments_in_either_order": (
|
||||
"""
|
||||
Key :: enum { first, last }
|
||||
choose proc($T type, value T, anchor @T) T { return value }
|
||||
payload proc($T type, anchor @T, value struct { item T }) T { return value.item }
|
||||
main proc() i16 {
|
||||
key Key :: .first
|
||||
selected :: choose(.last, &key)
|
||||
other :: payload(&key, { item = .first })
|
||||
return match selected { .last: match other { .first: 42, else: 0 }, else: 0 }
|
||||
}
|
||||
""", b"42\n", None,
|
||||
),
|
||||
"generic_contextual_enum_rejects_unknown_member": (
|
||||
"""
|
||||
Key :: enum { first }
|
||||
choose proc($T type, anchor @T, value T) T { return value }
|
||||
main proc() i16 { key Key :: .first
|
||||
choose(&key, .missing)
|
||||
return 42 }
|
||||
""", b"", b"record has no such field",
|
||||
),
|
||||
"generic_contextual_only_call_cannot_infer_nominal_type": (
|
||||
"""
|
||||
choose proc($T type, value T) T { return value }
|
||||
main proc() i16 { choose(.first)
|
||||
return 42 }
|
||||
""", b"", b"cannot infer consistent compile-time arguments",
|
||||
),
|
||||
"generic_contextual_arguments_do_not_hide_rigid_type_conflicts": (
|
||||
"""
|
||||
First :: enum { first }
|
||||
Second :: enum { first }
|
||||
choose proc($T type, left @T, right @T, value T) T { return value }
|
||||
main proc() i16 {
|
||||
left First :: .first
|
||||
right Second :: .first
|
||||
choose(&left, &right, .first)
|
||||
return 42
|
||||
}
|
||||
""", b"", b"cannot infer consistent compile-time arguments",
|
||||
),
|
||||
"undefined_rejects_immutable_local": (
|
||||
"main proc() i16 { value i16 :: undefined\nreturn value }",
|
||||
b"", b"undefined requires a concrete mutable local declaration",
|
||||
),
|
||||
"undefined_rejects_untyped_local": (
|
||||
"main proc() i16 { value := undefined\nreturn 42 }",
|
||||
b"", b"undefined requires a concrete mutable local declaration",
|
||||
),
|
||||
"undefined_rejects_noninitializer_expression": (
|
||||
"main proc() i16 { return undefined }",
|
||||
b"", b"undefined requires a concrete mutable local declaration",
|
||||
),
|
||||
"undefined_rejects_inferred_array_length": (
|
||||
"main proc() i16 { values [_]mut i16 := undefined\nreturn 42 }",
|
||||
b"", b"undefined requires a concrete mutable local declaration",
|
||||
),
|
||||
"undefined_rejects_global_storage": (
|
||||
"value i16 := undefined\nmain proc() i16 { return value }",
|
||||
b"", b"undefined requires a concrete mutable local declaration",
|
||||
),
|
||||
"inline_match_composed_enums_keep_static_sum_tags_and_runtime_dispatch": (
|
||||
"""
|
||||
First :: enum { skipped, first }
|
||||
@@ -2897,15 +3056,6 @@ CASES = {
|
||||
main proc() Bad() { return 42 }
|
||||
""", b"", b"compile-time execution exceeds its recursion or step limit",
|
||||
),
|
||||
"type_arrays_undefined_does_not_enable_runtime_storage": (
|
||||
"""
|
||||
main proc() i16 {
|
||||
values [1]mut i16 := undefined
|
||||
values[0] = 42
|
||||
return values[0]
|
||||
}
|
||||
""", b"", b"generic argument must be a compile-time value",
|
||||
),
|
||||
"value_arrays_undefined_loop_initialization_generates_storage": (
|
||||
"""
|
||||
Storage proc() type {
|
||||
@@ -7843,6 +7993,55 @@ def check_typeinfo_packages(temporary, executable):
|
||||
}
|
||||
""", b"42\n", None,
|
||||
),
|
||||
"real_enummap_init_get_sparse_alias_and_absence": (
|
||||
"""
|
||||
import "@std/meta"
|
||||
import "@std/enummap"
|
||||
Key :: enum(i16) { last = -7, first = 3, missing = 90 }
|
||||
KeyAlias :: alias Key
|
||||
main proc() i16 {
|
||||
values :: enummap.init(KeyAlias, i16, { last = 19, first = 23 })
|
||||
empty :: enummap.init(Key, i16, {})
|
||||
if (enummap.get(&values, .missing) != null) return -1
|
||||
if (enummap.get(&empty, .first) != null) return -2
|
||||
return enummap.get(&values, .last)? + enummap.get(Key, i16, &values, .first)?
|
||||
}
|
||||
""", b"42\n", None,
|
||||
),
|
||||
"real_enummap_composed_keys_and_owned_aggregate_values": (
|
||||
"""
|
||||
import "@std/meta"
|
||||
import "@std/enummap"
|
||||
First :: enum { first, absent }
|
||||
Second :: enum { last }
|
||||
Keys :: alias (First | (Second | First))
|
||||
Item :: struct { values [2]mut i16 }
|
||||
main proc() i16 {
|
||||
source Item := { values = [19, 23] }
|
||||
values :: enummap.init(Keys, Item, { first = source })
|
||||
source.values[0] = 0
|
||||
copy := enummap.get(&values, .first)?
|
||||
copy.values[1] = 0
|
||||
retained :: enummap.get(&values, .first)?
|
||||
if (enummap.get(&values, .last) != null or enummap.get(&values, .absent) != null) return -1
|
||||
return retained.values[0] + retained.values[1]
|
||||
}
|
||||
""", b"42\n", None,
|
||||
),
|
||||
"real_enummap_all_entries_materialize_at_compile_time": (
|
||||
"""
|
||||
import "@std/meta"
|
||||
import "@std/enummap"
|
||||
Key :: enum { first, last }
|
||||
known proc() enummap.EnumMap(Key, i16) {
|
||||
return enummap.init(Key, i16, { first = 19, last = 23 })
|
||||
}
|
||||
main proc() i16 {
|
||||
values :: $(known())
|
||||
return enummap.get(&values, .first)? + enummap.get(&values, .last)?
|
||||
}
|
||||
""", b"42\n", None,
|
||||
),
|
||||
"metadata_slices_cross_producers_without_losing_type_identity": (
|
||||
"""
|
||||
import "@std/meta"
|
||||
|
||||
Reference in New Issue
Block a user