IntType3
This commit is contained in:
parent
0fa076bb93
commit
14b8065974
@ -31,13 +31,15 @@ def type3(inp: Type3OrPlaceholder) -> str:
|
||||
return '(' + ', '.join(
|
||||
type3(x)
|
||||
for x in inp.args
|
||||
if isinstance(x, Type3) # Skip ints, not allowed here anyhow
|
||||
) + ', )'
|
||||
|
||||
if inp.base == type3types.static_array:
|
||||
assert 1 == len(inp.args)
|
||||
assert 2 == len(inp.args)
|
||||
assert isinstance(inp.args[0], Type3), TYPE3_ASSERTION_ERROR
|
||||
assert isinstance(inp.args[1], type3types.IntType3), TYPE3_ASSERTION_ERROR
|
||||
|
||||
return inp.args[0].name + '[3]' # FIXME: Where to store this value?
|
||||
return inp.args[0].name + '[' + inp.args[1].name + ']'
|
||||
|
||||
return inp.name
|
||||
|
||||
|
||||
@ -386,16 +386,28 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
|
||||
if isinstance(inp.varref.type3, type3types.AppliedType3):
|
||||
if inp.varref.type3.base == type3types.static_array:
|
||||
assert 1 == len(inp.varref.type3.args)
|
||||
assert 2 == len(inp.varref.type3.args)
|
||||
el_type = inp.varref.type3.args[0]
|
||||
assert isinstance(el_type, type3types.Type3)
|
||||
el_len = inp.varref.type3.args[1]
|
||||
assert isinstance(el_len, type3types.IntType3)
|
||||
|
||||
# OPTIMIZE: If index is a constant, we can use offset instead of multiply
|
||||
|
||||
# FIXME: Out of bounds check
|
||||
# and we don't need to do the out of bounds check
|
||||
|
||||
expression(wgn, inp.varref)
|
||||
|
||||
tmp_var = wgn.temp_var_i32('index')
|
||||
expression(wgn, inp.index)
|
||||
wgn.local.tee(tmp_var)
|
||||
|
||||
# Out of bounds check based on el_len.value
|
||||
wgn.i32.const(el_len.value)
|
||||
wgn.i32.ge_u()
|
||||
with wgn.if_():
|
||||
wgn.unreachable(comment='Out of bounds')
|
||||
|
||||
wgn.local.get(tmp_var)
|
||||
wgn.i32.const(_calculate_alloc_size(el_type))
|
||||
wgn.i32.mul()
|
||||
wgn.i32.add()
|
||||
@ -804,9 +816,12 @@ def _calculate_alloc_size(typ: Union[type3types.StructType3, type3types.Type3])
|
||||
if typ.base is type3types.tuple:
|
||||
size = 0
|
||||
for arg in typ.args:
|
||||
assert not isinstance(arg, type3types.IntType3)
|
||||
|
||||
if isinstance(arg, type3types.PlaceholderForType):
|
||||
assert not arg.resolve_as is None
|
||||
arg = arg.resolve_as
|
||||
|
||||
size += _calculate_alloc_size(arg)
|
||||
|
||||
return size
|
||||
|
||||
@ -583,7 +583,7 @@ class OurVisitor:
|
||||
if not isinstance(node.value, ast.Name):
|
||||
_raise_static_error(node, 'Must be name')
|
||||
if isinstance(node.slice, ast.Slice):
|
||||
_raise_static_error(node, 'Must subscript using an index') # FIXME: Do we use type level length?
|
||||
_raise_static_error(node, 'Must subscript using an index')
|
||||
if not isinstance(node.slice, ast.Constant):
|
||||
_raise_static_error(node, 'Must subscript using a constant index')
|
||||
if not isinstance(node.slice.value, int):
|
||||
@ -596,7 +596,7 @@ class OurVisitor:
|
||||
|
||||
return type3types.AppliedType3(
|
||||
type3types.static_array,
|
||||
[self.visit_type(module, node.value)],
|
||||
[self.visit_type(module, node.value), type3types.IntType3(node.slice.value)],
|
||||
)
|
||||
|
||||
if isinstance(node, ast.Tuple):
|
||||
|
||||
@ -77,7 +77,7 @@ class ConstraintBase:
|
||||
This function can return None, if the constraint holds, but no new
|
||||
information was deduced from evaluating this constraint.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
raise NotImplementedError(self.__class__, self.check)
|
||||
|
||||
def human_readable(self) -> HumanReadableRet:
|
||||
"""
|
||||
@ -104,6 +104,10 @@ class SameTypeConstraint(ConstraintBase):
|
||||
placeholders = []
|
||||
do_applied_placeholder_check: bool = False
|
||||
for typ in self.type_list:
|
||||
if isinstance(typ, types.IntType3):
|
||||
known_types.append(typ)
|
||||
continue
|
||||
|
||||
if isinstance(typ, (types.PrimitiveType3, types.StructType3, )):
|
||||
known_types.append(typ)
|
||||
continue
|
||||
@ -177,6 +181,46 @@ class SameTypeConstraint(ConstraintBase):
|
||||
|
||||
return f'SameTypeConstraint({args}, comment={repr(self.comment)})'
|
||||
|
||||
class IntegerCompareConstraint(ConstraintBase):
|
||||
"""
|
||||
Verifies that the given IntType3 are in order (<=)
|
||||
"""
|
||||
__slots__ = ('int_type3_list', )
|
||||
|
||||
int_type3_list: List[types.IntType3]
|
||||
|
||||
def __init__(self, *int_type3: types.IntType3, comment: Optional[str] = None) -> None:
|
||||
super().__init__(comment=comment)
|
||||
|
||||
assert len(int_type3) > 1
|
||||
self.int_type3_list = [*int_type3]
|
||||
|
||||
def check(self) -> CheckResult:
|
||||
val_list = [x.value for x in self.int_type3_list]
|
||||
|
||||
prev_val = val_list.pop(0)
|
||||
for next_val in val_list:
|
||||
if prev_val > next_val:
|
||||
return Error(f'{prev_val} must be less or equal than {next_val}')
|
||||
|
||||
prev_val = next_val
|
||||
|
||||
return None
|
||||
|
||||
def human_readable(self) -> HumanReadableRet:
|
||||
return (
|
||||
' <= '.join('{t' + str(idx) + '}' for idx in range(len(self.int_type3_list))),
|
||||
{
|
||||
't' + str(idx): typ
|
||||
for idx, typ in enumerate(self.int_type3_list)
|
||||
},
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
args = ', '.join(repr(x) for x in self.int_type3_list)
|
||||
|
||||
return f'IntegerCompareConstraint({args}, comment={repr(self.comment)})'
|
||||
|
||||
class CastableConstraint(ConstraintBase):
|
||||
"""
|
||||
A type can be cast to another type
|
||||
@ -363,11 +407,11 @@ class LiteralFitsConstraint(ConstraintBase):
|
||||
if not isinstance(self.literal, ourlang.ConstantTuple):
|
||||
return Error('Must be tuple')
|
||||
|
||||
assert 1 == len(self.type3.args)
|
||||
assert 2 == len(self.type3.args)
|
||||
assert isinstance(self.type3.args[1], types.IntType3)
|
||||
|
||||
# FIXME: How to store type level length?
|
||||
# if len(self.type3.args) != len(self.literal.value):
|
||||
# return Error('Tuple element count mismatch')
|
||||
if self.type3.args[1].value != len(self.literal.value):
|
||||
return Error('Member count mismatch')
|
||||
|
||||
res = []
|
||||
|
||||
@ -448,11 +492,24 @@ class CanBeSubscriptedConstraint(ConstraintBase):
|
||||
|
||||
if isinstance(self.type3, types.AppliedType3):
|
||||
if self.type3.base == types.static_array:
|
||||
return [
|
||||
result: List[ConstraintBase] = [
|
||||
SameTypeConstraint(types.u32, self.index_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
|
||||
SameTypeConstraint(self.type3.args[0], self.ret_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
|
||||
]
|
||||
|
||||
if isinstance(self.index, ourlang.ConstantPrimitive):
|
||||
assert isinstance(self.index.value, int)
|
||||
assert isinstance(self.type3.args[1], types.IntType3)
|
||||
|
||||
result.append(
|
||||
IntegerCompareConstraint(
|
||||
types.IntType3(0), types.IntType3(self.index.value), types.IntType3(self.type3.args[1].value - 1),
|
||||
comment='Subscript static array must fit the size of the array'
|
||||
)
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
if self.type3.base == types.tuple:
|
||||
if not isinstance(self.index, ourlang.ConstantPrimitive):
|
||||
return Error('Must index with literal')
|
||||
|
||||
@ -8,7 +8,7 @@ from .. import ourlang
|
||||
|
||||
from .constraints import ConstraintBase, Error, RequireTypeSubstitutes, SameTypeConstraint, SubstitutionMap
|
||||
from .constraintsgenerator import phasm_type3_generate_constraints
|
||||
from .types import AppliedType3, PlaceholderForType, PrimitiveType3, StructType3, Type3, Type3OrPlaceholder
|
||||
from .types import AppliedType3, IntType3, PlaceholderForType, PrimitiveType3, StructType3, Type3, Type3OrPlaceholder
|
||||
|
||||
MAX_RESTACK_COUNT = 100
|
||||
|
||||
@ -125,7 +125,7 @@ def print_constraint(placeholder_id_map: Dict[int, str], constraint: ConstraintB
|
||||
print('- ' + txt.format(**act_fmt))
|
||||
|
||||
def get_printable_type_name(inp: Type3OrPlaceholder, placeholder_id_map: Dict[int, str]) -> str:
|
||||
if isinstance(inp, (PrimitiveType3, StructType3, )):
|
||||
if isinstance(inp, (PrimitiveType3, StructType3, IntType3, )):
|
||||
return inp.name
|
||||
|
||||
if isinstance(inp, PlaceholderForType):
|
||||
|
||||
@ -69,6 +69,30 @@ class PrimitiveType3(Type3):
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
class IntType3(Type3):
|
||||
"""
|
||||
Sometimes you can have an int as type, e.g. when using static arrays
|
||||
"""
|
||||
|
||||
__slots__ = ('value', )
|
||||
|
||||
value: int
|
||||
|
||||
def __init__(self, value: int) -> None:
|
||||
super().__init__(str(value))
|
||||
|
||||
assert 0 <= value
|
||||
self.value = value
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
if isinstance(other, IntType3):
|
||||
return self.value == other.value
|
||||
|
||||
if isinstance(other, Type3):
|
||||
return False
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
class PlaceholderForType:
|
||||
"""
|
||||
A placeholder type, for when we don't know the final type yet
|
||||
|
||||
@ -4,26 +4,8 @@ from ..helpers import Suite
|
||||
|
||||
@pytest.mark.slow_integration_test
|
||||
def test_fib():
|
||||
code_py = """
|
||||
def helper(n: i32, a: i32, b: i32) -> i32:
|
||||
if n < 1:
|
||||
return a + b
|
||||
|
||||
return helper(n - 1, a + b, a)
|
||||
|
||||
def fib(n: i32) -> i32:
|
||||
if n == 0:
|
||||
return 0
|
||||
|
||||
if n == 1:
|
||||
return 1
|
||||
|
||||
return helper(n - 1, 0, 1)
|
||||
|
||||
@exported
|
||||
def testEntry() -> i32:
|
||||
return fib(40)
|
||||
"""
|
||||
with open('./examples/fib.py', 'r', encoding='UTF-8') as fil:
|
||||
code_py = "\n" + fil.read()
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
|
||||
@ -75,8 +75,7 @@ def testEntry() -> {type_}:
|
||||
assert 32.125 == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.skip('Awaiting result of Type3 experiment')
|
||||
def test_module_constant_entanglement():
|
||||
def test_module_constant_type_failure():
|
||||
code_py = """
|
||||
CONSTANT: u8 = 1000
|
||||
|
||||
@ -85,7 +84,7 @@ def testEntry() -> u32:
|
||||
return 14
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='u8.*1000'):
|
||||
with pytest.raises(Type3Exception, match=r'Must fit in 1 byte\(s\)'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
|
||||
@ -37,26 +37,6 @@ def testEntry() -> {type_}:
|
||||
assert 57 == result.returned_value
|
||||
assert TYPE_MAP[type_] == type(result.returned_value)
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.skip('To decide: What to do on out of index?')
|
||||
@pytest.mark.parametrize('type_', COMPLETE_NUMERIC_TYPES)
|
||||
def test_static_array_indexed(type_):
|
||||
code_py = f"""
|
||||
CONSTANT: {type_}[3] = (24, 57, 80, )
|
||||
|
||||
@exported
|
||||
def testEntry() -> {type_}:
|
||||
return helper(CONSTANT, 0, 1, 2)
|
||||
|
||||
def helper(array: {type_}[3], i0: u32, i1: u32, i2: u32) -> {type_}:
|
||||
return array[i0] + array[i1] + array[i2]
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert 161 == result.returned_value
|
||||
assert TYPE_MAP[type_] == type(result.returned_value)
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', COMPLETE_INT_TYPES)
|
||||
def test_function_call_int(type_):
|
||||
@ -146,7 +126,7 @@ def testEntry() -> u32:
|
||||
return CONSTANT
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match=r'static_array \(u8\) must be u32 instead'):
|
||||
with pytest.raises(Type3Exception, match=r'static_array \(u8\) \(3\) must be u32 instead'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@ -163,7 +143,7 @@ def testEntry() -> u8:
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_module_constant_type_mismatch_index_out_of_range():
|
||||
def test_module_constant_type_mismatch_index_out_of_range_constant():
|
||||
code_py = """
|
||||
CONSTANT: u8[3] = (24, 57, 80, )
|
||||
|
||||
@ -172,20 +152,33 @@ def testEntry() -> u8:
|
||||
return CONSTANT[3]
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='Type cannot be subscripted with index 3:'):
|
||||
with pytest.raises(Type3Exception, match='3 must be less or equal than 2'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_module_constant_type_mismatch_index_out_of_range_variable():
|
||||
code_py = """
|
||||
CONSTANT: u8[3] = (24, 57, 80, )
|
||||
|
||||
@exported
|
||||
def testEntry(x: u32) -> u8:
|
||||
return CONSTANT[x]
|
||||
"""
|
||||
|
||||
with pytest.raises(RuntimeError):
|
||||
Suite(code_py).run_code(3)
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_static_array_constant_too_few_values():
|
||||
code_py = """
|
||||
CONSTANT: u8[3] = (24, 57, )
|
||||
CONSTANT: u8[4] = (24, 57, )
|
||||
|
||||
@exported
|
||||
def testEntry() -> i32:
|
||||
return 0
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='Member count does not match'):
|
||||
with pytest.raises(Type3Exception, match='Member count mismatch'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@ -198,22 +191,5 @@ def testEntry() -> i32:
|
||||
return 0
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='Member count does not match'):
|
||||
with pytest.raises(Type3Exception, match='Member count mismatch'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.skip('To decide: What to do on out of index? Should be a panic.')
|
||||
def test_static_array_index_out_of_bounds():
|
||||
code_py = """
|
||||
CONSTANT0: u32[3] = (24, 57, 80, )
|
||||
|
||||
CONSTANT1: u32[16] = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, )
|
||||
|
||||
@exported
|
||||
def testEntry() -> u32:
|
||||
return CONSTANT0[16]
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert 0 == result.returned_value
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user