diff --git a/phasm/type3/constraints.py b/phasm/type3/constraints.py index e04e66c..c5bf372 100644 --- a/phasm/type3/constraints.py +++ b/phasm/type3/constraints.py @@ -50,7 +50,7 @@ NewConstraintList = List['ConstraintBase'] CheckResult = Union[None, SubstitutionMap, Error, NewConstraintList, RequireTypeSubstitutes] -HumanReadableRet = Tuple[str, Dict[str, Union[str, ourlang.Expression, Type3, PlaceholderForType]]] +HumanReadableRet = Tuple[str, Dict[str, Union[None, int, str, ourlang.Expression, Type3, PlaceholderForType]]] class Context: """ @@ -500,45 +500,42 @@ 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: Type3OrPlaceholder - type3: Type3OrPlaceholder - index: ourlang.Expression - index_phft: Type3OrPlaceholder + ret_type3: PlaceholderForType + type3: PlaceholderForType + index_type3: PlaceholderForType + index_const: int | None def __init__( self, ret_type3: PlaceholderForType, type3: PlaceholderForType, - index: ourlang.Expression, - index_phft: 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[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: - return Error('Tuple index out of range') + 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'), ] @@ -547,18 +544,15 @@ class CanBeSubscriptedConstraint(ConstraintBase): # 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]() @@ -567,12 +561,10 @@ class CanBeSubscriptedConstraint(ConstraintBase): GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple) def check(self) -> CheckResult: - exp_type = self.type3 - if isinstance(exp_type, PlaceholderForType): - if exp_type.resolve_as is None: - return RequireTypeSubstitutes() + 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) @@ -584,9 +576,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)})' diff --git a/phasm/type3/constraintsgenerator.py b/phasm/type3/constraintsgenerator.py index c956918..01c1549 100644 --- a/phasm/type3/constraintsgenerator.py +++ b/phasm/type3/constraintsgenerator.py @@ -193,7 +193,10 @@ 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) - 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): diff --git a/tests/integration/test_lang/test_subscriptable.py b/tests/integration/test_lang/test_subscriptable.py index 1009227..b6b0d35 100644 --- a/tests/integration/test_lang/test_subscriptable.py +++ b/tests/integration/test_lang/test_subscriptable.py @@ -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