It is now part of the normal constraints. Added a special workaround for functions, since otherwise the output is a bit redundant and quite confusing. Also, constraints are now processed in order of complexity. This does not affect type safety. It uses a bit more CPU. But it makes the output that much easier to read. Also, removes the weird FunctionInstance hack. Instead, the more industry standard way of annotation the types on the function call is used. As always, this requires some hackyness for Subscriptable. Also, adds a few comments to the type unification to help with debugging. Also, prints out the new constraints that are received.
254 lines
5.8 KiB
Python
254 lines
5.8 KiB
Python
import pytest
|
|
|
|
from phasm.type5.solver import Type5SolverException
|
|
|
|
from ..helpers import Suite
|
|
|
|
|
|
@pytest.mark.integration_test
|
|
def test_sof_in_code_0_arg():
|
|
code_py = """
|
|
def thirteen() -> i32:
|
|
return 13
|
|
|
|
def action(applicable: Callable[i32]) -> i32:
|
|
return applicable()
|
|
|
|
@exported
|
|
def testEntry() -> i32:
|
|
return action(thirteen)
|
|
"""
|
|
|
|
result = Suite(code_py).run_code()
|
|
|
|
assert 13 == result.returned_value
|
|
|
|
@pytest.mark.integration_test
|
|
def test_sof_in_code_1_arg():
|
|
code_py = """
|
|
def double(left: i32) -> i32:
|
|
return left * 2
|
|
|
|
def action(applicable: Callable[i32, i32], left: i32) -> i32:
|
|
return applicable(left)
|
|
|
|
@exported
|
|
def testEntry() -> i32:
|
|
return action(double, 13)
|
|
"""
|
|
|
|
result = Suite(code_py).run_code()
|
|
|
|
assert 26 == result.returned_value
|
|
|
|
@pytest.mark.integration_test
|
|
def test_sof_in_code_2_arg():
|
|
code_py = """
|
|
def add(left: i32, right: i32) -> i32:
|
|
return left + right
|
|
|
|
def action(applicable: Callable[i32, i32, i32], left: i32, right: i32) -> i32:
|
|
return applicable(left, right)
|
|
|
|
@exported
|
|
def testEntry() -> i32:
|
|
return action(add, 13, 14)
|
|
"""
|
|
|
|
result = Suite(code_py).run_code()
|
|
|
|
assert 27 == result.returned_value
|
|
|
|
@pytest.mark.integration_test
|
|
def test_sof_in_code_3_arg():
|
|
code_py = """
|
|
def add(left: i32, mid: i32, right: i32) -> i32:
|
|
return left + mid + right
|
|
|
|
def action(applicable: Callable[i32, i32, i32, i32], left: i32, mid: i32, right: i32) -> i32:
|
|
return applicable(left, mid, right)
|
|
|
|
@exported
|
|
def testEntry() -> i32:
|
|
return action(add, 13, 14, 15)
|
|
"""
|
|
|
|
result = Suite(code_py).run_code()
|
|
|
|
assert 42 == result.returned_value
|
|
|
|
@pytest.mark.integration_test
|
|
def test_sof_function_with_wrong_argument_type_use():
|
|
code_py = """
|
|
def double(left: i32) -> i32:
|
|
return left * 2
|
|
|
|
def action(applicable: Callable[i32, i32], left: f32) -> i32:
|
|
return applicable(left)
|
|
|
|
@exported
|
|
def testEntry() -> i32:
|
|
return action(double, 13.0)
|
|
"""
|
|
|
|
with pytest.raises(Type5SolverException, match='i32 ~ f32'):
|
|
Suite(code_py).run_code()
|
|
|
|
@pytest.mark.integration_test
|
|
def test_sof_function_with_wrong_argument_type_pass():
|
|
code_py = """
|
|
def double(left: f32) -> i32:
|
|
return truncate(left) * 2
|
|
|
|
def action(applicable: Callable[i32, i32], left: i32) -> i32:
|
|
return applicable(left)
|
|
|
|
@exported
|
|
def testEntry() -> i32:
|
|
return action(double, 13)
|
|
"""
|
|
|
|
with pytest.raises(Type5SolverException, match='i32 ~ f32'):
|
|
Suite(code_py).run_code()
|
|
|
|
@pytest.mark.integration_test
|
|
def test_sof_function_with_wrong_return_type_use():
|
|
code_py = """
|
|
def double(left: i32) -> i32:
|
|
return left * 2
|
|
|
|
def action(applicable: Callable[i32, i32], left: i32) -> f32:
|
|
return applicable(left)
|
|
|
|
@exported
|
|
def testEntry() -> f32:
|
|
return action(double, 13)
|
|
"""
|
|
|
|
with pytest.raises(Type5SolverException, match='i32 ~ f32'):
|
|
Suite(code_py).run_code()
|
|
|
|
@pytest.mark.integration_test
|
|
def test_sof_function_with_wrong_return_type_pass():
|
|
code_py = """
|
|
def double(left: i32) -> f32:
|
|
return convert(left) * 2.0
|
|
|
|
def action(applicable: Callable[i32, i32], left: i32) -> i32:
|
|
return applicable(left)
|
|
|
|
@exported
|
|
def testEntry() -> i32:
|
|
return action(double, 13)
|
|
"""
|
|
|
|
with pytest.raises(Type5SolverException, match='i32 ~ f32'):
|
|
Suite(code_py).run_code()
|
|
|
|
@pytest.mark.integration_test
|
|
def test_sof_not_enough_args_use():
|
|
code_py = """
|
|
def add(left: i32, right: i32) -> i32:
|
|
return left + right
|
|
|
|
def action(applicable: Callable[i32, i32, i32], left: i32) -> i32:
|
|
return applicable(left)
|
|
|
|
@exported
|
|
def testEntry() -> i32:
|
|
return action(add, 13)
|
|
"""
|
|
|
|
with pytest.raises(Type5SolverException, match='Not the same type'):
|
|
Suite(code_py).run_code()
|
|
|
|
@pytest.mark.integration_test
|
|
def test_sof_not_enough_args_pass():
|
|
code_py = """
|
|
def double(left: i32) -> i32:
|
|
return left * 2
|
|
|
|
def action(applicable: Callable[i32, i32, i32], left: i32, right: i32) -> i32:
|
|
return applicable(left, right)
|
|
|
|
@exported
|
|
def testEntry() -> i32:
|
|
return action(double, 13, 14)
|
|
"""
|
|
|
|
match = r'Callable\[i32, i32\] ~ i32'
|
|
with pytest.raises(Type5SolverException, match=match):
|
|
Suite(code_py).run_code()
|
|
|
|
@pytest.mark.integration_test
|
|
def test_sof_too_many_args_use_0():
|
|
code_py = """
|
|
def thirteen() -> i32:
|
|
return 13
|
|
|
|
def action(applicable: Callable[i32], left: i32) -> i32:
|
|
return applicable(left)
|
|
|
|
@exported
|
|
def testEntry() -> i32:
|
|
return action(thirteen, 13)
|
|
"""
|
|
|
|
match = r'\(\) ~ i32'
|
|
with pytest.raises(Type5SolverException, match=match):
|
|
Suite(code_py).run_code(verbose=True)
|
|
|
|
@pytest.mark.integration_test
|
|
def test_sof_too_many_args_use_1():
|
|
code_py = """
|
|
def thirteen(x: i32) -> i32:
|
|
return x
|
|
|
|
def action(applicable: Callable[i32, i32], left: i32, right: i32) -> i32:
|
|
return applicable(left, right)
|
|
|
|
@exported
|
|
def testEntry() -> i32:
|
|
return action(thirteen, 13, 26)
|
|
"""
|
|
|
|
match = r'i32 ~ Callable\[i32, i32\]'
|
|
with pytest.raises(Type5SolverException, match=match):
|
|
Suite(code_py).run_code(verbose=True)
|
|
|
|
@pytest.mark.integration_test
|
|
def test_sof_too_many_args_pass_0():
|
|
code_py = """
|
|
def double(left: i32) -> i32:
|
|
return left * 2
|
|
|
|
def action(applicable: Callable[i32], left: i32, right: i32) -> i32:
|
|
return applicable()
|
|
|
|
@exported
|
|
def testEntry() -> i32:
|
|
return action(double, 13, 14)
|
|
"""
|
|
|
|
match = r'\(\) ~ i32'
|
|
with pytest.raises(Type5SolverException, match=match):
|
|
Suite(code_py).run_code()
|
|
|
|
@pytest.mark.integration_test
|
|
def test_sof_too_many_args_pass_1():
|
|
code_py = """
|
|
def double(left: i32, right: i32) -> i32:
|
|
return left * right
|
|
|
|
def action(applicable: Callable[i32, i32], left: i32, right: i32) -> i32:
|
|
return applicable(left)
|
|
|
|
@exported
|
|
def testEntry() -> i32:
|
|
return action(double, 13, 14)
|
|
"""
|
|
|
|
match = r'i32 ~ Callable\[i32, i32\]'
|
|
with pytest.raises(Type5SolverException, match=match):
|
|
Suite(code_py).run_code()
|