From d52a00ae6f9c33d9fe65ddd46bcf94e39f53172f Mon Sep 17 00:00:00 2001 From: Mark Koch <48097969+mark-koch@users.noreply.github.com> Date: Thu, 12 Dec 2024 11:33:53 +0000 Subject: [PATCH] feat!: Turn py expression lists into arrays (#697) Closes #545. BREAKING CHANGE: Lists in `py(...)` expressions are now turned into Guppy arrays instead of lists. --- guppylang/checker/expr_checker.py | 5 +++-- guppylang/compiler/expr_compiler.py | 15 +++++++++++---- tests/error/py_errors/list_empty.err | 2 +- tests/integration/test_py.py | 8 ++++---- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/guppylang/checker/expr_checker.py b/guppylang/checker/expr_checker.py index be866ffa..8dcbdea3 100644 --- a/guppylang/checker/expr_checker.py +++ b/guppylang/checker/expr_checker.py @@ -106,6 +106,7 @@ from guppylang.span import Span, to_span from guppylang.tys.arg import TypeArg from guppylang.tys.builtin import ( + array_type, bool_type, get_element_type, is_bool_type, @@ -1193,7 +1194,7 @@ def _python_list_to_guppy_type( representable in Guppy. """ if len(vs) == 0: - return list_type(ExistentialTypeVar.fresh("T", False)) + return array_type(ExistentialTypeVar.fresh("T", False), 0) # All the list elements must have a unifiable types v, *rest = vs @@ -1207,4 +1208,4 @@ def _python_list_to_guppy_type( if (subst := unify(ty, el_ty, {})) is None: raise GuppyError(PyExprIncoherentListError(node)) el_ty = el_ty.substitute(subst) - return list_type(el_ty) + return array_type(el_ty, len(vs)) diff --git a/guppylang/compiler/expr_compiler.py b/guppylang/compiler/expr_compiler.py index b1d24fe7..d71b1c23 100644 --- a/guppylang/compiler/expr_compiler.py +++ b/guppylang/compiler/expr_compiler.py @@ -13,7 +13,6 @@ from hugr import val as hv from hugr.build.cond_loop import Conditional from hugr.build.dfg import DP, DfBase -from hugr.std.collections import ListVal from typing_extensions import assert_never from guppylang.ast_util import AstNode, AstVisitor, get_type @@ -55,8 +54,8 @@ from guppylang.tys.builtin import ( get_element_type, int_type, + is_array_type, is_bool_type, - is_list_type, ) from guppylang.tys.const import BoundConstVar, ConstValue, ExistentialConstVar from guppylang.tys.subst import Inst @@ -601,10 +600,18 @@ def python_value_to_hugr(v: Any, exp_ty: Type) -> hv.Value | None: if doesnt_contain_none(vs): return hv.Tuple(*vs) case list(elts): - assert is_list_type(exp_ty) + assert is_array_type(exp_ty) vs = [python_value_to_hugr(elt, get_element_type(exp_ty)) for elt in elts] if doesnt_contain_none(vs): - return ListVal(vs, get_element_type(exp_ty).to_hugr()) + # TODO: Use proper array value: https://github.com/CQCL/hugr/issues/1497 + return hv.Extension( + name="ArrayValue", + typ=exp_ty.to_hugr(), + # The value list must be serialized at this point, otherwise the + # `Extension` value would not be serializable. + val=[v._to_serial_root() for v in vs], + extensions=["unsupported"], + ) case _: # TODO replace with hugr protocol handling: https://github.com/CQCL/guppylang/issues/563 # Pytket conversion is an experimental feature diff --git a/tests/error/py_errors/list_empty.err b/tests/error/py_errors/list_empty.err index 381bd770..85087b5c 100644 --- a/tests/error/py_errors/list_empty.err +++ b/tests/error/py_errors/list_empty.err @@ -4,6 +4,6 @@ Error: Cannot infer type (at $FILE:6:9) 5 | def foo() -> None: 6 | xs = py([]) | ^^^^^^ Cannot infer type variables in expression of type - | `list[?T]` + | `array[?T, 0]` Guppy compilation failed due to 1 previous error diff --git a/tests/integration/test_py.py b/tests/integration/test_py.py index 7b945b89..f62d50a5 100644 --- a/tests/integration/test_py.py +++ b/tests/integration/test_py.py @@ -76,7 +76,7 @@ def foo() -> int: def test_list_basic(validate): @compile_guppy - def foo() -> list[int]: + def foo() -> array[int, 3]: xs = py([1, 2, 3]) return xs @@ -85,7 +85,7 @@ def foo() -> list[int]: def test_list_empty(validate): @compile_guppy - def foo() -> list[int]: + def foo() -> array[int, 0]: return py([]) validate(foo) @@ -94,7 +94,7 @@ def foo() -> list[int]: def test_list_empty_nested(validate): @compile_guppy def foo() -> None: - xs: list[tuple[int, list[bool]]] = py([(42, [])]) + xs: array[tuple[int, array[bool, 0]], 1] = py([(42, [])]) validate(foo) @@ -102,7 +102,7 @@ def foo() -> None: def test_list_empty_multiple(validate): @compile_guppy def foo() -> None: - xs: tuple[list[int], list[bool]] = py([], []) + xs: tuple[array[int, 0], array[bool, 0]] = py([], []) validate(foo)