Tuple instantiation works again

Typing is getting ugly again
This commit is contained in:
Johan B.W. de Vries 2023-01-01 16:00:18 +01:00
parent f281b78c78
commit 6231c6b279
7 changed files with 105 additions and 77 deletions

View File

@ -108,12 +108,16 @@ def expression(inp: ourlang.Expression) -> str:
if isinstance(inp.function, ourlang.StructConstructor): if isinstance(inp.function, ourlang.StructConstructor):
return f'{inp.function.struct_type3.name}({args})' return f'{inp.function.struct_type3.name}({args})'
# TODO: Broken after new type system
# if isinstance(inp.function, ourlang.TupleConstructor):
# return f'({args}, )'
return f'{inp.function.name}({args})' return f'{inp.function.name}({args})'
if isinstance(inp, ourlang.TupleInstantiation):
args = ', '.join(
expression(arg)
for arg in inp.elements
)
return f'({args}, )'
if isinstance(inp, ourlang.Subscript): if isinstance(inp, ourlang.Subscript):
varref = expression(inp.varref) varref = expression(inp.varref)
index = expression(inp.index) index = expression(inp.index)

View File

@ -80,6 +80,10 @@ def type3(inp: type3types.Type3OrPlaceholder) -> wasm.WasmType:
# Static Arrays are passed as pointer, which are i32 # Static Arrays are passed as pointer, which are i32
return wasm.WasmTypeInt32() return wasm.WasmTypeInt32()
if inp.base == type3types.tuple:
# Tuples are passed as pointer, which are i32
return wasm.WasmTypeInt32()
raise NotImplementedError(type3, inp) raise NotImplementedError(type3, inp)
# Operators that work for i32, i64, f32, f64 # Operators that work for i32, i64, f32, f64
@ -150,6 +154,45 @@ F64_OPERATOR_MAP = {
'/': 'div' # Division by zero is a trap and the program will panic '/': 'div' # Division by zero is a trap and the program will panic
} }
def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) -> None:
assert isinstance(inp.type3, type3types.AppliedType3)
assert inp.type3.base is type3types.tuple
assert len(inp.elements) == len(inp.type3.args)
comment_elements = ''
for element in inp.elements:
assert isinstance(element.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
comment_elements += f'{element.type3.name}, '
tmp_var = wgn.temp_var_i32('tuple_adr')
wgn.add_statement('nop', comment=f'tmp_var := ({comment_elements})')
# Allocated the required amounts of bytes in memory
wgn.i32.const(_calculate_alloc_size(inp.type3))
wgn.call(stdlib_alloc.__alloc__)
wgn.local.set(tmp_var)
# Store each element individually
offset = 0
for element, exp_type3 in zip(inp.elements, inp.type3.args):
if isinstance(exp_type3, type3types.PlaceholderForType):
assert exp_type3.resolve_as is not None
exp_type3 = exp_type3.resolve_as
assert element.type3 == exp_type3
assert isinstance(exp_type3, type3types.PrimitiveType3), NotImplementedError('Tuple of applied types / structs')
mtyp = LOAD_STORE_TYPE_MAP[exp_type3.name]
wgn.local.get(tmp_var)
expression(wgn, element)
wgn.add_statement(f'{mtyp}.store', 'offset=' + str(offset))
offset += _calculate_alloc_size(exp_type3)
# Return the allocated address
wgn.local.get(tmp_var)
def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None: def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
""" """
Compile: Any expression Compile: Any expression
@ -311,6 +354,10 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
wgn.add_statement('call', '${}'.format(inp.function.name)) wgn.add_statement('call', '${}'.format(inp.function.name))
return return
if isinstance(inp, ourlang.TupleInstantiation):
tuple_instantiation(wgn, inp)
return
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
@ -336,11 +383,8 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
wgn.i32.mul() wgn.i32.mul()
wgn.i32.add() wgn.i32.add()
mtyp = LOAD_STORE_TYPE_MAP.get(el_type.name) assert isinstance(el_type, type3types.PrimitiveType3), NotImplementedError('Tuple of applied types / structs')
if mtyp is None: mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
# In the future might extend this by having structs or tuples
# as members of struct or tuples
raise NotImplementedError(expression, inp, el_type)
wgn.add_statement(f'{mtyp}.load') wgn.add_statement(f'{mtyp}.load')
return return
@ -360,11 +404,8 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
expression(wgn, inp.varref) expression(wgn, inp.varref)
mtyp = LOAD_STORE_TYPE_MAP.get(el_type.name) assert isinstance(el_type, type3types.PrimitiveType3), NotImplementedError('Tuple of applied types / structs')
if mtyp is None: mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
# In the future might extend this by having structs or tuples
# as members of struct or tuples
raise NotImplementedError(expression, inp, el_type)
wgn.add_statement(f'{mtyp}.load', f'offset={offset}') wgn.add_statement(f'{mtyp}.load', f'offset={offset}')
return return
@ -372,11 +413,8 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
raise NotImplementedError(expression, inp, inp.varref.type3) raise NotImplementedError(expression, inp, inp.varref.type3)
if isinstance(inp, ourlang.AccessStructMember): if isinstance(inp, ourlang.AccessStructMember):
mtyp = LOAD_STORE_TYPE_MAP.get(inp.struct_type3.members[inp.member].name) assert isinstance(inp.struct_type3.members[inp.member], type3types.PrimitiveType3), NotImplementedError('Tuple of applied types / structs')
if mtyp is None: mtyp = LOAD_STORE_TYPE_MAP[inp.struct_type3.members[inp.member].name]
# In the future might extend this by having structs or tuples
# as members of struct or tuples
raise NotImplementedError(expression, inp, inp.struct_type3)
expression(wgn, inp.varref) expression(wgn, inp.varref)
wgn.add_statement(f'{mtyp}.load', 'offset=' + str(_calculate_member_offset( wgn.add_statement(f'{mtyp}.load', 'offset=' + str(_calculate_member_offset(
@ -538,9 +576,7 @@ def function(inp: ourlang.Function) -> wasm.Function:
wgn = WasmGenerator() wgn = WasmGenerator()
if False: # TODO: isinstance(inp, ourlang.TupleConstructor): if isinstance(inp, ourlang.StructConstructor):
pass # _generate_tuple_constructor(wgn, inp)
elif isinstance(inp, ourlang.StructConstructor):
_generate_struct_constructor(wgn, inp) _generate_struct_constructor(wgn, inp)
else: else:
for stat in inp.statements: for stat in inp.statements:
@ -706,30 +742,6 @@ def module(inp: ourlang.Module) -> wasm.Module:
return result return result
# TODO: Broken after new type system
# def _generate_tuple_constructor(wgn: WasmGenerator, inp: ourlang.TupleConstructor) -> None:
# tmp_var = wgn.temp_var_i32('tuple_adr')
#
# # Allocated the required amounts of bytes in memory
# wgn.i32.const(inp.tuple.alloc_size())
# wgn.call(stdlib_alloc.__alloc__)
# wgn.local.set(tmp_var)
#
# # Store each member individually
# for member in inp.tuple.members:
# mtyp = LOAD_STORE_TYPE_MAP.get(member.type.__class__)
# if mtyp is None:
# # In the future might extend this by having structs or tuples
# # as members of struct or tuples
# raise NotImplementedError(expression, inp, member)
#
# wgn.local.get(tmp_var)
# wgn.add_statement('local.get', f'$arg{member.idx}')
# wgn.add_statement(f'{mtyp}.store', 'offset=' + str(member.offset))
#
# # Return the allocated address
# wgn.local.get(tmp_var)
def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstructor) -> None: def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstructor) -> None:
tmp_var = wgn.temp_var_i32('struct_adr') tmp_var = wgn.temp_var_i32('struct_adr')
@ -771,6 +783,17 @@ def _calculate_alloc_size(typ: Union[type3types.StructType3, type3types.Type3])
for x in typ.members.values() for x in typ.members.values()
) )
if isinstance(typ, type3types.AppliedType3):
if typ.base is type3types.tuple:
size = 0
for arg in typ.args:
if isinstance(arg, type3types.PlaceholderForType):
assert not arg.resolve_as is None
arg = arg.resolve_as
size += _calculate_alloc_size(arg)
return size
raise NotImplementedError(_calculate_alloc_size, typ) raise NotImplementedError(_calculate_alloc_size, typ)
def _calculate_member_offset(struct_type3: type3types.StructType3, member: str) -> int: def _calculate_member_offset(struct_type3: type3types.StructType3, member: str) -> int:

View File

@ -142,14 +142,14 @@ class TupleInstantiation(Expression):
""" """
Instantiation a tuple Instantiation a tuple
""" """
__slots__ = ('args', ) __slots__ = ('elements', )
args: List[Expression] elements: List[Expression]
def __init__(self, args: List[Expression]) -> None: def __init__(self, elements: List[Expression]) -> None:
super().__init__() super().__init__()
self.args = args self.elements = elements
class Subscript(Expression): class Subscript(Expression):
""" """

View File

@ -60,14 +60,10 @@ class ConstraintBase:
def __init__(self, comment: Optional[str] = None) -> None: def __init__(self, comment: Optional[str] = None) -> None:
self.comment = comment self.comment = comment
def check(self, smap: SubstitutionMap) -> CheckResult: def check(self) -> CheckResult:
""" """
Checks if the constraint hold Checks if the constraint hold
smap will contain a mapping from placeholders to types, for
placeholders discovered from either other constraints or from
earlier calls to check in this constraint.
This function can return an error, if the constraint does not hold, This function can return an error, if the constraint does not hold,
which indicates an error in the typing of the input program. which indicates an error in the typing of the input program.
@ -103,7 +99,7 @@ class SameTypeConstraint(ConstraintBase):
assert len(type_list) > 1 assert len(type_list) > 1
self.type_list = [*type_list] self.type_list = [*type_list]
def check(self, smap: SubstitutionMap) -> CheckResult: def check(self) -> CheckResult:
known_types: List[types.Type3] = [] known_types: List[types.Type3] = []
placeholders = [] placeholders = []
do_applied_placeholder_check: bool = False do_applied_placeholder_check: bool = False
@ -118,8 +114,8 @@ class SameTypeConstraint(ConstraintBase):
continue continue
if isinstance(typ, types.PlaceholderForType): if isinstance(typ, types.PlaceholderForType):
if typ in smap: if typ.resolve_as is not None:
known_types.append(smap[typ]) known_types.append(typ.resolve_as)
else: else:
placeholders.append(typ) placeholders.append(typ)
continue continue
@ -159,6 +155,9 @@ class SameTypeConstraint(ConstraintBase):
if not placeholders: if not placeholders:
return None return None
for typ in placeholders:
typ.resolve_as = first_type
return { return {
typ: first_type typ: first_type
for typ in placeholders for typ in placeholders
@ -193,14 +192,14 @@ class CastableConstraint(ConstraintBase):
self.from_type3 = from_type3 self.from_type3 = from_type3
self.to_type3 = to_type3 self.to_type3 = to_type3
def check(self, smap: SubstitutionMap) -> CheckResult: def check(self) -> CheckResult:
ftyp = self.from_type3 ftyp = self.from_type3
if isinstance(ftyp, types.PlaceholderForType) and ftyp in smap: if isinstance(ftyp, types.PlaceholderForType) and ftyp.resolve_as is not None:
ftyp = smap[ftyp] ftyp = ftyp.resolve_as
ttyp = self.to_type3 ttyp = self.to_type3
if isinstance(ttyp, types.PlaceholderForType) and ttyp in smap: if isinstance(ttyp, types.PlaceholderForType) and ttyp.resolve_as is not None:
ttyp = smap[ttyp] ttyp = ttyp.resolve_as
if isinstance(ftyp, types.PlaceholderForType) or isinstance(ttyp, types.PlaceholderForType): if isinstance(ftyp, types.PlaceholderForType) or isinstance(ttyp, types.PlaceholderForType):
return RequireTypeSubstitutes() return RequireTypeSubstitutes()
@ -248,10 +247,10 @@ class MustImplementTypeClassConstraint(ConstraintBase):
self.type_class3 = type_class3 self.type_class3 = type_class3
self.type3 = type3 self.type3 = type3
def check(self, smap: SubstitutionMap) -> CheckResult: def check(self) -> CheckResult:
typ = self.type3 typ = self.type3
if isinstance(typ, types.PlaceholderForType) and typ in smap: if isinstance(typ, types.PlaceholderForType) and typ.resolve_as is not None:
typ = smap[typ] typ = typ.resolve_as
if isinstance(typ, types.PlaceholderForType): if isinstance(typ, types.PlaceholderForType):
return RequireTypeSubstitutes() return RequireTypeSubstitutes()
@ -293,7 +292,7 @@ class LiteralFitsConstraint(ConstraintBase):
self.type3 = type3 self.type3 = type3
self.literal = literal self.literal = literal
def check(self, smap: SubstitutionMap) -> CheckResult: def check(self) -> CheckResult:
int_table: Dict[str, Tuple[int, bool]] = { int_table: Dict[str, Tuple[int, bool]] = {
'u8': (1, False), 'u8': (1, False),
'u32': (4, False), 'u32': (4, False),
@ -309,10 +308,10 @@ class LiteralFitsConstraint(ConstraintBase):
} }
if isinstance(self.type3, types.PlaceholderForType): if isinstance(self.type3, types.PlaceholderForType):
if self.type3 not in smap: if self.type3.resolve_as is None:
return RequireTypeSubstitutes() return RequireTypeSubstitutes()
self.type3 = smap[self.type3] self.type3 = self.type3.resolve_as
if self.type3.name in int_table: if self.type3.name in int_table:
bts, sgn = int_table[self.type3.name] bts, sgn = int_table[self.type3.name]
@ -440,12 +439,12 @@ class CanBeSubscriptedConstraint(ConstraintBase):
self.index = index self.index = index
self.index_type3 = index.type3 self.index_type3 = index.type3
def check(self, smap: SubstitutionMap) -> CheckResult: def check(self) -> CheckResult:
if isinstance(self.type3, types.PlaceholderForType): if isinstance(self.type3, types.PlaceholderForType):
if self.type3 not in smap: if self.type3.resolve_as is None:
return RequireTypeSubstitutes() return RequireTypeSubstitutes()
self.type3 = smap[self.type3] 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:

View File

@ -104,7 +104,7 @@ def expression(ctx: Context, inp: ourlang.Expression) -> Generator[ConstraintBas
if isinstance(inp, ourlang.TupleInstantiation): if isinstance(inp, ourlang.TupleInstantiation):
r_type = [] r_type = []
for arg in inp.args: for arg in inp.elements:
yield from expression(ctx, arg) yield from expression(ctx, arg)
r_type.append(arg.type3) r_type.append(arg.type3)

View File

@ -1,7 +1,7 @@
""" """
Entry point to the type3 system Entry point to the type3 system
""" """
from typing import Dict, List from typing import Any, Dict, List, Set
from .. import codestyle from .. import codestyle
from .. import ourlang from .. import ourlang
@ -35,7 +35,7 @@ def phasm_type3(inp: ourlang.Module, verbose: bool = False) -> None:
new_constraint_list = [] new_constraint_list = []
for constraint in constraint_list: for constraint in constraint_list:
check_result = constraint.check(placeholder_substitutes) check_result = constraint.check()
if check_result is None: if check_result is None:
if verbose: if verbose:
print_constraint(placeholder_id_map, constraint) print_constraint(placeholder_id_map, constraint)

View File

@ -4,7 +4,7 @@ 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, Protocol, Union from typing import Any, Dict, Iterable, List, Optional, Protocol, Union
TYPE3_ASSERTION_ERROR = 'You must call phasm_type3 after calling phasm_parse before you can call any other method' TYPE3_ASSERTION_ERROR = 'You must call phasm_type3 after calling phasm_parse before you can call any other method'
@ -73,12 +73,14 @@ 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
""" """
__slots__ = ('update_on_substitution', ) __slots__ = ('update_on_substitution', 'resolve_as', )
update_on_substitution: List[ExpressionProtocol] update_on_substitution: List[ExpressionProtocol]
resolve_as: Optional[Type3]
def __init__(self, update_on_substitution: Iterable[ExpressionProtocol]) -> None: def __init__(self, update_on_substitution: Iterable[ExpressionProtocol]) -> None:
self.update_on_substitution = [*update_on_substitution] self.update_on_substitution = [*update_on_substitution]
self.resolve_as = None
def __repr__(self) -> str: def __repr__(self) -> str:
uos = ', '.join(repr(x) for x in self.update_on_substitution) uos = ', '.join(repr(x) for x in self.update_on_substitution)