diff --git a/tests/error/type_errors/not_unpackable.err b/tests/error/type_errors/not_unpackable.err new file mode 100644 index 00000000..8f4cb981 --- /dev/null +++ b/tests/error/type_errors/not_unpackable.err @@ -0,0 +1,8 @@ +Error: Unpackable (at $FILE:6:9) + | +4 | @compile_guppy +5 | def foo() -> int: +6 | a, = 1 + | ^ Expression of type `int` cannot be unpacked + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/type_errors/not_unpackable.py b/tests/error/type_errors/not_unpackable.py new file mode 100644 index 00000000..d66ab9cd --- /dev/null +++ b/tests/error/type_errors/not_unpackable.py @@ -0,0 +1,7 @@ +from tests.util import compile_guppy + + +@compile_guppy +def foo() -> int: + a, = 1 + return a diff --git a/tests/error/type_errors/unpack_generic_size.err b/tests/error/type_errors/unpack_generic_size.err new file mode 100644 index 00000000..37925025 --- /dev/null +++ b/tests/error/type_errors/unpack_generic_size.err @@ -0,0 +1,12 @@ +Error: Unpackable (at $FILE:11:13) + | + 9 | @guppy(module) +10 | def foo(xs: array[int, n]) -> int: +11 | a, *bs = xs + | ^^ Expression of type `array[int, n]` cannot be unpacked + +Note: Unpacking of iterable types like `array[int, n]` is only allowed if the +number of items yielded by the iterator is statically known. Here, the number of +items `n` is generic and can change between different function invocations. + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/type_errors/unpack_generic_size.py b/tests/error/type_errors/unpack_generic_size.py new file mode 100644 index 00000000..eb7d8013 --- /dev/null +++ b/tests/error/type_errors/unpack_generic_size.py @@ -0,0 +1,15 @@ +from guppylang import GuppyModule, guppy +from guppylang.std.builtins import array + + +module = GuppyModule('main') +n = guppy.nat_var("n", module=module) + + +@guppy(module) +def foo(xs: array[int, n]) -> int: + a, *bs = xs + return a + + +module.compile() diff --git a/tests/error/type_errors/unpack_non_static.err b/tests/error/type_errors/unpack_non_static.err new file mode 100644 index 00000000..a4624983 --- /dev/null +++ b/tests/error/type_errors/unpack_non_static.err @@ -0,0 +1,12 @@ +Error: Unpackable (at $FILE:6:13) + | +4 | @compile_guppy +5 | def foo(xs: list[int]) -> int: +6 | a, *bs = xs + | ^^ Expression of type `list[int]` cannot be unpacked + +Note: Unpacking of iterable types like `list[int]` is only allowed if the number +of items yielded by the iterator is statically known. This is not the case for +`list[int]`. + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/type_errors/unpack_non_static.py b/tests/error/type_errors/unpack_non_static.py new file mode 100644 index 00000000..2bc46f42 --- /dev/null +++ b/tests/error/type_errors/unpack_non_static.py @@ -0,0 +1,7 @@ +from tests.util import compile_guppy + + +@compile_guppy +def foo(xs: list[int]) -> int: + a, *bs = xs + return a diff --git a/tests/error/type_errors/unpack_tuple_starred.err b/tests/error/type_errors/unpack_tuple_starred.err new file mode 100644 index 00000000..02a10599 --- /dev/null +++ b/tests/error/type_errors/unpack_tuple_starred.err @@ -0,0 +1,10 @@ +Error: Invalid starred unpacking (at $FILE:6:8) + | +4 | @compile_guppy +5 | def foo() -> int: +6 | a, *bs = 1, 2, True + | ^^ Expression of type `(int, int, bool)` cannot be collected + | into a starred assignment since the yielded items + | have different types + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/type_errors/unpack_tuple_starred.py b/tests/error/type_errors/unpack_tuple_starred.py new file mode 100644 index 00000000..5c5c845b --- /dev/null +++ b/tests/error/type_errors/unpack_tuple_starred.py @@ -0,0 +1,7 @@ +from tests.util import compile_guppy + + +@compile_guppy +def foo() -> int: + a, *bs = 1, 2, True + return a diff --git a/tests/integration/test_unpack.py b/tests/integration/test_unpack.py new file mode 100644 index 00000000..c9cbe991 --- /dev/null +++ b/tests/integration/test_unpack.py @@ -0,0 +1,111 @@ +from guppylang.decorator import guppy +from guppylang.module import GuppyModule +from guppylang.std.builtins import array, owned + +from guppylang.std.quantum import qubit + + +def test_unpack_array(validate): + module = GuppyModule("test") + module.load(qubit) + + @guppy(module) + def main(qs: array[qubit, 3] @ owned) -> tuple[qubit, qubit, qubit]: + q1, q2, q3 = qs + return q1, q2, q3 + + # validate(module.compile()) + module.check() + + +def test_unpack_starred(validate): + module = GuppyModule("test") + module.load(qubit) + + @guppy(module) + def main( + qs: array[qubit, 10] @ owned, + ) -> tuple[qubit, qubit, qubit, qubit, qubit, qubit, array[qubit, 4]]: + q1, q2, *qs, q3 = qs + [q4, *qs] = qs + *qs, q5, q6 = qs + [*qs] = qs + return q1, q2, q3, q4, q5, q6, qs + + # validate(module.compile()) + module.check() + + +def test_unpack_starred_empty(validate): + module = GuppyModule("test") + module.load(qubit) + + @guppy(module) + def main(qs: array[qubit, 2] @ owned) -> tuple[qubit, array[qubit, 0], qubit]: + q1, *empty, q2 = qs + return q1, empty, q2 + + # validate(module.compile()) + module.check() + + +def test_unpack_big_iterable(validate): + # Test that the compile-time doesn't scale with the size of the unpacked iterable + module = GuppyModule("test") + module.load(qubit) + + @guppy(module) + def main(qs: array[qubit, 1000] @ owned) -> tuple[qubit, array[qubit, 998], qubit]: + q1, *qs, q2 = qs + return q1, qs, q2 + + # validate(module.compile()) + module.check() + + +def test_unpack_range(validate, run_int_fn): + module = GuppyModule("test") + + @guppy(module) + def main() -> int: + [_, x, *_, y, _] = range(10) + return x + y + + module.check() + # compiled = module.compile() + # validate(compiled) + # TODO: Enable execution test once array lowering is fully supported + # run_int_fn(compiled, expected=9) + + +def test_unpack_tuple_starred(validate, run_int_fn): + module = GuppyModule("test") + + @guppy(module) + def main() -> array[int, 2]: + x, *ys, z = 1, 2, 3, 4 + return ys + + # validate(module.compile()) + module.check() + + +def test_unpack_nested(validate, run_int_fn): + module = GuppyModule("test") + + @guppy(module) + def main( + xs: array[array[array[int, 5], 10], 20], + ) -> tuple[ + array[int, 5], # x + int, # y + array[int, 3], # z + array[array[int, 5], 8], # a + array[array[array[int, 5], 10], 18], # b + array[array[int, 5], 10], # c + ]: + (x, [y, *z, _], *a), *b, c = xs + return x, y, z, a, b, c + + # validate(module.compile()) + module.check()