Compare commits

..

No commits in common. "4f19a5e503495b6d90d87ffefc879712887ea7e7" and "f6cb1a8c1dbf0541df6bc23f94b261ef95cb3c97" have entirely different histories.

7 changed files with 76 additions and 123 deletions

View File

@ -28,8 +28,3 @@
- Functions don't seem to be a thing on typing level yet?
- Related to the FIXME in phasm_type3?
- Type constuctor should also be able to constuct placeholders - somehow.
- Check placeholders only used in constraints and constraintgenerator
- Simply typed check in compiler:
- isinstance(element.type3, type3types.Type3) => element.type3 is not None

View File

@ -6,6 +6,7 @@ from typing import Dict, Iterable, List, Optional, Union
from . import prelude
from .type3.functions import FunctionSignature, TypeVariableContext
from .type3.placeholders import PlaceholderForType, Type3OrPlaceholder
from .type3.typeclasses import Type3ClassMethod
from .type3.types import Type3
@ -16,10 +17,10 @@ class Expression:
"""
__slots__ = ('type3', )
type3: Type3 | None
type3: Type3OrPlaceholder
def __init__(self) -> None:
self.type3 = None
self.type3 = PlaceholderForType([self])
class Constant(Expression):
"""
@ -197,10 +198,10 @@ class AccessStructMember(Expression):
__slots__ = ('varref', 'struct_type3', 'member', )
varref: VariableReference
struct_type3: Type3
struct_type3: Type3OrPlaceholder
member: str
def __init__(self, varref: VariableReference, struct_type3: Type3, member: str) -> None:
def __init__(self, varref: VariableReference, struct_type3: Type3OrPlaceholder, member: str) -> None:
super().__init__()
self.varref = varref
@ -283,7 +284,7 @@ class FunctionParam:
__slots__ = ('name', 'type3', )
name: str
type3: Type3
type3: Type3OrPlaceholder
def __init__(self, name: str, type3: Type3) -> None:
self.name = name

View File

@ -3,11 +3,10 @@ This module contains possible constraints generated based on the AST
These need to be resolved before the program can be compiled.
"""
from typing import Dict, Iterable, List, Optional, Tuple, Union
from typing import Dict, List, Optional, Tuple, Union
from .. import ourlang, prelude
from . import placeholders, typeclasses, types
from .placeholders import PlaceholderForType
class Error:
@ -33,13 +32,13 @@ class RequireTypeSubstitutes:
typing of the program, so this constraint can be updated.
"""
SubstitutionMap = Dict[PlaceholderForType, types.Type3]
SubstitutionMap = Dict[placeholders.PlaceholderForType, types.Type3]
NewConstraintList = List['ConstraintBase']
CheckResult = Union[None, SubstitutionMap, Error, NewConstraintList, RequireTypeSubstitutes]
HumanReadableRet = Tuple[str, Dict[str, Union[str, ourlang.Expression, types.Type3, PlaceholderForType]]]
HumanReadableRet = Tuple[str, Dict[str, Union[str, ourlang.Expression, types.Type3, placeholders.PlaceholderForType]]]
class Context:
"""
@ -99,9 +98,9 @@ class SameTypeConstraint(ConstraintBase):
"""
__slots__ = ('type_list', )
type_list: List[PlaceholderForType]
type_list: List[placeholders.Type3OrPlaceholder]
def __init__(self, *type_list: PlaceholderForType, 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
@ -111,10 +110,18 @@ class SameTypeConstraint(ConstraintBase):
known_types: List[types.Type3] = []
phft_list = []
for typ in self.type_list:
if isinstance(typ, types.Type3):
known_types.append(typ)
continue
if isinstance(typ, placeholders.PlaceholderForType):
if typ.resolve_as is not None:
known_types.append(typ.resolve_as)
else:
phft_list.append(typ)
continue
raise NotImplementedError(typ)
if not known_types:
return RequireTypeSubstitutes()
@ -150,7 +157,7 @@ class SameTypeConstraint(ConstraintBase):
return f'SameTypeConstraint({args}, comment={repr(self.comment)})'
class TupleMatchConstraint(ConstraintBase):
def __init__(self, exp_type: PlaceholderForType, args: Iterable[PlaceholderForType], comment: str):
def __init__(self, exp_type: placeholders.Type3OrPlaceholder, args: List[placeholders.Type3OrPlaceholder], comment: str):
super().__init__(comment=comment)
self.exp_type = exp_type
@ -158,7 +165,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()
@ -198,13 +205,13 @@ class MustImplementTypeClassConstraint(ConstraintBase):
context: Context
type_class3: Union[str, typeclasses.Type3Class]
types: list[PlaceholderForType]
types: list[placeholders.Type3OrPlaceholder]
DATA = {
'bytes': {'Foldable'},
}
def __init__(self, context: Context, type_class3: Union[str, typeclasses.Type3Class], types: list[PlaceholderForType], 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
@ -214,10 +221,10 @@ class MustImplementTypeClassConstraint(ConstraintBase):
def check(self) -> CheckResult:
typ_list = []
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()
typ_list.append(typ)
@ -259,12 +266,12 @@ class LiteralFitsConstraint(ConstraintBase):
"""
__slots__ = ('type3', 'literal', )
type3: PlaceholderForType
type3: placeholders.Type3OrPlaceholder
literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct]
def __init__(
self,
type3: PlaceholderForType,
type3: placeholders.Type3OrPlaceholder,
literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct],
comment: Optional[str] = None,
) -> None:
@ -288,7 +295,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()
@ -341,12 +348,8 @@ class LiteralFitsConstraint(ConstraintBase):
LiteralFitsConstraint(x, y)
for x, y in zip(tp_args, self.literal.value, strict=True)
)
# Generate placeholders so each Literal expression
# gets updated when we figure out the type of the
# expression the literal is used in
res.extend(
SameTypeConstraint(x, PlaceholderForType([y]))
SameTypeConstraint(x, y.type3)
for x, y in zip(tp_args, self.literal.value, strict=True)
)
@ -368,12 +371,8 @@ class LiteralFitsConstraint(ConstraintBase):
LiteralFitsConstraint(sa_type, y)
for y in self.literal.value
)
# Generate placeholders so each Literal expression
# gets updated when we figure out the type of the
# expression the literal is used in
res.extend(
SameTypeConstraint(sa_type, PlaceholderForType([y]))
SameTypeConstraint(sa_type, y.type3)
for y in self.literal.value
)
@ -397,12 +396,8 @@ class LiteralFitsConstraint(ConstraintBase):
LiteralFitsConstraint(x, y)
for x, y in zip(st_args.values(), self.literal.value, strict=True)
)
# Generate placeholders so each Literal expression
# 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, y.type3, comment=f'{self.literal.struct_name}.{x_n}')
for (x_n, x_t, ), y in zip(st_args.items(), self.literal.value, strict=True)
)
@ -426,31 +421,24 @@ 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', 'index_type3', )
ret_type3: PlaceholderForType
type3: PlaceholderForType
ret_type3: placeholders.Type3OrPlaceholder
type3: placeholders.Type3OrPlaceholder
index: ourlang.Expression
index_phft: PlaceholderForType
index_type3: placeholders.Type3OrPlaceholder
def __init__(
self,
ret_type3: PlaceholderForType,
type3: PlaceholderForType,
index: ourlang.Expression,
index_phft: PlaceholderForType,
comment: Optional[str] = None,
) -> None:
def __init__(self, ret_type3: placeholders.Type3OrPlaceholder, type3: placeholders.Type3OrPlaceholder, index: ourlang.Expression, 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
def check(self) -> CheckResult:
exp_type = self.type3
if isinstance(exp_type, PlaceholderForType):
if isinstance(exp_type, placeholders.PlaceholderForType):
if exp_type.resolve_as is None:
return RequireTypeSubstitutes()
@ -463,7 +451,7 @@ class CanBeSubscriptedConstraint(ConstraintBase):
sa_type, sa_len = sa_args
result: List[ConstraintBase] = [
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'),
]
@ -490,13 +478,13 @@ class CanBeSubscriptedConstraint(ConstraintBase):
return Error('Tuple index out of range')
return [
SameTypeConstraint(prelude.u32, self.index_phft, comment=f'Tuple subscript index {self.index.value}'),
SameTypeConstraint(prelude.u32, self.index_type3, 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}'),
]
if exp_type is prelude.bytes_:
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'),
]

View File

@ -19,7 +19,6 @@ from .constraints import (
SameTypeConstraint,
TupleMatchConstraint,
)
from .placeholders import PlaceholderForType
ConstraintGenerator = Generator[ConstraintBase, None, None]
@ -29,23 +28,23 @@ 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) -> ConstraintGenerator:
if isinstance(inp, (ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct)):
yield LiteralFitsConstraint(
phft, inp,
inp.type3, inp,
comment='The given literal must fit the expected type'
)
return
raise NotImplementedError(constant, inp)
def expression(ctx: Context, inp: ourlang.Expression, phft: placeholders.PlaceholderForType) -> ConstraintGenerator:
def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
if isinstance(inp, ourlang.Constant):
yield from constant(ctx, inp, phft)
yield from constant(ctx, inp)
return
if isinstance(inp, ourlang.VariableReference):
yield SameTypeConstraint(inp.variable.type3, phft,
yield SameTypeConstraint(inp.variable.type3, inp.type3,
comment=f'typeOf("{inp.variable.name}") == typeOf({inp.variable.name})')
return
@ -61,14 +60,8 @@ def expression(ctx: Context, inp: ourlang.Expression, phft: placeholders.Placeho
if isinstance(x, functions.TypeVariable)
}
arg_placeholders = {
arg_expr: PlaceholderForType([arg_expr])
for arg_expr in arguments
}
arg_placeholders[inp] = phft
for arg_expr in arguments:
yield from expression(ctx, arg_expr, arg_placeholders[arg_expr])
for call_arg in arguments:
yield from expression(ctx, call_arg)
for constraint in signature.context.constraints:
if isinstance(constraint, functions.Constraint_TypeClassInstanceExists):
@ -88,11 +81,11 @@ def expression(ctx: Context, inp: ourlang.Expression, phft: placeholders.Placeho
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_placeholders[arg_expr], comment=comment)
yield SameTypeConstraint(type_var_map[sig_part], arg_expr.type3, comment=comment)
continue
if isinstance(sig_part, type3types.Type3):
yield SameTypeConstraint(sig_part, arg_placeholders[arg_expr], comment=comment)
yield SameTypeConstraint(sig_part, arg_expr.type3, comment=comment)
continue
raise NotImplementedError(sig_part)
@ -101,12 +94,11 @@ def expression(ctx: Context, inp: ourlang.Expression, phft: placeholders.Placeho
if isinstance(inp, ourlang.TupleInstantiation):
r_type = []
for arg in inp.elements:
arg_phft = PlaceholderForType([arg])
yield from expression(ctx, arg, arg_phft)
r_type.append(arg_phft)
yield from expression(ctx, arg)
r_type.append(arg.type3)
yield TupleMatchConstraint(
phft,
inp.type3,
r_type,
comment='The type of a tuple is a combination of its members'
)
@ -114,13 +106,10 @@ def expression(ctx: Context, inp: ourlang.Expression, phft: placeholders.Placeho
return
if isinstance(inp, ourlang.Subscript):
varref_phft = PlaceholderForType([inp.varref])
index_phft = PlaceholderForType([inp.index])
yield from expression(ctx, inp.varref)
yield from expression(ctx, inp.index)
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)
yield CanBeSubscriptedConstraint(inp.type3, inp.varref.type3, inp.index)
return
if isinstance(inp, ourlang.AccessStructMember):
@ -128,40 +117,33 @@ def expression(ctx: Context, inp: ourlang.Expression, phft: placeholders.Placeho
st_args = prelude.struct.did_construct(inp.struct_type3)
assert st_args is not None # FIXME: See test_struct.py::test_struct_not_accessible
yield from expression(ctx, inp.varref, PlaceholderForType([inp.varref])) # TODO
yield SameTypeConstraint(st_args[inp.member], phft,
yield from expression(ctx, inp.varref)
yield SameTypeConstraint(st_args[inp.member], inp.type3,
comment=f'The type of a struct member reference is the same as the type of struct member {inp.struct_type3.name}.{inp.member}')
return
if isinstance(inp, ourlang.Fold):
base_phft = PlaceholderForType([inp.base])
iter_phft = PlaceholderForType([inp.iter])
yield from expression(ctx, inp.base)
yield from expression(ctx, inp.iter)
yield from expression(ctx, inp.base, base_phft)
yield from expression(ctx, inp.iter, iter_phft)
yield SameTypeConstraint(inp.func.posonlyargs[0].type3, inp.func.returns_type3, base_phft, phft,
yield SameTypeConstraint(inp.func.posonlyargs[0].type3, inp.func.returns_type3, inp.base.type3, inp.type3,
comment='foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b')
yield MustImplementTypeClassConstraint(ctx, 'Foldable', [iter_phft])
yield MustImplementTypeClassConstraint(ctx, 'Foldable', [inp.iter.type3])
return
raise NotImplementedError(expression, inp)
def statement_return(ctx: Context, fun: ourlang.Function, inp: ourlang.StatementReturn) -> ConstraintGenerator:
phft = PlaceholderForType([inp.value])
yield from expression(ctx, inp.value)
yield from expression(ctx, inp.value, phft)
yield SameTypeConstraint(fun.returns_type3, phft,
yield SameTypeConstraint(fun.returns_type3, inp.value.type3,
comment=f'The type of the value returned from function {fun.name} should match its return type')
def statement_if(ctx: Context, fun: ourlang.Function, inp: ourlang.StatementIf) -> ConstraintGenerator:
test_phft = PlaceholderForType([inp.test])
yield from expression(ctx, inp.test)
yield from expression(ctx, inp.test, test_phft)
yield SameTypeConstraint(test_phft, prelude.bool_,
yield SameTypeConstraint(inp.test.type3, prelude.bool_,
comment='Must pass a boolean expression to if')
for stmt in inp.statements:
@ -191,10 +173,8 @@ def function(ctx: Context, inp: ourlang.Function) -> ConstraintGenerator:
yield from statement(ctx, inp, stmt)
def module_constant_def(ctx: Context, inp: ourlang.ModuleConstantDef) -> ConstraintGenerator:
phft = PlaceholderForType([inp.constant])
yield from constant(ctx, inp.constant, phft)
yield SameTypeConstraint(inp.type3, phft,
yield from constant(ctx, inp.constant)
yield SameTypeConstraint(inp.type3, inp.constant.type3,
comment=f'The type of the value for module constant definition {inp.name} should match the type of that constant')
def module(ctx: Context, inp: ourlang.Module) -> ConstraintGenerator:

View File

@ -115,6 +115,8 @@ def phasm_type3(inp: ourlang.Module, verbose: bool = False) -> None:
# FIXME: This doesn't work with e.g. `:: [a] -> a`, as the placeholder is inside a type
for plh, typ in placeholder_substitutes.items():
for expr in plh.update_on_substitution:
assert expr.type3 is plh
expr.type3 = typ
def print_constraint(placeholder_id_map: Dict[int, str], constraint: ConstraintBase) -> None:

View File

@ -14,7 +14,7 @@ class ExpressionProtocol(Protocol):
A protocol for classes that should be updated on substitution
"""
type3: Type3 | None
type3: 'Type3OrPlaceholder'
"""
The type to update
"""

View File

@ -6,20 +6,7 @@ from ..helpers import Suite
@pytest.mark.integration_test
def test_expr_constant_literal_does_not_fit_module_constant():
code_py = """
CONSTANT: u8 = 1000
@exported
def testEntry() -> u8:
return CONSTANT
"""
with pytest.raises(Type3Exception, match=r'Must fit in 1 byte\(s\)'):
Suite(code_py).run_code()
@pytest.mark.integration_test
def test_expr_constant_literal_does_not_fit_return():
def test_expr_constant_literal_does_not_fit():
code_py = """
@exported
def testEntry() -> u8: