Ideas [skip-ci]

This commit is contained in:
Johan B.W. de Vries 2025-04-11 15:50:52 +02:00
parent 87866cff55
commit a59bc9c31d
13 changed files with 382 additions and 336 deletions

View File

@ -15,9 +15,7 @@
- static_array and tuple should probably not be PrimitiveType3, but instead subclass AppliedType3? - static_array and tuple should probably not be PrimitiveType3, but instead subclass AppliedType3?
- See if we want to replace Fractional with Real, and add Rational, Irrationl, Algebraic, Transendental - See if we want to replace Fractional with Real, and add Rational, Irrationl, Algebraic, Transendental
- test_bitwise_or_inv_type
- test_bytes_index_out_of_bounds vs static trap(?) - test_bytes_index_out_of_bounds vs static trap(?)
- test_num.py is probably best as part of the generator?
- Find pytest.mark.skip - Find pytest.mark.skip
- There's a weird resolve_as reference in calculate_alloc_size - There's a weird resolve_as reference in calculate_alloc_size
- Either there should be more of them or less - Either there should be more of them or less
@ -36,3 +34,4 @@
- Filter out methods that aren't used; other the other way around (easier?) only add __ methods when needed - Filter out methods that aren't used; other the other way around (easier?) only add __ methods when needed
- Move UnaryOp.operator into type class methods - Move UnaryOp.operator into type class methods
- Move foldr into type class methods - Move foldr into type class methods
- PrimitiveType is just the type, nothing primitive about it (change the name). u32 are still basic types or something.

View File

@ -27,21 +27,6 @@ def type3(inp: Type3OrPlaceholder) -> str:
if inp is type3types.none: if inp is type3types.none:
return 'None' return 'None'
if isinstance(inp, type3types.AppliedType3):
if inp.base == type3types.tuple:
return '(' + ', '.join(
type3(x)
for x in inp.args
if isinstance(x, Type3) # Skip ints, not allowed here anyhow
) + ', )'
if inp.base == type3types.static_array:
assert 2 == len(inp.args)
assert isinstance(inp.args[0], Type3), TYPE3_ASSERTION_ERROR
assert isinstance(inp.args[1], type3types.IntType3), TYPE3_ASSERTION_ERROR
return type3(inp.args[0]) + '[' + inp.args[1].name + ']'
return inp.name return inp.name
def struct_definition(inp: ourlang.StructDefinition) -> str: def struct_definition(inp: ourlang.StructDefinition) -> str:

View File

@ -286,7 +286,7 @@ def type3(inp: type3types.Type3OrPlaceholder) -> wasm.WasmType:
if inp == type3types.f64: if inp == type3types.f64:
return wasm.WasmTypeFloat64() return wasm.WasmTypeFloat64()
if inp == type3types.bytes: if inp == type3types.bytes_:
# bytes are passed as pointer # bytes are passed as pointer
# And pointers are i32 # And pointers are i32
return wasm.WasmTypeInt32() return wasm.WasmTypeInt32()
@ -295,13 +295,7 @@ def type3(inp: type3types.Type3OrPlaceholder) -> wasm.WasmType:
# Structs are passed as pointer, which are i32 # Structs are passed as pointer, which are i32
return wasm.WasmTypeInt32() return wasm.WasmTypeInt32()
if isinstance(inp, type3types.AppliedType3): if type3classes.InternalPassAsPointer in inp.classes:
if inp.base == type3types.static_array:
# Static Arrays are passed as pointer, which are i32
return wasm.WasmTypeInt32()
if inp.base == type3types.tuple:
# Tuples are passed as pointer, which are i32
return wasm.WasmTypeInt32() return wasm.WasmTypeInt32()
raise NotImplementedError(type3, inp) raise NotImplementedError(type3, inp)
@ -310,9 +304,21 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
""" """
Compile: Instantiation (allocation) of a tuple Compile: Instantiation (allocation) of a tuple
""" """
assert isinstance(inp.type3, type3types.AppliedType3) assert isinstance(inp.type3, type3types.PrimitiveType3)
assert inp.type3.base is type3types.tuple
assert len(inp.elements) == len(inp.type3.args) args: list[type3types.PrimitiveType3] = []
sa_args = type3types.static_array.did_construct(inp.type3)
if sa_args is not None:
sa_type, sa_len = sa_args
args = [sa_type for _ in range(sa_len.value)]
if not args:
tp_args = type3types.tuple_.did_construct(inp.type3)
if tp_args is None:
raise NotImplementedError
args = list(tp_args)
comment_elements = '' comment_elements = ''
for element in inp.elements: for element in inp.elements:
@ -329,14 +335,15 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
# Store each element individually # Store each element individually
offset = 0 offset = 0
for element, exp_type3 in zip(inp.elements, inp.type3.args): for element, exp_type3 in zip(inp.elements, args):
if isinstance(exp_type3, type3types.PlaceholderForType): if isinstance(exp_type3, type3types.PlaceholderForType):
assert exp_type3.resolve_as is not None assert exp_type3.resolve_as is not None
assert isinstance(exp_type3.resolve_as, type3types.PrimitiveType3)
exp_type3 = exp_type3.resolve_as exp_type3 = exp_type3.resolve_as
assert element.type3 == exp_type3 assert element.type3 == exp_type3
if isinstance(exp_type3, type3types.StructType3) or isinstance(exp_type3, type3types.AppliedType3): if type3classes.InternalPassAsPointer in exp_type3.classes:
mtyp = 'i32' mtyp = 'i32'
else: else:
assert isinstance(exp_type3, type3types.PrimitiveType3), NotImplementedError('Tuple of applied types / structs') assert isinstance(exp_type3, type3types.PrimitiveType3), NotImplementedError('Tuple of applied types / structs')
@ -357,6 +364,9 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
""" """
Compile: Any expression Compile: Any expression
""" """
if isinstance(inp, ourlang.ConstantTuple):
raise Exception
if isinstance(inp, ourlang.ConstantPrimitive): if isinstance(inp, ourlang.ConstantPrimitive):
assert isinstance(inp.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR assert isinstance(inp.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
@ -401,11 +411,20 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
if isinstance(inp.variable, ourlang.ModuleConstantDef): if isinstance(inp.variable, ourlang.ModuleConstantDef):
assert isinstance(inp.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR assert isinstance(inp.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
if type3classes.InternalPassAsPointer in inp.type3.classes:
# FIXME: Artifact from older version
assert isinstance(inp.variable.constant, ourlang.ConstantTuple)
address = inp.variable.constant.data_block.address
assert address is not None, 'Value not allocated'
wgn.i32.const(address)
return
if isinstance(inp.type3, type3types.PrimitiveType3): if isinstance(inp.type3, type3types.PrimitiveType3):
expression(wgn, inp.variable.constant) expression(wgn, inp.variable.constant)
return return
if inp.type3 is type3types.bytes: if inp.type3 is type3types.bytes_:
assert isinstance(inp.variable.constant, ourlang.ConstantBytes) assert isinstance(inp.variable.constant, ourlang.ConstantBytes)
address = inp.variable.constant.data_block.address address = inp.variable.constant.data_block.address
@ -421,25 +440,6 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
wgn.i32.const(address) wgn.i32.const(address)
return return
if isinstance(inp.type3, type3types.AppliedType3):
if inp.type3.base == type3types.static_array:
assert isinstance(inp.variable.constant, ourlang.ConstantTuple)
address = inp.variable.constant.data_block.address
assert address is not None, 'Value not allocated'
wgn.i32.const(address)
return
if inp.type3.base == type3types.tuple:
assert isinstance(inp.variable.constant, ourlang.ConstantTuple)
address = inp.variable.constant.data_block.address
assert address is not None, 'Value not allocated'
wgn.i32.const(address)
return
raise NotImplementedError(expression, inp.variable, inp.type3.base)
raise NotImplementedError(expression, inp) raise NotImplementedError(expression, inp)
raise NotImplementedError(expression, inp.variable) raise NotImplementedError(expression, inp.variable)
@ -479,7 +479,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
if inp.type3 == type3types.u32: if inp.type3 == type3types.u32:
if inp.operator == 'len': if inp.operator == 'len':
if inp.right.type3 == type3types.bytes: if inp.right.type3 == type3types.bytes_:
wgn.i32.load() wgn.i32.load()
return return
@ -528,71 +528,71 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
if isinstance(inp, ourlang.Subscript): if isinstance(inp, ourlang.Subscript):
assert isinstance(inp.varref.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR assert isinstance(inp.varref.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
if inp.varref.type3 is type3types.bytes: if inp.varref.type3 is type3types.bytes_:
expression(wgn, inp.varref) expression(wgn, inp.varref)
expression(wgn, inp.index) expression(wgn, inp.index)
wgn.call(stdlib_types.__subscript_bytes__) wgn.call(stdlib_types.__subscript_bytes__)
return return
if isinstance(inp.varref.type3, type3types.AppliedType3): # if isinstance(inp.varref.type3, type3types.AppliedType3):
if inp.varref.type3.base == type3types.static_array: # if inp.varref.type3.base == type3types.static_array:
assert 2 == len(inp.varref.type3.args) # assert 2 == len(inp.varref.type3.args)
el_type = inp.varref.type3.args[0] # el_type = inp.varref.type3.args[0]
assert isinstance(el_type, type3types.Type3) # assert isinstance(el_type, type3types.Type3)
el_len = inp.varref.type3.args[1] # el_len = inp.varref.type3.args[1]
assert isinstance(el_len, type3types.IntType3) # assert isinstance(el_len, type3types.IntType3)
# OPTIMIZE: If index is a constant, we can use offset instead of multiply # # OPTIMIZE: If index is a constant, we can use offset instead of multiply
# and we don't need to do the out of bounds check # # and we don't need to do the out of bounds check
expression(wgn, inp.varref) # expression(wgn, inp.varref)
tmp_var = wgn.temp_var_i32('index') # tmp_var = wgn.temp_var_i32('index')
expression(wgn, inp.index) # expression(wgn, inp.index)
wgn.local.tee(tmp_var) # wgn.local.tee(tmp_var)
# Out of bounds check based on el_len.value # # Out of bounds check based on el_len.value
wgn.i32.const(el_len.value) # wgn.i32.const(el_len.value)
wgn.i32.ge_u() # wgn.i32.ge_u()
with wgn.if_(): # with wgn.if_():
wgn.unreachable(comment='Out of bounds') # wgn.unreachable(comment='Out of bounds')
wgn.local.get(tmp_var) # wgn.local.get(tmp_var)
wgn.i32.const(calculate_alloc_size(el_type)) # wgn.i32.const(calculate_alloc_size(el_type))
wgn.i32.mul() # wgn.i32.mul()
wgn.i32.add() # wgn.i32.add()
assert isinstance(el_type, type3types.PrimitiveType3), NotImplementedError('Tuple of applied types / structs') # assert isinstance(el_type, type3types.PrimitiveType3), NotImplementedError('Tuple of applied types / structs')
mtyp = LOAD_STORE_TYPE_MAP[el_type.name] # mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
wgn.add_statement(f'{mtyp}.load') # wgn.add_statement(f'{mtyp}.load')
return # return
if inp.varref.type3.base == type3types.tuple: # if inp.varref.type3.base == type3types.tuple:
assert isinstance(inp.index, ourlang.ConstantPrimitive) # assert isinstance(inp.index, ourlang.ConstantPrimitive)
assert isinstance(inp.index.value, int) # assert isinstance(inp.index.value, int)
offset = 0 # offset = 0
for el_type in inp.varref.type3.args[0:inp.index.value]: # for el_type in inp.varref.type3.args[0:inp.index.value]:
assert isinstance(el_type, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR # assert isinstance(el_type, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
offset += calculate_alloc_size(el_type) # offset += calculate_alloc_size(el_type)
# This doubles as the out of bounds check # # This doubles as the out of bounds check
el_type = inp.varref.type3.args[inp.index.value] # el_type = inp.varref.type3.args[inp.index.value]
assert isinstance(el_type, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR # assert isinstance(el_type, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
expression(wgn, inp.varref) # expression(wgn, inp.varref)
if isinstance(el_type, type3types.StructType3): # if isinstance(el_type, type3types.StructType3):
mtyp = 'i32' # mtyp = 'i32'
elif isinstance(el_type, type3types.AppliedType3) and el_type.base is type3types.tuple: # elif type3classes.InternalPassAsPointer in el_type.classes:
mtyp = 'i32' # mtyp = 'i32'
else: # else:
assert isinstance(el_type, type3types.PrimitiveType3), NotImplementedError('Tuple of applied types / structs') # assert isinstance(el_type, type3types.PrimitiveType3), NotImplementedError('Tuple of applied types / structs')
mtyp = LOAD_STORE_TYPE_MAP[el_type.name] # mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
wgn.add_statement(f'{mtyp}.load', f'offset={offset}') # wgn.add_statement(f'{mtyp}.load', f'offset={offset}')
return # return
raise NotImplementedError(expression, inp, inp.varref.type3) raise NotImplementedError(expression, inp, inp.varref.type3)
@ -618,7 +618,7 @@ def expression_fold(wgn: WasmGenerator, inp: ourlang.Fold) -> None:
""" """
assert isinstance(inp.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR assert isinstance(inp.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
if inp.iter.type3 is not type3types.bytes: if inp.iter.type3 is not type3types.bytes_:
raise NotImplementedError(expression_fold, inp, inp.iter.type3) raise NotImplementedError(expression_fold, inp, inp.iter.type3)
wgn.add_statement('nop', comment='acu :: u8') wgn.add_statement('nop', comment='acu :: u8')
@ -694,6 +694,7 @@ def statement_return(wgn: WasmGenerator, inp: ourlang.StatementReturn) -> None:
""" """
Compile: Return statement Compile: Return statement
""" """
print('inp', inp)
expression(wgn, inp.value) expression(wgn, inp.value)
wgn.return_() wgn.return_()
@ -906,7 +907,7 @@ def module_data(inp: ourlang.ModuleData) -> bytes:
data_list.append(module_data_f64(constant.value)) data_list.append(module_data_f64(constant.value))
continue continue
if constant.type3 == type3types.bytes: if constant.type3 == type3types.bytes_:
assert isinstance(constant, ourlang.ConstantBytes) assert isinstance(constant, ourlang.ConstantBytes)
assert isinstance(constant.value, bytes) assert isinstance(constant.value, bytes)
data_list.append(module_data_u32(len(constant.value))) data_list.append(module_data_u32(len(constant.value)))
@ -987,7 +988,7 @@ def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstruc
# Store each member individually # Store each member individually
for memname, mtyp3 in inp.struct_type3.members.items(): for memname, mtyp3 in inp.struct_type3.members.items():
mtyp: Optional[str] mtyp: Optional[str]
if isinstance(mtyp3, type3types.StructType3) or isinstance(mtyp3, type3types.AppliedType3): if type3classes.InternalPassAsPointer in mtyp3.classes:
mtyp = 'i32' mtyp = 'i32'
else: else:
mtyp = LOAD_STORE_TYPE_MAP.get(mtyp3.name) mtyp = LOAD_STORE_TYPE_MAP.get(mtyp3.name)

View File

@ -275,6 +275,9 @@ class StatementReturn(Statement):
def __init__(self, value: Expression) -> None: def __init__(self, value: Expression) -> None:
self.value = value self.value = value
def __repr__(self) -> str:
return f'StatementReturn({repr(self.value)})'
class StatementIf(Statement): class StatementIf(Statement):
""" """
An if statement within a function An if statement within a function

View File

@ -664,7 +664,7 @@ class OurVisitor:
raise NotImplementedError(f'{node.value} as constant') raise NotImplementedError(f'{node.value} as constant')
def visit_type(self, module: Module, node: ast.expr) -> type3types.Type3: def visit_type(self, module: Module, node: ast.expr) -> type3types.PrimitiveType3:
if isinstance(node, ast.Constant): if isinstance(node, ast.Constant):
if node.value is None: if node.value is None:
return type3types.none return type3types.none
@ -693,18 +693,17 @@ class OurVisitor:
if not isinstance(node.ctx, ast.Load): if not isinstance(node.ctx, ast.Load):
_raise_static_error(node, 'Must be load context') _raise_static_error(node, 'Must be load context')
return type3types.AppliedType3( return type3types.static_array(
type3types.static_array, self.visit_type(module, node.value),
[self.visit_type(module, node.value), type3types.IntType3(node.slice.value)], type3types.IntType3(node.slice.value),
) )
if isinstance(node, ast.Tuple): if isinstance(node, ast.Tuple):
if not isinstance(node.ctx, ast.Load): if not isinstance(node.ctx, ast.Load):
_raise_static_error(node, 'Must be load context') _raise_static_error(node, 'Must be load context')
return type3types.AppliedType3( return type3types.tuple_(
type3types.tuple, *(self.visit_type(module, elt) for elt in node.elts)
(self.visit_type(module, elt) for elt in node.elts)
) )
raise NotImplementedError(f'{node} as type') raise NotImplementedError(f'{node} as type')

View File

@ -11,7 +11,7 @@ def calculate_alloc_size(typ: type3types.Type3, is_member: bool = False) -> int:
if typ in (type3types.u64, type3types.i64, type3types.f64, ): if typ in (type3types.u64, type3types.i64, type3types.f64, ):
return 8 return 8
if typ == type3types.bytes: if typ == type3types.bytes_:
if is_member: if is_member:
return 4 return 4
@ -27,28 +27,30 @@ def calculate_alloc_size(typ: type3types.Type3, is_member: bool = False) -> int:
for x in typ.members.values() for x in typ.members.values()
) )
if isinstance(typ, type3types.AppliedType3): assert isinstance(typ, type3types.PrimitiveType3)
if typ.base is type3types.static_array:
sa_args = type3types.static_array.did_construct(typ)
if sa_args is not None:
if is_member: if is_member:
# tuples referred to by other structs or tuples are pointers # tuples referred to by other structs or tuples are pointers
return 4 return 4
assert isinstance(typ.args[0], type3types.Type3) sa_type, sa_len = sa_args
assert isinstance(typ.args[1], type3types.IntType3)
return typ.args[1].value * calculate_alloc_size(typ.args[0], is_member=True) return sa_len.value * calculate_alloc_size(sa_type, is_member=True)
if typ.base is type3types.tuple: tp_args = type3types.tuple_.did_construct(typ)
if tp_args is not None:
if is_member: if is_member:
# tuples referred to by other structs or tuples are pointers # tuples referred to by other structs or tuples are pointers
return 4 return 4
size = 0 size = 0
for arg in typ.args: for arg in tp_args:
assert not isinstance(arg, type3types.IntType3) assert not isinstance(arg, type3types.IntType3)
if isinstance(arg, type3types.PlaceholderForType): if isinstance(arg, type3types.PlaceholderForType):
assert arg.resolve_as is not None assert isinstance(arg.resolve_as, type3types.PrimitiveType3)
arg = arg.resolve_as arg = arg.resolve_as
size += calculate_alloc_size(arg, is_member=True) size += calculate_alloc_size(arg, is_member=True)

View File

@ -112,10 +112,6 @@ class SameTypeConstraint(ConstraintBase):
known_types.append(typ) known_types.append(typ)
continue continue
if isinstance(typ, types.AppliedType3):
known_types.append(typ)
continue
if isinstance(typ, types.PlaceholderForType): if isinstance(typ, types.PlaceholderForType):
if typ.resolve_as is not None: if typ.resolve_as is not None:
known_types.append(typ.resolve_as) known_types.append(typ.resolve_as)
@ -132,38 +128,6 @@ class SameTypeConstraint(ConstraintBase):
first_type = known_types[0] first_type = known_types[0]
for typ in known_types[1:]: for typ in known_types[1:]:
if isinstance(first_type, types.AppliedType3) and isinstance(typ, types.AppliedType3):
if first_type.base is types.tuple and typ.base is types.static_array:
# Swap so we can reuse the code below
# Hope that it still gives proper type errors
first_type, typ = typ, first_type
if first_type.base is types.static_array and typ.base is types.tuple:
assert isinstance(first_type.args[1], types.IntType3)
length = first_type.args[1].value
if len(typ.args) != length:
return Error('Mismatch between applied types argument count', comment=self.comment)
for typ_arg in typ.args:
new_constraint_list.append(SameTypeConstraint(
first_type.args[0], typ_arg
))
continue
if first_type.base != typ.base:
return Error('Mismatch between applied types base', comment=self.comment)
if len(first_type.args) != len(typ.args):
return Error('Mismatch between applied types argument count', comment=self.comment)
for first_type_arg, typ_arg in zip(first_type.args, typ.args):
new_constraint_list.append(SameTypeConstraint(
first_type_arg, typ_arg
))
continue
if typ != first_type: if typ != first_type:
return Error(f'{typ:s} must be {first_type:s} instead', comment=self.comment) return Error(f'{typ:s} must be {first_type:s} instead', comment=self.comment)
@ -198,6 +162,47 @@ class SameTypeConstraint(ConstraintBase):
return f'SameTypeConstraint({args}, comment={repr(self.comment)})' return f'SameTypeConstraint({args}, comment={repr(self.comment)})'
class TupleMatchConstraint(ConstraintBase):
def __init__(self, exp_type: types.Type3OrPlaceholder, args: List[types.Type3OrPlaceholder], comment: str):
super().__init__(comment=comment)
self.exp_type = exp_type
self.args = list(args)
def check(self) -> CheckResult:
exp_type = self.exp_type
if isinstance(exp_type, types.PlaceholderForType):
if exp_type.resolve_as is None:
return RequireTypeSubstitutes()
exp_type = exp_type.resolve_as
assert isinstance(exp_type, types.PrimitiveType3)
sa_args = types.static_array.did_construct(exp_type)
if sa_args is not None:
sa_type, sa_len = sa_args
if sa_len.value != len(self.args):
return Error('Mismatch between applied types argument count', comment=self.comment)
return [
SameTypeConstraint(arg, sa_type)
for arg in self.args
]
tp_args = types.tuple_.did_construct(exp_type)
if tp_args is not None:
if len(tp_args) != len(self.args):
return Error('Mismatch between applied types argument count', comment=self.comment)
return [
SameTypeConstraint(arg, oth_arg)
for arg, oth_arg in zip(self.args, tp_args)
]
raise NotImplementedError(exp_type)
class IntegerCompareConstraint(ConstraintBase): class IntegerCompareConstraint(ConstraintBase):
""" """
Verifies that the given IntType3 are in order (<=) Verifies that the given IntType3 are in order (<=)
@ -401,7 +406,7 @@ class LiteralFitsConstraint(ConstraintBase):
return Error('Must be real', comment=self.comment) # FIXME: Add line information return Error('Must be real', comment=self.comment) # FIXME: Add line information
if self.type3 is types.bytes: if self.type3 is types.bytes_:
if isinstance(self.literal.value, bytes): if isinstance(self.literal.value, bytes):
return None return None
@ -409,45 +414,47 @@ class LiteralFitsConstraint(ConstraintBase):
res: NewConstraintList res: NewConstraintList
if isinstance(self.type3, types.AppliedType3): assert isinstance(self.type3, types.PrimitiveType3)
if self.type3.base == types.tuple:
tp_args = types.tuple_.did_construct(self.type3)
if tp_args is not None:
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)
if len(self.type3.args) != len(self.literal.value): if len(tp_args) != len(self.literal.value):
return Error('Tuple element count mismatch', comment=self.comment) return Error('Tuple element count mismatch', comment=self.comment)
res = [] res = []
res.extend( res.extend(
LiteralFitsConstraint(x, y) LiteralFitsConstraint(x, y)
for x, y in zip(self.type3.args, self.literal.value) for x, y in zip(tp_args, self.literal.value)
) )
res.extend( res.extend(
SameTypeConstraint(x, y.type3) SameTypeConstraint(x, y.type3)
for x, y in zip(self.type3.args, self.literal.value) for x, y in zip(tp_args, self.literal.value)
) )
return res return res
if self.type3.base == types.static_array: sa_args = types.static_array.did_construct(self.type3)
if sa_args is not None:
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)
assert 2 == len(self.type3.args) sa_type, sa_len = sa_args
assert isinstance(self.type3.args[1], types.IntType3)
if self.type3.args[1].value != len(self.literal.value): if sa_len.value != len(self.literal.value):
return Error('Member count mismatch', comment=self.comment) return Error('Member count mismatch', comment=self.comment)
res = [] res = []
res.extend( res.extend(
LiteralFitsConstraint(self.type3.args[0], y) LiteralFitsConstraint(sa_type, y)
for y in self.literal.value for y in self.literal.value
) )
res.extend( res.extend(
SameTypeConstraint(self.type3.args[0], y.type3) SameTypeConstraint(sa_type, y.type3)
for y in self.literal.value for y in self.literal.value
) )
@ -517,42 +524,42 @@ class CanBeSubscriptedConstraint(ConstraintBase):
self.type3 = self.type3.resolve_as self.type3 = self.type3.resolve_as
if isinstance(self.type3, types.AppliedType3): # if isinstance(self.type3, types.AppliedType3):
if self.type3.base == types.static_array: # if self.type3.base == types.static_array:
result: List[ConstraintBase] = [ # result: List[ConstraintBase] = [
SameTypeConstraint(types.u32, self.index_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'), # SameTypeConstraint(types.u32, self.index_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
SameTypeConstraint(self.type3.args[0], self.ret_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'), # SameTypeConstraint(self.type3.args[0], self.ret_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
] # ]
if isinstance(self.index, ourlang.ConstantPrimitive): # if isinstance(self.index, ourlang.ConstantPrimitive):
assert isinstance(self.index.value, int) # assert isinstance(self.index.value, int)
assert isinstance(self.type3.args[1], types.IntType3) # assert isinstance(self.type3.args[1], types.IntType3)
result.append( # result.append(
IntegerCompareConstraint( # IntegerCompareConstraint(
types.IntType3(0), types.IntType3(self.index.value), types.IntType3(self.type3.args[1].value - 1), # types.IntType3(0), types.IntType3(self.index.value), types.IntType3(self.type3.args[1].value - 1),
comment='Subscript static array must fit the size of the array' # comment='Subscript static array must fit the size of the array'
) # )
) # )
return result # return result
if self.type3.base == types.tuple: # if self.type3.base == types.tuple:
if not isinstance(self.index, ourlang.ConstantPrimitive): # if not isinstance(self.index, ourlang.ConstantPrimitive):
return Error('Must index with literal') # return Error('Must index with literal')
if not isinstance(self.index.value, int): # if not isinstance(self.index.value, int):
return Error('Must index with integer literal') # return Error('Must index with integer literal')
if self.index.value < 0 or len(self.type3.args) <= self.index.value: # if self.index.value < 0 or len(self.type3.args) <= self.index.value:
return Error('Tuple index out of range') # return Error('Tuple index out of range')
return [ # return [
SameTypeConstraint(types.u32, self.index_type3, comment=f'Tuple subscript index {self.index.value}'), # SameTypeConstraint(types.u32, self.index_type3, comment=f'Tuple subscript index {self.index.value}'),
SameTypeConstraint(self.type3.args[self.index.value], self.ret_type3, comment=f'Tuple subscript index {self.index.value}'), # SameTypeConstraint(self.type3.args[self.index.value], self.ret_type3, comment=f'Tuple subscript index {self.index.value}'),
] # ]
if self.type3 is types.bytes: if self.type3 is types.bytes_:
return [ return [
SameTypeConstraint(types.u32, self.index_type3, comment='([]) :: bytes -> u32 -> u8'), SameTypeConstraint(types.u32, self.index_type3, comment='([]) :: bytes -> u32 -> u8'),
SameTypeConstraint(types.u8, self.ret_type3, comment='([]) :: bytes -> u32 -> u8'), SameTypeConstraint(types.u8, self.ret_type3, comment='([]) :: bytes -> u32 -> u8'),

View File

@ -16,6 +16,7 @@ from .constraints import (
LiteralFitsConstraint, LiteralFitsConstraint,
MustImplementTypeClassConstraint, MustImplementTypeClassConstraint,
SameTypeConstraint, SameTypeConstraint,
TupleMatchConstraint,
) )
ConstraintGenerator = Generator[ConstraintBase, None, None] ConstraintGenerator = Generator[ConstraintBase, None, None]
@ -142,9 +143,9 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
yield from expression(ctx, arg) yield from expression(ctx, arg)
r_type.append(arg.type3) r_type.append(arg.type3)
yield SameTypeConstraint( yield TupleMatchConstraint(
inp.type3, inp.type3,
type3types.AppliedType3(type3types.tuple, r_type), r_type,
comment='The type of a tuple is a combination of its members' comment='The type of a tuple is a combination of its members'
) )

View File

@ -13,7 +13,6 @@ from .constraints import (
) )
from .constraintsgenerator import phasm_type3_generate_constraints from .constraintsgenerator import phasm_type3_generate_constraints
from .types import ( from .types import (
AppliedType3,
IntType3, IntType3,
PlaceholderForType, PlaceholderForType,
PrimitiveType3, PrimitiveType3,
@ -153,14 +152,6 @@ def get_printable_type_name(inp: Type3OrPlaceholder, placeholder_id_map: Dict[in
placeholder_id_map[placeholder_id] = 'T' + str(len(placeholder_id_map) + 1) placeholder_id_map[placeholder_id] = 'T' + str(len(placeholder_id_map) + 1)
return placeholder_id_map[placeholder_id] return placeholder_id_map[placeholder_id]
if isinstance(inp, AppliedType3):
return (
get_printable_type_name(inp.base, placeholder_id_map)
+ ' ('
+ ') ('.join(get_printable_type_name(x, placeholder_id_map) for x in inp.args)
+ ')'
)
raise NotImplementedError(inp) raise NotImplementedError(inp)
def print_constraint_list(placeholder_id_map: Dict[int, str], constraint_list: List[ConstraintBase], placeholder_substitutes: SubstitutionMap) -> None: def print_constraint_list(placeholder_id_map: Dict[int, str], constraint_list: List[ConstraintBase], placeholder_substitutes: SubstitutionMap) -> None:

View File

@ -93,6 +93,8 @@ class Type3Class:
def __repr__(self) -> str: def __repr__(self) -> str:
return self.name return self.name
InternalPassAsPointer = Type3Class('InternalPassAsPointer', ['a'], methods={}, operators={})
Eq = Type3Class('Eq', ['a'], methods={}, operators={ Eq = Type3Class('Eq', ['a'], methods={}, operators={
'==': 'a -> a -> bool', '==': 'a -> a -> bool',
'!=': 'a -> a -> bool', '!=': 'a -> a -> bool',

View File

@ -4,7 +4,19 @@ Contains the final types for use in Phasm
These are actual, instantiated types; not the abstract types that the These are actual, instantiated types; not the abstract types that the
constraint generator works with. constraint generator works with.
""" """
from typing import Any, Dict, Iterable, List, Optional, Protocol, Set, Union from typing import (
Any,
Dict,
Generic,
Iterable,
List,
Optional,
Protocol,
Set,
Tuple,
TypeVar,
Union,
)
from .typeclasses import ( from .typeclasses import (
Bits, Bits,
@ -12,6 +24,7 @@ from .typeclasses import (
Floating, Floating,
Fractional, Fractional,
Integral, Integral,
InternalPassAsPointer,
IntNum, IntNum,
NatNum, NatNum,
Ord, Ord,
@ -83,7 +96,7 @@ class Type3:
return not self.__eq__(other) return not self.__eq__(other)
def __hash__(self) -> int: def __hash__(self) -> int:
raise NotImplementedError return hash(self.name)
def __bool__(self) -> bool: def __bool__(self) -> bool:
raise NotImplementedError raise NotImplementedError
@ -98,6 +111,9 @@ class PrimitiveType3(Type3):
class IntType3(Type3): class IntType3(Type3):
""" """
Sometimes you can have an int as type, e.g. when using static arrays Sometimes you can have an int as type, e.g. when using static arrays
This is not the same as an int on the language level.
[1.0, 1.2] :: Float[2] :: * -> Int -> *
""" """
__slots__ = ('value', ) __slots__ = ('value', )
@ -119,6 +135,9 @@ class IntType3(Type3):
raise NotImplementedError raise NotImplementedError
def __hash__(self) -> int:
return hash(self.value)
class PlaceholderForType: class PlaceholderForType:
""" """
A placeholder type, for when we don't know the final type yet A placeholder type, for when we don't know the final type yet
@ -166,64 +185,100 @@ class PlaceholderForType:
Type3OrPlaceholder = Union[Type3, PlaceholderForType] Type3OrPlaceholder = Union[Type3, PlaceholderForType]
class AppliedType3(Type3): T = TypeVar('T')
"""
A Type3 that has been applied to another type
"""
__slots__ = ('base', 'args', )
base: PrimitiveType3 class TypeConstructor(Generic[T]):
""" """
The base type Base class for type construtors
"""
__slots__ = ('name', 'classes', 'type_classes', '_cache', '_reverse_cache')
name: str
"""
The name of the type constructor
""" """
args: List[Type3OrPlaceholder] classes: Set[Type3Class]
""" """
The applied types (or placeholders there for) The type classes that this constructor implements
""" """
def __init__(self, base: PrimitiveType3, args: Iterable[Type3OrPlaceholder]) -> None: type_classes: Set[Type3Class]
args = [*args] """
assert args, 'Must at least one argument' The type classes that the constructed types implement
"""
super().__init__( _cache: dict[T, PrimitiveType3]
base.name """
+ ' (' When constructing a type with the same arguments,
+ ') ('.join(str(x) for x in args) # FIXME: Do we need to redo the name on substitution? it should produce the exact same result.
+ ')', """
[]
)
self.base = base _reverse_cache: dict[PrimitiveType3, T]
self.args = args """
Sometimes we need to know the key that created a type.
"""
@property def __init__(self, name: str, classes: Iterable[Type3Class], type_classes: Iterable[Type3Class]) -> None:
def has_placeholders(self) -> bool: self.name = name
return any( self.classes = set(classes)
isinstance(x, PlaceholderForType) self.type_classes = set(type_classes)
for x in self.args
)
def __eq__(self, other: Any) -> bool: self._cache = {}
if not isinstance(other, Type3): self._reverse_cache = {}
def make_name(self, key: T) -> str:
raise NotImplementedError raise NotImplementedError
if not isinstance(other, AppliedType3): def did_construct(self, typ: PrimitiveType3) -> T | None:
return False return self._reverse_cache.get(typ)
return ( def construct(self, key: T) -> PrimitiveType3:
self.base == other.base result = self._cache.get(key, None)
and len(self.args) == len(other.args) if result is None:
and all( self._cache[key] = result = PrimitiveType3(self.make_name(key), self.type_classes)
s == x self._reverse_cache[result] = key
for s, x in zip(self.args, other.args)
)
)
def __repr__(self) -> str: return result
return f'AppliedType3({repr(self.base)}, {repr(self.args)})'
class StructType3(Type3): class TypeConstructor_Type(TypeConstructor[PrimitiveType3]):
"""
Base class type constructors of kind: * -> *
"""
__slots__ = ()
def __call__(self, arg: PrimitiveType3) -> PrimitiveType3:
raise NotImplementedError
class TypeConstructor_TypeInt(TypeConstructor[Tuple[PrimitiveType3, IntType3]]):
"""
Base class type constructors of kind: * -> Int -> *
"""
__slots__ = ()
def make_name(self, key: Tuple[PrimitiveType3, IntType3]) -> str:
return f'{self.name} {key[0].name} {key[1].value}'
def __call__(self, arg0: PrimitiveType3, arg1: IntType3) -> PrimitiveType3:
return self.construct((arg0, arg1))
class TypeConstructor_TypeStar(TypeConstructor[Tuple[PrimitiveType3, ...]]):
"""
Base class type constructors of variadic kind
"""
def __call__(self, *args: PrimitiveType3) -> PrimitiveType3:
key: Tuple[PrimitiveType3, ...] = tuple(args)
return self.construct(key)
class TypeConstructor_StaticArray(TypeConstructor_TypeInt):
def make_name(self, key: Tuple[PrimitiveType3, IntType3]) -> str:
return f'{key[0].name}[{key[1].value}]'
class TypeConstructor_Tuple(TypeConstructor_TypeStar):
def make_name(self, key: Tuple[PrimitiveType3, ...]) -> str:
return '(' + ', '.join(x.name for x in key) + ', )'
class StructType3(PrimitiveType3):
""" """
A Type3 struct with named members A Type3 struct with named members
""" """
@ -315,20 +370,27 @@ f64 = PrimitiveType3('f64', [Eq, Floating, Fractional, IntNum, NatNum, Ord])
A 32-bits IEEE 754 float, of 64 bits width. A 32-bits IEEE 754 float, of 64 bits width.
""" """
bytes = PrimitiveType3('bytes', []) bytes_ = PrimitiveType3('bytes', [])
""" """
This is a runtime-determined length piece of memory that can be indexed at runtime. This is a runtime-determined length piece of memory that can be indexed at runtime.
""" """
static_array = PrimitiveType3('static_array', []) static_array = TypeConstructor_StaticArray('static_array', [], [
Eq,
InternalPassAsPointer,
])
""" """
This is a fixed length piece of memory that can be indexed at runtime. A type constructor.
Any static array is a fixed length piece of memory that can be indexed at runtime.
It should be applied with one argument. It has a runtime-dynamic length It should be applied with one argument. It has a runtime-dynamic length
of the same type repeated. of the same type repeated.
""" """
tuple = PrimitiveType3('tuple', []) # pylint: disable=W0622 tuple_ = TypeConstructor_Tuple('tuple', [], [
InternalPassAsPointer,
])
""" """
This is a fixed length piece of memory. This is a fixed length piece of memory.
@ -336,7 +398,7 @@ It should be applied with zero or more arguments. It has a compile time
determined length, and each argument can be different. determined length, and each argument can be different.
""" """
LOOKUP_TABLE: Dict[str, Type3] = { LOOKUP_TABLE: Dict[str, PrimitiveType3] = {
'none': none, 'none': none,
'bool': bool_, 'bool': bool_,
'u8': u8, 'u8': u8,
@ -347,5 +409,5 @@ LOOKUP_TABLE: Dict[str, Type3] = {
'i64': i64, 'i64': i64,
'f32': f32, 'f32': f32,
'f64': f64, 'f64': f64,
'bytes': bytes, 'bytes': bytes_,
} }

View File

@ -5,6 +5,7 @@ from typing import Any, Generator, Iterable, List, TextIO, Union
from phasm import compiler from phasm import compiler
from phasm.codestyle import phasm_render from phasm.codestyle import phasm_render
from phasm.runtime import calculate_alloc_size from phasm.runtime import calculate_alloc_size
from phasm.type3 import typeclasses as type3classes
from phasm.type3 import types as type3types from phasm.type3 import types as type3types
from . import runners from . import runners
@ -82,21 +83,22 @@ class Suite:
wasm_args.append(arg) wasm_args.append(arg)
continue continue
if arg_typ is type3types.bytes: if arg_typ is type3types.bytes_:
adr = _allocate_memory_stored_value(runner, arg_typ, arg) adr = _allocate_memory_stored_value(runner, arg_typ, arg)
wasm_args.append(adr) wasm_args.append(adr)
continue continue
if isinstance(arg_typ, type3types.AppliedType3): assert isinstance(arg_typ, type3types.PrimitiveType3)
if arg_typ.base is type3types.static_array: sa_args = type3types.static_array.did_construct(arg_typ)
if sa_args is not None:
adr = _allocate_memory_stored_value(runner, arg_typ, arg) adr = _allocate_memory_stored_value(runner, arg_typ, arg)
wasm_args.append(adr) wasm_args.append(adr)
continue continue
if arg_typ.base is type3types.tuple: # if arg_typ.base is type3types.tuple:
adr = _allocate_memory_stored_value(runner, arg_typ, arg) # adr = _allocate_memory_stored_value(runner, arg_typ, arg)
wasm_args.append(adr) # wasm_args.append(adr)
continue # continue
if isinstance(arg_typ, type3types.StructType3): if isinstance(arg_typ, type3types.StructType3):
adr = _allocate_memory_stored_value(runner, arg_typ, arg) adr = _allocate_memory_stored_value(runner, arg_typ, arg)
@ -142,7 +144,7 @@ def _write_memory_stored_value(
val_typ: type3types.Type3, val_typ: type3types.Type3,
val: Any, val: Any,
) -> int: ) -> int:
if val_typ is type3types.bytes: if val_typ is type3types.bytes_:
adr2 = _allocate_memory_stored_value(runner, val_typ, val) adr2 = _allocate_memory_stored_value(runner, val_typ, val)
runner.interpreter_write_memory(adr, compiler.module_data_u32(adr2)) runner.interpreter_write_memory(adr, compiler.module_data_u32(adr2))
return 4 return 4
@ -152,11 +154,11 @@ def _write_memory_stored_value(
runner.interpreter_write_memory(adr, to_write) runner.interpreter_write_memory(adr, to_write)
return len(to_write) return len(to_write)
if isinstance(val_typ, type3types.AppliedType3): # if isinstance(val_typ, type3types.AppliedType3):
if val_typ.base in (type3types.static_array, type3types.tuple, ): # if val_typ.base in (type3types.static_array, type3types.tuple, ):
adr2 = _allocate_memory_stored_value(runner, val_typ, val) # adr2 = _allocate_memory_stored_value(runner, val_typ, val)
runner.interpreter_write_memory(adr, compiler.module_data_u32(adr2)) # runner.interpreter_write_memory(adr, compiler.module_data_u32(adr2))
return 4 # return 4
if isinstance(val_typ, type3types.StructType3): if isinstance(val_typ, type3types.StructType3):
adr2 = _allocate_memory_stored_value(runner, val_typ, val) adr2 = _allocate_memory_stored_value(runner, val_typ, val)
@ -170,7 +172,7 @@ def _allocate_memory_stored_value(
val_typ: type3types.Type3, val_typ: type3types.Type3,
val: Any val: Any
) -> int: ) -> int:
if val_typ is type3types.bytes: if val_typ is type3types.bytes_:
assert isinstance(val, bytes) assert isinstance(val, bytes)
adr = runner.call('stdlib.types.__alloc_bytes__', len(val)) adr = runner.call('stdlib.types.__alloc_bytes__', len(val))
@ -180,44 +182,42 @@ def _allocate_memory_stored_value(
runner.interpreter_write_memory(adr + 4, val) runner.interpreter_write_memory(adr + 4, val)
return adr return adr
if isinstance(val_typ, type3types.AppliedType3): assert isinstance(val_typ, type3types.PrimitiveType3)
if val_typ.base is type3types.static_array: sa_args = type3types.static_array.did_construct(val_typ)
if sa_args is not None:
assert isinstance(val, tuple) assert isinstance(val, tuple)
sa_type, sa_len = sa_args
alloc_size = calculate_alloc_size(val_typ) alloc_size = calculate_alloc_size(val_typ)
adr = runner.call('stdlib.alloc.__alloc__', alloc_size) adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
assert isinstance(adr, int) assert isinstance(adr, int)
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n') sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
val_el_typ = val_typ.args[0] tuple_len = sa_len.value
assert not isinstance(val_el_typ, type3types.PlaceholderForType)
tuple_len_obj = val_typ.args[1]
assert isinstance(tuple_len_obj, type3types.IntType3)
tuple_len = tuple_len_obj.value
assert tuple_len == len(val) assert tuple_len == len(val)
offset = adr offset = adr
for val_el_val in val: for val_el_val in val:
offset += _write_memory_stored_value(runner, offset, val_el_typ, val_el_val) offset += _write_memory_stored_value(runner, offset, sa_type, val_el_val)
return adr return adr
if val_typ.base is type3types.tuple: # if val_typ.base is type3types.tuple:
assert isinstance(val, tuple) # assert isinstance(val, tuple)
alloc_size = calculate_alloc_size(val_typ) # alloc_size = calculate_alloc_size(val_typ)
adr = runner.call('stdlib.alloc.__alloc__', alloc_size) # adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
assert isinstance(adr, int) # assert isinstance(adr, int)
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n') # sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
assert len(val) == len(val_typ.args) # assert len(val) == len(val_typ.args)
offset = adr # offset = adr
for val_el_val, val_el_typ in zip(val, val_typ.args): # for val_el_val, val_el_typ in zip(val, val_typ.args):
assert not isinstance(val_el_typ, type3types.PlaceholderForType) # assert not isinstance(val_el_typ, type3types.PlaceholderForType)
offset += _write_memory_stored_value(runner, offset, val_el_typ, val_el_val) # offset += _write_memory_stored_value(runner, offset, val_el_typ, val_el_val)
return adr # return adr
if isinstance(val_typ, type3types.StructType3): if isinstance(val_typ, type3types.StructType3):
assert isinstance(val, dict) assert isinstance(val, dict)
@ -278,21 +278,23 @@ def _load_memory_stored_returned_value(
assert isinstance(wasm_value, float), wasm_value assert isinstance(wasm_value, float), wasm_value
return wasm_value return wasm_value
if ret_type3 is type3types.bytes: if ret_type3 is type3types.bytes_:
assert isinstance(wasm_value, int), wasm_value assert isinstance(wasm_value, int), wasm_value
return _load_bytes_from_address(runner, ret_type3, wasm_value) return _load_bytes_from_address(runner, ret_type3, wasm_value)
if isinstance(ret_type3, type3types.AppliedType3): assert isinstance(ret_type3, type3types.PrimitiveType3) # Type hint
if ret_type3.base is type3types.static_array:
sa_args = type3types.static_array.did_construct(ret_type3)
if sa_args is not None:
assert isinstance(wasm_value, int), wasm_value assert isinstance(wasm_value, int), wasm_value
return _load_static_array_from_address(runner, ret_type3, wasm_value) return _load_static_array_from_address(runner, sa_args[0], sa_args[1], wasm_value)
if ret_type3.base is type3types.tuple: # if ret_type3.base is type3types.tuple:
assert isinstance(wasm_value, int), wasm_value # assert isinstance(wasm_value, int), wasm_value
return _load_tuple_from_address(runner, ret_type3, wasm_value) # return _load_tuple_from_address(runner, ret_type3, wasm_value)
if isinstance(ret_type3, type3types.StructType3): if isinstance(ret_type3, type3types.StructType3):
return _load_struct_from_address(runner, ret_type3, wasm_value) return _load_struct_from_address(runner, ret_type3, wasm_value)
@ -334,23 +336,28 @@ def _unpack(runner: runners.RunnerBase, typ: type3types.Type3, inp: bytes) -> An
assert len(inp) == 8 assert len(inp) == 8
return struct.unpack('<d', inp)[0] return struct.unpack('<d', inp)[0]
if typ is type3types.bytes: if typ is type3types.bytes_:
# Note: For bytes, inp should contain a 4 byte pointer # Note: For bytes, inp should contain a 4 byte pointer
assert len(inp) == 4 assert len(inp) == 4
adr = struct.unpack('<I', inp)[0] adr = struct.unpack('<I', inp)[0]
return _load_bytes_from_address(runner, typ, adr) return _load_bytes_from_address(runner, typ, adr)
if isinstance(typ, type3types.AppliedType3): if type3classes.InternalPassAsPointer in typ.classes:
# Note: For applied types, inp should contain a 4 byte pointer # Note: For applied types, inp should contain a 4 byte pointer
assert len(inp) == 4 assert len(inp) == 4
adr = struct.unpack('<I', inp)[0] adr = struct.unpack('<I', inp)[0]
if typ.base is type3types.static_array: assert isinstance(typ, type3types.PrimitiveType3)
return _load_static_array_from_address(runner, typ, adr)
if typ.base is type3types.tuple: sa_args = type3types.static_array.did_construct(typ)
return _load_tuple_from_address(runner, typ, adr) if sa_args is not None:
sa_type, sa_len = sa_args
return _load_static_array_from_address(runner, sa_type, sa_len, adr)
tp_args = type3types.tuple_.did_construct(typ)
if tp_args is not None:
return _load_tuple_from_address(runner, tp_args, adr)
if isinstance(typ, type3types.StructType3): if isinstance(typ, type3types.StructType3):
# Note: For structs, inp should contain a 4 byte pointer # Note: For structs, inp should contain a 4 byte pointer
@ -375,11 +382,8 @@ 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_static_array_from_address(runner: runners.RunnerBase, typ: type3types.AppliedType3, adr: int) -> Any: def _load_static_array_from_address(runner: runners.RunnerBase, sub_typ: type3types.PrimitiveType3, len_typ: type3types.IntType3, adr: int) -> Any:
sys.stderr.write(f'Reading 0x{adr:08x} {typ:s}\n') sys.stderr.write(f'Reading 0x{adr:08x} {sub_typ:s} {len_typ:s}\n')
assert 2 == len(typ.args)
sub_typ, len_typ = typ.args
assert not isinstance(sub_typ, type3types.PlaceholderForType) assert not isinstance(sub_typ, type3types.PlaceholderForType)
assert isinstance(len_typ, type3types.IntType3) assert isinstance(len_typ, type3types.IntType3)
@ -396,29 +400,19 @@ def _load_static_array_from_address(runner: runners.RunnerBase, typ: type3types.
for arg_bytes in _split_read_bytes(read_bytes, arg_sizes) for arg_bytes in _split_read_bytes(read_bytes, arg_sizes)
) )
def _load_tuple_from_address(runner: runners.RunnerBase, typ: type3types.Type3, adr: int) -> Any: def _load_tuple_from_address(runner: runners.RunnerBase, typ_args: tuple[type3types.PrimitiveType3, ...], adr: int) -> Any:
sys.stderr.write(f'Reading 0x{adr:08x} {typ:s}\n') sys.stderr.write(f'Reading 0x{adr:08x} tuple {len(typ_args)}\n')
assert isinstance(typ, type3types.AppliedType3)
assert typ.base is type3types.tuple
typ_list = [
x
for x in typ.args
if not isinstance(x, type3types.PlaceholderForType)
]
assert len(typ_list) == len(typ.args)
arg_sizes = [ arg_sizes = [
calculate_alloc_size(x, is_member=True) calculate_alloc_size(x, is_member=True)
for x in typ_list for x in typ_args
] ]
read_bytes = runner.interpreter_read_memory(adr, sum(arg_sizes)) read_bytes = runner.interpreter_read_memory(adr, sum(arg_sizes))
return tuple( return tuple(
_unpack(runner, arg_typ, arg_bytes) _unpack(runner, arg_typ, arg_bytes)
for arg_typ, arg_bytes in zip(typ_list, _split_read_bytes(read_bytes, arg_sizes)) for arg_typ, arg_bytes in zip(typ_args, _split_read_bytes(read_bytes, arg_sizes))
) )
def _load_struct_from_address(runner: runners.RunnerBase, typ: type3types.Type3, adr: int) -> Any: def _load_struct_from_address(runner: runners.RunnerBase, typ: type3types.Type3, adr: int) -> Any:

View File

@ -116,11 +116,11 @@ def testEntry() -> i32:
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_'): if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_'):
expect_type_error( expect_type_error(
'Mismatch between applied types argument count', 'Mismatch between applied types argument count',
'The type of the value returned from function constant should match its return type', 'The type of a tuple is a combination of its members',
) )
elif TYPE_NAME.startswith('struct_'): elif TYPE_NAME.startswith('struct_'):
expect_type_error( expect_type_error(
TYPE + ' must be tuple (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',
) )
else: else:
@ -177,17 +177,17 @@ 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_'):
expect_type_error( expect_type_error(
'Mismatch between applied types argument count', 'u64[32] 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',
) )
elif TYPE_NAME.startswith('struct_'): elif TYPE_NAME.startswith('struct_'):
expect_type_error( expect_type_error(
TYPE + ' must be tuple (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',
) )
else: else:
expect_type_error( expect_type_error(
TYPE_NAME + ' must be tuple (u32) instead', TYPE_NAME + ' 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',
) )
``` ```
@ -233,17 +233,17 @@ def select(x: $TYPE) -> (u32, ):
```py ```py
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_'): if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_'):
expect_type_error( expect_type_error(
'Mismatch between applied types argument count', 'u64[32] 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',
) )
elif TYPE_NAME.startswith('struct_'): elif TYPE_NAME.startswith('struct_'):
expect_type_error( expect_type_error(
TYPE + ' must be tuple (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',
) )
else: else:
expect_type_error( expect_type_error(
TYPE_NAME + ' must be tuple (u32) instead', TYPE_NAME + ' 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',
) )
``` ```
@ -287,11 +287,11 @@ if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_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?
'The type of the value passed to argument x of function helper should match the type of that argument', 'The type of a tuple is a combination of its members',
) )
elif TYPE_NAME.startswith('struct_'): elif TYPE_NAME.startswith('struct_'):
expect_type_error( expect_type_error(
TYPE + ' must be tuple (u32) instead', TYPE + ' must be (u32) instead',
'The type of the value passed to argument x of function helper should match the type of that argument', 'The type of the value passed to argument x of function helper should match the type of that argument',
) )
else: else:
@ -342,17 +342,17 @@ 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_'):
expect_type_error( expect_type_error(
'Mismatch between applied types argument count', 'u64[32] must be (u32) instead',
'The type of the value passed to argument x of function helper should match the type of that argument', 'The type of the value passed to argument x of function helper should match the type of that argument',
) )
elif TYPE_NAME.startswith('struct_'): elif TYPE_NAME.startswith('struct_'):
expect_type_error( expect_type_error(
TYPE + ' must be tuple (u32) instead', TYPE + ' must be (u32) instead',
'The type of the value passed to argument x of function helper should match the type of that argument', 'The type of the value passed to argument x of function helper should match the type of that argument',
) )
else: else:
expect_type_error( expect_type_error(
TYPE_NAME + ' must be tuple (u32) instead', TYPE_NAME + ' must be (u32) instead',
'The type of the value passed to argument x of function helper should match the type of that argument', 'The type of the value passed to argument x of function helper should match the type of that argument',
) )
``` ```