Compare commits

..

No commits in common. "ac4b46bbe72b2dd5c2cccf3103792123e3382db4" and "b5f0fda13374da11e7c1f88e9bf5cf664adf65ef" have entirely different histories.

8 changed files with 122 additions and 175 deletions

View File

@ -63,7 +63,7 @@ def expression(inp: ourlang.Expression) -> str:
) + ', )'
if isinstance(inp, ourlang.ConstantStruct):
return inp.struct_type3.name + '(' + ', '.join(
return inp.struct_name + '(' + ', '.join(
expression(x)
for x in inp.value
) + ')'

View File

@ -8,18 +8,10 @@ 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.functions import TypeVariable
from .type3 import functions as type3functions
from .type3 import typeclasses as type3classes
from .type3 import types as type3types
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'
@ -45,7 +37,7 @@ def phasm_compile(inp: ourlang.Module) -> wasm.Module:
"""
return module(inp)
def type3(inp: Type3) -> wasm.WasmType:
def type3(inp: type3types.Type3) -> wasm.WasmType:
"""
Compile: type
@ -106,18 +98,18 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
"""
assert inp.type3 is not None, TYPE3_ASSERTION_ERROR
args: tuple[Type3, ...]
args: tuple[type3types.Type3, ...]
if isinstance(inp.type3.application, TypeApplication_TypeStar):
if isinstance(inp.type3.application, type3types.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, TypeConstructor_Tuple)
assert isinstance(inp.type3.application.constructor, type3types.TypeConstructor_Tuple)
args = inp.type3.application.arguments
elif isinstance(inp.type3.application, TypeApplication_TypeInt):
elif isinstance(inp.type3.application, type3types.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, TypeConstructor_StaticArray)
assert isinstance(inp.type3.application.constructor, type3types.TypeConstructor_StaticArray)
sa_type, sa_len = inp.type3.application.arguments
@ -170,7 +162,7 @@ def expression_subscript_bytes(
def expression_subscript_static_array(
attrs: tuple[WasmGenerator, ourlang.Subscript],
args: tuple[Type3, IntType3],
args: tuple[type3types.Type3, type3types.IntType3],
) -> None:
wgn, inp = attrs
@ -202,7 +194,7 @@ def expression_subscript_static_array(
def expression_subscript_tuple(
attrs: tuple[WasmGenerator, ourlang.Subscript],
args: tuple[Type3, ...],
args: tuple[type3types.Type3, ...],
) -> None:
wgn, inp = attrs
@ -300,16 +292,16 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
expression(wgn, inp.left)
expression(wgn, inp.right)
type_var_map: dict[TypeVariable, Type3] = {}
type_var_map: dict[type3functions.TypeVariable, type3types.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, Type3):
if isinstance(type_var, type3types.Type3):
# Fixed type, not part of the lookup requirements
continue
if isinstance(type_var, TypeVariable):
if isinstance(type_var, type3functions.TypeVariable):
type_var_map[type_var] = arg_expr.type3
continue
@ -323,18 +315,18 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
for arg in inp.arguments:
expression(wgn, arg)
if isinstance(inp.function, Type3ClassMethod):
if isinstance(inp.function, type3classes.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, Type3):
if isinstance(type_var, type3types.Type3):
# Fixed type, not part of the lookup requirements
continue
if isinstance(type_var, TypeVariable):
if isinstance(type_var, type3functions.TypeVariable):
type_var_map[type_var] = arg_expr.type3
continue
@ -364,7 +356,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, TypeApplication_Struct)
assert isinstance(inp.struct_type3.application, type3types.TypeApplication_Struct)
member_type = dict(inp.struct_type3.application.arguments)[inp.member]
@ -747,7 +739,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, TypeApplication_Struct)
assert isinstance(inp.struct_type3.application, type3types.TypeApplication_Struct)
st_args = inp.struct_type3.application.arguments

View File

@ -98,21 +98,21 @@ class ConstantStruct(ConstantMemoryStored):
"""
A Struct constant value expression within a statement
"""
__slots__ = ('struct_type3', 'value', )
__slots__ = ('struct_name', 'value', )
struct_type3: Type3
struct_name: str
value: List[Union[ConstantPrimitive, ConstantBytes, ConstantTuple, 'ConstantStruct']]
def __init__(self, struct_type3: Type3, value: List[Union[ConstantPrimitive, ConstantBytes, ConstantTuple, 'ConstantStruct']], data_block: 'ModuleDataBlock') -> None:
def __init__(self, struct_name: str, value: List[Union[ConstantPrimitive, ConstantBytes, ConstantTuple, 'ConstantStruct']], data_block: 'ModuleDataBlock') -> None:
super().__init__(data_block)
self.struct_type3 = struct_type3
self.struct_name = struct_name
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({self.struct_type3!r}, {self.value!r}, @{self.data_block.address!r})'
return f'ConstantStruct({repr(self.struct_name)}, {repr(self.value)}, @{repr(self.data_block.address)})'
class VariableReference(Expression):
"""

View File

@ -32,8 +32,8 @@ from .ourlang import (
VariableReference,
)
from .prelude import PRELUDE_METHODS, PRELUDE_OPERATORS, PRELUDE_TYPES
from .type3.typeclasses import Type3ClassMethod
from .type3.types import IntType3, Type3
from .type3 import typeclasses as type3typeclasses
from .type3 import types as type3types
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, Type3] = {}
members: Dict[str, type3types.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, Type3ClassMethod]
operator: Union[str, type3typeclasses.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, Type3ClassMethod]
func: Union[Function, type3typeclasses.Type3ClassMethod]
if node.func.id in PRELUDE_METHODS:
func = PRELUDE_METHODS[node.func.id]
@ -584,8 +584,7 @@ class OurVisitor:
if not isinstance(node.func.ctx, ast.Load):
_raise_static_error(node.func, 'Must be load context')
struct_def = module.struct_definitions.get(node.func.id)
if struct_def is None:
if node.func.id not in module.struct_definitions:
_raise_static_error(node.func, 'Undefined struct')
if node.keywords:
@ -601,7 +600,7 @@ class OurVisitor:
data_block = ModuleDataBlock(struct_data)
module.data.blocks.append(data_block)
return ConstantStruct(struct_def.struct_type3, struct_data, data_block)
return ConstantStruct(node.func.id, struct_data, data_block)
_not_implemented(node.kind is None, 'Constant.kind')
@ -618,7 +617,7 @@ class OurVisitor:
raise NotImplementedError(f'{node.value} as constant')
def visit_type(self, module: Module, node: ast.expr) -> Type3:
def visit_type(self, module: Module, node: ast.expr) -> type3types.Type3:
if isinstance(node, ast.Constant):
if node.value is None:
return prelude.none
@ -646,7 +645,7 @@ class OurVisitor:
return prelude.static_array(
self.visit_type(module, node.value),
IntType3(node.slice.value),
type3types.IntType3(node.slice.value),
)
if isinstance(node, ast.Tuple):

View File

@ -6,19 +6,9 @@ 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 .placeholders import PlaceholderForType, Type3OrPlaceholder
from . import placeholders, typeclasses, types
from .placeholders import PlaceholderForType
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:
@ -44,13 +34,13 @@ class RequireTypeSubstitutes:
typing of the program, so this constraint can be updated.
"""
SubstitutionMap = Dict[PlaceholderForType, Type3]
SubstitutionMap = Dict[placeholders.PlaceholderForType, types.Type3]
NewConstraintList = List['ConstraintBase']
CheckResult = Union[None, SubstitutionMap, Error, NewConstraintList, RequireTypeSubstitutes]
HumanReadableRet = Tuple[str, Dict[str, Union[None, int, str, ourlang.Expression, Type3, PlaceholderForType]]]
HumanReadableRet = Tuple[str, Dict[str, Union[str, ourlang.Expression, types.Type3, placeholders.PlaceholderForType]]]
class Context:
"""
@ -60,7 +50,7 @@ class Context:
__slots__ = ('type_class_instances_existing', )
# Constraint_TypeClassInstanceExists
type_class_instances_existing: set[tuple[Type3Class, tuple[Union[Type3, TypeConstructor_Base[Any], TypeConstructor_Struct], ...]]]
type_class_instances_existing: set[tuple[typeclasses.Type3Class, tuple[Union[types.Type3, types.TypeConstructor_Base[Any], types.TypeConstructor_Struct], ...]]]
def __init__(self) -> None:
self.type_class_instances_existing = set()
@ -110,23 +100,23 @@ class SameTypeConstraint(ConstraintBase):
"""
__slots__ = ('type_list', )
type_list: List[Type3OrPlaceholder]
type_list: List[placeholders.Type3OrPlaceholder]
def __init__(self, *type_list: Type3OrPlaceholder, comment: Optional[str] = None) -> None:
def __init__(self, *type_list: placeholders.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[Type3] = []
known_types: List[types.Type3] = []
phft_list = []
for typ in self.type_list:
if isinstance(typ, Type3):
if isinstance(typ, types.Type3):
known_types.append(typ)
continue
if isinstance(typ, PlaceholderForType):
if isinstance(typ, placeholders.PlaceholderForType):
if typ.resolve_as is not None:
known_types.append(typ.resolve_as)
else:
@ -143,7 +133,7 @@ class SameTypeConstraint(ConstraintBase):
if ktyp != first_type:
return Error(f'{ktyp:s} must be {first_type:s} instead', comment=self.comment)
if not phft_list:
if not placeholders:
return None
for phft in phft_list:
@ -187,10 +177,10 @@ class SameTypeArgumentConstraint(ConstraintBase):
tc_typ = self.tc_var.resolve_as
arg_typ = self.arg_var.resolve_as
if isinstance(tc_typ.application, TypeApplication_Nullary):
if isinstance(tc_typ.application, types.TypeApplication_Nullary):
return Error(f'{tc_typ:s} must be a constructed type instead')
if isinstance(tc_typ.application, TypeApplication_TypeStar):
if isinstance(tc_typ.application, types.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`
@ -200,7 +190,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, TypeApplication_TypeInt):
if isinstance(tc_typ.application, types.TypeApplication_TypeInt):
if tc_typ.application.arguments[0] == arg_typ:
return None
@ -211,16 +201,16 @@ class SameTypeArgumentConstraint(ConstraintBase):
class TupleMatchConstraint(ConstraintBase):
__slots__ = ('exp_type', 'args', )
exp_type: Type3OrPlaceholder
args: list[Type3OrPlaceholder]
exp_type: placeholders.Type3OrPlaceholder
args: list[placeholders.Type3OrPlaceholder]
def __init__(self, exp_type: Type3OrPlaceholder, args: Iterable[Type3OrPlaceholder], comment: str):
def __init__(self, exp_type: placeholders.Type3OrPlaceholder, args: Iterable[placeholders.Type3OrPlaceholder], comment: str):
super().__init__(comment=comment)
self.exp_type = exp_type
self.args = list(args)
def _generate_static_array(self, sa_args: tuple[Type3, IntType3]) -> CheckResult:
def _generate_static_array(self, sa_args: tuple[types.Type3, types.IntType3]) -> CheckResult:
sa_type, sa_len = sa_args
if sa_len.value != len(self.args):
@ -231,7 +221,7 @@ class TupleMatchConstraint(ConstraintBase):
for arg in self.args
]
def _generate_tuple(self, tp_args: tuple[Type3, ...]) -> CheckResult:
def _generate_tuple(self, tp_args: tuple[types.Type3, ...]) -> CheckResult:
if len(tp_args) != len(self.args):
return Error('Mismatch between applied types argument count', comment=self.comment)
@ -246,7 +236,7 @@ class TupleMatchConstraint(ConstraintBase):
def check(self) -> CheckResult:
exp_type = self.exp_type
if isinstance(exp_type, PlaceholderForType):
if isinstance(exp_type, placeholders.PlaceholderForType):
if exp_type.resolve_as is None:
return RequireTypeSubstitutes()
@ -264,34 +254,34 @@ class MustImplementTypeClassConstraint(ConstraintBase):
__slots__ = ('context', 'type_class3', 'types', )
context: Context
type_class3: Union[str, Type3Class]
types: list[Type3OrPlaceholder]
type_class3: Union[str, typeclasses.Type3Class]
types: list[placeholders.Type3OrPlaceholder]
DATA = {
'bytes': {'Foldable'},
}
def __init__(self, context: Context, type_class3: Union[str, Type3Class], typ_list: list[Type3OrPlaceholder], comment: Optional[str] = None) -> None:
def __init__(self, context: Context, type_class3: Union[str, typeclasses.Type3Class], types: list[placeholders.Type3OrPlaceholder], comment: Optional[str] = None) -> None:
super().__init__(comment=comment)
self.context = context
self.type_class3 = type_class3
self.types = typ_list
self.types = types
def check(self) -> CheckResult:
typ_list: list[Type3 | TypeConstructor_Base[Any] | TypeConstructor_Struct] = []
typ_list: list[types.Type3 | types.TypeConstructor_Base[Any] | types.TypeConstructor_Struct] = []
for typ in self.types:
if isinstance(typ, PlaceholderForType) and typ.resolve_as is not None:
if isinstance(typ, placeholders.PlaceholderForType) and typ.resolve_as is not None:
typ = typ.resolve_as
if isinstance(typ, PlaceholderForType):
if isinstance(typ, placeholders.PlaceholderForType):
return RequireTypeSubstitutes()
if isinstance(typ.application, (TypeApplication_Nullary, TypeApplication_Struct, )):
if isinstance(typ.application, (types.TypeApplication_Nullary, types.TypeApplication_Struct, )):
typ_list.append(typ)
continue
if isinstance(typ.application, (TypeApplication_TypeInt, TypeApplication_TypeStar)):
if isinstance(typ.application, (types.TypeApplication_TypeInt, types.TypeApplication_TypeStar)):
typ_list.append(typ.application.constructor)
continue
@ -299,7 +289,7 @@ class MustImplementTypeClassConstraint(ConstraintBase):
assert len(typ_list) == len(self.types)
if isinstance(self.type_class3, Type3Class):
if isinstance(self.type_class3, typeclasses.Type3Class):
key = (self.type_class3, tuple(typ_list), )
if key in self.context.type_class_instances_existing:
return None
@ -334,12 +324,12 @@ class LiteralFitsConstraint(ConstraintBase):
"""
__slots__ = ('type3', 'literal', )
type3: Type3OrPlaceholder
type3: placeholders.Type3OrPlaceholder
literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct]
def __init__(
self,
type3: Type3OrPlaceholder,
type3: placeholders.Type3OrPlaceholder,
literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct],
comment: Optional[str] = None,
) -> None:
@ -348,7 +338,7 @@ class LiteralFitsConstraint(ConstraintBase):
self.type3 = type3
self.literal = literal
def _generate_static_array(self, sa_args: tuple[Type3, IntType3]) -> CheckResult:
def _generate_static_array(self, sa_args: tuple[types.Type3, types.IntType3]) -> CheckResult:
if not isinstance(self.literal, ourlang.ConstantTuple):
return Error('Must be tuple', comment=self.comment)
@ -374,7 +364,7 @@ class LiteralFitsConstraint(ConstraintBase):
return res
def _generate_struct(self, st_args: tuple[tuple[str, Type3], ...]) -> CheckResult:
def _generate_struct(self, st_args: tuple[tuple[str, types.Type3], ...]) -> CheckResult:
if not isinstance(self.literal, ourlang.ConstantStruct):
return Error('Must be struct')
@ -392,19 +382,13 @@ 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_type3.name}.{x_n}')
SameTypeConstraint(x_t, PlaceholderForType([y]), comment=f'{self.literal.struct_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[Type3, ...]) -> CheckResult:
def _generate_tuple(self, tp_args: tuple[types.Type3, ...]) -> CheckResult:
if not isinstance(self.literal, ourlang.ConstantTuple):
return Error('Must be tuple', comment=self.comment)
@ -448,7 +432,7 @@ class LiteralFitsConstraint(ConstraintBase):
'f64': None,
}
if isinstance(self.type3, PlaceholderForType):
if isinstance(self.type3, placeholders.PlaceholderForType):
if self.type3.resolve_as is None:
return RequireTypeSubstitutes()
@ -506,59 +490,65 @@ class CanBeSubscriptedConstraint(ConstraintBase):
"""
A value that is subscipted, i.e. a[0] (tuple) or a[b] (static array)
"""
__slots__ = ('ret_type3', 'type3', 'index_type3', 'index_const', )
__slots__ = ('ret_type3', 'type3', 'index', 'index_phft', )
ret_type3: PlaceholderForType
type3: PlaceholderForType
index_type3: PlaceholderForType
index_const: int | None
ret_type3: placeholders.Type3OrPlaceholder
type3: placeholders.Type3OrPlaceholder
index: ourlang.Expression
index_phft: placeholders.Type3OrPlaceholder
def __init__(
self,
ret_type3: PlaceholderForType,
type3: PlaceholderForType,
index_type3: PlaceholderForType,
index_const: int | None,
ret_type3: placeholders.PlaceholderForType,
type3: placeholders.PlaceholderForType,
index: ourlang.Expression,
index_phft: placeholders.PlaceholderForType,
comment: Optional[str] = None,
) -> None:
super().__init__(comment=comment)
self.ret_type3 = ret_type3
self.type3 = type3
self.index_type3 = index_type3
self.index_const = index_const
self.index = index
self.index_phft = index_phft
def _generate_bytes(self) -> CheckResult:
return [
SameTypeConstraint(prelude.u32, self.index_type3, comment='([]) :: bytes -> u32 -> u8'),
SameTypeConstraint(prelude.u32, self.index_phft, comment='([]) :: bytes -> u32 -> u8'),
SameTypeConstraint(prelude.u8, self.ret_type3, comment='([]) :: bytes -> u32 -> u8'),
]
def _generate_static_array(self, sa_args: tuple[Type3, IntType3]) -> CheckResult:
def _generate_static_array(self, sa_args: tuple[types.Type3, types.IntType3]) -> CheckResult:
sa_type, sa_len = sa_args
if self.index_const is not None and (self.index_const < 0 or sa_len.value <= self.index_const):
if isinstance(self.index, ourlang.ConstantPrimitive):
assert isinstance(self.index.value, int)
if self.index.value < 0 or sa_len.value <= self.index.value:
return Error('Tuple index out of range')
return [
SameTypeConstraint(prelude.u32, self.index_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
SameTypeConstraint(prelude.u32, self.index_phft, 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[Type3, ...]) -> CheckResult:
def _generate_tuple(self, tp_args: tuple[types.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 self.index_const is None:
if not isinstance(self.index, ourlang.ConstantPrimitive):
return Error('Must index with literal')
if not isinstance(self.index.value, int):
return Error('Must index with integer literal')
if self.index_const < 0 or len(tp_args) <= self.index_const:
if self.index.value < 0 or len(tp_args) <= self.index.value:
return Error('Tuple index out of range')
return [
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}'),
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}'),
]
GENERATE_ROUTER = TypeApplicationRouter['CanBeSubscriptedConstraint', CheckResult]()
@ -567,10 +557,12 @@ class CanBeSubscriptedConstraint(ConstraintBase):
GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple)
def check(self) -> CheckResult:
if self.type3.resolve_as is None:
exp_type = self.type3
if isinstance(exp_type, placeholders.PlaceholderForType):
if exp_type.resolve_as is None:
return RequireTypeSubstitutes()
exp_type = self.type3.resolve_as
exp_type = exp_type.resolve_as
try:
return self.__class__.GENERATE_ROUTER(self, exp_type)
@ -582,9 +574,9 @@ class CanBeSubscriptedConstraint(ConstraintBase):
'{type3}[{index}]',
{
'type3': self.type3,
'index': self.index_type3 if self.index_const is None else self.index_const,
'index': self.index,
},
)
def __repr__(self) -> str:
return f'CanBeSubscriptedConstraint({self.ret_type3!r}, {self.type3!r}, {self.index_type3!r}, {self.index_const!r}, comment={repr(self.comment)})'
return f'CanBeSubscriptedConstraint({repr(self.type3)}, {repr(self.index)}, comment={repr(self.comment)})'

View File

@ -6,6 +6,10 @@ 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,
@ -16,14 +20,7 @@ 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]
@ -33,7 +30,7 @@ def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase
return [*module(ctx, inp)]
def constant(ctx: Context, inp: ourlang.Constant, phft: PlaceholderForType) -> ConstraintGenerator:
def constant(ctx: Context, inp: ourlang.Constant, phft: placeholders.PlaceholderForType) -> ConstraintGenerator:
if isinstance(inp, (ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct)):
yield LiteralFitsConstraint(
phft, inp,
@ -66,7 +63,7 @@ def expression_function_call(ctx: Context, inp: ourlang.FunctionCall, phft: Plac
def _expression_function_call(
ctx: Context,
func_name: str,
signature: FunctionSignature,
signature: functions.FunctionSignature,
arguments: list[ourlang.Expression],
return_expr: ourlang.Expression,
return_phft: PlaceholderForType,
@ -95,13 +92,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: PlaceholderForType([])
x: placeholders.PlaceholderForType([])
for x in signature.args
if isinstance(x, TypeVariable)
if isinstance(x, functions.TypeVariable)
}
for constraint in signature.context.constraints:
if isinstance(constraint, Constraint_TypeClassInstanceExists):
if isinstance(constraint, functions.Constraint_TypeClassInstanceExists):
yield MustImplementTypeClassConstraint(
ctx,
constraint.type_class3,
@ -116,7 +113,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, Type3):
if isinstance(sig_arg, type3types.Type3):
# Not a type variable at all
continue
@ -124,7 +121,7 @@ def _expression_function_call(
# Not a type variable for a type constructor
continue
if not isinstance(sig_arg.application, TypeVariableApplication_Unary):
if not isinstance(sig_arg.application, functions.TypeVariableApplication_Unary):
raise NotImplementedError(sig_arg.application)
assert sig_arg.application.arguments in type_var_map # When does this happen?
@ -142,18 +139,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, TypeVariable):
if isinstance(sig_part, functions.TypeVariable):
yield SameTypeConstraint(type_var_map[sig_part], arg_placeholders[arg_expr], comment=comment)
continue
if isinstance(sig_part, Type3):
if isinstance(sig_part, type3types.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: PlaceholderForType) -> ConstraintGenerator:
def expression(ctx: Context, inp: ourlang.Expression, phft: placeholders.PlaceholderForType) -> ConstraintGenerator:
if isinstance(inp, ourlang.Constant):
yield from constant(ctx, inp, phft)
return
@ -193,14 +190,11 @@ def expression(ctx: Context, inp: ourlang.Expression, phft: PlaceholderForType)
yield from expression(ctx, inp.varref, varref_phft)
yield from expression(ctx, 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)
yield CanBeSubscriptedConstraint(phft, varref_phft, inp.index, index_phft)
return
if isinstance(inp, ourlang.AccessStructMember):
assert isinstance(inp.struct_type3.application, TypeApplication_Struct) # FIXME: See test_struct.py::test_struct_not_accessible
assert isinstance(inp.struct_type3.application, type3types.TypeApplication_Struct) # FIXME: See test_struct.py::test_struct_not_accessible
mem_typ = dict(inp.struct_type3.application.arguments)[inp.member]

View File

@ -64,36 +64,6 @@ 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_):

View File

@ -64,7 +64,7 @@ def testEntry(x: (u8, u32, u64), y: u8) -> u64:
return x[y]
"""
with pytest.raises(Type3Exception, match='Must index with integer literal'):
with pytest.raises(Type3Exception, match='Must index with literal'):
Suite(code_py).run_code()
@pytest.mark.integration_test