feat: support source pointers and references

This commit is contained in:
2026-10-26 10:37:08 +01:00
parent 9966a1f523
commit 676f3666b4
17 changed files with 981 additions and 78 deletions
+261 -1
View File
@@ -11,6 +11,245 @@ HONEY = ROOT / "build/honey"
# Explicit expectations guard against frontend bugs shared by both execution paths.
CASES = {
"pointer_assignment_evaluates_rhs_first_once": (
"""
Box :: struct { value i16, count i16 }
target proc(p @mut Box) @mut Box {
p.count = p.count + 1
return p
}
input proc(p @mut Box) i16 {
p.count = p.count + 10
return p.count
}
main proc() [2]i16 {
box Box := { value = 0, count = 0 }
target(&box).value = input(&box)
return [box.value, box.count]
}
""", b"[10, 11]\n", None,
),
"pointer_recursive_record_type": (
"""
Node :: struct { value i16, next @Node }
read_next proc(p @Node) i16 { return p.next.value }
main proc() i16 { return 42 }
""", b"42\n", None,
),
"pointer_many_dereference": (
"""
read proc(p *i16) i16 { return p^ }
main proc() i16 {
values [1]mut i16 := [42]
return read((&values).ptr)
}
""", b"42\n", None,
),
"pointer_many_from_array_element": (
"""
main proc() i16 {
values [3]mut i16 := [1, 2, 3]
pointer *mut i16 :: &values[0]
pointer[2] = 42
return values[2]
}
""", b"42\n", None,
),
"pointer_readonly_child_type_rejected": (
"read proc(p @u16) u16 { return p^ }\nmain proc() u16 { value i16 := 42\nreturn read(&value) }",
b"", b"incompatible value type",
),
"pointer_scalar_alias_across_calls": (
"""
identity proc(p @mut i16) @mut i16 { return p }
update proc(p @mut i16) void { p^ = p^ + 2 }
main proc() i16 {
value i16 := 40
pointer :: identity(&value)
update(pointer)
return value
}
""", b"42\n", None,
),
"pointer_readonly_parameter": (
"""
read proc(p @i16) i16 { return p^ }
main proc() i16 {
value i16 := 42
return read(&value)
}
""", b"42\n", None,
),
"pointer_record_alias_and_value_copy": (
"""
Point :: struct { x i16, y i16 }
set proc(p @mut Point) void { p.x = 40 }
main proc() i16 {
point Point := { x = 1, y = 2 }
pointer :: &point
copy :: pointer^
set(pointer)
if (copy.x != 1) return -1
return point.x + pointer.y
}
""", b"42\n", None,
),
"pointer_array_and_many_index": (
"""
update proc(p @mut [3]mut i16) void {
p[1] = p[0] + 2
values :: p.ptr
values[2] = values[1]
}
main proc() i16 {
values [3]mut i16 := [40, 0, 0]
update(&values)
if ((&values).len != 3) return -1
return values[2]
}
""", b"42\n", None,
),
"pointer_aggregate_copy_keeps_alias": (
"""
Holder :: struct { pointer @mut i16 }
identity proc(h Holder) Holder { return h }
main proc() i16 {
value i16 := 40
first :: Holder { pointer = &value }
second :: identity(first)
pointers [2]@mut i16 :: [first.pointer, second.pointer]
pointers[1]^ = pointers[0]^ + 2
return value
}
""", b"42\n", None,
),
"pointer_global_returned_from_callee": (
"""
value i16 :: 42
address proc() @i16 { return &value }
recurse proc(p @i16, n i16) i16 {
if (n == 0) return p^
return recurse(p, n - 1)
}
main proc() i16 { return recurse(address(), 20) }
""", b"42\n", None,
),
"pointer_location_identity": (
"""
Point :: struct { x i16, y i16 }
identity proc(p @mut i16) @mut i16 { return p }
main proc() bool {
point Point := { x = 40, y = 2 }
values [2]mut i16 := [40, 2]
first :: &point
second :: &point
return (
first == second
and identity(&point.x) == &point.x
and &point.x != &point.y
and (&values).ptr == (&values).ptr
)
}
""", b"true\n", None,
),
"pointer_union_roundtrip": (
"""
Storage :: union { pointer @mut i16, same @mut i16 }
main proc() i16 {
value i16 := 40
storage :: Storage { pointer = &value }
pointer :: storage.same
pointer^ = pointer^ + 2
return value
}
""", b"42\n", None,
),
"pointer_nested_array_aggregate": (
"""
Point :: struct { x i16, y i16 }
main proc() i16 {
values [2]mut Point := [Point { x = 40, y = 2 }, Point { x = 0, y = 0 }]
pointer :: (&values).ptr
pointer[1] = pointer[0]
pointer[0].x = 9
return values[1].x + values[1].y
}
""", b"42\n", None,
),
"pointer_to_pointer": (
"""
set proc(p @mut @mut i16) void { p^^ = 42 }
main proc() i16 {
value i16 := 0
pointer := &value
set(&pointer)
return value
}
""", b"42\n", None,
),
"pointer_repeated_address_in_loop": (
"""
main proc() i16 {
value i16 := 0
for 0..42 |i| {
pointer :: &value
pointer^ = i16(i) + 1
}
return value
}
""", b"42\n", None,
),
"pointer_zero_sized_array_elements": (
"""
Empty :: struct {}
main proc() bool {
values [2]mut Empty := [Empty {}, Empty {}]
pointer :: (&values).ptr
pointer[1] = pointer[0]
return (&values).len == 2
}
""", b"true\n", None,
),
"pointer_array_bounds_trap": (
"main proc() i16 { values [1]mut i16 := [42]\nreturn (&values)[1] }",
b"", b"index out of bounds",
),
"pointer_address_before_initialization": (
"main proc() i16 { pointer :: &value\nvalue i16 := 42\nreturn pointer^ }",
b"", b"read before initialization",
),
"pointer_constant_is_readonly": (
"main proc() i16 { value i16 :: 40\npointer :: &value\npointer^ = 42\nreturn value }",
b"", b"",
),
"pointer_readonly_parameter_write_rejected": (
"set proc(p @i16) void { p^ = 42 }\nmain proc() i16 { value i16 := 0\nset(&value)\nreturn value }",
b"", b"",
),
"pointer_array_readonly_elements": (
"main proc() i16 { values [1]i16 := [40]\npointer :: &values\npointer[0] = 42\nreturn values[0] }",
b"", b"",
),
"pointer_readonly_not_mutable_argument": (
"set proc(p @mut i16) void { p^ = 42 }\nmain proc() i16 { value i16 :: 0\nset(&value)\nreturn value }",
b"", b"incompatible value type",
),
"pointer_nonpointer_dereference_rejected": (
"main proc() i16 { value i16 :: 42\nreturn value^ }",
b"", b"dereference requires",
),
"pointer_void_pointee_rejected": (
"unused proc(p @void) void {}\nmain proc() int { return 42 }",
b"42\n", b"pointer requires",
),
"pointer_temporary_address_rejected": (
"main proc() i16 { pointer :: &(40 + 2)\nreturn pointer^ }",
b"", b"address-of requires",
),
"pointer_unused_invalid_path": (
"unused proc() int { return 42^ }\nmain proc() int { return 42 }",
b"42\n", b"dereference requires",
),
"match_float_ranges": (
"""
score proc(n f64) i16 {
@@ -1000,11 +1239,25 @@ def check_record_packages(temporary, executable):
}
""").strip() + "\n")
check(app, executable, (b"42\n", None))
(library / "functions.hon").write_text(
"set proc(p @mut Point) void { p.x = 40 }\n"
"identity proc(p @mut Point) @mut Point { return p }\n"
)
(app / "main.hon").write_text(textwrap.dedent("""
lib :: import "../library"
main proc() i16 {
p lib.Point := { x = 1 }
pointer :: lib.identity(&p)
lib.set(pointer)
return pointer.x + p.y
}
""").strip() + "\n")
check(app, executable, (b"42\n", None))
types.write_text("Point :: struct { x i16, y i16 = 32767 + 1 }\n")
check(app, executable, (b"", b"integer arithmetic overflow"))
trapped = run(str(HONEY), "run", str(app))
assert trapped.stderr.startswith(f"{types.resolve()}:1:".encode()), outcome(trapped)
return 2
return 3
def check_data_model_packages(temporary, executable):
@@ -1105,6 +1358,13 @@ def check_dependency_provenance(temporary, executable):
return a[1]
}
""", 5, 12, False),
("""
fail proc() int {
a :: [42]
pointer :: &a
return pointer[1]
}
""", 6, 12, False),
("""
fail proc() int {
before :: after