Reworks type class instantiation

Before, a type class was a property of a type.
But that doesn't make any sense for multi parameter
type classes.

Had to make a hacky way for type classes with
type constructors.
This commit is contained in:
Johan B.W. de Vries 2025-04-27 17:45:13 +02:00
parent 11fde4cb9e
commit a2e1dfd799
11 changed files with 137 additions and 133 deletions

View File

@ -296,7 +296,7 @@ def type3(inp: type3placeholders.Type3OrPlaceholder) -> wasm.WasmType:
# And pointers are i32 # And pointers are i32
return wasm.WasmTypeInt32() return wasm.WasmTypeInt32()
if prelude.InternalPassAsPointer in inp.classes: if (prelude.InternalPassAsPointer, (inp, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
return wasm.WasmTypeInt32() return wasm.WasmTypeInt32()
raise NotImplementedError(type3, inp) raise NotImplementedError(type3, inp)
@ -344,7 +344,7 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
assert element.type3 == exp_type3 assert element.type3 == exp_type3
if prelude.InternalPassAsPointer in exp_type3.classes: if (prelude.InternalPassAsPointer, (exp_type3, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
mtyp = 'i32' mtyp = 'i32'
else: else:
assert isinstance(exp_type3, type3types.Type3), NotImplementedError('Tuple of applied types / structs') assert isinstance(exp_type3, type3types.Type3), NotImplementedError('Tuple of applied types / structs')
@ -413,7 +413,7 @@ 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), type3placeholders.TYPE3_ASSERTION_ERROR assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
if prelude.InternalPassAsPointer in inp.type3.classes: if (prelude.InternalPassAsPointer, (inp.type3, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
assert isinstance(inp.variable.constant, (ourlang.ConstantBytes, ourlang.ConstantStruct, ourlang.ConstantTuple, )) assert isinstance(inp.variable.constant, (ourlang.ConstantBytes, ourlang.ConstantStruct, ourlang.ConstantTuple, ))
address = inp.variable.constant.data_block.address address = inp.variable.constant.data_block.address
@ -560,7 +560,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
expression(wgn, inp.varref) expression(wgn, inp.varref)
if prelude.InternalPassAsPointer in el_type.classes: if (prelude.InternalPassAsPointer, (el_type, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
mtyp = 'i32' mtyp = 'i32'
else: else:
assert isinstance(el_type, type3types.Type3), NotImplementedError('Tuple of applied types / structs') assert isinstance(el_type, type3types.Type3), NotImplementedError('Tuple of applied types / structs')
@ -972,7 +972,7 @@ def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstruc
# Store each member individually # Store each member individually
for memname, mtyp3 in st_args.items(): for memname, mtyp3 in st_args.items():
mtyp: Optional[str] mtyp: Optional[str]
if prelude.InternalPassAsPointer in mtyp3.classes: if (prelude.InternalPassAsPointer, (mtyp3, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
mtyp = 'i32' mtyp = 'i32'
else: else:
mtyp = LOAD_STORE_TYPE_MAP.get(mtyp3.name) mtyp = LOAD_STORE_TYPE_MAP.get(mtyp3.name)

View File

@ -247,7 +247,7 @@ class OurVisitor:
members[stmt.target.id] = self.visit_type(module, stmt.annotation) members[stmt.target.id] = self.visit_type(module, stmt.annotation)
return StructDefinition(prelude.struct(node.name, members, set()), node.lineno) return StructDefinition(prelude.struct(node.name, members), node.lineno)
def pre_visit_Module_AnnAssign(self, module: Module, node: ast.AnnAssign) -> ModuleConstantDef: def pre_visit_Module_AnnAssign(self, module: Module, node: ast.AnnAssign) -> ModuleConstantDef:
if not isinstance(node.target, ast.Name): if not isinstance(node.target, ast.Name):

View File

@ -3,7 +3,7 @@ The prelude are all the builtin types, type classes and methods
""" """
from ..type3.functions import TypeVariable from ..type3.functions import TypeVariable
from ..type3.typeclasses import Type3Class, instance_type_class from ..type3.typeclasses import Type3Class
from ..type3.types import ( from ..type3.types import (
Type3, Type3,
TypeConstructor_StaticArray, TypeConstructor_StaticArray,
@ -11,40 +11,50 @@ from ..type3.types import (
TypeConstructor_Tuple, TypeConstructor_Tuple,
) )
none = Type3('none', []) PRELUDE_TYPE_CLASS_INSTANCES_EXISTING: set[tuple[Type3Class, tuple[Type3, ...]]] = set()
def instance_type_class(cls: Type3Class, typ: Type3) -> None:
global PRELUDE_TYPE_CLASS_INSTANCES_EXISTING
# TODO: Check for required existing instantiations
PRELUDE_TYPE_CLASS_INSTANCES_EXISTING.add((cls, (typ, ), ))
none = Type3('none')
""" """
The none type, for when functions simply don't return anything. e.g., IO(). The none type, for when functions simply don't return anything. e.g., IO().
""" """
bool_ = Type3('bool', []) bool_ = Type3('bool')
""" """
The bool type, either True or False The bool type, either True or False
Suffixes with an underscores, as it's a Python builtin Suffixes with an underscores, as it's a Python builtin
""" """
u8 = Type3('u8', []) u8 = Type3('u8')
""" """
The unsigned 8-bit integer type. The unsigned 8-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^8. Operations on variables employ modular arithmetic, with modulus 2^8.
""" """
u32 = Type3('u32', []) u32 = Type3('u32')
""" """
The unsigned 32-bit integer type. The unsigned 32-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^32. Operations on variables employ modular arithmetic, with modulus 2^32.
""" """
u64 = Type3('u64', []) u64 = Type3('u64')
""" """
The unsigned 64-bit integer type. The unsigned 64-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^64. Operations on variables employ modular arithmetic, with modulus 2^64.
""" """
i8 = Type3('i8', []) i8 = Type3('i8')
""" """
The signed 8-bit integer type. The signed 8-bit integer type.
@ -52,7 +62,7 @@ Operations on variables employ modular arithmetic, with modulus 2^8, but
with the middel point being 0. with the middel point being 0.
""" """
i32 = Type3('i32', []) i32 = Type3('i32')
""" """
The unsigned 32-bit integer type. The unsigned 32-bit integer type.
@ -60,7 +70,7 @@ Operations on variables employ modular arithmetic, with modulus 2^32, but
with the middel point being 0. with the middel point being 0.
""" """
i64 = Type3('i64', []) i64 = Type3('i64')
""" """
The unsigned 64-bit integer type. The unsigned 64-bit integer type.
@ -68,22 +78,25 @@ Operations on variables employ modular arithmetic, with modulus 2^64, but
with the middel point being 0. with the middel point being 0.
""" """
f32 = Type3('f32', []) f32 = Type3('f32')
""" """
A 32-bits IEEE 754 float, of 32 bits width. A 32-bits IEEE 754 float, of 32 bits width.
""" """
f64 = Type3('f64', []) f64 = Type3('f64')
""" """
A 32-bits IEEE 754 float, of 64 bits width. A 32-bits IEEE 754 float, of 64 bits width.
""" """
bytes_ = Type3('bytes', []) bytes_ = Type3('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 = TypeConstructor_StaticArray('static_array', [], []) def sa_on_create(typ: Type3) -> None:
instance_type_class(InternalPassAsPointer, typ)
static_array = TypeConstructor_StaticArray('static_array', on_create=sa_on_create)
""" """
A type constructor. A type constructor.
@ -93,7 +106,10 @@ It should be applied with one argument. It has a runtime-dynamic length
of the same type repeated. of the same type repeated.
""" """
tuple_ = TypeConstructor_Tuple('tuple', [], []) def tp_on_create(typ: Type3) -> None:
instance_type_class(InternalPassAsPointer, typ)
tuple_ = TypeConstructor_Tuple('tuple', on_create=tp_on_create)
""" """
This is a fixed length piece of memory. This is a fixed length piece of memory.
@ -101,7 +117,10 @@ 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.
""" """
struct = TypeConstructor_Struct('struct', [], []) def st_on_create(typ: Type3) -> None:
instance_type_class(InternalPassAsPointer, typ)
struct = TypeConstructor_Struct('struct', on_create=st_on_create)
""" """
This is like a tuple, but each argument is named, so that developers 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.
@ -130,9 +149,9 @@ 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)
Eq = Type3Class('Eq', [a], methods={}, operators={ Eq = Type3Class('Eq', [a], methods={}, operators={
'==': [a, a, bool_], '==': [a, a, bool_],
@ -148,7 +167,6 @@ instance_type_class(Eq, i32)
instance_type_class(Eq, i64) instance_type_class(Eq, i64)
instance_type_class(Eq, f32) instance_type_class(Eq, f32)
instance_type_class(Eq, f64) instance_type_class(Eq, f64)
instance_type_class(Eq, static_array)
Ord = Type3Class('Ord', [a], methods={ Ord = Type3Class('Ord', [a], methods={
'min': [a, a, a], 'min': [a, a, a],

View File

@ -45,7 +45,13 @@ class Context:
Context for constraints Context for constraints
""" """
__slots__ = () __slots__ = ('type_class_instances_existing', )
# Constraint_TypeClassInstanceExists
type_class_instances_existing: set[tuple[typeclasses.Type3Class, tuple[types.Type3, ...]]]
def __init__(self) -> None:
self.type_class_instances_existing = set()
class ConstraintBase: class ConstraintBase:
""" """
@ -239,49 +245,64 @@ class MustImplementTypeClassConstraint(ConstraintBase):
""" """
A type must implement a given type class A type must implement a given type class
""" """
__slots__ = ('type_class3', 'type3', ) __slots__ = ('context', 'type_class3', 'types', )
context: Context
type_class3: Union[str, typeclasses.Type3Class] type_class3: Union[str, typeclasses.Type3Class]
type3: placeholders.Type3OrPlaceholder types: list[placeholders.Type3OrPlaceholder]
DATA = { DATA = {
'bytes': {'Foldable'}, 'bytes': {'Foldable'},
} }
def __init__(self, type_class3: Union[str, typeclasses.Type3Class], type3: placeholders.Type3OrPlaceholder, comment: Optional[str] = None) -> None: def __init__(self, context: Context, type_class3: Union[str, typeclasses.Type3Class], types: list[placeholders.Type3OrPlaceholder], comment: Optional[str] = None) -> None:
super().__init__(comment=comment) super().__init__(comment=comment)
self.context = context
self.type_class3 = type_class3 self.type_class3 = type_class3
self.type3 = type3 self.types = types
def check(self) -> CheckResult: def check(self) -> CheckResult:
typ = self.type3 typ_list = []
for typ in self.types:
if isinstance(typ, placeholders.PlaceholderForType) and typ.resolve_as is not None: if isinstance(typ, placeholders.PlaceholderForType) and typ.resolve_as is not None:
typ = typ.resolve_as typ = typ.resolve_as
if isinstance(typ, placeholders.PlaceholderForType): if isinstance(typ, placeholders.PlaceholderForType):
return RequireTypeSubstitutes() return RequireTypeSubstitutes()
typ_list.append(typ)
assert len(typ_list) == len(self.types)
if isinstance(self.type_class3, typeclasses.Type3Class): if isinstance(self.type_class3, typeclasses.Type3Class):
if self.type_class3 in typ.classes: key = (self.type_class3, tuple(typ_list), )
if key in self.context.type_class_instances_existing:
return None return None
else: else:
if self.type_class3 in self.__class__.DATA.get(typ.name, set()): if self.type_class3 in self.__class__.DATA.get(typ_list[0].name, set()):
return None return None
return Error(f'{typ.name} does not implement the {self.type_class3} type class') typ_cls_name = self.type_class3 if isinstance(self.type_class3, str) else self.type_class3.name
typ_name_list = ' '.join(x.name for x in typ_list)
return Error(f'Missing type class instantation: {typ_cls_name} {typ_name_list}')
def human_readable(self) -> HumanReadableRet: def human_readable(self) -> HumanReadableRet:
keys = {
f'type{idx}': typ
for idx, typ in enumerate(self.types)
}
return ( return (
'{type3} derives {type_class3}', 'Exists instance {type_class3} ' + ' '.join(f'{{{x}}}' for x in keys),
{ {
'type_class3': str(self.type_class3), 'type_class3': str(self.type_class3),
'type3': self.type3, **keys,
}, },
) )
def __repr__(self) -> str: def __repr__(self) -> str:
return f'MustImplementTypeClassConstraint({repr(self.type_class3)}, {repr(self.type3)}, comment={repr(self.comment)})' return f'MustImplementTypeClassConstraint({repr(self.type_class3)}, {repr(self.types)}, comment={repr(self.comment)})'
class LiteralFitsConstraint(ConstraintBase): class LiteralFitsConstraint(ConstraintBase):
""" """

View File

@ -25,6 +25,7 @@ ConstraintGenerator = Generator[ConstraintBase, None, None]
def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase]: def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase]:
ctx = Context() ctx = Context()
ctx.type_class_instances_existing.update(prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING)
return [*module(ctx, inp)] return [*module(ctx, inp)]
@ -71,14 +72,12 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
for call_arg in arguments: for call_arg in arguments:
yield from expression(ctx, call_arg) yield from expression(ctx, call_arg)
for type_var, constraint_list in signature.context.constraints.items(): for constraint in signature.context.constraints:
assert type_var in type_var_map # When can this happen? if isinstance(constraint, functions.Constraint_TypeClassInstanceExists):
for constraint in constraint_list:
if isinstance(constraint, functions.TypeVariableConstraint_TypeHasTypeClass):
yield MustImplementTypeClassConstraint( yield MustImplementTypeClassConstraint(
ctx,
constraint.type_class3, constraint.type_class3,
type_var_map[type_var], [type_var_map[x] for x in constraint.types],
) )
continue continue
@ -138,7 +137,7 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
yield SameTypeConstraint(inp.func.posonlyargs[0].type3, inp.func.returns_type3, inp.base.type3, inp.type3, yield SameTypeConstraint(inp.func.posonlyargs[0].type3, inp.func.returns_type3, inp.base.type3, inp.type3,
comment='foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b') comment='foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b')
yield MustImplementTypeClassConstraint('Foldable', inp.iter.type3) yield MustImplementTypeClassConstraint(ctx, 'Foldable', [inp.iter.type3])
return return

View File

@ -34,26 +34,37 @@ class TypeVariable:
def __repr__(self) -> str: def __repr__(self) -> str:
return f'TypeVariable({repr(self.letter)})' return f'TypeVariable({repr(self.letter)})'
class TypeVariableConstraintBase: class ConstraintBase:
__slots__ = () __slots__ = ()
class TypeVariableConstraint_TypeHasTypeClass(TypeVariableConstraintBase): class Constraint_TypeClassInstanceExists(ConstraintBase):
__slots__ = ('type_class3', ) __slots__ = ('type_class3', 'types', )
def __init__(self, type_class3: 'Type3Class') -> None: type_class3: 'Type3Class'
types: list[TypeVariable]
def __init__(self, type_class3: 'Type3Class', types: Iterable[TypeVariable]) -> None:
self.type_class3 = type_class3 self.type_class3 = type_class3
self.types = list(types)
# Sanity check. AFAIK, if you have a multi-parameter type class,
# you can only add a constraint by supplying types for all variables
assert len(self.type_class3.args) == len(self.types)
class TypeVariableContext: class TypeVariableContext:
__slots__ = ('constraints', ) __slots__ = ('variables', 'constraints', )
constraints: dict[TypeVariable, list[TypeVariableConstraintBase]] variables: set[TypeVariable]
constraints: list[ConstraintBase]
def __init__(self) -> None: def __init__(self) -> None:
self.constraints = {} self.variables = set()
self.constraints = []
def __copy__(self) -> 'TypeVariableContext': def __copy__(self) -> 'TypeVariableContext':
result = TypeVariableContext() result = TypeVariableContext()
result.constraints.update(self.constraints) result.variables.update(self.variables)
result.constraints.extend(self.constraints)
return result return result
class FunctionSignature: class FunctionSignature:

View File

@ -1,12 +1,12 @@
from typing import Any, Dict, Iterable, List, Mapping, Optional, Union from typing import Dict, Iterable, List, Mapping, Optional, Union
from .functions import ( from .functions import (
Constraint_TypeClassInstanceExists,
FunctionSignature, FunctionSignature,
TypeVariable, TypeVariable,
TypeVariableConstraint_TypeHasTypeClass,
TypeVariableContext, TypeVariableContext,
) )
from .types import Type3, TypeConstructor, TypeConstructor_Struct from .types import Type3
class Type3ClassMethod: class Type3ClassMethod:
@ -43,17 +43,7 @@ class Type3Class:
self.args = list(args) self.args = list(args)
context = TypeVariableContext() context = TypeVariableContext()
for arg in args: context.constraints.append(Constraint_TypeClassInstanceExists(self, args))
context.constraints[arg] = [
TypeVariableConstraint_TypeHasTypeClass(self)
]
# FIXME: Multi parameter class types
# To fix this, realise that an instantiation of a multi paramater type class
# means that the instantiation depends on the combination of type classes
# and so we can't store the type classes on the types anymore
# This also means constraints should store a tuple of types as its key
assert len(context.constraints) <= 1
self.methods = { self.methods = {
k: Type3ClassMethod(k, FunctionSignature(context, v)) k: Type3ClassMethod(k, FunctionSignature(context, v))
@ -67,9 +57,3 @@ class Type3Class:
def __repr__(self) -> str: def __repr__(self) -> str:
return self.name return self.name
def instance_type_class(cls: Type3Class, typ: Type3 | TypeConstructor[Any] | TypeConstructor_Struct) -> None:
if isinstance(typ, Type3):
typ.classes.add(cls)
else:
typ.type_classes.add(cls)

View File

@ -2,18 +2,13 @@
Contains the final types for use in Phasm, as well as construtors. Contains the final types for use in Phasm, as well as construtors.
""" """
from typing import ( from typing import (
TYPE_CHECKING,
Any, Any,
Callable,
Generic, Generic,
Iterable,
Set,
Tuple, Tuple,
TypeVar, TypeVar,
) )
if TYPE_CHECKING:
from .typeclasses import Type3Class
class KindArgument: class KindArgument:
pass pass
@ -25,32 +20,18 @@ class Type3(KindArgument):
(Having a separate name makes it easier to distinguish from (Having a separate name makes it easier to distinguish from
Python's Type) Python's Type)
""" """
__slots__ = ('name', 'classes', ) __slots__ = ('name', )
name: str name: str
""" """
The name of the string, as parsed and outputted by codestyle. The name of the string, as parsed and outputted by codestyle.
""" """
classes: Set['Type3Class'] def __init__(self, name: str) -> None:
"""
The type classes that this type implements
"""
def __init__(self, name: str, classes: Iterable['Type3Class']) -> None:
self.name = name self.name = name
self.classes = set(classes)
for cls in self.classes:
for inh_cls in cls.inherited_classes:
if inh_cls not in self.classes:
raise Exception(
f'No instance for ({inh_cls} {self.name})'
f'; required for ({cls} {self.name})'
)
def __repr__(self) -> str: def __repr__(self) -> str:
return f'Type3({repr(self.name)}, {repr(self.classes)})' return f'Type3({repr(self.name)})'
def __str__(self) -> str: def __str__(self) -> str:
return self.name return self.name
@ -118,21 +99,16 @@ class TypeConstructor(Generic[T]):
""" """
Base class for type construtors Base class for type construtors
""" """
__slots__ = ('name', 'classes', 'type_classes', '_cache', '_reverse_cache') __slots__ = ('name', 'on_create', '_cache', '_reverse_cache')
name: str name: str
""" """
The name of the type constructor The name of the type constructor
""" """
classes: Set['Type3Class'] on_create: Callable[[Type3], None]
""" """
The type classes that this constructor implements Who to let know if a type is created
"""
type_classes: Set['Type3Class']
"""
The type classes that the constructed types implement
""" """
_cache: dict[T, Type3] _cache: dict[T, Type3]
@ -146,10 +122,9 @@ class TypeConstructor(Generic[T]):
Sometimes we need to know the key that created a type. Sometimes we need to know the key that created a type.
""" """
def __init__(self, name: str, classes: Iterable['Type3Class'], type_classes: Iterable['Type3Class']) -> None: def __init__(self, name: str, on_create: Callable[[Type3], None]) -> None:
self.name = name self.name = name
self.classes = set(classes) self.on_create = on_create
self.type_classes = set(type_classes)
self._cache = {} self._cache = {}
self._reverse_cache = {} self._reverse_cache = {}
@ -175,8 +150,9 @@ class TypeConstructor(Generic[T]):
""" """
result = self._cache.get(key, None) result = self._cache.get(key, None)
if result is None: if result is None:
self._cache[key] = result = Type3(self.make_name(key), self.type_classes) self._cache[key] = result = Type3(self.make_name(key))
self._reverse_cache[result] = key self._reverse_cache[result] = key
self.on_create(result)
return result return result
@ -225,21 +201,16 @@ class TypeConstructor_Struct:
""" """
Base class for type construtors Base class for type construtors
""" """
__slots__ = ('name', 'classes', 'type_classes', '_cache', '_reverse_cache') __slots__ = ('name', 'on_create', '_cache', '_reverse_cache')
name: str name: str
""" """
The name of the type constructor The name of the type constructor
""" """
classes: Set['Type3Class'] on_create: Callable[[Type3], None]
""" """
The type classes that this constructor implements Who to let know if a type is created
"""
type_classes: Set['Type3Class']
"""
The type classes that the constructed types implement
""" """
_cache: dict[str, Type3] _cache: dict[str, Type3]
@ -254,10 +225,9 @@ class TypeConstructor_Struct:
used for making the type used for making the type
""" """
def __init__(self, name: str, classes: Iterable['Type3Class'], type_classes: Iterable['Type3Class']) -> None: def __init__(self, name: str, on_create: Callable[[Type3], None]) -> None:
self.name = name self.name = name
self.classes = set(classes) self.on_create = on_create
self.type_classes = set(type_classes)
self._cache = {} self._cache = {}
self._reverse_cache = {} self._reverse_cache = {}
@ -270,8 +240,9 @@ class TypeConstructor_Struct:
""" """
return self._reverse_cache.get(typ) return self._reverse_cache.get(typ)
def __call__(self, name: str, args: dict[str, Type3], classes: Set['Type3Class']) -> Type3: def __call__(self, name: str, args: dict[str, Type3]) -> Type3:
result = Type3(name, classes | self.type_classes) result = Type3(name)
self._reverse_cache[result] = args self._reverse_cache[result] = args
self.on_create(result)
return result return result

View File

@ -354,7 +354,7 @@ def _unpack(runner: runners.RunnerBase, typ: type3types.Type3, inp: bytes) -> An
return _load_bytes_from_address(runner, typ, adr) return _load_bytes_from_address(runner, typ, adr)
if prelude.InternalPassAsPointer in typ.classes: if (prelude.InternalPassAsPointer, (typ, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
# 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]

View File

@ -29,7 +29,7 @@ def testEntry(x: Foo, y: Foo) -> Foo:
return x == y return x == y
""" """
with pytest.raises(Type3Exception, match='Foo does not implement the Eq type class'): with pytest.raises(Type3Exception, match='Missing type class instantation: Eq Foo'):
Suite(code_py).run_code() Suite(code_py).run_code()
@pytest.mark.integration_test @pytest.mark.integration_test
@ -111,7 +111,7 @@ def testEntry(x: Foo, y: Foo) -> Foo:
return x != y return x != y
""" """
with pytest.raises(Type3Exception, match='Foo does not implement the Eq type class'): with pytest.raises(Type3Exception, match='Missing type class instantation: Eq Foo'):
Suite(code_py).run_code() Suite(code_py).run_code()
@pytest.mark.integration_test @pytest.mark.integration_test

View File

@ -27,7 +27,7 @@ def testEntry(x: Foo, y: Foo) -> Foo:
return x + y return x + y
""" """
with pytest.raises(Type3Exception, match='Foo does not implement the NatNum type class'): with pytest.raises(Type3Exception, match='Missing type class instantation: NatNum Foo'):
Suite(code_py).run_code() Suite(code_py).run_code()
@pytest.mark.integration_test @pytest.mark.integration_test