MVP #1
@ -97,7 +97,7 @@ class TupleMember:
|
||||
|
||||
class OurTypeTuple(OurType):
|
||||
"""
|
||||
The tuple type, 64 bits wide
|
||||
The tuple type
|
||||
"""
|
||||
__slots__ = ('members', )
|
||||
|
||||
@ -842,8 +842,14 @@ class OurVisitor:
|
||||
if not isinstance(node.ctx, ast.Load):
|
||||
_raise_static_error(node, 'Must be load context')
|
||||
|
||||
if node.id in our_locals:
|
||||
return VariableReference(our_locals[node.id], node.id)
|
||||
if node.id not in our_locals:
|
||||
_raise_static_error(node, 'Undefined variable')
|
||||
|
||||
act_type = our_locals[node.id]
|
||||
if exp_type != act_type:
|
||||
_raise_static_error(node, f'Expected {exp_type.render()}, {node.id} is actually {act_type.render()}')
|
||||
|
||||
return VariableReference(act_type, node.id)
|
||||
|
||||
if isinstance(node, ast.Tuple):
|
||||
if not isinstance(node.ctx, ast.Load):
|
||||
@ -884,7 +890,7 @@ class OurVisitor:
|
||||
func = module.functions[struct_constructor.name]
|
||||
elif node.func.id in WEBASSEMBLY_BUILDIN_FLOAT_OPS:
|
||||
if not isinstance(exp_type, (OurTypeFloat32, OurTypeFloat64, )):
|
||||
_raise_static_error(node, f'Cannot make square root result in {exp_type}')
|
||||
_raise_static_error(node, f'Cannot make {node.func.id} result in {exp_type}')
|
||||
|
||||
if 1 != len(node.args):
|
||||
_raise_static_error(node, f'Function {node.func.id} requires 1 arguments but {len(node.args)} are given')
|
||||
@ -901,7 +907,7 @@ class OurVisitor:
|
||||
func = module.functions[node.func.id]
|
||||
|
||||
if func.returns != exp_type:
|
||||
_raise_static_error(node, f'Function {node.func.id} does not return {exp_type.render()}')
|
||||
_raise_static_error(node, f'Expected {exp_type.render()}, {func.name} actually returns {func.returns.render()}')
|
||||
|
||||
if len(func.posonlyargs) != len(node.args):
|
||||
_raise_static_error(node, f'Function {node.func.id} requires {len(func.posonlyargs)} arguments but {len(node.args)} are given')
|
||||
@ -932,7 +938,7 @@ class OurVisitor:
|
||||
_raise_static_error(node, f'{node_typ.name} has no attribute {node.attr}')
|
||||
|
||||
if exp_type != member.type:
|
||||
_raise_static_error(node, f'Expected {exp_type.render()}, got {member.type.render()} instead')
|
||||
_raise_static_error(node, f'Expected {exp_type.render()}, {node.value.id}.{member.name} is actually {member.type.render()}')
|
||||
|
||||
return AccessStructMember(
|
||||
VariableReference(node_typ, node.value.id),
|
||||
@ -949,7 +955,8 @@ class OurVisitor:
|
||||
if not isinstance(node.slice.value, ast.Constant):
|
||||
_raise_static_error(node, 'Must subscript using a constant index') # FIXME: Implement variable indexes
|
||||
|
||||
if not isinstance(node.slice.value.value, int):
|
||||
idx = node.slice.value.value
|
||||
if not isinstance(idx, int):
|
||||
_raise_static_error(node, 'Must subscript using a constant integer index')
|
||||
|
||||
if not isinstance(node.ctx, ast.Load):
|
||||
@ -962,12 +969,12 @@ class OurVisitor:
|
||||
if not isinstance(node_typ, OurTypeTuple):
|
||||
_raise_static_error(node, f'Cannot take index of non-tuple {node.value.id}')
|
||||
|
||||
if len(node_typ.members) <= node.slice.value.value:
|
||||
_raise_static_error(node, f'Index {node.slice.value.value} out of bounds for tuple {node.value.id}')
|
||||
if len(node_typ.members) <= idx:
|
||||
_raise_static_error(node, f'Index {idx} out of bounds for tuple {node.value.id}')
|
||||
|
||||
member = node_typ.members[node.slice.value.value]
|
||||
member = node_typ.members[idx]
|
||||
if exp_type != member.type:
|
||||
_raise_static_error(node, f'Expected {exp_type.render()}, got {member.type.render()} instead')
|
||||
_raise_static_error(node, f'Expected {exp_type.render()}, {node.value.id}[{idx}] is actually {member.type.render()}')
|
||||
|
||||
return AccessTupleMember(
|
||||
VariableReference(node_typ, node.value.id),
|
||||
|
||||
56
tests/integration/test_static_checking.py
Normal file
56
tests/integration/test_static_checking.py
Normal file
@ -0,0 +1,56 @@
|
||||
import pytest
|
||||
|
||||
from py2wasm.utils import our_process
|
||||
|
||||
from py2wasm.ourlang import StaticError
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', ['i32', 'i64', 'f32', 'f64'])
|
||||
def test_type_mismatch_function_argument(type_):
|
||||
code_py = f"""
|
||||
def helper(a: {type_}) -> (i32, i32, ):
|
||||
return a
|
||||
"""
|
||||
|
||||
with pytest.raises(StaticError, match=f'Static error on line 3: Expected \\(i32, i32, \\), a is actually {type_}'):
|
||||
our_process(code_py, 'test')
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', ['i32', 'i64', 'f32', 'f64'])
|
||||
def test_type_mismatch_struct_member(type_):
|
||||
code_py = f"""
|
||||
class Struct:
|
||||
param: {type_}
|
||||
|
||||
def testEntry(arg: Struct) -> (i32, i32, ):
|
||||
return arg.param
|
||||
"""
|
||||
|
||||
with pytest.raises(StaticError, match=f'Static error on line 6: Expected \\(i32, i32, \\), arg.param is actually {type_}'):
|
||||
our_process(code_py, 'test')
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', ['i32', 'i64', 'f32', 'f64'])
|
||||
def test_type_mismatch_tuple_member(type_):
|
||||
code_py = f"""
|
||||
def testEntry(arg: ({type_}, )) -> (i32, i32, ):
|
||||
return arg[0]
|
||||
"""
|
||||
|
||||
with pytest.raises(StaticError, match=f'Static error on line 3: Expected \\(i32, i32, \\), arg\\[0\\] is actually {type_}'):
|
||||
our_process(code_py, 'test')
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', ['i32', 'i64', 'f32', 'f64'])
|
||||
def test_type_mismatch_function_result(type_):
|
||||
code_py = f"""
|
||||
def helper() -> {type_}:
|
||||
return 1
|
||||
|
||||
@exported
|
||||
def testEntry() -> (i32, i32, ):
|
||||
return helper()
|
||||
"""
|
||||
|
||||
with pytest.raises(StaticError, match=f'Static error on line 7: Expected \\(i32, i32, \\), helper actually returns {type_}'):
|
||||
our_process(code_py, 'test')
|
||||
Loading…
x
Reference in New Issue
Block a user