diff --git a/phasm/compiler.py b/phasm/compiler.py index d4534f5..d098499 100644 --- a/phasm/compiler.py +++ b/phasm/compiler.py @@ -8,10 +8,18 @@ from . import codestyle, ourlang, prelude, wasm from .runtime import calculate_alloc_size, calculate_member_offset from .stdlib import alloc as stdlib_alloc from .stdlib import types as stdlib_types -from .type3 import functions as type3functions -from .type3 import typeclasses as type3classes -from .type3 import types as type3types +from .type3.functions import TypeVariable from .type3.routers import NoRouteForTypeException, TypeApplicationRouter +from .type3.typeclasses import Type3ClassMethod +from .type3.types import ( + IntType3, + Type3, + TypeApplication_Struct, + TypeApplication_TypeInt, + TypeApplication_TypeStar, + TypeConstructor_StaticArray, + TypeConstructor_Tuple, +) from .wasmgenerator import Generator as WasmGenerator TYPE3_ASSERTION_ERROR = 'You must call phasm_type3 after calling phasm_parse before your program can be compiled' @@ -37,7 +45,7 @@ def phasm_compile(inp: ourlang.Module) -> wasm.Module: """ return module(inp) -def type3(inp: type3types.Type3) -> wasm.WasmType: +def type3(inp: Type3) -> wasm.WasmType: """ Compile: type @@ -98,18 +106,18 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) -> """ assert inp.type3 is not None, TYPE3_ASSERTION_ERROR - args: tuple[type3types.Type3, ...] + args: tuple[Type3, ...] - if isinstance(inp.type3.application, type3types.TypeApplication_TypeStar): + if isinstance(inp.type3.application, TypeApplication_TypeStar): # Possibly paranoid assert. If we have a future variadic type, # does it also do this tuple instantation like this? - assert isinstance(inp.type3.application.constructor, type3types.TypeConstructor_Tuple) + assert isinstance(inp.type3.application.constructor, TypeConstructor_Tuple) args = inp.type3.application.arguments - elif isinstance(inp.type3.application, type3types.TypeApplication_TypeInt): + elif isinstance(inp.type3.application, TypeApplication_TypeInt): # Possibly paranoid assert. If we have a future type of kind * -> Int -> *, # does it also do this tuple instantation like this? - assert isinstance(inp.type3.application.constructor, type3types.TypeConstructor_StaticArray) + assert isinstance(inp.type3.application.constructor, TypeConstructor_StaticArray) sa_type, sa_len = inp.type3.application.arguments @@ -162,7 +170,7 @@ def expression_subscript_bytes( def expression_subscript_static_array( attrs: tuple[WasmGenerator, ourlang.Subscript], - args: tuple[type3types.Type3, type3types.IntType3], + args: tuple[Type3, IntType3], ) -> None: wgn, inp = attrs @@ -194,7 +202,7 @@ def expression_subscript_static_array( def expression_subscript_tuple( attrs: tuple[WasmGenerator, ourlang.Subscript], - args: tuple[type3types.Type3, ...], + args: tuple[Type3, ...], ) -> None: wgn, inp = attrs @@ -292,16 +300,16 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None: expression(wgn, inp.left) expression(wgn, inp.right) - type_var_map: dict[type3functions.TypeVariable, type3types.Type3] = {} + type_var_map: dict[TypeVariable, Type3] = {} for type_var, arg_expr in zip(inp.operator.signature.args, [inp.left, inp.right, inp], strict=True): assert arg_expr.type3 is not None, TYPE3_ASSERTION_ERROR - if isinstance(type_var, type3types.Type3): + if isinstance(type_var, Type3): # Fixed type, not part of the lookup requirements continue - if isinstance(type_var, type3functions.TypeVariable): + if isinstance(type_var, TypeVariable): type_var_map[type_var] = arg_expr.type3 continue @@ -315,18 +323,18 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None: for arg in inp.arguments: expression(wgn, arg) - if isinstance(inp.function, type3classes.Type3ClassMethod): + if isinstance(inp.function, Type3ClassMethod): # FIXME: Duplicate code with BinaryOp type_var_map = {} for type_var, arg_expr in zip(inp.function.signature.args, inp.arguments + [inp], strict=True): assert arg_expr.type3 is not None, TYPE3_ASSERTION_ERROR - if isinstance(type_var, type3types.Type3): + if isinstance(type_var, Type3): # Fixed type, not part of the lookup requirements continue - if isinstance(type_var, type3functions.TypeVariable): + if isinstance(type_var, TypeVariable): type_var_map[type_var] = arg_expr.type3 continue @@ -356,7 +364,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None: if isinstance(inp, ourlang.AccessStructMember): assert inp.struct_type3 is not None, TYPE3_ASSERTION_ERROR - assert isinstance(inp.struct_type3.application, type3types.TypeApplication_Struct) + assert isinstance(inp.struct_type3.application, TypeApplication_Struct) member_type = dict(inp.struct_type3.application.arguments)[inp.member] @@ -739,7 +747,7 @@ def module(inp: ourlang.Module) -> wasm.Module: return result def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstructor) -> None: - assert isinstance(inp.struct_type3.application, type3types.TypeApplication_Struct) + assert isinstance(inp.struct_type3.application, TypeApplication_Struct) st_args = inp.struct_type3.application.arguments diff --git a/phasm/parser.py b/phasm/parser.py index 95c6703..e32563a 100644 --- a/phasm/parser.py +++ b/phasm/parser.py @@ -32,8 +32,8 @@ from .ourlang import ( VariableReference, ) from .prelude import PRELUDE_METHODS, PRELUDE_OPERATORS, PRELUDE_TYPES -from .type3 import typeclasses as type3typeclasses -from .type3 import types as type3types +from .type3.typeclasses import Type3ClassMethod +from .type3.types import IntType3, Type3 def phasm_parse(source: str) -> Module: @@ -226,7 +226,7 @@ class OurVisitor: _not_implemented(not node.keywords, 'ClassDef.keywords') _not_implemented(not node.decorator_list, 'ClassDef.decorator_list') - members: Dict[str, type3types.Type3] = {} + members: Dict[str, Type3] = {} for stmt in node.body: if not isinstance(stmt, ast.AnnAssign): @@ -352,7 +352,7 @@ class OurVisitor: def visit_Module_FunctionDef_expr(self, module: Module, function: Function, our_locals: OurLocals, node: ast.expr) -> Expression: if isinstance(node, ast.BinOp): - operator: Union[str, type3typeclasses.Type3ClassMethod] + operator: Union[str, Type3ClassMethod] if isinstance(node.op, ast.Add): operator = '+' @@ -471,7 +471,7 @@ class OurVisitor: if not isinstance(node.func.ctx, ast.Load): _raise_static_error(node, 'Must be load context') - func: Union[Function, type3typeclasses.Type3ClassMethod] + func: Union[Function, Type3ClassMethod] if node.func.id in PRELUDE_METHODS: func = PRELUDE_METHODS[node.func.id] @@ -617,7 +617,7 @@ class OurVisitor: 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) -> Type3: if isinstance(node, ast.Constant): if node.value is None: return prelude.none @@ -645,7 +645,7 @@ class OurVisitor: return prelude.static_array( self.visit_type(module, node.value), - type3types.IntType3(node.slice.value), + IntType3(node.slice.value), ) if isinstance(node, ast.Tuple): diff --git a/phasm/type3/constraints.py b/phasm/type3/constraints.py index 94a17de..e04e66c 100644 --- a/phasm/type3/constraints.py +++ b/phasm/type3/constraints.py @@ -6,9 +6,19 @@ These need to be resolved before the program can be compiled. from typing import Any, Dict, Iterable, List, Optional, Tuple, Union from .. import ourlang, prelude -from . import placeholders, typeclasses, types -from .placeholders import PlaceholderForType +from .placeholders import PlaceholderForType, Type3OrPlaceholder from .routers import NoRouteForTypeException, TypeApplicationRouter +from .typeclasses import Type3Class +from .types import ( + IntType3, + Type3, + TypeApplication_Nullary, + TypeApplication_Struct, + TypeApplication_TypeInt, + TypeApplication_TypeStar, + TypeConstructor_Base, + TypeConstructor_Struct, +) class Error: @@ -34,13 +44,13 @@ class RequireTypeSubstitutes: typing of the program, so this constraint can be updated. """ -SubstitutionMap = Dict[placeholders.PlaceholderForType, types.Type3] +SubstitutionMap = Dict[PlaceholderForType, Type3] NewConstraintList = List['ConstraintBase'] CheckResult = Union[None, SubstitutionMap, Error, NewConstraintList, RequireTypeSubstitutes] -HumanReadableRet = Tuple[str, Dict[str, Union[str, ourlang.Expression, types.Type3, placeholders.PlaceholderForType]]] +HumanReadableRet = Tuple[str, Dict[str, Union[str, ourlang.Expression, Type3, PlaceholderForType]]] class Context: """ @@ -50,7 +60,7 @@ class Context: __slots__ = ('type_class_instances_existing', ) # Constraint_TypeClassInstanceExists - type_class_instances_existing: set[tuple[typeclasses.Type3Class, tuple[Union[types.Type3, types.TypeConstructor_Base[Any], types.TypeConstructor_Struct], ...]]] + type_class_instances_existing: set[tuple[Type3Class, tuple[Union[Type3, TypeConstructor_Base[Any], TypeConstructor_Struct], ...]]] def __init__(self) -> None: self.type_class_instances_existing = set() @@ -100,23 +110,23 @@ class SameTypeConstraint(ConstraintBase): """ __slots__ = ('type_list', ) - type_list: List[placeholders.Type3OrPlaceholder] + type_list: List[Type3OrPlaceholder] - def __init__(self, *type_list: placeholders.Type3OrPlaceholder, comment: Optional[str] = None) -> None: + def __init__(self, *type_list: Type3OrPlaceholder, comment: Optional[str] = None) -> None: super().__init__(comment=comment) assert len(type_list) > 1 self.type_list = [*type_list] def check(self) -> CheckResult: - known_types: List[types.Type3] = [] + known_types: List[Type3] = [] phft_list = [] for typ in self.type_list: - if isinstance(typ, types.Type3): + if isinstance(typ, Type3): known_types.append(typ) continue - if isinstance(typ, placeholders.PlaceholderForType): + if isinstance(typ, PlaceholderForType): if typ.resolve_as is not None: known_types.append(typ.resolve_as) else: @@ -133,7 +143,7 @@ class SameTypeConstraint(ConstraintBase): if ktyp != first_type: return Error(f'{ktyp:s} must be {first_type:s} instead', comment=self.comment) - if not placeholders: + if not phft_list: return None for phft in phft_list: @@ -177,10 +187,10 @@ class SameTypeArgumentConstraint(ConstraintBase): tc_typ = self.tc_var.resolve_as arg_typ = self.arg_var.resolve_as - if isinstance(tc_typ.application, types.TypeApplication_Nullary): + if isinstance(tc_typ.application, TypeApplication_Nullary): return Error(f'{tc_typ:s} must be a constructed type instead') - if isinstance(tc_typ.application, types.TypeApplication_TypeStar): + if isinstance(tc_typ.application, TypeApplication_TypeStar): # Sure, it's a constructed type. But it's like a struct, # though without the way to implement type classes # Presumably, doing a naked `foo :: t a -> a` @@ -190,7 +200,7 @@ class SameTypeArgumentConstraint(ConstraintBase): # FIXME: This feels sketchy. Shouldn't the type variable # have the exact same number as arguments? - if isinstance(tc_typ.application, types.TypeApplication_TypeInt): + if isinstance(tc_typ.application, TypeApplication_TypeInt): if tc_typ.application.arguments[0] == arg_typ: return None @@ -201,16 +211,16 @@ class SameTypeArgumentConstraint(ConstraintBase): class TupleMatchConstraint(ConstraintBase): __slots__ = ('exp_type', 'args', ) - exp_type: placeholders.Type3OrPlaceholder - args: list[placeholders.Type3OrPlaceholder] + exp_type: Type3OrPlaceholder + args: list[Type3OrPlaceholder] - def __init__(self, exp_type: placeholders.Type3OrPlaceholder, args: Iterable[placeholders.Type3OrPlaceholder], comment: str): + def __init__(self, exp_type: Type3OrPlaceholder, args: Iterable[Type3OrPlaceholder], comment: str): super().__init__(comment=comment) self.exp_type = exp_type self.args = list(args) - def _generate_static_array(self, sa_args: tuple[types.Type3, types.IntType3]) -> CheckResult: + def _generate_static_array(self, sa_args: tuple[Type3, IntType3]) -> CheckResult: sa_type, sa_len = sa_args if sa_len.value != len(self.args): @@ -221,7 +231,7 @@ class TupleMatchConstraint(ConstraintBase): for arg in self.args ] - def _generate_tuple(self, tp_args: tuple[types.Type3, ...]) -> CheckResult: + def _generate_tuple(self, tp_args: tuple[Type3, ...]) -> CheckResult: if len(tp_args) != len(self.args): return Error('Mismatch between applied types argument count', comment=self.comment) @@ -236,7 +246,7 @@ class TupleMatchConstraint(ConstraintBase): def check(self) -> CheckResult: exp_type = self.exp_type - if isinstance(exp_type, placeholders.PlaceholderForType): + if isinstance(exp_type, PlaceholderForType): if exp_type.resolve_as is None: return RequireTypeSubstitutes() @@ -254,34 +264,34 @@ class MustImplementTypeClassConstraint(ConstraintBase): __slots__ = ('context', 'type_class3', 'types', ) context: Context - type_class3: Union[str, typeclasses.Type3Class] - types: list[placeholders.Type3OrPlaceholder] + type_class3: Union[str, Type3Class] + types: list[Type3OrPlaceholder] DATA = { 'bytes': {'Foldable'}, } - def __init__(self, context: Context, type_class3: Union[str, typeclasses.Type3Class], types: list[placeholders.Type3OrPlaceholder], comment: Optional[str] = None) -> None: + def __init__(self, context: Context, type_class3: Union[str, Type3Class], typ_list: list[Type3OrPlaceholder], comment: Optional[str] = None) -> None: super().__init__(comment=comment) self.context = context self.type_class3 = type_class3 - self.types = types + self.types = typ_list def check(self) -> CheckResult: - typ_list: list[types.Type3 | types.TypeConstructor_Base[Any] | types.TypeConstructor_Struct] = [] + typ_list: list[Type3 | TypeConstructor_Base[Any] | TypeConstructor_Struct] = [] for typ in self.types: - if isinstance(typ, placeholders.PlaceholderForType) and typ.resolve_as is not None: + if isinstance(typ, PlaceholderForType) and typ.resolve_as is not None: typ = typ.resolve_as - if isinstance(typ, placeholders.PlaceholderForType): + if isinstance(typ, PlaceholderForType): return RequireTypeSubstitutes() - if isinstance(typ.application, (types.TypeApplication_Nullary, types.TypeApplication_Struct, )): + if isinstance(typ.application, (TypeApplication_Nullary, TypeApplication_Struct, )): typ_list.append(typ) continue - if isinstance(typ.application, (types.TypeApplication_TypeInt, types.TypeApplication_TypeStar)): + if isinstance(typ.application, (TypeApplication_TypeInt, TypeApplication_TypeStar)): typ_list.append(typ.application.constructor) continue @@ -289,7 +299,7 @@ class MustImplementTypeClassConstraint(ConstraintBase): assert len(typ_list) == len(self.types) - if isinstance(self.type_class3, typeclasses.Type3Class): + if isinstance(self.type_class3, Type3Class): key = (self.type_class3, tuple(typ_list), ) if key in self.context.type_class_instances_existing: return None @@ -324,12 +334,12 @@ class LiteralFitsConstraint(ConstraintBase): """ __slots__ = ('type3', 'literal', ) - type3: placeholders.Type3OrPlaceholder + type3: Type3OrPlaceholder literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct] def __init__( self, - type3: placeholders.Type3OrPlaceholder, + type3: Type3OrPlaceholder, literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct], comment: Optional[str] = None, ) -> None: @@ -338,7 +348,7 @@ class LiteralFitsConstraint(ConstraintBase): self.type3 = type3 self.literal = literal - def _generate_static_array(self, sa_args: tuple[types.Type3, types.IntType3]) -> CheckResult: + def _generate_static_array(self, sa_args: tuple[Type3, IntType3]) -> CheckResult: if not isinstance(self.literal, ourlang.ConstantTuple): return Error('Must be tuple', comment=self.comment) @@ -364,7 +374,7 @@ class LiteralFitsConstraint(ConstraintBase): return res - def _generate_struct(self, st_args: tuple[tuple[str, types.Type3], ...]) -> CheckResult: + def _generate_struct(self, st_args: tuple[tuple[str, Type3], ...]) -> CheckResult: if not isinstance(self.literal, ourlang.ConstantStruct): return Error('Must be struct') @@ -388,7 +398,7 @@ class LiteralFitsConstraint(ConstraintBase): return res - def _generate_tuple(self, tp_args: tuple[types.Type3, ...]) -> CheckResult: + def _generate_tuple(self, tp_args: tuple[Type3, ...]) -> CheckResult: if not isinstance(self.literal, ourlang.ConstantTuple): return Error('Must be tuple', comment=self.comment) @@ -432,7 +442,7 @@ class LiteralFitsConstraint(ConstraintBase): 'f64': None, } - if isinstance(self.type3, placeholders.PlaceholderForType): + if isinstance(self.type3, PlaceholderForType): if self.type3.resolve_as is None: return RequireTypeSubstitutes() @@ -492,17 +502,17 @@ class CanBeSubscriptedConstraint(ConstraintBase): """ __slots__ = ('ret_type3', 'type3', 'index', 'index_phft', ) - ret_type3: placeholders.Type3OrPlaceholder - type3: placeholders.Type3OrPlaceholder + ret_type3: Type3OrPlaceholder + type3: Type3OrPlaceholder index: ourlang.Expression - index_phft: placeholders.Type3OrPlaceholder + index_phft: Type3OrPlaceholder def __init__( self, - ret_type3: placeholders.PlaceholderForType, - type3: placeholders.PlaceholderForType, + ret_type3: PlaceholderForType, + type3: PlaceholderForType, index: ourlang.Expression, - index_phft: placeholders.PlaceholderForType, + index_phft: PlaceholderForType, comment: Optional[str] = None, ) -> None: super().__init__(comment=comment) @@ -518,7 +528,7 @@ class CanBeSubscriptedConstraint(ConstraintBase): SameTypeConstraint(prelude.u8, self.ret_type3, comment='([]) :: bytes -> u32 -> u8'), ] - def _generate_static_array(self, sa_args: tuple[types.Type3, types.IntType3]) -> CheckResult: + def _generate_static_array(self, sa_args: tuple[Type3, IntType3]) -> CheckResult: sa_type, sa_len = sa_args if isinstance(self.index, ourlang.ConstantPrimitive): @@ -532,7 +542,7 @@ class CanBeSubscriptedConstraint(ConstraintBase): SameTypeConstraint(sa_type, self.ret_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'), ] - def _generate_tuple(self, tp_args: tuple[types.Type3, ...]) -> CheckResult: + def _generate_tuple(self, tp_args: tuple[Type3, ...]) -> CheckResult: # We special case tuples to allow for ease of use to the programmer # e.g. rather than having to do `fst a` and `snd a` and only have to-sized tuples # we use a[0] and a[1] and allow for a[2] and on. @@ -558,7 +568,7 @@ class CanBeSubscriptedConstraint(ConstraintBase): def check(self) -> CheckResult: exp_type = self.type3 - if isinstance(exp_type, placeholders.PlaceholderForType): + if isinstance(exp_type, PlaceholderForType): if exp_type.resolve_as is None: return RequireTypeSubstitutes() diff --git a/phasm/type3/constraintsgenerator.py b/phasm/type3/constraintsgenerator.py index 2b98966..c956918 100644 --- a/phasm/type3/constraintsgenerator.py +++ b/phasm/type3/constraintsgenerator.py @@ -6,10 +6,6 @@ The constraints solver can then try to resolve all constraints. from typing import Generator, List from .. import ourlang, prelude -from . import functions as functions -from . import placeholders as placeholders -from . import typeclasses as typeclasses -from . import types as type3types from .constraints import ( CanBeSubscriptedConstraint, ConstraintBase, @@ -20,7 +16,14 @@ from .constraints import ( SameTypeConstraint, TupleMatchConstraint, ) +from .functions import ( + Constraint_TypeClassInstanceExists, + FunctionSignature, + TypeVariable, + TypeVariableApplication_Unary, +) from .placeholders import PlaceholderForType +from .types import Type3, TypeApplication_Struct ConstraintGenerator = Generator[ConstraintBase, None, None] @@ -30,7 +33,7 @@ def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase return [*module(ctx, inp)] -def constant(ctx: Context, inp: ourlang.Constant, phft: placeholders.PlaceholderForType) -> ConstraintGenerator: +def constant(ctx: Context, inp: ourlang.Constant, phft: PlaceholderForType) -> ConstraintGenerator: if isinstance(inp, (ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct)): yield LiteralFitsConstraint( phft, inp, @@ -63,7 +66,7 @@ def expression_function_call(ctx: Context, inp: ourlang.FunctionCall, phft: Plac def _expression_function_call( ctx: Context, func_name: str, - signature: functions.FunctionSignature, + signature: FunctionSignature, arguments: list[ourlang.Expression], return_expr: ourlang.Expression, return_phft: PlaceholderForType, @@ -92,13 +95,13 @@ def _expression_function_call( # placeholder here. These don't need to update anything once # subsituted - that's done by arg_placeholders. type_var_map = { - x: placeholders.PlaceholderForType([]) + x: PlaceholderForType([]) for x in signature.args - if isinstance(x, functions.TypeVariable) + if isinstance(x, TypeVariable) } for constraint in signature.context.constraints: - if isinstance(constraint, functions.Constraint_TypeClassInstanceExists): + if isinstance(constraint, Constraint_TypeClassInstanceExists): yield MustImplementTypeClassConstraint( ctx, constraint.type_class3, @@ -113,7 +116,7 @@ def _expression_function_call( # That is, given `foo :: t a -> a` we need to ensure # that both a's are the same. for sig_arg in signature.args: - if isinstance(sig_arg, type3types.Type3): + if isinstance(sig_arg, Type3): # Not a type variable at all continue @@ -121,7 +124,7 @@ def _expression_function_call( # Not a type variable for a type constructor continue - if not isinstance(sig_arg.application, functions.TypeVariableApplication_Unary): + if not isinstance(sig_arg.application, TypeVariableApplication_Unary): raise NotImplementedError(sig_arg.application) assert sig_arg.application.arguments in type_var_map # When does this happen? @@ -139,18 +142,18 @@ def _expression_function_call( else: comment = f'The type of the value passed to argument {arg_no} of function {func_name} should match the type of that argument' - if isinstance(sig_part, functions.TypeVariable): + if isinstance(sig_part, TypeVariable): yield SameTypeConstraint(type_var_map[sig_part], arg_placeholders[arg_expr], comment=comment) continue - if isinstance(sig_part, type3types.Type3): + if isinstance(sig_part, Type3): yield SameTypeConstraint(sig_part, arg_placeholders[arg_expr], comment=comment) continue raise NotImplementedError(sig_part) return -def expression(ctx: Context, inp: ourlang.Expression, phft: placeholders.PlaceholderForType) -> ConstraintGenerator: +def expression(ctx: Context, inp: ourlang.Expression, phft: PlaceholderForType) -> ConstraintGenerator: if isinstance(inp, ourlang.Constant): yield from constant(ctx, inp, phft) return @@ -194,7 +197,7 @@ def expression(ctx: Context, inp: ourlang.Expression, phft: placeholders.Placeho return if isinstance(inp, ourlang.AccessStructMember): - assert isinstance(inp.struct_type3.application, type3types.TypeApplication_Struct) # FIXME: See test_struct.py::test_struct_not_accessible + assert isinstance(inp.struct_type3.application, TypeApplication_Struct) # FIXME: See test_struct.py::test_struct_not_accessible mem_typ = dict(inp.struct_type3.application.arguments)[inp.member]