Compare commits
3 Commits
b5f0fda133
...
ac4b46bbe7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac4b46bbe7 | ||
|
|
67af569448 | ||
|
|
df5c1911bf |
@ -63,7 +63,7 @@ def expression(inp: ourlang.Expression) -> str:
|
||||
) + ', )'
|
||||
|
||||
if isinstance(inp, ourlang.ConstantStruct):
|
||||
return inp.struct_name + '(' + ', '.join(
|
||||
return inp.struct_type3.name + '(' + ', '.join(
|
||||
expression(x)
|
||||
for x in inp.value
|
||||
) + ')'
|
||||
|
||||
@ -8,10 +8,18 @@ from . import codestyle, ourlang, prelude, wasm
|
||||
from .runtime import calculate_alloc_size, calculate_member_offset
|
||||
from .stdlib import alloc as stdlib_alloc
|
||||
from .stdlib import types as stdlib_types
|
||||
from .type3 import functions as type3functions
|
||||
from .type3 import typeclasses as type3classes
|
||||
from .type3 import types as type3types
|
||||
from .type3.functions import TypeVariable
|
||||
from .type3.routers import NoRouteForTypeException, TypeApplicationRouter
|
||||
from .type3.typeclasses import Type3ClassMethod
|
||||
from .type3.types import (
|
||||
IntType3,
|
||||
Type3,
|
||||
TypeApplication_Struct,
|
||||
TypeApplication_TypeInt,
|
||||
TypeApplication_TypeStar,
|
||||
TypeConstructor_StaticArray,
|
||||
TypeConstructor_Tuple,
|
||||
)
|
||||
from .wasmgenerator import Generator as WasmGenerator
|
||||
|
||||
TYPE3_ASSERTION_ERROR = 'You must call phasm_type3 after calling phasm_parse before your program can be compiled'
|
||||
@ -37,7 +45,7 @@ def phasm_compile(inp: ourlang.Module) -> wasm.Module:
|
||||
"""
|
||||
return module(inp)
|
||||
|
||||
def type3(inp: type3types.Type3) -> wasm.WasmType:
|
||||
def type3(inp: Type3) -> wasm.WasmType:
|
||||
"""
|
||||
Compile: type
|
||||
|
||||
@ -98,18 +106,18 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
|
||||
"""
|
||||
assert inp.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
args: tuple[type3types.Type3, ...]
|
||||
args: tuple[Type3, ...]
|
||||
|
||||
if isinstance(inp.type3.application, type3types.TypeApplication_TypeStar):
|
||||
if isinstance(inp.type3.application, TypeApplication_TypeStar):
|
||||
# Possibly paranoid assert. If we have a future variadic type,
|
||||
# does it also do this tuple instantation like this?
|
||||
assert isinstance(inp.type3.application.constructor, type3types.TypeConstructor_Tuple)
|
||||
assert isinstance(inp.type3.application.constructor, TypeConstructor_Tuple)
|
||||
|
||||
args = inp.type3.application.arguments
|
||||
elif isinstance(inp.type3.application, type3types.TypeApplication_TypeInt):
|
||||
elif isinstance(inp.type3.application, TypeApplication_TypeInt):
|
||||
# Possibly paranoid assert. If we have a future type of kind * -> Int -> *,
|
||||
# does it also do this tuple instantation like this?
|
||||
assert isinstance(inp.type3.application.constructor, type3types.TypeConstructor_StaticArray)
|
||||
assert isinstance(inp.type3.application.constructor, TypeConstructor_StaticArray)
|
||||
|
||||
sa_type, sa_len = inp.type3.application.arguments
|
||||
|
||||
@ -162,7 +170,7 @@ def expression_subscript_bytes(
|
||||
|
||||
def expression_subscript_static_array(
|
||||
attrs: tuple[WasmGenerator, ourlang.Subscript],
|
||||
args: tuple[type3types.Type3, type3types.IntType3],
|
||||
args: tuple[Type3, IntType3],
|
||||
) -> None:
|
||||
wgn, inp = attrs
|
||||
|
||||
@ -194,7 +202,7 @@ def expression_subscript_static_array(
|
||||
|
||||
def expression_subscript_tuple(
|
||||
attrs: tuple[WasmGenerator, ourlang.Subscript],
|
||||
args: tuple[type3types.Type3, ...],
|
||||
args: tuple[Type3, ...],
|
||||
) -> None:
|
||||
wgn, inp = attrs
|
||||
|
||||
@ -292,16 +300,16 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
expression(wgn, inp.left)
|
||||
expression(wgn, inp.right)
|
||||
|
||||
type_var_map: dict[type3functions.TypeVariable, type3types.Type3] = {}
|
||||
type_var_map: dict[TypeVariable, Type3] = {}
|
||||
|
||||
for type_var, arg_expr in zip(inp.operator.signature.args, [inp.left, inp.right, inp], strict=True):
|
||||
assert arg_expr.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
if isinstance(type_var, type3types.Type3):
|
||||
if isinstance(type_var, Type3):
|
||||
# Fixed type, not part of the lookup requirements
|
||||
continue
|
||||
|
||||
if isinstance(type_var, type3functions.TypeVariable):
|
||||
if isinstance(type_var, TypeVariable):
|
||||
type_var_map[type_var] = arg_expr.type3
|
||||
continue
|
||||
|
||||
@ -315,18 +323,18 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
for arg in inp.arguments:
|
||||
expression(wgn, arg)
|
||||
|
||||
if isinstance(inp.function, type3classes.Type3ClassMethod):
|
||||
if isinstance(inp.function, Type3ClassMethod):
|
||||
# FIXME: Duplicate code with BinaryOp
|
||||
type_var_map = {}
|
||||
|
||||
for type_var, arg_expr in zip(inp.function.signature.args, inp.arguments + [inp], strict=True):
|
||||
assert arg_expr.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
if isinstance(type_var, type3types.Type3):
|
||||
if isinstance(type_var, Type3):
|
||||
# Fixed type, not part of the lookup requirements
|
||||
continue
|
||||
|
||||
if isinstance(type_var, type3functions.TypeVariable):
|
||||
if isinstance(type_var, TypeVariable):
|
||||
type_var_map[type_var] = arg_expr.type3
|
||||
continue
|
||||
|
||||
@ -356,7 +364,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
if isinstance(inp, ourlang.AccessStructMember):
|
||||
assert inp.struct_type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
assert isinstance(inp.struct_type3.application, type3types.TypeApplication_Struct)
|
||||
assert isinstance(inp.struct_type3.application, TypeApplication_Struct)
|
||||
|
||||
member_type = dict(inp.struct_type3.application.arguments)[inp.member]
|
||||
|
||||
@ -739,7 +747,7 @@ def module(inp: ourlang.Module) -> wasm.Module:
|
||||
return result
|
||||
|
||||
def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstructor) -> None:
|
||||
assert isinstance(inp.struct_type3.application, type3types.TypeApplication_Struct)
|
||||
assert isinstance(inp.struct_type3.application, TypeApplication_Struct)
|
||||
|
||||
st_args = inp.struct_type3.application.arguments
|
||||
|
||||
|
||||
@ -98,21 +98,21 @@ class ConstantStruct(ConstantMemoryStored):
|
||||
"""
|
||||
A Struct constant value expression within a statement
|
||||
"""
|
||||
__slots__ = ('struct_name', 'value', )
|
||||
__slots__ = ('struct_type3', 'value', )
|
||||
|
||||
struct_name: str
|
||||
struct_type3: Type3
|
||||
value: List[Union[ConstantPrimitive, ConstantBytes, ConstantTuple, 'ConstantStruct']]
|
||||
|
||||
def __init__(self, struct_name: str, value: List[Union[ConstantPrimitive, ConstantBytes, ConstantTuple, 'ConstantStruct']], data_block: 'ModuleDataBlock') -> None:
|
||||
def __init__(self, struct_type3: Type3, value: List[Union[ConstantPrimitive, ConstantBytes, ConstantTuple, 'ConstantStruct']], data_block: 'ModuleDataBlock') -> None:
|
||||
super().__init__(data_block)
|
||||
self.struct_name = struct_name
|
||||
self.struct_type3 = struct_type3
|
||||
self.value = value
|
||||
|
||||
def __repr__(self) -> str:
|
||||
# Do not repr the whole ModuleDataBlock
|
||||
# As this has a reference back to this constant for its data
|
||||
# which it needs to compile the data into the program
|
||||
return f'ConstantStruct({repr(self.struct_name)}, {repr(self.value)}, @{repr(self.data_block.address)})'
|
||||
return f'ConstantStruct({self.struct_type3!r}, {self.value!r}, @{self.data_block.address!r})'
|
||||
|
||||
class VariableReference(Expression):
|
||||
"""
|
||||
|
||||
@ -32,8 +32,8 @@ from .ourlang import (
|
||||
VariableReference,
|
||||
)
|
||||
from .prelude import PRELUDE_METHODS, PRELUDE_OPERATORS, PRELUDE_TYPES
|
||||
from .type3 import typeclasses as type3typeclasses
|
||||
from .type3 import types as type3types
|
||||
from .type3.typeclasses import Type3ClassMethod
|
||||
from .type3.types import IntType3, Type3
|
||||
|
||||
|
||||
def phasm_parse(source: str) -> Module:
|
||||
@ -226,7 +226,7 @@ class OurVisitor:
|
||||
_not_implemented(not node.keywords, 'ClassDef.keywords')
|
||||
_not_implemented(not node.decorator_list, 'ClassDef.decorator_list')
|
||||
|
||||
members: Dict[str, type3types.Type3] = {}
|
||||
members: Dict[str, Type3] = {}
|
||||
|
||||
for stmt in node.body:
|
||||
if not isinstance(stmt, ast.AnnAssign):
|
||||
@ -352,7 +352,7 @@ class OurVisitor:
|
||||
|
||||
def visit_Module_FunctionDef_expr(self, module: Module, function: Function, our_locals: OurLocals, node: ast.expr) -> Expression:
|
||||
if isinstance(node, ast.BinOp):
|
||||
operator: Union[str, type3typeclasses.Type3ClassMethod]
|
||||
operator: Union[str, Type3ClassMethod]
|
||||
|
||||
if isinstance(node.op, ast.Add):
|
||||
operator = '+'
|
||||
@ -471,7 +471,7 @@ class OurVisitor:
|
||||
if not isinstance(node.func.ctx, ast.Load):
|
||||
_raise_static_error(node, 'Must be load context')
|
||||
|
||||
func: Union[Function, type3typeclasses.Type3ClassMethod]
|
||||
func: Union[Function, Type3ClassMethod]
|
||||
|
||||
if node.func.id in PRELUDE_METHODS:
|
||||
func = PRELUDE_METHODS[node.func.id]
|
||||
@ -584,7 +584,8 @@ class OurVisitor:
|
||||
if not isinstance(node.func.ctx, ast.Load):
|
||||
_raise_static_error(node.func, 'Must be load context')
|
||||
|
||||
if node.func.id not in module.struct_definitions:
|
||||
struct_def = module.struct_definitions.get(node.func.id)
|
||||
if struct_def is None:
|
||||
_raise_static_error(node.func, 'Undefined struct')
|
||||
|
||||
if node.keywords:
|
||||
@ -600,7 +601,7 @@ class OurVisitor:
|
||||
|
||||
data_block = ModuleDataBlock(struct_data)
|
||||
module.data.blocks.append(data_block)
|
||||
return ConstantStruct(node.func.id, struct_data, data_block)
|
||||
return ConstantStruct(struct_def.struct_type3, struct_data, data_block)
|
||||
|
||||
_not_implemented(node.kind is None, 'Constant.kind')
|
||||
|
||||
@ -617,7 +618,7 @@ class OurVisitor:
|
||||
|
||||
raise NotImplementedError(f'{node.value} as constant')
|
||||
|
||||
def visit_type(self, module: Module, node: ast.expr) -> type3types.Type3:
|
||||
def visit_type(self, module: Module, node: ast.expr) -> Type3:
|
||||
if isinstance(node, ast.Constant):
|
||||
if node.value is None:
|
||||
return prelude.none
|
||||
@ -645,7 +646,7 @@ class OurVisitor:
|
||||
|
||||
return prelude.static_array(
|
||||
self.visit_type(module, node.value),
|
||||
type3types.IntType3(node.slice.value),
|
||||
IntType3(node.slice.value),
|
||||
)
|
||||
|
||||
if isinstance(node, ast.Tuple):
|
||||
|
||||
@ -6,9 +6,19 @@ These need to be resolved before the program can be compiled.
|
||||
from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
|
||||
|
||||
from .. import ourlang, prelude
|
||||
from . import placeholders, typeclasses, types
|
||||
from .placeholders import PlaceholderForType
|
||||
from .placeholders import PlaceholderForType, Type3OrPlaceholder
|
||||
from .routers import NoRouteForTypeException, TypeApplicationRouter
|
||||
from .typeclasses import Type3Class
|
||||
from .types import (
|
||||
IntType3,
|
||||
Type3,
|
||||
TypeApplication_Nullary,
|
||||
TypeApplication_Struct,
|
||||
TypeApplication_TypeInt,
|
||||
TypeApplication_TypeStar,
|
||||
TypeConstructor_Base,
|
||||
TypeConstructor_Struct,
|
||||
)
|
||||
|
||||
|
||||
class Error:
|
||||
@ -34,13 +44,13 @@ class RequireTypeSubstitutes:
|
||||
typing of the program, so this constraint can be updated.
|
||||
"""
|
||||
|
||||
SubstitutionMap = Dict[placeholders.PlaceholderForType, types.Type3]
|
||||
SubstitutionMap = Dict[PlaceholderForType, Type3]
|
||||
|
||||
NewConstraintList = List['ConstraintBase']
|
||||
|
||||
CheckResult = Union[None, SubstitutionMap, Error, NewConstraintList, RequireTypeSubstitutes]
|
||||
|
||||
HumanReadableRet = Tuple[str, Dict[str, Union[str, ourlang.Expression, types.Type3, placeholders.PlaceholderForType]]]
|
||||
HumanReadableRet = Tuple[str, Dict[str, Union[None, int, str, ourlang.Expression, Type3, PlaceholderForType]]]
|
||||
|
||||
class Context:
|
||||
"""
|
||||
@ -50,7 +60,7 @@ class Context:
|
||||
__slots__ = ('type_class_instances_existing', )
|
||||
|
||||
# Constraint_TypeClassInstanceExists
|
||||
type_class_instances_existing: set[tuple[typeclasses.Type3Class, tuple[Union[types.Type3, types.TypeConstructor_Base[Any], types.TypeConstructor_Struct], ...]]]
|
||||
type_class_instances_existing: set[tuple[Type3Class, tuple[Union[Type3, TypeConstructor_Base[Any], TypeConstructor_Struct], ...]]]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.type_class_instances_existing = set()
|
||||
@ -100,23 +110,23 @@ class SameTypeConstraint(ConstraintBase):
|
||||
"""
|
||||
__slots__ = ('type_list', )
|
||||
|
||||
type_list: List[placeholders.Type3OrPlaceholder]
|
||||
type_list: List[Type3OrPlaceholder]
|
||||
|
||||
def __init__(self, *type_list: placeholders.Type3OrPlaceholder, comment: Optional[str] = None) -> None:
|
||||
def __init__(self, *type_list: Type3OrPlaceholder, comment: Optional[str] = None) -> None:
|
||||
super().__init__(comment=comment)
|
||||
|
||||
assert len(type_list) > 1
|
||||
self.type_list = [*type_list]
|
||||
|
||||
def check(self) -> CheckResult:
|
||||
known_types: List[types.Type3] = []
|
||||
known_types: List[Type3] = []
|
||||
phft_list = []
|
||||
for typ in self.type_list:
|
||||
if isinstance(typ, types.Type3):
|
||||
if isinstance(typ, Type3):
|
||||
known_types.append(typ)
|
||||
continue
|
||||
|
||||
if isinstance(typ, placeholders.PlaceholderForType):
|
||||
if isinstance(typ, PlaceholderForType):
|
||||
if typ.resolve_as is not None:
|
||||
known_types.append(typ.resolve_as)
|
||||
else:
|
||||
@ -133,7 +143,7 @@ class SameTypeConstraint(ConstraintBase):
|
||||
if ktyp != first_type:
|
||||
return Error(f'{ktyp:s} must be {first_type:s} instead', comment=self.comment)
|
||||
|
||||
if not placeholders:
|
||||
if not phft_list:
|
||||
return None
|
||||
|
||||
for phft in phft_list:
|
||||
@ -177,10 +187,10 @@ class SameTypeArgumentConstraint(ConstraintBase):
|
||||
tc_typ = self.tc_var.resolve_as
|
||||
arg_typ = self.arg_var.resolve_as
|
||||
|
||||
if isinstance(tc_typ.application, types.TypeApplication_Nullary):
|
||||
if isinstance(tc_typ.application, TypeApplication_Nullary):
|
||||
return Error(f'{tc_typ:s} must be a constructed type instead')
|
||||
|
||||
if isinstance(tc_typ.application, types.TypeApplication_TypeStar):
|
||||
if isinstance(tc_typ.application, TypeApplication_TypeStar):
|
||||
# Sure, it's a constructed type. But it's like a struct,
|
||||
# though without the way to implement type classes
|
||||
# Presumably, doing a naked `foo :: t a -> a`
|
||||
@ -190,7 +200,7 @@ class SameTypeArgumentConstraint(ConstraintBase):
|
||||
|
||||
# FIXME: This feels sketchy. Shouldn't the type variable
|
||||
# have the exact same number as arguments?
|
||||
if isinstance(tc_typ.application, types.TypeApplication_TypeInt):
|
||||
if isinstance(tc_typ.application, TypeApplication_TypeInt):
|
||||
if tc_typ.application.arguments[0] == arg_typ:
|
||||
return None
|
||||
|
||||
@ -201,16 +211,16 @@ class SameTypeArgumentConstraint(ConstraintBase):
|
||||
class TupleMatchConstraint(ConstraintBase):
|
||||
__slots__ = ('exp_type', 'args', )
|
||||
|
||||
exp_type: placeholders.Type3OrPlaceholder
|
||||
args: list[placeholders.Type3OrPlaceholder]
|
||||
exp_type: Type3OrPlaceholder
|
||||
args: list[Type3OrPlaceholder]
|
||||
|
||||
def __init__(self, exp_type: placeholders.Type3OrPlaceholder, args: Iterable[placeholders.Type3OrPlaceholder], comment: str):
|
||||
def __init__(self, exp_type: Type3OrPlaceholder, args: Iterable[Type3OrPlaceholder], comment: str):
|
||||
super().__init__(comment=comment)
|
||||
|
||||
self.exp_type = exp_type
|
||||
self.args = list(args)
|
||||
|
||||
def _generate_static_array(self, sa_args: tuple[types.Type3, types.IntType3]) -> CheckResult:
|
||||
def _generate_static_array(self, sa_args: tuple[Type3, IntType3]) -> CheckResult:
|
||||
sa_type, sa_len = sa_args
|
||||
|
||||
if sa_len.value != len(self.args):
|
||||
@ -221,7 +231,7 @@ class TupleMatchConstraint(ConstraintBase):
|
||||
for arg in self.args
|
||||
]
|
||||
|
||||
def _generate_tuple(self, tp_args: tuple[types.Type3, ...]) -> CheckResult:
|
||||
def _generate_tuple(self, tp_args: tuple[Type3, ...]) -> CheckResult:
|
||||
if len(tp_args) != len(self.args):
|
||||
return Error('Mismatch between applied types argument count', comment=self.comment)
|
||||
|
||||
@ -236,7 +246,7 @@ class TupleMatchConstraint(ConstraintBase):
|
||||
|
||||
def check(self) -> CheckResult:
|
||||
exp_type = self.exp_type
|
||||
if isinstance(exp_type, placeholders.PlaceholderForType):
|
||||
if isinstance(exp_type, PlaceholderForType):
|
||||
if exp_type.resolve_as is None:
|
||||
return RequireTypeSubstitutes()
|
||||
|
||||
@ -254,34 +264,34 @@ class MustImplementTypeClassConstraint(ConstraintBase):
|
||||
__slots__ = ('context', 'type_class3', 'types', )
|
||||
|
||||
context: Context
|
||||
type_class3: Union[str, typeclasses.Type3Class]
|
||||
types: list[placeholders.Type3OrPlaceholder]
|
||||
type_class3: Union[str, Type3Class]
|
||||
types: list[Type3OrPlaceholder]
|
||||
|
||||
DATA = {
|
||||
'bytes': {'Foldable'},
|
||||
}
|
||||
|
||||
def __init__(self, context: Context, type_class3: Union[str, typeclasses.Type3Class], types: list[placeholders.Type3OrPlaceholder], comment: Optional[str] = None) -> None:
|
||||
def __init__(self, context: Context, type_class3: Union[str, Type3Class], typ_list: list[Type3OrPlaceholder], comment: Optional[str] = None) -> None:
|
||||
super().__init__(comment=comment)
|
||||
|
||||
self.context = context
|
||||
self.type_class3 = type_class3
|
||||
self.types = types
|
||||
self.types = typ_list
|
||||
|
||||
def check(self) -> CheckResult:
|
||||
typ_list: list[types.Type3 | types.TypeConstructor_Base[Any] | types.TypeConstructor_Struct] = []
|
||||
typ_list: list[Type3 | TypeConstructor_Base[Any] | TypeConstructor_Struct] = []
|
||||
for typ in self.types:
|
||||
if isinstance(typ, placeholders.PlaceholderForType) and typ.resolve_as is not None:
|
||||
if isinstance(typ, PlaceholderForType) and typ.resolve_as is not None:
|
||||
typ = typ.resolve_as
|
||||
|
||||
if isinstance(typ, placeholders.PlaceholderForType):
|
||||
if isinstance(typ, PlaceholderForType):
|
||||
return RequireTypeSubstitutes()
|
||||
|
||||
if isinstance(typ.application, (types.TypeApplication_Nullary, types.TypeApplication_Struct, )):
|
||||
if isinstance(typ.application, (TypeApplication_Nullary, TypeApplication_Struct, )):
|
||||
typ_list.append(typ)
|
||||
continue
|
||||
|
||||
if isinstance(typ.application, (types.TypeApplication_TypeInt, types.TypeApplication_TypeStar)):
|
||||
if isinstance(typ.application, (TypeApplication_TypeInt, TypeApplication_TypeStar)):
|
||||
typ_list.append(typ.application.constructor)
|
||||
continue
|
||||
|
||||
@ -289,7 +299,7 @@ class MustImplementTypeClassConstraint(ConstraintBase):
|
||||
|
||||
assert len(typ_list) == len(self.types)
|
||||
|
||||
if isinstance(self.type_class3, typeclasses.Type3Class):
|
||||
if isinstance(self.type_class3, Type3Class):
|
||||
key = (self.type_class3, tuple(typ_list), )
|
||||
if key in self.context.type_class_instances_existing:
|
||||
return None
|
||||
@ -324,12 +334,12 @@ class LiteralFitsConstraint(ConstraintBase):
|
||||
"""
|
||||
__slots__ = ('type3', 'literal', )
|
||||
|
||||
type3: placeholders.Type3OrPlaceholder
|
||||
type3: Type3OrPlaceholder
|
||||
literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
type3: placeholders.Type3OrPlaceholder,
|
||||
type3: Type3OrPlaceholder,
|
||||
literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct],
|
||||
comment: Optional[str] = None,
|
||||
) -> None:
|
||||
@ -338,7 +348,7 @@ class LiteralFitsConstraint(ConstraintBase):
|
||||
self.type3 = type3
|
||||
self.literal = literal
|
||||
|
||||
def _generate_static_array(self, sa_args: tuple[types.Type3, types.IntType3]) -> CheckResult:
|
||||
def _generate_static_array(self, sa_args: tuple[Type3, IntType3]) -> CheckResult:
|
||||
if not isinstance(self.literal, ourlang.ConstantTuple):
|
||||
return Error('Must be tuple', comment=self.comment)
|
||||
|
||||
@ -364,7 +374,7 @@ class LiteralFitsConstraint(ConstraintBase):
|
||||
|
||||
return res
|
||||
|
||||
def _generate_struct(self, st_args: tuple[tuple[str, types.Type3], ...]) -> CheckResult:
|
||||
def _generate_struct(self, st_args: tuple[tuple[str, Type3], ...]) -> CheckResult:
|
||||
if not isinstance(self.literal, ourlang.ConstantStruct):
|
||||
return Error('Must be struct')
|
||||
|
||||
@ -382,13 +392,19 @@ class LiteralFitsConstraint(ConstraintBase):
|
||||
# gets updated when we figure out the type of the
|
||||
# expression the literal is used in
|
||||
res.extend(
|
||||
SameTypeConstraint(x_t, PlaceholderForType([y]), comment=f'{self.literal.struct_name}.{x_n}')
|
||||
SameTypeConstraint(x_t, PlaceholderForType([y]), comment=f'{self.literal.struct_type3.name}.{x_n}')
|
||||
for (x_n, x_t, ), y in zip(st_args, self.literal.value, strict=True)
|
||||
)
|
||||
|
||||
res.append(SameTypeConstraint(
|
||||
self.literal.struct_type3,
|
||||
self.type3,
|
||||
comment='Struct types must match',
|
||||
))
|
||||
|
||||
return res
|
||||
|
||||
def _generate_tuple(self, tp_args: tuple[types.Type3, ...]) -> CheckResult:
|
||||
def _generate_tuple(self, tp_args: tuple[Type3, ...]) -> CheckResult:
|
||||
if not isinstance(self.literal, ourlang.ConstantTuple):
|
||||
return Error('Must be tuple', comment=self.comment)
|
||||
|
||||
@ -432,7 +448,7 @@ class LiteralFitsConstraint(ConstraintBase):
|
||||
'f64': None,
|
||||
}
|
||||
|
||||
if isinstance(self.type3, placeholders.PlaceholderForType):
|
||||
if isinstance(self.type3, PlaceholderForType):
|
||||
if self.type3.resolve_as is None:
|
||||
return RequireTypeSubstitutes()
|
||||
|
||||
@ -490,65 +506,59 @@ class CanBeSubscriptedConstraint(ConstraintBase):
|
||||
"""
|
||||
A value that is subscipted, i.e. a[0] (tuple) or a[b] (static array)
|
||||
"""
|
||||
__slots__ = ('ret_type3', 'type3', 'index', 'index_phft', )
|
||||
__slots__ = ('ret_type3', 'type3', 'index_type3', 'index_const', )
|
||||
|
||||
ret_type3: placeholders.Type3OrPlaceholder
|
||||
type3: placeholders.Type3OrPlaceholder
|
||||
index: ourlang.Expression
|
||||
index_phft: placeholders.Type3OrPlaceholder
|
||||
ret_type3: PlaceholderForType
|
||||
type3: PlaceholderForType
|
||||
index_type3: PlaceholderForType
|
||||
index_const: int | None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
ret_type3: placeholders.PlaceholderForType,
|
||||
type3: placeholders.PlaceholderForType,
|
||||
index: ourlang.Expression,
|
||||
index_phft: placeholders.PlaceholderForType,
|
||||
ret_type3: PlaceholderForType,
|
||||
type3: PlaceholderForType,
|
||||
index_type3: PlaceholderForType,
|
||||
index_const: int | None,
|
||||
comment: Optional[str] = None,
|
||||
) -> None:
|
||||
super().__init__(comment=comment)
|
||||
|
||||
self.ret_type3 = ret_type3
|
||||
self.type3 = type3
|
||||
self.index = index
|
||||
self.index_phft = index_phft
|
||||
self.index_type3 = index_type3
|
||||
self.index_const = index_const
|
||||
|
||||
def _generate_bytes(self) -> CheckResult:
|
||||
return [
|
||||
SameTypeConstraint(prelude.u32, self.index_phft, comment='([]) :: bytes -> u32 -> u8'),
|
||||
SameTypeConstraint(prelude.u32, self.index_type3, comment='([]) :: bytes -> u32 -> u8'),
|
||||
SameTypeConstraint(prelude.u8, self.ret_type3, comment='([]) :: bytes -> u32 -> u8'),
|
||||
]
|
||||
|
||||
def _generate_static_array(self, sa_args: tuple[types.Type3, types.IntType3]) -> CheckResult:
|
||||
def _generate_static_array(self, sa_args: tuple[Type3, IntType3]) -> CheckResult:
|
||||
sa_type, sa_len = sa_args
|
||||
|
||||
if isinstance(self.index, ourlang.ConstantPrimitive):
|
||||
assert isinstance(self.index.value, int)
|
||||
|
||||
if self.index.value < 0 or sa_len.value <= self.index.value:
|
||||
if self.index_const is not None and (self.index_const < 0 or sa_len.value <= self.index_const):
|
||||
return Error('Tuple index out of range')
|
||||
|
||||
return [
|
||||
SameTypeConstraint(prelude.u32, self.index_phft, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
|
||||
SameTypeConstraint(prelude.u32, self.index_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
|
||||
SameTypeConstraint(sa_type, self.ret_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
|
||||
]
|
||||
|
||||
def _generate_tuple(self, tp_args: tuple[types.Type3, ...]) -> CheckResult:
|
||||
def _generate_tuple(self, tp_args: tuple[Type3, ...]) -> CheckResult:
|
||||
# We special case tuples to allow for ease of use to the programmer
|
||||
# e.g. rather than having to do `fst a` and `snd a` and only have to-sized tuples
|
||||
# we use a[0] and a[1] and allow for a[2] and on.
|
||||
|
||||
if not isinstance(self.index, ourlang.ConstantPrimitive):
|
||||
return Error('Must index with literal')
|
||||
|
||||
if not isinstance(self.index.value, int):
|
||||
if self.index_const is None:
|
||||
return Error('Must index with integer literal')
|
||||
|
||||
if self.index.value < 0 or len(tp_args) <= self.index.value:
|
||||
if self.index_const < 0 or len(tp_args) <= self.index_const:
|
||||
return Error('Tuple index out of range')
|
||||
|
||||
return [
|
||||
SameTypeConstraint(prelude.u32, self.index_phft, comment=f'Tuple subscript index {self.index.value}'),
|
||||
SameTypeConstraint(tp_args[self.index.value], self.ret_type3, comment=f'Tuple subscript index {self.index.value}'),
|
||||
SameTypeConstraint(prelude.u32, self.index_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
|
||||
SameTypeConstraint(tp_args[self.index_const], self.ret_type3, comment=f'Tuple subscript index {self.index_const}'),
|
||||
]
|
||||
|
||||
GENERATE_ROUTER = TypeApplicationRouter['CanBeSubscriptedConstraint', CheckResult]()
|
||||
@ -557,12 +567,10 @@ class CanBeSubscriptedConstraint(ConstraintBase):
|
||||
GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple)
|
||||
|
||||
def check(self) -> CheckResult:
|
||||
exp_type = self.type3
|
||||
if isinstance(exp_type, placeholders.PlaceholderForType):
|
||||
if exp_type.resolve_as is None:
|
||||
if self.type3.resolve_as is None:
|
||||
return RequireTypeSubstitutes()
|
||||
|
||||
exp_type = exp_type.resolve_as
|
||||
exp_type = self.type3.resolve_as
|
||||
|
||||
try:
|
||||
return self.__class__.GENERATE_ROUTER(self, exp_type)
|
||||
@ -574,9 +582,9 @@ class CanBeSubscriptedConstraint(ConstraintBase):
|
||||
'{type3}[{index}]',
|
||||
{
|
||||
'type3': self.type3,
|
||||
'index': self.index,
|
||||
'index': self.index_type3 if self.index_const is None else self.index_const,
|
||||
},
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'CanBeSubscriptedConstraint({repr(self.type3)}, {repr(self.index)}, comment={repr(self.comment)})'
|
||||
return f'CanBeSubscriptedConstraint({self.ret_type3!r}, {self.type3!r}, {self.index_type3!r}, {self.index_const!r}, comment={repr(self.comment)})'
|
||||
|
||||
@ -6,10 +6,6 @@ The constraints solver can then try to resolve all constraints.
|
||||
from typing import Generator, List
|
||||
|
||||
from .. import ourlang, prelude
|
||||
from . import functions as functions
|
||||
from . import placeholders as placeholders
|
||||
from . import typeclasses as typeclasses
|
||||
from . import types as type3types
|
||||
from .constraints import (
|
||||
CanBeSubscriptedConstraint,
|
||||
ConstraintBase,
|
||||
@ -20,7 +16,14 @@ from .constraints import (
|
||||
SameTypeConstraint,
|
||||
TupleMatchConstraint,
|
||||
)
|
||||
from .functions import (
|
||||
Constraint_TypeClassInstanceExists,
|
||||
FunctionSignature,
|
||||
TypeVariable,
|
||||
TypeVariableApplication_Unary,
|
||||
)
|
||||
from .placeholders import PlaceholderForType
|
||||
from .types import Type3, TypeApplication_Struct
|
||||
|
||||
ConstraintGenerator = Generator[ConstraintBase, None, None]
|
||||
|
||||
@ -30,7 +33,7 @@ def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase
|
||||
|
||||
return [*module(ctx, inp)]
|
||||
|
||||
def constant(ctx: Context, inp: ourlang.Constant, phft: placeholders.PlaceholderForType) -> ConstraintGenerator:
|
||||
def constant(ctx: Context, inp: ourlang.Constant, phft: PlaceholderForType) -> ConstraintGenerator:
|
||||
if isinstance(inp, (ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct)):
|
||||
yield LiteralFitsConstraint(
|
||||
phft, inp,
|
||||
@ -63,7 +66,7 @@ def expression_function_call(ctx: Context, inp: ourlang.FunctionCall, phft: Plac
|
||||
def _expression_function_call(
|
||||
ctx: Context,
|
||||
func_name: str,
|
||||
signature: functions.FunctionSignature,
|
||||
signature: FunctionSignature,
|
||||
arguments: list[ourlang.Expression],
|
||||
return_expr: ourlang.Expression,
|
||||
return_phft: PlaceholderForType,
|
||||
@ -92,13 +95,13 @@ def _expression_function_call(
|
||||
# placeholder here. These don't need to update anything once
|
||||
# subsituted - that's done by arg_placeholders.
|
||||
type_var_map = {
|
||||
x: placeholders.PlaceholderForType([])
|
||||
x: PlaceholderForType([])
|
||||
for x in signature.args
|
||||
if isinstance(x, functions.TypeVariable)
|
||||
if isinstance(x, TypeVariable)
|
||||
}
|
||||
|
||||
for constraint in signature.context.constraints:
|
||||
if isinstance(constraint, functions.Constraint_TypeClassInstanceExists):
|
||||
if isinstance(constraint, Constraint_TypeClassInstanceExists):
|
||||
yield MustImplementTypeClassConstraint(
|
||||
ctx,
|
||||
constraint.type_class3,
|
||||
@ -113,7 +116,7 @@ def _expression_function_call(
|
||||
# That is, given `foo :: t a -> a` we need to ensure
|
||||
# that both a's are the same.
|
||||
for sig_arg in signature.args:
|
||||
if isinstance(sig_arg, type3types.Type3):
|
||||
if isinstance(sig_arg, Type3):
|
||||
# Not a type variable at all
|
||||
continue
|
||||
|
||||
@ -121,7 +124,7 @@ def _expression_function_call(
|
||||
# Not a type variable for a type constructor
|
||||
continue
|
||||
|
||||
if not isinstance(sig_arg.application, functions.TypeVariableApplication_Unary):
|
||||
if not isinstance(sig_arg.application, TypeVariableApplication_Unary):
|
||||
raise NotImplementedError(sig_arg.application)
|
||||
|
||||
assert sig_arg.application.arguments in type_var_map # When does this happen?
|
||||
@ -139,18 +142,18 @@ def _expression_function_call(
|
||||
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):
|
||||
if isinstance(sig_part, TypeVariable):
|
||||
yield SameTypeConstraint(type_var_map[sig_part], arg_placeholders[arg_expr], comment=comment)
|
||||
continue
|
||||
|
||||
if isinstance(sig_part, type3types.Type3):
|
||||
if isinstance(sig_part, Type3):
|
||||
yield SameTypeConstraint(sig_part, arg_placeholders[arg_expr], comment=comment)
|
||||
continue
|
||||
|
||||
raise NotImplementedError(sig_part)
|
||||
return
|
||||
|
||||
def expression(ctx: Context, inp: ourlang.Expression, phft: placeholders.PlaceholderForType) -> ConstraintGenerator:
|
||||
def expression(ctx: Context, inp: ourlang.Expression, phft: PlaceholderForType) -> ConstraintGenerator:
|
||||
if isinstance(inp, ourlang.Constant):
|
||||
yield from constant(ctx, inp, phft)
|
||||
return
|
||||
@ -190,11 +193,14 @@ def expression(ctx: Context, inp: ourlang.Expression, phft: placeholders.Placeho
|
||||
yield from expression(ctx, inp.varref, varref_phft)
|
||||
yield from expression(ctx, inp.index, index_phft)
|
||||
|
||||
yield CanBeSubscriptedConstraint(phft, varref_phft, inp.index, index_phft)
|
||||
if isinstance(inp.index, ourlang.ConstantPrimitive) and isinstance(inp.index.value, int):
|
||||
yield CanBeSubscriptedConstraint(phft, varref_phft, index_phft, inp.index.value)
|
||||
else:
|
||||
yield CanBeSubscriptedConstraint(phft, varref_phft, index_phft, None)
|
||||
return
|
||||
|
||||
if isinstance(inp, ourlang.AccessStructMember):
|
||||
assert isinstance(inp.struct_type3.application, type3types.TypeApplication_Struct) # FIXME: See test_struct.py::test_struct_not_accessible
|
||||
assert isinstance(inp.struct_type3.application, TypeApplication_Struct) # FIXME: See test_struct.py::test_struct_not_accessible
|
||||
|
||||
mem_typ = dict(inp.struct_type3.application.arguments)[inp.member]
|
||||
|
||||
|
||||
@ -64,6 +64,36 @@ def helper(shape1: Rectangle, shape2: Rectangle) -> i32:
|
||||
|
||||
assert 545 == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_type_mismatch_struct_call_root():
|
||||
code_py = """
|
||||
class CheckedValueBlue:
|
||||
value: i32
|
||||
|
||||
class CheckedValueRed:
|
||||
value: i32
|
||||
|
||||
CONST: CheckedValueBlue = CheckedValueRed(1)
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='CheckedValueBlue must be CheckedValueRed instead'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_type_mismatch_struct_call_nested():
|
||||
code_py = """
|
||||
class CheckedValueBlue:
|
||||
value: i32
|
||||
|
||||
class CheckedValueRed:
|
||||
value: i32
|
||||
|
||||
CONST: (CheckedValueBlue, u32, ) = (CheckedValueRed(1), 16, )
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='CheckedValueBlue must be CheckedValueRed instead'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', ['i32', 'i64', 'f32', 'f64'])
|
||||
def test_type_mismatch_struct_member(type_):
|
||||
|
||||
@ -64,7 +64,7 @@ def testEntry(x: (u8, u32, u64), y: u8) -> u64:
|
||||
return x[y]
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='Must index with literal'):
|
||||
with pytest.raises(Type3Exception, match='Must index with integer literal'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user