Separates out TypeVariable and constraints
They look a lot like placeholders, but they exist before the typing system takes place. And there's a (much smaller) context to deal with. For now, removes Placeholders in user function definitions as they were not implemented. Adds signature to function to try to get them closer to type class methods. Already seeing some benefit in the constraint generator. Stricter zipping for safety.
This commit is contained in:
parent
d3e38b96b2
commit
c8009403c4
@ -8,6 +8,7 @@ from . import codestyle, ourlang, prelude, wasm
|
|||||||
from .runtime import calculate_alloc_size, calculate_member_offset
|
from .runtime import calculate_alloc_size, calculate_member_offset
|
||||||
from .stdlib import alloc as stdlib_alloc
|
from .stdlib import alloc as stdlib_alloc
|
||||||
from .stdlib import types as stdlib_types
|
from .stdlib import types as stdlib_types
|
||||||
|
from .type3 import functions as type3functions
|
||||||
from .type3 import placeholders as type3placeholders
|
from .type3 import placeholders as type3placeholders
|
||||||
from .type3 import typeclasses as type3classes
|
from .type3 import typeclasses as type3classes
|
||||||
from .type3 import types as type3types
|
from .type3 import types as type3types
|
||||||
@ -335,7 +336,7 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
|
|||||||
|
|
||||||
# Store each element individually
|
# Store each element individually
|
||||||
offset = 0
|
offset = 0
|
||||||
for element, exp_type3 in zip(inp.elements, args):
|
for element, exp_type3 in zip(inp.elements, args, strict=True):
|
||||||
if isinstance(exp_type3, type3placeholders.PlaceholderForType):
|
if isinstance(exp_type3, type3placeholders.PlaceholderForType):
|
||||||
assert exp_type3.resolve_as is not None
|
assert exp_type3.resolve_as is not None
|
||||||
assert isinstance(exp_type3.resolve_as, type3types.Type3)
|
assert isinstance(exp_type3.resolve_as, type3types.Type3)
|
||||||
@ -434,10 +435,10 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
|||||||
|
|
||||||
assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||||
|
|
||||||
type_var_map: Dict[type3classes.TypeVariable, type3types.Type3] = {}
|
type_var_map: Dict[type3functions.TypeVariable, type3types.Type3] = {}
|
||||||
|
|
||||||
for type_var, arg_expr in zip(inp.operator.signature, [inp.left, inp.right, inp]):
|
for type_var, arg_expr in zip(inp.operator.signature.args, [inp.left, inp.right, inp], strict=True):
|
||||||
if not isinstance(type_var, type3classes.TypeVariable):
|
if not isinstance(type_var, type3functions.TypeVariable):
|
||||||
# Fixed type, not part of the lookup requirements
|
# Fixed type, not part of the lookup requirements
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -476,8 +477,8 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
|||||||
# FIXME: Duplicate code with BinaryOp
|
# FIXME: Duplicate code with BinaryOp
|
||||||
type_var_map = {}
|
type_var_map = {}
|
||||||
|
|
||||||
for type_var, arg_expr in zip(inp.function.signature, inp.arguments + [inp]):
|
for type_var, arg_expr in zip(inp.function.signature.args, inp.arguments + [inp], strict=True):
|
||||||
if not isinstance(type_var, type3classes.TypeVariable):
|
if not isinstance(type_var, type3functions.TypeVariable):
|
||||||
# Fixed type, not part of the lookup requirements
|
# Fixed type, not part of the lookup requirements
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import enum
|
|||||||
from typing import Dict, Iterable, List, Optional, Union
|
from typing import Dict, Iterable, List, Optional, Union
|
||||||
|
|
||||||
from . import prelude
|
from . import prelude
|
||||||
|
from .type3.functions import FunctionSignature, TypeVariableContext
|
||||||
from .type3.placeholders import PlaceholderForType, Type3OrPlaceholder
|
from .type3.placeholders import PlaceholderForType, Type3OrPlaceholder
|
||||||
from .type3.typeclasses import Type3ClassMethod
|
from .type3.typeclasses import Type3ClassMethod
|
||||||
from .type3.types import Type3
|
from .type3.types import Type3
|
||||||
@ -300,21 +301,22 @@ class FunctionParam:
|
|||||||
name: str
|
name: str
|
||||||
type3: Type3OrPlaceholder
|
type3: Type3OrPlaceholder
|
||||||
|
|
||||||
def __init__(self, name: str, type3: Optional[Type3]) -> None:
|
def __init__(self, name: str, type3: Type3) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.type3 = PlaceholderForType([self]) if type3 is None else type3
|
self.type3 = type3
|
||||||
|
|
||||||
class Function:
|
class Function:
|
||||||
"""
|
"""
|
||||||
A function processes input and produces output
|
A function processes input and produces output
|
||||||
"""
|
"""
|
||||||
__slots__ = ('name', 'lineno', 'exported', 'imported', 'statements', 'returns_type3', 'posonlyargs', )
|
__slots__ = ('name', 'lineno', 'exported', 'imported', 'statements', 'signature', 'returns_type3', 'posonlyargs', )
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
lineno: int
|
lineno: int
|
||||||
exported: bool
|
exported: bool
|
||||||
imported: Optional[str]
|
imported: Optional[str]
|
||||||
statements: List[Statement]
|
statements: List[Statement]
|
||||||
|
signature: FunctionSignature
|
||||||
returns_type3: Type3
|
returns_type3: Type3
|
||||||
posonlyargs: List[FunctionParam]
|
posonlyargs: List[FunctionParam]
|
||||||
|
|
||||||
@ -324,6 +326,7 @@ class Function:
|
|||||||
self.exported = False
|
self.exported = False
|
||||||
self.imported = None
|
self.imported = None
|
||||||
self.statements = []
|
self.statements = []
|
||||||
|
self.signature = FunctionSignature(TypeVariableContext(), [])
|
||||||
self.returns_type3 = prelude.none # FIXME: This could be a placeholder
|
self.returns_type3 = prelude.none # FIXME: This could be a placeholder
|
||||||
self.posonlyargs = []
|
self.posonlyargs = []
|
||||||
|
|
||||||
@ -354,12 +357,14 @@ class StructConstructor(Function):
|
|||||||
def __init__(self, struct_type3: Type3) -> None:
|
def __init__(self, struct_type3: Type3) -> None:
|
||||||
super().__init__(f'@{struct_type3.name}@__init___@', -1)
|
super().__init__(f'@{struct_type3.name}@__init___@', -1)
|
||||||
|
|
||||||
self.returns_type3 = struct_type3
|
|
||||||
|
|
||||||
st_args = prelude.struct.did_construct(struct_type3)
|
st_args = prelude.struct.did_construct(struct_type3)
|
||||||
assert st_args is not None
|
assert st_args is not None
|
||||||
for mem, typ in st_args.items():
|
for mem, typ in st_args.items():
|
||||||
self.posonlyargs.append(FunctionParam(mem, typ, ))
|
self.posonlyargs.append(FunctionParam(mem, typ, ))
|
||||||
|
self.signature.args.append(typ)
|
||||||
|
|
||||||
|
self.returns_type3 = struct_type3
|
||||||
|
self.signature.args.append(struct_type3)
|
||||||
|
|
||||||
self.struct_type3 = struct_type3
|
self.struct_type3 = struct_type3
|
||||||
|
|
||||||
|
|||||||
@ -159,9 +159,18 @@ class OurVisitor:
|
|||||||
_not_implemented(not node.args.posonlyargs, 'FunctionDef.args.posonlyargs')
|
_not_implemented(not node.args.posonlyargs, 'FunctionDef.args.posonlyargs')
|
||||||
|
|
||||||
for arg in node.args.args:
|
for arg in node.args.args:
|
||||||
|
if arg.annotation is None:
|
||||||
|
_raise_static_error(node, 'Must give a argument type')
|
||||||
|
|
||||||
|
arg_type = self.visit_type(module, arg.annotation)
|
||||||
|
|
||||||
|
# if isisntance(arg_type, TypeVariable):
|
||||||
|
# arg_type = PlaceHolderForType()
|
||||||
|
|
||||||
|
function.signature.args.append(arg_type)
|
||||||
function.posonlyargs.append(FunctionParam(
|
function.posonlyargs.append(FunctionParam(
|
||||||
arg.arg,
|
arg.arg,
|
||||||
self.visit_type(module, arg.annotation) if arg.annotation else None,
|
arg_type,
|
||||||
))
|
))
|
||||||
|
|
||||||
_not_implemented(not node.args.vararg, 'FunctionDef.args.vararg')
|
_not_implemented(not node.args.vararg, 'FunctionDef.args.vararg')
|
||||||
@ -202,11 +211,11 @@ class OurVisitor:
|
|||||||
else:
|
else:
|
||||||
function.imported = 'imports'
|
function.imported = 'imports'
|
||||||
|
|
||||||
if node.returns is not None: # Note: `-> None` would be a ast.Constant
|
if node.returns is None: # Note: `-> None` would be a ast.Constant
|
||||||
function.returns_type3 = self.visit_type(module, node.returns)
|
_raise_static_error(node, 'Must give a return type')
|
||||||
else:
|
return_type = self.visit_type(module, node.returns)
|
||||||
# FIXME: Mostly works already, needs to fix Function.returns_type3 and have it updated
|
function.signature.args.append(return_type)
|
||||||
raise NotImplementedError('Function without an explicit return type')
|
function.returns_type3 = return_type
|
||||||
|
|
||||||
_not_implemented(not node.type_comment, 'FunctionDef.type_comment')
|
_not_implemented(not node.type_comment, 'FunctionDef.type_comment')
|
||||||
|
|
||||||
@ -518,7 +527,7 @@ class OurVisitor:
|
|||||||
|
|
||||||
func = module.functions[node.func.id]
|
func = module.functions[node.func.id]
|
||||||
|
|
||||||
exp_arg_count = len(func.posonlyargs) if isinstance(func, Function) else len(func.signature) - 1
|
exp_arg_count = len(func.signature.args) - 1
|
||||||
|
|
||||||
if exp_arg_count != len(node.args):
|
if exp_arg_count != len(node.args):
|
||||||
_raise_static_error(node, f'Function {node.func.id} requires {exp_arg_count} arguments but {len(node.args)} are given')
|
_raise_static_error(node, f'Function {node.func.id} requires {exp_arg_count} arguments but {len(node.args)} are given')
|
||||||
|
|||||||
@ -2,7 +2,8 @@
|
|||||||
The prelude are all the builtin types, type classes and methods
|
The prelude are all the builtin types, type classes and methods
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from ..type3.typeclasses import Type3Class, TypeVariable, instance_type_class
|
from ..type3.functions import TypeVariable
|
||||||
|
from ..type3.typeclasses import Type3Class, instance_type_class
|
||||||
from ..type3.types import (
|
from ..type3.types import (
|
||||||
Type3,
|
Type3,
|
||||||
TypeConstructor_StaticArray,
|
TypeConstructor_StaticArray,
|
||||||
|
|||||||
@ -186,7 +186,7 @@ class TupleMatchConstraint(ConstraintBase):
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
SameTypeConstraint(arg, oth_arg)
|
SameTypeConstraint(arg, oth_arg)
|
||||||
for arg, oth_arg in zip(self.args, tp_args)
|
for arg, oth_arg in zip(self.args, tp_args, strict=True)
|
||||||
]
|
]
|
||||||
|
|
||||||
raise NotImplementedError(exp_type)
|
raise NotImplementedError(exp_type)
|
||||||
@ -369,11 +369,11 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
|
|
||||||
res.extend(
|
res.extend(
|
||||||
LiteralFitsConstraint(x, y)
|
LiteralFitsConstraint(x, y)
|
||||||
for x, y in zip(tp_args, self.literal.value)
|
for x, y in zip(tp_args, self.literal.value, strict=True)
|
||||||
)
|
)
|
||||||
res.extend(
|
res.extend(
|
||||||
SameTypeConstraint(x, y.type3)
|
SameTypeConstraint(x, y.type3)
|
||||||
for x, y in zip(tp_args, self.literal.value)
|
for x, y in zip(tp_args, self.literal.value, strict=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
@ -417,11 +417,11 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
|
|
||||||
res.extend(
|
res.extend(
|
||||||
LiteralFitsConstraint(x, y)
|
LiteralFitsConstraint(x, y)
|
||||||
for x, y in zip(st_args.values(), self.literal.value)
|
for x, y in zip(st_args.values(), self.literal.value, strict=True)
|
||||||
)
|
)
|
||||||
res.extend(
|
res.extend(
|
||||||
SameTypeConstraint(x_t, y.type3, comment=f'{self.literal.struct_name}.{x_n}')
|
SameTypeConstraint(x_t, y.type3, comment=f'{self.literal.struct_name}.{x_n}')
|
||||||
for (x_n, x_t, ), y in zip(st_args.items(), self.literal.value)
|
for (x_n, x_t, ), y in zip(st_args.items(), self.literal.value, strict=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|||||||
@ -6,8 +6,9 @@ The constraints solver can then try to resolve all constraints.
|
|||||||
from typing import Generator, List
|
from typing import Generator, List
|
||||||
|
|
||||||
from .. import ourlang, prelude
|
from .. import ourlang, prelude
|
||||||
|
from . import functions as functions
|
||||||
from . import placeholders as placeholders
|
from . import placeholders as placeholders
|
||||||
from . import typeclasses as type3typeclasses
|
from . import typeclasses as typeclasses
|
||||||
from . import types as type3types
|
from . import types as type3types
|
||||||
from .constraints import (
|
from .constraints import (
|
||||||
CanBeSubscriptedConstraint,
|
CanBeSubscriptedConstraint,
|
||||||
@ -55,81 +56,51 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
|
|||||||
|
|
||||||
raise NotImplementedError(expression, inp, inp.operator)
|
raise NotImplementedError(expression, inp, inp.operator)
|
||||||
|
|
||||||
if isinstance(inp, ourlang.BinaryOp):
|
if isinstance(inp, ourlang.BinaryOp) or isinstance(inp, ourlang.FunctionCall):
|
||||||
|
signature = inp.operator.signature if isinstance(inp, ourlang.BinaryOp) else inp.function.signature
|
||||||
|
arguments = [inp.left, inp.right] if isinstance(inp, ourlang.BinaryOp) else inp.arguments
|
||||||
|
|
||||||
|
func_name = f'({inp.operator.name})' if isinstance(inp, ourlang.BinaryOp) else inp.function.name
|
||||||
|
|
||||||
type_var_map = {
|
type_var_map = {
|
||||||
x: placeholders.PlaceholderForType([])
|
x: placeholders.PlaceholderForType([])
|
||||||
for x in inp.operator.signature
|
for x in signature.args
|
||||||
if isinstance(x, type3typeclasses.TypeVariable)
|
if isinstance(x, functions.TypeVariable)
|
||||||
}
|
}
|
||||||
|
|
||||||
yield from expression(ctx, inp.left)
|
for call_arg in arguments:
|
||||||
yield from expression(ctx, inp.right)
|
yield from expression(ctx, call_arg)
|
||||||
|
|
||||||
for type_var in inp.operator.type3_class.args:
|
for type_var, constraint_list in signature.context.constraints.items():
|
||||||
assert type_var in type_var_map # When can this happen?
|
assert type_var in type_var_map # When can this happen?
|
||||||
|
|
||||||
yield MustImplementTypeClassConstraint(
|
for constraint in constraint_list:
|
||||||
inp.operator.type3_class,
|
if isinstance(constraint, functions.TypeVariableConstraint_TypeHasTypeClass):
|
||||||
type_var_map[type_var],
|
yield MustImplementTypeClassConstraint(
|
||||||
)
|
constraint.type_class3,
|
||||||
|
type_var_map[type_var],
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
for sig_part, arg_expr in zip(inp.operator.signature, [inp.left, inp.right, inp]):
|
raise NotImplementedError(constraint)
|
||||||
if isinstance(sig_part, type3typeclasses.TypeVariable):
|
|
||||||
yield SameTypeConstraint(type_var_map[sig_part], arg_expr.type3)
|
for arg_no, (sig_part, arg_expr) in enumerate(zip(signature.args, arguments + [inp], strict=True)):
|
||||||
|
if arg_no == len(arguments):
|
||||||
|
comment = f'The type of a function call to {func_name} is the same as the type that the function returns'
|
||||||
|
else:
|
||||||
|
comment = f'The type of the value passed to argument {arg_no} of function {func_name} should match the type of that argument'
|
||||||
|
|
||||||
|
if isinstance(sig_part, functions.TypeVariable):
|
||||||
|
yield SameTypeConstraint(type_var_map[sig_part], arg_expr.type3, comment=comment)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if isinstance(sig_part, type3types.Type3):
|
if isinstance(sig_part, type3types.Type3):
|
||||||
yield SameTypeConstraint(sig_part, arg_expr.type3)
|
yield SameTypeConstraint(sig_part, arg_expr.type3, comment=comment)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
raise NotImplementedError(sig_part)
|
raise NotImplementedError(sig_part)
|
||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(inp, ourlang.FunctionCall):
|
|
||||||
if isinstance(inp.function, type3typeclasses.Type3ClassMethod):
|
|
||||||
# FIXME: Duplicate code with BinaryOp
|
|
||||||
|
|
||||||
type_var_map = {
|
|
||||||
x: placeholders.PlaceholderForType([])
|
|
||||||
for x in inp.function.signature
|
|
||||||
if isinstance(x, type3typeclasses.TypeVariable)
|
|
||||||
}
|
|
||||||
|
|
||||||
for call_arg in inp.arguments:
|
|
||||||
yield from expression(ctx, call_arg)
|
|
||||||
|
|
||||||
for type_var in inp.function.type3_class.args:
|
|
||||||
assert type_var in type_var_map # When can this happen?
|
|
||||||
|
|
||||||
yield MustImplementTypeClassConstraint(
|
|
||||||
inp.function.type3_class,
|
|
||||||
type_var_map[type_var],
|
|
||||||
)
|
|
||||||
|
|
||||||
for sig_part, arg_expr in zip(inp.function.signature, inp.arguments + [inp]):
|
|
||||||
if isinstance(sig_part, type3typeclasses.TypeVariable):
|
|
||||||
yield SameTypeConstraint(type_var_map[sig_part], arg_expr.type3)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if isinstance(sig_part, type3types.Type3):
|
|
||||||
yield SameTypeConstraint(sig_part, arg_expr.type3)
|
|
||||||
continue
|
|
||||||
|
|
||||||
raise NotImplementedError(sig_part)
|
|
||||||
return
|
|
||||||
|
|
||||||
yield SameTypeConstraint(inp.function.returns_type3, inp.type3,
|
|
||||||
comment=f'The type of a function call to {inp.function.name} is the same as the type that the function returns')
|
|
||||||
|
|
||||||
assert len(inp.arguments) == len(inp.function.posonlyargs) # FIXME: Make this a Constraint
|
|
||||||
|
|
||||||
for fun_arg, call_arg in zip(inp.function.posonlyargs, inp.arguments):
|
|
||||||
yield from expression(ctx, call_arg)
|
|
||||||
yield SameTypeConstraint(fun_arg.type3, call_arg.type3,
|
|
||||||
comment=f'The type of the value passed to argument {fun_arg.name} of function {inp.function.name} should match the type of that argument')
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
if isinstance(inp, ourlang.TupleInstantiation):
|
if isinstance(inp, ourlang.TupleInstantiation):
|
||||||
r_type = []
|
r_type = []
|
||||||
for arg in inp.elements:
|
for arg in inp.elements:
|
||||||
|
|||||||
67
phasm/type3/functions.py
Normal file
67
phasm/type3/functions.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
from typing import TYPE_CHECKING, Any, Iterable, List, Union
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .typeclasses import Type3Class
|
||||||
|
from .types import Type3
|
||||||
|
|
||||||
|
|
||||||
|
class TypeVariable:
|
||||||
|
"""
|
||||||
|
Types variable are used in function definition.
|
||||||
|
|
||||||
|
They are used in places where you don't know the exact type.
|
||||||
|
They are different from PlaceholderForType, as those are instanced
|
||||||
|
during type checking. These type variables are used solely in the
|
||||||
|
function's definition
|
||||||
|
"""
|
||||||
|
__slots__ = ('letter', )
|
||||||
|
|
||||||
|
letter: str
|
||||||
|
|
||||||
|
def __init__(self, letter: str) -> None:
|
||||||
|
assert len(letter) == 1, f'{letter} is not a valid type variable'
|
||||||
|
self.letter = letter
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return hash(self.letter)
|
||||||
|
|
||||||
|
def __eq__(self, other: Any) -> bool:
|
||||||
|
if not isinstance(other, TypeVariable):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
return self.letter == other.letter
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f'TypeVariable({repr(self.letter)})'
|
||||||
|
|
||||||
|
class TypeVariableConstraintBase:
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
class TypeVariableConstraint_TypeHasTypeClass(TypeVariableConstraintBase):
|
||||||
|
__slots__ = ('type_class3', )
|
||||||
|
|
||||||
|
def __init__(self, type_class3: 'Type3Class') -> None:
|
||||||
|
self.type_class3 = type_class3
|
||||||
|
|
||||||
|
class TypeVariableContext:
|
||||||
|
__slots__ = ('constraints', )
|
||||||
|
|
||||||
|
constraints: dict[TypeVariable, list[TypeVariableConstraintBase]]
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.constraints = {}
|
||||||
|
|
||||||
|
def __copy__(self) -> 'TypeVariableContext':
|
||||||
|
result = TypeVariableContext()
|
||||||
|
result.constraints.update(self.constraints)
|
||||||
|
return result
|
||||||
|
|
||||||
|
class FunctionSignature:
|
||||||
|
__slots__ = ('context', 'args', )
|
||||||
|
|
||||||
|
context: TypeVariableContext
|
||||||
|
args: List[Union['Type3', TypeVariable]]
|
||||||
|
|
||||||
|
def __init__(self, context: TypeVariableContext, args: Iterable[Union['Type3', TypeVariable]]) -> None:
|
||||||
|
self.context = context.__copy__()
|
||||||
|
self.args = list(args)
|
||||||
@ -1,64 +1,26 @@
|
|||||||
from typing import Any, Dict, Iterable, List, Mapping, Optional, Union
|
from typing import Any, Dict, Iterable, List, Mapping, Optional, Union
|
||||||
|
|
||||||
|
from .functions import (
|
||||||
|
FunctionSignature,
|
||||||
|
TypeVariable,
|
||||||
|
TypeVariableConstraint_TypeHasTypeClass,
|
||||||
|
TypeVariableContext,
|
||||||
|
)
|
||||||
from .types import Type3, TypeConstructor, TypeConstructor_Struct
|
from .types import Type3, TypeConstructor, TypeConstructor_Struct
|
||||||
|
|
||||||
|
|
||||||
class TypeVariable:
|
|
||||||
__slots__ = ('letter', )
|
|
||||||
|
|
||||||
letter: str
|
|
||||||
|
|
||||||
def __init__(self, letter: str) -> None:
|
|
||||||
assert len(letter) == 1, f'{letter} is not a valid type variable'
|
|
||||||
self.letter = letter
|
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
|
||||||
return hash(self.letter)
|
|
||||||
|
|
||||||
def __eq__(self, other: Any) -> bool:
|
|
||||||
if not isinstance(other, TypeVariable):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
return self.letter == other.letter
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f'TypeVariable({repr(self.letter)})'
|
|
||||||
|
|
||||||
class TypeReference:
|
|
||||||
__slots__ = ('name', )
|
|
||||||
|
|
||||||
name: str
|
|
||||||
|
|
||||||
def __init__(self, name: str) -> None:
|
|
||||||
assert len(name) > 1, f'{name} is not a valid type reference'
|
|
||||||
self.name = name
|
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
|
||||||
return hash(self.name)
|
|
||||||
|
|
||||||
def __eq__(self, other: Any) -> bool:
|
|
||||||
if not isinstance(other, TypeReference):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
return self.name == other.name
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f'TypeReference({repr(self.name)})'
|
|
||||||
|
|
||||||
class Type3ClassMethod:
|
class Type3ClassMethod:
|
||||||
__slots__ = ('type3_class', 'name', 'signature', )
|
__slots__ = ('name', 'signature', )
|
||||||
|
|
||||||
type3_class: 'Type3Class'
|
|
||||||
name: str
|
name: str
|
||||||
signature: List[Union[Type3, TypeVariable]]
|
signature: FunctionSignature
|
||||||
|
|
||||||
def __init__(self, type3_class: 'Type3Class', name: str, signature: Iterable[Union[Type3, TypeVariable]]) -> None:
|
def __init__(self, name: str, signature: FunctionSignature) -> None:
|
||||||
self.type3_class = type3_class
|
|
||||||
self.name = name
|
self.name = name
|
||||||
self.signature = list(signature)
|
self.signature = signature
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'Type3ClassMethod({repr(self.type3_class)}, {repr(self.name)}, {repr(self.signature)})'
|
return f'Type3ClassMethod({repr(self.name)}, {repr(self.signature)})'
|
||||||
|
|
||||||
class Type3Class:
|
class Type3Class:
|
||||||
__slots__ = ('name', 'args', 'methods', 'operators', 'inherited_classes', )
|
__slots__ = ('name', 'args', 'methods', 'operators', 'inherited_classes', )
|
||||||
@ -79,12 +41,26 @@ class Type3Class:
|
|||||||
) -> None:
|
) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.args = list(args)
|
self.args = list(args)
|
||||||
|
|
||||||
|
context = TypeVariableContext()
|
||||||
|
for arg in args:
|
||||||
|
context.constraints[arg] = [
|
||||||
|
TypeVariableConstraint_TypeHasTypeClass(self)
|
||||||
|
]
|
||||||
|
|
||||||
|
# FIXME: Multi parameter class types
|
||||||
|
# To fix this, realise that an instantiation of a multi paramater type class
|
||||||
|
# means that the instantiation depends on the combination of type classes
|
||||||
|
# and so we can't store the type classes on the types anymore
|
||||||
|
# This also means constraints should store a tuple of types as its key
|
||||||
|
assert len(context.constraints) <= 1
|
||||||
|
|
||||||
self.methods = {
|
self.methods = {
|
||||||
k: Type3ClassMethod(self, k, v)
|
k: Type3ClassMethod(k, FunctionSignature(context, v))
|
||||||
for k, v in methods.items()
|
for k, v in methods.items()
|
||||||
}
|
}
|
||||||
self.operators = {
|
self.operators = {
|
||||||
k: Type3ClassMethod(self, k, v)
|
k: Type3ClassMethod(k, FunctionSignature(context, v))
|
||||||
for k, v in operators.items()
|
for k, v in operators.items()
|
||||||
}
|
}
|
||||||
self.inherited_classes = inherited_classes or []
|
self.inherited_classes = inherited_classes or []
|
||||||
|
|||||||
@ -64,7 +64,7 @@ class Suite:
|
|||||||
write_header(sys.stderr, 'Memory (pre alloc)')
|
write_header(sys.stderr, 'Memory (pre alloc)')
|
||||||
runner.interpreter_dump_memory(sys.stderr)
|
runner.interpreter_dump_memory(sys.stderr)
|
||||||
|
|
||||||
for arg, arg_typ in zip(args, func_args):
|
for arg, arg_typ in zip(args, func_args, strict=True):
|
||||||
assert not isinstance(arg_typ, type3placeholders.PlaceholderForType), \
|
assert not isinstance(arg_typ, type3placeholders.PlaceholderForType), \
|
||||||
'Cannot call polymorphic function from outside'
|
'Cannot call polymorphic function from outside'
|
||||||
|
|
||||||
@ -221,7 +221,7 @@ def _allocate_memory_stored_value(
|
|||||||
assert len(val) == len(tp_args)
|
assert len(val) == len(tp_args)
|
||||||
|
|
||||||
offset = adr
|
offset = adr
|
||||||
for val_el_val, val_el_typ in zip(val, tp_args):
|
for val_el_val, val_el_typ in zip(val, tp_args, strict=True):
|
||||||
assert not isinstance(val_el_typ, type3placeholders.PlaceholderForType)
|
assert not isinstance(val_el_typ, type3placeholders.PlaceholderForType)
|
||||||
|
|
||||||
offset += _write_memory_stored_value(runner, offset, val_el_typ, val_el_val)
|
offset += _write_memory_stored_value(runner, offset, val_el_typ, val_el_val)
|
||||||
@ -424,7 +424,7 @@ def _load_tuple_from_address(runner: runners.RunnerBase, typ_args: tuple[type3ty
|
|||||||
|
|
||||||
return tuple(
|
return tuple(
|
||||||
_unpack(runner, arg_typ, arg_bytes)
|
_unpack(runner, arg_typ, arg_bytes)
|
||||||
for arg_typ, arg_bytes in zip(typ_args, _split_read_bytes(read_bytes, arg_sizes))
|
for arg_typ, arg_bytes in zip(typ_args, _split_read_bytes(read_bytes, arg_sizes), strict=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
def _load_struct_from_address(runner: runners.RunnerBase, st_args: dict[str, type3types.Type3], adr: int) -> Any:
|
def _load_struct_from_address(runner: runners.RunnerBase, st_args: dict[str, type3types.Type3], adr: int) -> Any:
|
||||||
@ -444,5 +444,5 @@ def _load_struct_from_address(runner: runners.RunnerBase, st_args: dict[str, typ
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
arg_name: _unpack(runner, arg_typ, arg_bytes)
|
arg_name: _unpack(runner, arg_typ, arg_bytes)
|
||||||
for arg_name, arg_typ, arg_bytes in zip(name_list, typ_list, _split_read_bytes(read_bytes, arg_sizes))
|
for arg_name, arg_typ, arg_bytes in zip(name_list, typ_list, _split_read_bytes(read_bytes, arg_sizes), strict=True)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -333,11 +333,11 @@ def testEntry() -> i32:
|
|||||||
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_') or TYPE_NAME.startswith('struct_'):
|
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_') or TYPE_NAME.startswith('struct_'):
|
||||||
expect_type_error(
|
expect_type_error(
|
||||||
TYPE + ' must be (u32, ) instead',
|
TYPE + ' must be (u32, ) instead',
|
||||||
'The type of the value passed to argument x of function helper should match the type of that argument',
|
'The type of the value passed to argument 0 of function helper should match the type of that argument',
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
expect_type_error(
|
expect_type_error(
|
||||||
TYPE_NAME + ' must be (u32, ) instead',
|
TYPE_NAME + ' must be (u32, ) instead',
|
||||||
'The type of the value passed to argument x of function helper should match the type of that argument',
|
'The type of the value passed to argument 0 of function helper should match the type of that argument',
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|||||||
@ -1,27 +1,8 @@
|
|||||||
import sys
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from ..helpers import Suite, write_header
|
from ..helpers import Suite
|
||||||
from ..runners import RunnerWasmtime
|
|
||||||
|
|
||||||
|
|
||||||
def setup_interpreter(phash_code: str) -> RunnerWasmtime:
|
|
||||||
runner = RunnerWasmtime(phash_code)
|
|
||||||
|
|
||||||
runner.parse()
|
|
||||||
runner.compile_ast()
|
|
||||||
runner.compile_wat()
|
|
||||||
runner.interpreter_setup()
|
|
||||||
runner.interpreter_load()
|
|
||||||
|
|
||||||
write_header(sys.stderr, 'Phasm')
|
|
||||||
runner.dump_phasm_code(sys.stderr)
|
|
||||||
write_header(sys.stderr, 'Assembly')
|
|
||||||
runner.dump_wasm_wat(sys.stderr)
|
|
||||||
|
|
||||||
return runner
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
def test_foldl_1():
|
def test_foldl_1():
|
||||||
code_py = """
|
code_py = """
|
||||||
|
|||||||
20
tests/integration/test_lang/test_typeclass.py
Normal file
20
tests/integration/test_lang/test_typeclass.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from phasm.exceptions import StaticError
|
||||||
|
|
||||||
|
from ..helpers import Suite
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
def test_must_give_type_annotations():
|
||||||
|
code_py = """
|
||||||
|
def is_equal(lft, rgt) -> bool:
|
||||||
|
return lft == rgt
|
||||||
|
|
||||||
|
def testEntry() -> bool:
|
||||||
|
return is_equal(5, 15)
|
||||||
|
"""
|
||||||
|
suite = Suite(code_py)
|
||||||
|
|
||||||
|
with pytest.raises(StaticError, match='Must give a argument type'):
|
||||||
|
suite.run_code()
|
||||||
Loading…
x
Reference in New Issue
Block a user