Reworks bytes into dynamic array

bytes continues to be the preferred name for u8[...].
Also, putting bytes values into the VM and taking them
out still uses Python bytes values.

This also lets used use the len function on them, for
whatever that's worth.
This commit is contained in:
Johan B.W. de Vries 2025-05-18 15:37:13 +02:00
parent a72bd60de2
commit 83186cce78
13 changed files with 210 additions and 41 deletions

View File

@ -15,8 +15,10 @@ from .type3.types import (
IntType3, IntType3,
Type3, Type3,
TypeApplication_Struct, TypeApplication_Struct,
TypeApplication_Type,
TypeApplication_TypeInt, TypeApplication_TypeInt,
TypeApplication_TypeStar, TypeApplication_TypeStar,
TypeConstructor_DynamicArray,
TypeConstructor_Function, TypeConstructor_Function,
TypeConstructor_StaticArray, TypeConstructor_StaticArray,
TypeConstructor_Tuple, TypeConstructor_Tuple,
@ -109,12 +111,25 @@ def tuple_instantiation(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Tu
args: tuple[Type3, ...] args: tuple[Type3, ...]
if isinstance(inp.type3.application, TypeApplication_TypeStar): alloc_size_header = None
if isinstance(inp.type3.application, TypeApplication_Type):
# 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_DynamicArray)
sa_type, = inp.type3.application.arguments
args = tuple(sa_type for _ in inp.elements)
alloc_size = 4 + calculate_alloc_size(sa_type, is_member=False) * len(inp.elements)
alloc_size_header = len(inp.elements)
elif isinstance(inp.type3.application, TypeApplication_TypeStar):
# Possibly paranoid assert. If we have a future variadic type, # Possibly paranoid assert. If we have a future variadic type,
# does it also do this tuple instantation like this? # does it also do this tuple instantation like this?
assert isinstance(inp.type3.application.constructor, TypeConstructor_Tuple) assert isinstance(inp.type3.application.constructor, TypeConstructor_Tuple)
args = inp.type3.application.arguments args = inp.type3.application.arguments
alloc_size = calculate_alloc_size(inp.type3, is_member=False)
elif isinstance(inp.type3.application, TypeApplication_TypeInt): elif isinstance(inp.type3.application, TypeApplication_TypeInt):
# Possibly paranoid assert. If we have a future type of kind * -> Int -> *, # Possibly paranoid assert. If we have a future type of kind * -> Int -> *,
# does it also do this tuple instantation like this? # does it also do this tuple instantation like this?
@ -123,6 +138,7 @@ def tuple_instantiation(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Tu
sa_type, sa_len = inp.type3.application.arguments sa_type, sa_len = inp.type3.application.arguments
args = tuple(sa_type for _ in range(sa_len.value)) args = tuple(sa_type for _ in range(sa_len.value))
alloc_size = calculate_alloc_size(inp.type3, is_member=False)
else: else:
raise NotImplementedError('tuple_instantiation', inp.type3) raise NotImplementedError('tuple_instantiation', inp.type3)
@ -135,12 +151,17 @@ def tuple_instantiation(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Tu
wgn.add_statement('nop', comment=f'{tmp_var.name} := ({comment_elements})') wgn.add_statement('nop', comment=f'{tmp_var.name} := ({comment_elements})')
# Allocated the required amounts of bytes in memory # Allocated the required amounts of bytes in memory
wgn.i32.const(calculate_alloc_size(inp.type3, is_member=False)) wgn.i32.const(alloc_size)
wgn.call(stdlib_alloc.__alloc__) wgn.call(stdlib_alloc.__alloc__)
wgn.local.set(tmp_var) wgn.local.set(tmp_var)
if alloc_size_header is not None:
wgn.local.get(tmp_var)
wgn.i32.const(alloc_size_header)
wgn.i32.store()
# Store each element individually # Store each element individually
offset = 0 offset = 0 if alloc_size_header is None else 4
for element, exp_type3 in zip(inp.elements, args, strict=True): for element, exp_type3 in zip(inp.elements, args, strict=True):
assert element.type3 == exp_type3 assert element.type3 == exp_type3

View File

@ -301,6 +301,9 @@ class FunctionParam:
self.name = name self.name = name
self.type3 = type3 self.type3 = type3
def __repr__(self) -> str:
return f'FunctionParam({self.name!r}, {self.type3!r})'
class Function: class Function:
""" """
A function processes input and produces output A function processes input and produces output

View File

@ -655,8 +655,15 @@ class OurVisitor:
if isinstance(node.slice, ast.Slice): if isinstance(node.slice, ast.Slice):
_raise_static_error(node, 'Must subscript using an index') _raise_static_error(node, 'Must subscript using an index')
if not isinstance(node.slice, ast.Constant): if not isinstance(node.slice, ast.Constant):
_raise_static_error(node, 'Must subscript using a constant index') _raise_static_error(node, 'Must subscript using a constant index')
if node.slice.value is Ellipsis:
return prelude.dynamic_array(
self.visit_type(module, node.value),
)
if not isinstance(node.slice.value, int): if not isinstance(node.slice.value, int):
_raise_static_error(node, 'Must subscript using a constant integer index') _raise_static_error(node, 'Must subscript using a constant integer index')
if not isinstance(node.ctx, ast.Load): if not isinstance(node.ctx, ast.Load):

View File

@ -20,6 +20,7 @@ from ..type3.types import (
Type3, Type3,
TypeApplication_Nullary, TypeApplication_Nullary,
TypeConstructor_Base, TypeConstructor_Base,
TypeConstructor_DynamicArray,
TypeConstructor_Function, TypeConstructor_Function,
TypeConstructor_StaticArray, TypeConstructor_StaticArray,
TypeConstructor_Struct, TypeConstructor_Struct,
@ -158,9 +159,15 @@ f64 = Type3('f64', TypeApplication_Nullary(None, None))
A 32-bits IEEE 754 float, of 64 bits width. A 32-bits IEEE 754 float, of 64 bits width.
""" """
bytes_ = Type3('bytes', TypeApplication_Nullary(None, None)) def da_on_create(args: tuple[Type3], typ: Type3) -> None:
instance_type_class(InternalPassAsPointer, typ)
dynamic_array = TypeConstructor_DynamicArray('dynamic_array', on_create=da_on_create)
""" """
This is a runtime-determined length piece of memory that can be indexed at runtime. This is a dynamic length piece of memory.
It should be applied with two arguments. It has a runtime
determined length, and each argument is the same.
""" """
def sa_on_create(args: tuple[Type3, IntType3], typ: Type3) -> None: def sa_on_create(args: tuple[Type3, IntType3], typ: Type3) -> None:
@ -168,12 +175,10 @@ def sa_on_create(args: tuple[Type3, IntType3], typ: Type3) -> None:
static_array = TypeConstructor_StaticArray('static_array', on_create=sa_on_create) static_array = TypeConstructor_StaticArray('static_array', on_create=sa_on_create)
""" """
A type constructor. This is a fixed length piece of memory.
Any static array is a fixed length piece of memory that can be indexed at runtime. It should be applied with two arguments. It has a compile time
determined length, and each argument is the same.
It should be applied with one argument. It has a runtime-dynamic length
of the same type repeated.
""" """
def tp_on_create(args: tuple[Type3, ...], typ: Type3) -> None: def tp_on_create(args: tuple[Type3, ...], typ: Type3) -> None:
@ -208,20 +213,6 @@ This is like a tuple, but each argument is named, so that developers
can get and set fields by name. can get and set fields by name.
""" """
PRELUDE_TYPES: dict[str, Type3] = {
'none': none,
'bool': bool_,
'u8': u8,
'u32': u32,
'u64': u64,
'i8': i8,
'i32': i32,
'i64': i64,
'f32': f32,
'f64': f64,
'bytes': bytes_,
}
a = TypeVariable('a', TypeVariableApplication_Nullary(None, None)) a = TypeVariable('a', TypeVariableApplication_Nullary(None, None))
b = TypeVariable('b', TypeVariableApplication_Nullary(None, None)) b = TypeVariable('b', TypeVariableApplication_Nullary(None, None))
@ -232,7 +223,7 @@ InternalPassAsPointer = Type3Class('InternalPassAsPointer', (a, ), methods={}, o
Internal type class to keep track which types we pass arounds as a pointer. Internal type class to keep track which types we pass arounds as a pointer.
""" """
instance_type_class(InternalPassAsPointer, bytes_) # instance_type_class(InternalPassAsPointer, bytes_)
# instance_type_class(InternalPassAsPointer, static_array) # instance_type_class(InternalPassAsPointer, static_array)
# instance_type_class(InternalPassAsPointer, tuple_) # instance_type_class(InternalPassAsPointer, tuple_)
# instance_type_class(InternalPassAsPointer, struct) # instance_type_class(InternalPassAsPointer, struct)
@ -537,12 +528,15 @@ instance_type_class(Floating, f64, methods={
'sqrt': stdtypes.f64_floating_sqrt, 'sqrt': stdtypes.f64_floating_sqrt,
}) })
Sized_ = Type3Class('Sized', (a, ), methods={ Sized_ = Type3Class('Sized', (t, ), methods={
'len': [a, u32], 'len': [t(a), u32],
}, operators={}) # FIXME: Once we get type class families, add [] here }, operators={}) # FIXME: Once we get type class families, add [] here
instance_type_class(Sized_, bytes_, methods={ instance_type_class(Sized_, dynamic_array, methods={
'len': stdtypes.bytes_sized_len, 'len': stdtypes.dynamic_array_sized_len,
})
instance_type_class(Sized_, static_array, methods={
'len': stdtypes.static_array_sized_len,
}) })
Extendable = Type3Class('Extendable', (a, b, ), methods={ Extendable = Type3Class('Extendable', (a, b, ), methods={
@ -595,6 +589,22 @@ instance_type_class(Foldable, static_array, methods={
'sum': stdtypes.static_array_sum, 'sum': stdtypes.static_array_sum,
}) })
bytes_ = dynamic_array(u8)
PRELUDE_TYPES: dict[str, Type3] = {
'none': none,
'bool': bool_,
'u8': u8,
'u32': u32,
'u64': u64,
'i8': i8,
'i32': i32,
'i64': i64,
'f32': f32,
'f64': f64,
'bytes': bytes_,
}
PRELUDE_TYPE_CLASSES = { PRELUDE_TYPE_CLASSES = {
'Eq': Eq, 'Eq': Eq,
'Ord': Ord, 'Ord': Ord,

View File

@ -1006,11 +1006,19 @@ def f64_intnum_neg(g: Generator, tv_map: TypeVariableLookup) -> None:
## ### ## ###
## Class Sized ## Class Sized
def bytes_sized_len(g: Generator, tv_map: TypeVariableLookup) -> None: def dynamic_array_sized_len(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map del tv_map
# The length is stored in the first 4 bytes # The length is stored in the first 4 bytes
g.i32.load() g.i32.load()
def static_array_sized_len(g: Generator, tv_map: TypeVariableLookup) -> None:
assert len(tv_map) == 1
sa_type, sa_len = next(iter(tv_map.values()))
assert isinstance(sa_type, Type3)
assert isinstance(sa_len, IntType3)
g.i32.const(sa_len.value)
## ### ## ###
## Extendable ## Extendable

View File

@ -15,6 +15,7 @@ from .types import (
Type3, Type3,
TypeApplication_Nullary, TypeApplication_Nullary,
TypeApplication_Struct, TypeApplication_Struct,
TypeApplication_Type,
TypeApplication_TypeInt, TypeApplication_TypeInt,
TypeApplication_TypeStar, TypeApplication_TypeStar,
TypeConstructor_Base, TypeConstructor_Base,
@ -292,6 +293,14 @@ class TupleMatchConstraint(ConstraintBase):
self.exp_type = exp_type self.exp_type = exp_type
self.args = list(args) self.args = list(args)
def _generate_dynamic_array(self, sa_args: tuple[Type3]) -> CheckResult:
sa_type, = sa_args
return [
SameTypeConstraint(arg, sa_type)
for arg in self.args
]
def _generate_static_array(self, sa_args: tuple[Type3, IntType3]) -> CheckResult: def _generate_static_array(self, sa_args: tuple[Type3, IntType3]) -> CheckResult:
sa_type, sa_len = sa_args sa_type, sa_len = sa_args
@ -313,6 +322,7 @@ class TupleMatchConstraint(ConstraintBase):
] ]
GENERATE_ROUTER = TypeApplicationRouter['TupleMatchConstraint', CheckResult]() GENERATE_ROUTER = TypeApplicationRouter['TupleMatchConstraint', CheckResult]()
GENERATE_ROUTER.add(prelude.dynamic_array, _generate_dynamic_array)
GENERATE_ROUTER.add(prelude.static_array, _generate_static_array) GENERATE_ROUTER.add(prelude.static_array, _generate_static_array)
GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple) GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple)
@ -340,7 +350,7 @@ class MustImplementTypeClassConstraint(ConstraintBase):
types: list[Type3OrPlaceholder] types: list[Type3OrPlaceholder]
DATA = { DATA = {
'bytes': {'Foldable'}, 'dynamic_array': {'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, Type3Class], typ_list: list[Type3OrPlaceholder], comment: Optional[str] = None) -> None:
@ -363,7 +373,7 @@ class MustImplementTypeClassConstraint(ConstraintBase):
typ_list.append(typ) typ_list.append(typ)
continue continue
if isinstance(typ.application, (TypeApplication_TypeInt, TypeApplication_TypeStar)): if isinstance(typ.application, (TypeApplication_Type, TypeApplication_TypeInt, TypeApplication_TypeStar)):
typ_list.append(typ.application.constructor) typ_list.append(typ.application.constructor)
continue continue
@ -420,6 +430,29 @@ class LiteralFitsConstraint(ConstraintBase):
self.type3 = type3 self.type3 = type3
self.literal = literal self.literal = literal
def _generate_dynamic_array(self, da_args: tuple[Type3]) -> CheckResult:
if not isinstance(self.literal, ourlang.ConstantTuple):
return Error('Must be tuple', comment=self.comment)
da_type, = da_args
res: list[ConstraintBase] = []
res.extend(
LiteralFitsConstraint(da_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(da_type, PlaceholderForType([y]))
for y in self.literal.value
)
return res
def _generate_static_array(self, sa_args: tuple[Type3, IntType3]) -> CheckResult: def _generate_static_array(self, sa_args: tuple[Type3, IntType3]) -> CheckResult:
if not isinstance(self.literal, ourlang.ConstantTuple): if not isinstance(self.literal, ourlang.ConstantTuple):
return Error('Must be tuple', comment=self.comment) return Error('Must be tuple', comment=self.comment)
@ -501,6 +534,7 @@ class LiteralFitsConstraint(ConstraintBase):
return res return res
GENERATE_ROUTER = TypeApplicationRouter['LiteralFitsConstraint', CheckResult]() GENERATE_ROUTER = TypeApplicationRouter['LiteralFitsConstraint', CheckResult]()
GENERATE_ROUTER.add(prelude.dynamic_array, _generate_dynamic_array)
GENERATE_ROUTER.add(prelude.static_array, _generate_static_array) GENERATE_ROUTER.add(prelude.static_array, _generate_static_array)
GENERATE_ROUTER.add(prelude.struct, _generate_struct) GENERATE_ROUTER.add(prelude.struct, _generate_struct)
GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple) GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple)

View File

@ -176,7 +176,10 @@ def _expression_function_call(
if not isinstance(sig_arg.application, TypeVariableApplication_Unary): if not isinstance(sig_arg.application, TypeVariableApplication_Unary):
raise NotImplementedError(sig_arg.application) raise NotImplementedError(sig_arg.application)
assert sig_arg.application.arguments in type_var_map # When does this happen? if sig_arg.application.arguments not in type_var_map:
# e.g., len :: t a -> u32
# i.e. "a" does not matter at all
continue
yield SameTypeArgumentConstraint( yield SameTypeArgumentConstraint(
type_var_map[sig_arg], type_var_map[sig_arg],

View File

@ -6,7 +6,13 @@ from .functions import (
TypeVariableApplication_Unary, TypeVariableApplication_Unary,
) )
from .typeclasses import Type3ClassArgs from .typeclasses import Type3ClassArgs
from .types import KindArgument, Type3, TypeApplication_TypeInt, TypeConstructor_Base from .types import (
KindArgument,
Type3,
TypeApplication_Type,
TypeApplication_TypeInt,
TypeConstructor_Base,
)
class NoRouteForTypeException(Exception): class NoRouteForTypeException(Exception):
@ -104,6 +110,10 @@ class TypeClassArgsRouter[S, R]:
key.append(typ.application.constructor) key.append(typ.application.constructor)
if isinstance(tvar.application, TypeVariableApplication_Unary): if isinstance(tvar.application, TypeVariableApplication_Unary):
if isinstance(typ.application, TypeApplication_Type):
arguments[tvar.application.arguments] = typ.application.arguments
continue
# FIXME: This feels sketchy. Shouldn't the type variable # FIXME: This feels sketchy. Shouldn't the type variable
# have the exact same number as arguments? # have the exact same number as arguments?
if isinstance(typ.application, TypeApplication_TypeInt): if isinstance(typ.application, TypeApplication_TypeInt):

View File

@ -195,6 +195,26 @@ class TypeConstructor_Base[T]:
def __repr__(self) -> str: def __repr__(self) -> str:
return f'{self.__class__.__name__}({self.name!r}, ...)' return f'{self.__class__.__name__}({self.name!r}, ...)'
class TypeConstructor_Type(TypeConstructor_Base[Tuple[Type3]]):
"""
Base class type constructors of kind: * -> *
Notably, static array.
"""
__slots__ = ()
def make_application(self, key: Tuple[Type3]) -> 'TypeApplication_Type':
return TypeApplication_Type(self, key)
def make_name(self, key: Tuple[Type3]) -> str:
return f'{self.name} {key[0].name} '
def __call__(self, arg0: Type3) -> Type3:
return self.construct((arg0, ))
class TypeApplication_Type(TypeApplication_Base[TypeConstructor_Type, Tuple[Type3]]):
pass
class TypeConstructor_TypeInt(TypeConstructor_Base[Tuple[Type3, IntType3]]): class TypeConstructor_TypeInt(TypeConstructor_Base[Tuple[Type3, IntType3]]):
""" """
Base class type constructors of kind: * -> Int -> * Base class type constructors of kind: * -> Int -> *
@ -231,6 +251,13 @@ class TypeConstructor_TypeStar(TypeConstructor_Base[Tuple[Type3, ...]]):
class TypeApplication_TypeStar(TypeApplication_Base[TypeConstructor_TypeStar, Tuple[Type3, ...]]): class TypeApplication_TypeStar(TypeApplication_Base[TypeConstructor_TypeStar, Tuple[Type3, ...]]):
pass pass
class TypeConstructor_DynamicArray(TypeConstructor_Type):
def make_name(self, key: Tuple[Type3]) -> str:
if 'u8' == key[0].name:
return 'bytes'
return f'{key[0].name}[...]'
class TypeConstructor_StaticArray(TypeConstructor_TypeInt): class TypeConstructor_StaticArray(TypeConstructor_TypeInt):
def make_name(self, key: Tuple[Type3, IntType3]) -> str: def make_name(self, key: Tuple[Type3, IntType3]) -> str:
return f'{key[0].name}[{key[1].value}]' return f'{key[0].name}[{key[1].value}]'

View File

@ -152,6 +152,25 @@ def _allocate_memory_stored_bytes(attrs: tuple[runners.RunnerBase, bytes]) -> in
runner.interpreter_write_memory(adr + 4, val) runner.interpreter_write_memory(adr + 4, val)
return adr return adr
def _allocate_memory_stored_dynamic_array(attrs: tuple[runners.RunnerBase, Any], da_args: tuple[type3types.Type3]) -> int:
runner, val = attrs
da_type, = da_args
if not isinstance(val, tuple):
raise InvalidArgumentException(f'Expected tuple; got {val!r} instead')
alloc_size = 4 + len(val) * calculate_alloc_size(da_type, True)
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
assert isinstance(adr, int) # Type int
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
offset = adr
offset += _write_memory_stored_value(runner, offset, prelude.u32, len(val))
for val_el_val in val:
offset += _write_memory_stored_value(runner, offset, da_type, val_el_val)
return adr
def _allocate_memory_stored_static_array(attrs: tuple[runners.RunnerBase, Any], sa_args: tuple[type3types.Type3, type3types.IntType3]) -> int: def _allocate_memory_stored_static_array(attrs: tuple[runners.RunnerBase, Any], sa_args: tuple[type3types.Type3, type3types.IntType3]) -> int:
runner, val = attrs runner, val = attrs
@ -211,6 +230,7 @@ def _allocate_memory_stored_tuple(attrs: tuple[runners.RunnerBase, Any], tp_args
ALLOCATE_MEMORY_STORED_ROUTER = TypeApplicationRouter[tuple[runners.RunnerBase, Any], Any]() ALLOCATE_MEMORY_STORED_ROUTER = TypeApplicationRouter[tuple[runners.RunnerBase, Any], Any]()
ALLOCATE_MEMORY_STORED_ROUTER.add_n(prelude.bytes_, _allocate_memory_stored_bytes) ALLOCATE_MEMORY_STORED_ROUTER.add_n(prelude.bytes_, _allocate_memory_stored_bytes)
ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.dynamic_array, _allocate_memory_stored_dynamic_array)
ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.static_array, _allocate_memory_stored_static_array) ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.static_array, _allocate_memory_stored_static_array)
ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.struct, _allocate_memory_stored_struct) ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.struct, _allocate_memory_stored_struct)
ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.tuple_, _allocate_memory_stored_tuple) ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.tuple_, _allocate_memory_stored_tuple)
@ -325,6 +345,26 @@ def _split_read_bytes(all_bytes: bytes, split_sizes: Iterable[int]) -> Generator
yield all_bytes[offset:offset + size] yield all_bytes[offset:offset + size]
offset += size offset += size
def _load_dynamic_array_from_address(attrs: tuple[runners.RunnerBase, int], da_args: tuple[type3types.Type3]) -> Any:
runner, adr = attrs
da_type, = da_args
sys.stderr.write(f'Reading 0x{adr:08x} {da_type:s}[...]\n')
read_bytes = runner.interpreter_read_memory(adr, 4)
array_len, = struct.unpack('<I', read_bytes)
adr += 4
arg_size_1 = calculate_alloc_size(da_type, is_member=True)
arg_sizes = [arg_size_1 for _ in range(array_len)] # _split_read_bytes requires one arg per value
read_bytes = runner.interpreter_read_memory(adr, sum(arg_sizes))
return tuple(
_unpack(runner, da_type, arg_bytes)
for arg_bytes in _split_read_bytes(read_bytes, arg_sizes)
)
def _load_static_array_from_address(attrs: tuple[runners.RunnerBase, int], sa_args: tuple[type3types.Type3, type3types.IntType3]) -> Any: def _load_static_array_from_address(attrs: tuple[runners.RunnerBase, int], sa_args: tuple[type3types.Type3, type3types.IntType3]) -> Any:
runner, adr = attrs runner, adr = attrs
sub_typ, len_typ = sa_args sub_typ, len_typ = sa_args
@ -379,6 +419,7 @@ def _load_tuple_from_address(attrs: tuple[runners.RunnerBase, int], tp_args: tup
LOAD_FROM_ADDRESS_ROUTER = TypeApplicationRouter[tuple[runners.RunnerBase, int], Any]() LOAD_FROM_ADDRESS_ROUTER = TypeApplicationRouter[tuple[runners.RunnerBase, int], Any]()
LOAD_FROM_ADDRESS_ROUTER.add_n(prelude.bytes_, _load_bytes_from_address) LOAD_FROM_ADDRESS_ROUTER.add_n(prelude.bytes_, _load_bytes_from_address)
LOAD_FROM_ADDRESS_ROUTER.add(prelude.dynamic_array, _load_dynamic_array_from_address)
LOAD_FROM_ADDRESS_ROUTER.add(prelude.static_array, _load_static_array_from_address) LOAD_FROM_ADDRESS_ROUTER.add(prelude.static_array, _load_static_array_from_address)
LOAD_FROM_ADDRESS_ROUTER.add(prelude.struct, _load_struct_from_address) LOAD_FROM_ADDRESS_ROUTER.add(prelude.struct, _load_struct_from_address)
LOAD_FROM_ADDRESS_ROUTER.add(prelude.tuple_, _load_tuple_from_address) LOAD_FROM_ADDRESS_ROUTER.add(prelude.tuple_, _load_tuple_from_address)

View File

@ -60,7 +60,7 @@ CONSTANT: (u32, ) = $VAL0
``` ```
```py ```py
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_'): if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_') or TYPE_NAME.startswith('dynamic_array_'):
expect_type_error( expect_type_error(
'Tuple element count mismatch', 'Tuple element count mismatch',
'The given literal must fit the expected type', 'The given literal must fit the expected type',
@ -113,7 +113,7 @@ def testEntry() -> i32:
``` ```
```py ```py
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_'): if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_') or TYPE_NAME.startswith('dynamic_array_'):
expect_type_error( expect_type_error(
'Mismatch between applied types argument count', 'Mismatch between applied types argument count',
'The type of a tuple is a combination of its members', 'The type of a tuple is a combination of its members',
@ -175,7 +175,7 @@ def testEntry() -> i32:
``` ```
```py ```py
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_') or TYPE_NAME.startswith('struct_'): if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_') or TYPE_NAME.startswith('dynamic_array_') or TYPE_NAME.startswith('struct_'):
expect_type_error( expect_type_error(
TYPE + ' must be (u32, ) instead', TYPE + ' must be (u32, ) instead',
'The type of the value returned from function constant should match its return type', 'The type of the value returned from function constant should match its return type',
@ -226,7 +226,7 @@ def select(x: $TYPE) -> (u32, ):
``` ```
```py ```py
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_') or TYPE_NAME.startswith('struct_'): if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_') or TYPE_NAME.startswith('dynamic_array_') or TYPE_NAME.startswith('struct_'):
expect_type_error( expect_type_error(
TYPE + ' must be (u32, ) instead', TYPE + ' must be (u32, ) instead',
'The type of the value returned from function select should match its return type', 'The type of the value returned from function select should match its return type',
@ -273,7 +273,7 @@ def testEntry() -> i32:
``` ```
```py ```py
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_'): if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_') or TYPE_NAME.startswith('dynamic_array_'):
expect_type_error( expect_type_error(
'Mismatch between applied types argument count', 'Mismatch between applied types argument count',
# FIXME: Shouldn't this be the same as for the else statement? # FIXME: Shouldn't this be the same as for the else statement?
@ -330,7 +330,7 @@ def testEntry() -> i32:
``` ```
```py ```py
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_') or TYPE_NAME.startswith('struct_'): if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_') or TYPE_NAME.startswith('dynamic_array_') or TYPE_NAME.startswith('struct_'):
expect_type_error( expect_type_error(
TYPE + ' must be (u32, ) instead', TYPE + ' must be (u32, ) instead',
'The type of the value passed to argument 0 of function helper should match the type of that argument', 'The type of the value passed to argument 0 of function helper should match the type of that argument',

View File

@ -0,0 +1,5 @@
{
"TYPE_NAME": "dynamic_array_u64",
"TYPE": "u64[...]",
"VAL0": "(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, )"
}

View File

@ -6,7 +6,7 @@ from ..helpers import Suite
@pytest.mark.integration_test @pytest.mark.integration_test
@pytest.mark.parametrize('type_, in_put, exp_result', [ @pytest.mark.parametrize('type_, in_put, exp_result', [
('bytes', b'Hello, world!', 13), ('bytes', b'Hello, world!', 13),
# ('u8[4]', (1, 2, 3, 4), 4), # FIXME: Implement this ('u8[4]', (1, 2, 3, 4), 4),
]) ])
def test_len(type_, in_put, exp_result): def test_len(type_, in_put, exp_result):
code_py = f""" code_py = f"""