diff --git a/phasm/ourlang.py b/phasm/ourlang.py index 273d82e..55d2bc2 100644 --- a/phasm/ourlang.py +++ b/phasm/ourlang.py @@ -53,6 +53,9 @@ class ConstantPrimitive(Constant): super().__init__() self.value = value + def __repr__(self) -> str: + return f'ConstantPrimitive({repr(self.value)})' + class ConstantTuple(Constant): """ A Tuple constant value expression within a statement diff --git a/phasm/type3/constraints.py b/phasm/type3/constraints.py index f2027c9..4acf8c8 100644 --- a/phasm/type3/constraints.py +++ b/phasm/type3/constraints.py @@ -60,14 +60,16 @@ class SameTypeConstraint(ConstraintBase): """ Verifies that an expression has an expected type """ - __slots__ = ('expected', 'actual', ) + __slots__ = ('expected', 'actual', 'message', ) expected: types.Type3 actual: types.Type3OrPlaceholder + message: str - def __init__(self, expected: types.Type3, actual: types.Type3OrPlaceholder) -> None: + def __init__(self, expected: types.Type3, actual: types.Type3OrPlaceholder, message: str) -> None: self.expected = expected self.actual = actual + self.message = message def check(self) -> CheckResult: if isinstance(self.actual, types.PlaceholderForType): @@ -91,6 +93,9 @@ class SameTypeConstraint(ConstraintBase): self.actual.get_substituted(smap[self.actual]) self.actual = smap[self.actual] + def __repr__(self) -> str: + return f'SameTypeConstraint({repr(self.expected)}, {repr(self.actual)}, {repr(self.message)})' + class LiteralFitsConstraint(ConstraintBase): """ A literal value fits a given type @@ -148,3 +153,11 @@ class LiteralFitsConstraint(ConstraintBase): return Error('Must be real') # FIXME: Add line information raise NotImplementedError + + def substitute_placeholders(self, smap: SubstitutionMap) -> None: # FIXME: Duplicate code + if isinstance(self.type3, types.PlaceholderForType) and self.type3 in smap: # FIXME: Check recursive? + self.type3.get_substituted(smap[self.type3]) + self.type3 = smap[self.type3] + + def __repr__(self) -> str: + return f'LiteralFitsConstraint({repr(self.type3)}, {repr(self.literal)})' diff --git a/phasm/type3/constraintsgenerator.py b/phasm/type3/constraintsgenerator.py index 447d5d1..673cf63 100644 --- a/phasm/type3/constraintsgenerator.py +++ b/phasm/type3/constraintsgenerator.py @@ -22,13 +22,7 @@ def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase def constant(ctx: Context, inp: ourlang.Constant) -> Generator[ConstraintBase, None, None]: if isinstance(inp, ourlang.ConstantPrimitive): - # A constant by itself doesn't have any constraints - yield from () - return - - if isinstance(inp, ourlang.ConstantTuple): - # A constant by itself doesn't have any constraints - yield from () + yield LiteralFitsConstraint(inp.type3, inp) return raise NotImplementedError(constant, inp) @@ -39,7 +33,19 @@ def expression(ctx: Context, inp: ourlang.Expression) -> Generator[ConstraintBas return if isinstance(inp, ourlang.VariableReference): - yield SameTypeConstraint(inp.variable.type3, inp.type3) + yield SameTypeConstraint(inp.variable.type3, inp.type3, f'The type of a variable reference is the same as the type of variable {inp.variable.name}') + return + + if isinstance(inp, ourlang.FunctionCall): + yield SameTypeConstraint(inp.function.returns_type3, inp.type3, 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, + 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 raise NotImplementedError(expression, inp) @@ -50,36 +56,11 @@ def function(ctx: Context, inp: ourlang.Function) -> Generator[ConstraintBase, N yield from expression(ctx, inp.statements[0].value) - yield SameTypeConstraint(inp.returns_type3, inp.statements[0].value.type3) + yield SameTypeConstraint(inp.returns_type3, inp.statements[0].value.type3, f'The type of the value returned from function {inp.name} should match its return type') def module_constant_def(ctx: Context, inp: ourlang.ModuleConstantDef) -> Generator[ConstraintBase, None, None]: - yield from constant(ctx, inp.constant) - - # FIXME: Redo this part - - if ( - inp.type3 is types.u8 - or inp.type3 is types.u32 - or inp.type3 is types.u64 - or inp.type3 is types.i32 - or inp.type3 is types.i64 - or inp.type3 is types.f32 - or inp.type3 is types.f64 - ) and isinstance(inp.constant, ourlang.ConstantPrimitive): - yield LiteralFitsConstraint(inp.type3, inp.constant) - inp.constant.type3 = inp.type3 - return - - if isinstance(inp.type3, types.AppliedType3): - if inp.type3.base is types.static_array and isinstance(inp.constant, ourlang.ConstantTuple): - for lit in inp.constant.value: - yield LiteralFitsConstraint(inp.type3.args[0], lit) - # lit.type3 = inp.type3.args[0] - raise NotImplementedError - return - - raise NotImplementedError(constant, inp, inp.type3) + yield SameTypeConstraint(inp.type3, inp.constant.type3, 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) -> Generator[ConstraintBase, None, None]: for cdef in inp.constant_defs.values(): diff --git a/phasm/type3/entry.py b/phasm/type3/entry.py index d6c822e..792fc61 100644 --- a/phasm/type3/entry.py +++ b/phasm/type3/entry.py @@ -9,6 +9,8 @@ from .constraints import Error, RequireTypeSubstitutes from .constraintsgenerator import phasm_type3_generate_constraints from .types import PlaceholderForType, Type3 +MAX_RESTACK_COUNT = 10 + class Type3Exception(BaseException): """ Thrown when the Type3 system detects constraints that do not hold @@ -20,8 +22,10 @@ def phasm_type3(inp: ourlang.Module) -> None: placeholder_substitutes: Dict[PlaceholderForType, Type3] = {} + restack_counter = 0 + error_list: List[Error] = [] - while constraint_list: # FIXME: How to detect infinite loop? Is that necessary? + while constraint_list: constraint = constraint_list.pop(0) constraint.substitute_placeholders(placeholder_substitutes) @@ -36,6 +40,11 @@ def phasm_type3(inp: ourlang.Module) -> None: continue if isinstance(check_result, RequireTypeSubstitutes): + # FIXME: How to detect infinite loop? Is that necessary? + restack_counter += 1 + if restack_counter > MAX_RESTACK_COUNT: + raise Exception('This looks like an infinite loop', constraint_list) + constraint_list.append(constraint) continue @@ -45,4 +54,3 @@ def phasm_type3(inp: ourlang.Module) -> None: raise Type3Exception(error_list) # TODO: Implement type substitution on the AST - print('placeholder_substitutes', placeholder_substitutes)