MVP #1
@ -97,7 +97,7 @@ class TupleMember:
|
|||||||
|
|
||||||
class OurTypeTuple(OurType):
|
class OurTypeTuple(OurType):
|
||||||
"""
|
"""
|
||||||
The tuple type, 64 bits wide
|
The tuple type
|
||||||
"""
|
"""
|
||||||
__slots__ = ('members', )
|
__slots__ = ('members', )
|
||||||
|
|
||||||
@ -842,8 +842,14 @@ class OurVisitor:
|
|||||||
if not isinstance(node.ctx, ast.Load):
|
if not isinstance(node.ctx, ast.Load):
|
||||||
_raise_static_error(node, 'Must be load context')
|
_raise_static_error(node, 'Must be load context')
|
||||||
|
|
||||||
if node.id in our_locals:
|
if node.id not in our_locals:
|
||||||
return VariableReference(our_locals[node.id], node.id)
|
_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 isinstance(node, ast.Tuple):
|
||||||
if not isinstance(node.ctx, ast.Load):
|
if not isinstance(node.ctx, ast.Load):
|
||||||
@ -884,7 +890,7 @@ class OurVisitor:
|
|||||||
func = module.functions[struct_constructor.name]
|
func = module.functions[struct_constructor.name]
|
||||||
elif node.func.id in WEBASSEMBLY_BUILDIN_FLOAT_OPS:
|
elif node.func.id in WEBASSEMBLY_BUILDIN_FLOAT_OPS:
|
||||||
if not isinstance(exp_type, (OurTypeFloat32, OurTypeFloat64, )):
|
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):
|
if 1 != len(node.args):
|
||||||
_raise_static_error(node, f'Function {node.func.id} requires 1 arguments but {len(node.args)} are given')
|
_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]
|
func = module.functions[node.func.id]
|
||||||
|
|
||||||
if func.returns != exp_type:
|
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):
|
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')
|
_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}')
|
_raise_static_error(node, f'{node_typ.name} has no attribute {node.attr}')
|
||||||
|
|
||||||
if exp_type != member.type:
|
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(
|
return AccessStructMember(
|
||||||
VariableReference(node_typ, node.value.id),
|
VariableReference(node_typ, node.value.id),
|
||||||
@ -949,7 +955,8 @@ class OurVisitor:
|
|||||||
if not isinstance(node.slice.value, ast.Constant):
|
if not isinstance(node.slice.value, ast.Constant):
|
||||||
_raise_static_error(node, 'Must subscript using a constant index') # FIXME: Implement variable indexes
|
_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')
|
_raise_static_error(node, 'Must subscript using a constant integer index')
|
||||||
|
|
||||||
if not isinstance(node.ctx, ast.Load):
|
if not isinstance(node.ctx, ast.Load):
|
||||||
@ -962,12 +969,12 @@ class OurVisitor:
|
|||||||
if not isinstance(node_typ, OurTypeTuple):
|
if not isinstance(node_typ, OurTypeTuple):
|
||||||
_raise_static_error(node, f'Cannot take index of non-tuple {node.value.id}')
|
_raise_static_error(node, f'Cannot take index of non-tuple {node.value.id}')
|
||||||
|
|
||||||
if len(node_typ.members) <= node.slice.value.value:
|
if len(node_typ.members) <= idx:
|
||||||
_raise_static_error(node, f'Index {node.slice.value.value} out of bounds for tuple {node.value.id}')
|
_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:
|
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(
|
return AccessTupleMember(
|
||||||
VariableReference(node_typ, node.value.id),
|
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