feat: infer compile-time metadata locals

This commit is contained in:
2026-10-31 10:50:37 +01:00
parent 4fea18b834
commit e036070bf2
7 changed files with 419 additions and 78 deletions
+175 -22
View File
@@ -11,6 +11,161 @@ HONEY = ROOT / "build/honey"
# Explicit expectations guard against frontend bugs shared by both execution paths.
CASES = {
"compiletime_only_main_cannot_select_another_runtime_function": (
"""
fallback proc() i16 { return -99 }
main proc() i16 {
types := [i16, bool]
return 42
}
""", b"", b"main must be a runtime function with no parameters",
),
"inferred_metadata_locals_preserve_owned_copies_and_updates": (
"""
Config :: struct { children [2]mut ?type, count usize }
compute proc(count usize) i16 {
config := Config { children = [i8, null], count = 0 }
saved :: config
while (config.count < count) {
config.children[0] = i16
config.children[1] = config.children[0]
config.count += 1
}
types := [i8, bool]
before :: types
types = [i16, bool]
tuple := { types[0], config.count }
tuple.0 = i8
if (saved.children[0]? != i8 or saved.count != 0 or before[0] != i8) return -1
if (tuple.0 != i8 or tuple.1 != 2) return -2
for config.children |child| { if child |T| { if (T != i16) return -3 } }
Element :: types[0]
values [count]Element :: [19, 23]
return values[0] + values[1]
}
main proc() i16 { return compute(2) }
""", b"42\n", None,
),
"inferred_type_locals_recurse_and_preserve_cleanup": (
"""
Error :: enum { failed }
count proc(depth usize) i16 {
T := i8
T = i16
defer if (T != i16) unreachable
if (depth == 0) return 21
return count(depth - 1) + 21
}
fail proc() i16 ! Error {
T := ?i16
T = ?i8
errdefer if (T != ?i8) unreachable
return .failed
}
main proc() i16 { return fail() catch { yield count(1) } }
""", b"42\n", None,
),
"inferred_metadata_call_results_and_checked_projections": (
"""
Config :: struct { child type, count usize }
Schema :: alias Config
create proc() Schema { return { child = i8, count = 2 } }
children proc() [2]type { return [i8, bool] }
optional proc() ?type { return i8 }
pair proc() struct { type, usize } { return {i8, 2} }
field_type proc(value i16) i16 {
T := create().child
T = i16
result T :: value
return result
}
indexed_type proc(value i16) i16 {
T := children()[0]
T = i16
result T :: value
return result
}
unwrapped_type proc(value i16) i16 {
T := optional()?
T = i16
result T :: value
return result
}
tuple_type proc(value i16) i16 {
T := pair().0
T = i16
result T :: value
return result
}
compute proc() i16 {
config := create()
saved :: config
config.child = i16
if (saved.child != i8) return -1
return field_type(19) + indexed_type(19) + unwrapped_type(2) + tuple_type(2)
}
main proc() i16 { return compute() }
""", b"42\n", None,
),
"inferred_metadata_value_blocks_matches_and_fallible_calls": (
"""
Error :: enum { failed }
Config :: struct { child type, count usize }
create proc() Config ! Error { return { child = i8, count = 2 } }
compute proc(flag bool) i16 ! Error {
errdefer unreachable
config := try create()
saved :: config
config.child = i16
fields := { yield [i8, bool] }
selected := if (flag) { yield { child = i16, count = config.count } }
else { yield { child = i8, count = usize(1) } }
if (saved.child != i8 or fields[1] != bool or selected.child != i16) return -1
Element :: selected.child
values [selected.count]Element :: [19, 23]
return values[0] + values[1]
}
main proc() i16 { return compute(true) catch -2 }
""", b"42\n", None,
),
"inferred_metadata_locals_require_constant_inputs": (
"""
Config :: struct { child type, count usize }
count proc(value usize) usize {
config := Config { child = i16, count = value }
return config.count
}
runtime proc(value usize) usize { return count(value) }
main proc() usize { return runtime(42) }
""", b"", b"generic argument must be a compile-time value",
),
"inferred_metadata_arrays_keep_readonly_elements": (
"""
compute proc() usize {
types := [i8, bool]
types[0] = i16
return types.len
}
main proc() usize { return compute() }
""", b"", b"indexed assignment requires a writable array element",
),
"immutable_type_aliases_and_scalar_metadata_projections_stay_runtime": (
"""
Config :: struct { child type, count usize }
create proc() Config { return { child = i16, count = 2 } }
Child proc() type { return i16 }
add proc(value i16) i16 {
Local :: alias i16
Other :: alias Child()
amount := create().count
argument Local :: value
result Other :: argument + i16(amount)
return result
}
forward proc(value i16) i16 { return add(value) }
main proc() i16 { return forward(40) }
""", b"42\n", None,
),
"explicit_metadata_locals_compute_without_metadata_inputs": (
"""
Config :: struct { children [2]mut ?type, count usize }
@@ -3919,15 +4074,6 @@ CASES = {
main proc() Selected() { return 42 }
""", b"", b"compile-time execution exceeds its recursion or step limit",
),
"type_record_procedure_results_do_not_enable_runtime_storage": (
"""
config proc() struct { child type, count i16 } { return { child = i16, count = 42 } }
main proc() i16 {
input :: config()
return input.count
}
""", b"", b"",
),
"type_records_reject_unknown_initializer_field": (
"""
Bad proc() type {
@@ -4020,14 +4166,6 @@ CASES = {
main proc() Bad() { return 42 }
""", b"", b"outside its runtime type",
),
"type_records_do_not_enable_runtime_storage": (
"""
main proc() i16 {
config :: { child = i16, count = 42 }
return config.count
}
""", b"", b"type name cannot be used as a value",
),
"field_reflection_runtime_places_callbacks_and_single_evaluation": (
"""
Point :: struct { value i16, callback @proc(value i16) i16 }
@@ -11964,13 +12102,28 @@ def check_typeinfo_packages(temporary, executable):
""",
b"indexed assignment requires a writable array element",
),
(
"runtime_metadata_escape",
"main proc() i16 { info :: typeinfo!(i16)\nreturn 42 }",
b"record fields require a concrete value type",
),
):
cases[name] = ('import "@std/meta"\n' + text, b"", error)
cases["inferred_reflection_locals_compute_without_metadata_parameters"] = (
"""
import "@std/meta"
Record :: struct { child i16, enabled bool }
compute proc(bias i16) i16 {
info :: typeinfo!(Record)
match info {
.record |record|: {
saved :: record.fields[0]
field := saved
field.type = i8
if (saved.type != i16 or field.type != i8) return -1
return bias + i16(record.fields.len)
}
else: return -2,
}
}
main proc() i16 { return compute(40) }
""", b"42\n", None,
)
cases["missing_meta_import"] = (
"main proc() i16 { typeinfo!(i16)\nreturn 42 }",
b"", b"typeinfo! requires importing @std/meta",