Changeset - 6c13962edb01
[Not reviewed]
0 1 0
MH - 3 years ago 2022-02-18 18:05:05
contact@maxhenger.nl
WIP: More refactoring of type inference
1 file changed with 147 insertions and 74 deletions:
0 comments (0 inline, 0 general)
src/protocol/parser/pass_typing.rs
Show inline comments
 
@@ -764,131 +764,131 @@ impl<'a> Iterator for InferenceTypeMarkerIter<'a> {
 
                // Modify internal index, then return items
 
                self.idx = end_idx;
 
                return Some((marker, &self.parts[start_idx..end_idx]));
 
            }
 

	
 
            self.idx += 1;
 
        }
 

	
 
        None
 
    }
 
}
 

	
 
#[derive(Debug, PartialEq, Eq)]
 
enum DualInferenceResult {
 
    Neither,        // neither argument is clarified
 
    First,          // first argument is clarified using the second one
 
    Second,         // second argument is clarified using the first one
 
    Both,           // both arguments are clarified
 
    Incompatible,   // types are incompatible: programmer error
 
}
 

	
 
impl DualInferenceResult {
 
    fn modified_lhs(&self) -> bool {
 
        match self {
 
            DualInferenceResult::First | DualInferenceResult::Both => true,
 
            _ => false
 
        }
 
    }
 
    fn modified_rhs(&self) -> bool {
 
        match self {
 
            DualInferenceResult::Second | DualInferenceResult::Both => true,
 
            _ => false
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, PartialEq, Eq)]
 
enum SingleInferenceResult {
 
    Unmodified,
 
    Modified,
 
    Incompatible
 
}
 

	
 
// -----------------------------------------------------------------------------
 
// PassTyping - Public Interface
 
// -----------------------------------------------------------------------------
 

	
 
type InferNodeIndex = usize;
 
type ExtraIndex = usize;
 
type PolyDataIndex = usize;
 

	
 
enum DefinitionType{
 
    Component(ComponentDefinitionId),
 
    Function(FunctionDefinitionId),
 
}
 

	
 
impl DefinitionType {
 
    fn definition_id(&self) -> DefinitionId {
 
        match self {
 
            DefinitionType::Component(v) => v.upcast(),
 
            DefinitionType::Function(v) => v.upcast(),
 
        }
 
    }
 
}
 

	
 
pub(crate) struct ResolveQueueElement {
 
    // Note that using the `definition_id` and the `monomorph_idx` one may
 
    // query the type table for the full procedure type, thereby retrieving
 
    // the polymorphic arguments to the procedure.
 
    pub(crate) root_id: RootId,
 
    pub(crate) definition_id: DefinitionId,
 
    pub(crate) reserved_type_id: TypeId,
 
}
 

	
 
pub(crate) type ResolveQueue = Vec<ResolveQueueElement>;
 

	
 
#[derive(Clone)]
 
struct InferenceNode {
 
    expr_type: InferenceType,       // result type from expression
 
    expr_id: ExpressionId,          // expression that is evaluated
 
    inference_rule: InferenceRule,
 
    parent_index: Option<InferNodeIndex>,
 
    field_or_monomorph_index: i32,    // index of field
 
    extra_index: ExtraIndex,            // index of extra data needed for inference
 
    poly_data_index: PolyDataIndex,            // index of extra data needed for inference
 
    type_id: TypeId,                // when applicable indexes into type table
 
}
 

	
 
/// Inferencing rule to apply. Some of these are reasonably generic. Other ones
 
/// require so much custom logic that we'll not try to come up with an
 
/// abstraction.
 
enum InferenceRule {
 
    Noop,
 
    MonoTemplate(InferenceRuleTemplate),
 
    BiEqual(InferenceRuleBiEqual),
 
    TriEqualArgs(InferenceRuleTriEqualArgs),
 
    TriEqualAll(InferenceRuleTriEqualAll),
 
    Concatenate(InferenceRuleTwoArgs),
 
    IndexingExpr(InferenceRuleIndexingExpr),
 
    SlicingExpr(InferenceRuleSlicingExpr),
 
    SelectStructField(InferenceRuleSelectStructField),
 
    SelectTupleMember(InferenceRuleSelectTupleMember),
 
    LiteralStruct(InferenceRuleLiteralStruct),
 
    LiteralEnum(InferenceRuleLiteralEnum),
 
    LiteralUnion(InferenceRuleLiteralUnion),
 
    LiteralArray(InferenceRuleLiteralArray),
 
    LiteralTuple(InferenceRuleLiteralTuple),
 
    CastExpr(InferenceRuleCastExpr),
 
    CallExpr(InferenceRuleCallExpr),
 
    VariableExpr(InferenceRuleVariableExpr),
 
}
 

	
 
impl InferenceRule {
 
    union_cast_method_impl!(as_mono_template, InferenceRuleTemplate, InferenceRule::MonoTemplate);
 
    union_cast_method_impl!(as_bi_equal, InferenceRuleBiEqual, InferenceRule::BiEqual);
 
    union_cast_method_impl!(as_tri_equal_args, InferenceRuleTriEqualArgs, InferenceRule::TriEqualArgs);
 
    union_cast_method_impl!(as_tri_equal_all, InferenceRuleTriEqualAll, InferenceRule::TriEqualAll);
 
    union_cast_method_impl!(as_concatenate, InferenceRuleTwoArgs, InferenceRule::Concatenate);
 
    union_cast_method_impl!(as_indexing_expr, InferenceRuleIndexingExpr, InferenceRule::IndexingExpr);
 
    union_cast_method_impl!(as_slicing_expr, InferenceRuleSlicingExpr, InferenceRule::SlicingExpr);
 
    union_cast_method_impl!(as_select_struct_field, InferenceRuleSelectStructField, InferenceRule::SelectStructField);
 
    union_cast_method_impl!(as_select_union_member, InferenceRuleSelectTupleMember, InferenceRule::SelectTupleMember);
 
}
 

	
 
struct InferenceRuleTemplate {
 
    template: &'static [InferenceTypePart],
 
    application: InferenceRuleTemplateApplication,
 
}
 

	
 
impl InferenceRuleTemplate {
 
    fn new_none() -> Self {
 
        return Self{
 
            template: &[],
 
@@ -973,243 +973,269 @@ struct InferenceRuleSelectTupleMember {
 

	
 
struct InferenceRuleLiteralStruct {
 
    element_indices: Vec<InferNodeIndex>,
 
}
 

	
 
struct InferenceRuleLiteralEnum {
 

	
 
}
 

	
 
struct InferenceRuleLiteralUnion {
 
    element_indices: Vec<InferNodeIndex>
 
}
 

	
 
struct InferenceRuleLiteralArray {
 
    element_indices: Vec<InferNodeIndex>
 
}
 

	
 
struct InferenceRuleLiteralTuple {
 
    element_indices: Vec<InferNodeIndex>
 
}
 

	
 
struct InferenceRuleCastExpr {
 
    subject_index: InferNodeIndex,
 
}
 

	
 
struct InferenceRuleCallExpr {
 
    argument_indices: Vec<InferNodeIndex>
 
}
 

	
 
/// Data associated with a variable expression: an expression that reads the
 
/// value from a variable.
 
struct InferenceRuleVariableExpr {
 
    variable_index: InferNodeIndex,
 
}
 

	
 
/// Data associated with a variable: keeping track of all the variable
 
/// expressions in which it is used.
 
struct InferenceRuleVariable {
 
    used_at_indices: Vec<InferNodeIndex>,
 
}
 

	
 
/// This particular visitor will recurse depth-first into the AST and ensures
 
/// that all expressions have the appropriate types.
 
pub(crate) struct PassTyping {
 
    // Current definition we're typechecking.
 
    reserved_type_id: TypeId,
 
    definition_type: DefinitionType,
 
    poly_vars: Vec<ConcreteType>,
 
    // Temporary variables during construction of inference rules
 
    // Temporary variables during construction of inference rulesr
 
    parent_index: Option<InferNodeIndex>,
 
    // Buffers for iteration over various types
 
    var_buffer: ScopedBuffer<VariableId>,
 
    expr_buffer: ScopedBuffer<ExpressionId>,
 
    stmt_buffer: ScopedBuffer<StatementId>,
 
    bool_buffer: ScopedBuffer<bool>,
 
    index_buffer: ScopedBuffer<usize>,
 
    // Mapping from parser type to inferred type. We attempt to continue to
 
    // specify these types until we're stuck or we've fully determined the type.
 
    var_types: HashMap<VariableId, VarData>,            // types of variables
 
    infer_nodes: Vec<InferenceNode>,                     // will be transferred to type table at end
 
    extra_data: Vec<ExtraData>,       // data for polymorph inference
 
    poly_data: Vec<PolyData>,       // data for polymorph inference
 
    // Keeping track of which expressions need to be reinferred because the
 
    // expressions they're linked to made progression on an associated type
 
    expr_queued: DequeSet<i32>,
 
}
 

	
 
// TODO: @Rename, this is used for a lot of type inferencing. It seems like
 
//  there is a different underlying architecture waiting to surface.
 
struct ExtraData {
 
/// Generic struct that is used to store inferred types associated with
 
/// polymorphic types.
 
struct PolyData {
 
    definition_id: DefinitionId, // the definition, only used for user feedback
 
    /// Progression of polymorphic variables (if any)
 
    /// Inferred types of the polymorphic variables as they are written down
 
    /// at the type's definition.
 
    poly_vars: Vec<InferenceType>,
 
    /// Progression of types of call arguments or struct members
 
    embedded: Vec<InferenceType>,
 
    /// Inferred types of associated types (e.g. struct fields, tuple members,
 
    /// function arguments). These types may depend on the polymorphic variables
 
    /// defined above.
 
    associated: Vec<InferenceType>,
 
    /// Inferred "returned" type (e.g. if a struct field is selected, then this
 
    /// contains the type of the selected field, for a function call it contains
 
    /// the return type). May depend on the polymorphic variables defined above.
 
    returned: InferenceType,
 
}
 

	
 
impl Default for ExtraData {
 
impl Default for PolyData {
 
    fn default() -> Self {
 
        Self{
 
            expr_id: ExpressionId::new_invalid(),
 
        Self {
 
            definition_id: DefinitionId::new_invalid(),
 
            poly_vars: Vec::new(),
 
            embedded: Vec::new(),
 
            associated: Vec::new(),
 
            returned: InferenceType::default(),
 
        }
 
    }
 
}
 

	
 
enum PolyDataTypeIndex {
 
    Associated(usize), // indexes into `PolyData.associated`
 
    Returned,
 
}
 

	
 
impl PolyData {
 
    fn get_type(&self, index: PolyDataTypeIndex) -> &InferenceType {
 
        match index {
 
            PolyDataTypeIndex::Associated(index) => return &self.associated[index],
 
            PolyDataTypeIndex::Returned => return &self.returned,
 
        }
 
    }
 

	
 
    fn get_type_mut(&mut self, index: PolyDataTypeIndex) -> &mut InferenceType {
 
        match index {
 
            PolyDataTypeIndex::Associated(index) => return &mut self.associated[index],
 
            PolyDataTypeIndex::Returned => return &mut self.returned,
 
        }
 
    }
 
}
 

	
 
struct VarData {
 
    /// Type of the variable
 
    var_type: InferenceType,
 
    /// VariableExpressions that use the variable
 
    used_at: Vec<ExpressionId>,
 
    /// For channel statements we link to the other variable such that when one
 
    /// channel's interior type is resolved, we can also resolve the other one.
 
    linked_var: Option<VariableId>,
 
}
 

	
 
impl VarData {
 
    fn new_channel(var_type: InferenceType, other_port: VariableId) -> Self {
 
        Self{ var_type, used_at: Vec::new(), linked_var: Some(other_port) }
 
    }
 
    fn new_local(var_type: InferenceType) -> Self {
 
        Self{ var_type, used_at: Vec::new(), linked_var: None }
 
    }
 
}
 

	
 
impl PassTyping {
 
    pub(crate) fn new() -> Self {
 
        PassTyping {
 
            reserved_type_id: TypeId::new_invalid(),
 
            definition_type: DefinitionType::Function(FunctionDefinitionId::new_invalid()),
 
            poly_vars: Vec::new(),
 
            parent_index: None,
 
            var_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_LARGE),
 
            expr_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_LARGE),
 
            stmt_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_LARGE),
 
            bool_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_SMALL),
 
            var_types: HashMap::new(),
 
            infer_nodes: Vec::new(),
 
            extra_data: Vec::new(),
 
            poly_data: Vec::new(),
 
            expr_queued: DequeSet::new(),
 
        }
 
    }
 

	
 
    pub(crate) fn queue_module_definitions(ctx: &mut Ctx, queue: &mut ResolveQueue) {
 
        debug_assert_eq!(ctx.module().phase, ModuleCompilationPhase::ValidatedAndLinked);
 
        let root_id = ctx.module().root_id;
 
        let root = &ctx.heap.protocol_descriptions[root_id];
 
        for definition_id in &root.definitions {
 
            let definition = &ctx.heap[*definition_id];
 

	
 
            let first_concrete_part = match definition {
 
                Definition::Function(definition) => {
 
                    if definition.poly_vars.is_empty() {
 
                        Some(ConcreteTypePart::Function(*definition_id, 0))
 
                    } else {
 
                        None
 
                    }
 
                }
 
                Definition::Component(definition) => {
 
                    if definition.poly_vars.is_empty() {
 
                        Some(ConcreteTypePart::Component(*definition_id, 0))
 
                    } else {
 
                        None
 
                    }
 
                },
 
                Definition::Enum(_) | Definition::Struct(_) | Definition::Union(_) => None,
 
            };
 

	
 
            if let Some(first_concrete_part) = first_concrete_part {
 
                let concrete_type = ConcreteType{ parts: vec![first_concrete_part] };
 
                let type_id = ctx.types.reserve_procedure_monomorph_type_id(definition_id, concrete_type);
 
                queue.push(ResolveQueueElement{
 
                    root_id,
 
                    definition_id: *definition_id,
 
                    reserved_type_id: type_id,
 
                })
 
            }
 
        }
 
    }
 

	
 
    pub(crate) fn handle_module_definition(
 
        &mut self, ctx: &mut Ctx, queue: &mut ResolveQueue, element: ResolveQueueElement
 
    ) -> VisitorResult {
 
        self.reset();
 
        debug_assert_eq!(ctx.module().root_id, element.root_id);
 
        debug_assert!(self.poly_vars.is_empty());
 

	
 
        // Prepare for visiting the definition
 
        self.reserved_type_id = element.reserved_type_id;
 

	
 
        let proc_base = ctx.types.get_base_definition(&element.definition_id).unwrap();
 
        if proc_base.is_polymorph {
 
            let monomorph = ctx.types.get_monomorph(element.reserved_type_id);
 
            for poly_arg in monomorph.concrete_type.embedded_iter(0) {
 
                self.poly_vars.push(ConcreteType{ parts: Vec::from(poly_arg) });
 
            }
 
        }
 

	
 
        // Visit the definition, setting up the type resolving process, then
 
        // (attempt to) resolve all types
 
        self.visit_definition(ctx, element.definition_id)?;
 
        self.resolve_types(ctx, queue)?;
 
        Ok(())
 
    }
 

	
 
    fn reset(&mut self) {
 
        self.reserved_type_id = TypeId::new_invalid();
 
        self.definition_type = DefinitionType::Function(FunctionDefinitionId::new_invalid());
 
        self.poly_vars.clear();
 
        self.var_types.clear();
 
        self.infer_nodes.clear();
 
        self.extra_data.clear();
 
        self.poly_data.clear();
 
        self.expr_queued.clear();
 
    }
 
}
 

	
 
// -----------------------------------------------------------------------------
 
// PassTyping - Visitor-like implementation
 
// -----------------------------------------------------------------------------
 

	
 
type VisitorResult = Result<(), ParseError>;
 
type VisitExprResult = Result<InferNodeIndex, ParseError>;
 

	
 
impl PassTyping {
 
    // Definitions
 

	
 
    fn visit_definition(&mut self, ctx: &mut Ctx, id: DefinitionId) -> VisitorResult {
 
        return visitor_recursive_definition_impl!(self, &ctx.heap[id], ctx);
 
    }
 

	
 
    fn visit_enum_definition(&mut self, _: &mut Ctx, _: EnumDefinitionId) -> VisitorResult { return Ok(()) }
 
    fn visit_struct_definition(&mut self, _: &mut Ctx, _: StructDefinitionId) -> VisitorResult { return Ok(()) }
 
    fn visit_union_definition(&mut self, _: &mut Ctx, _: UnionDefinitionId) -> VisitorResult { return Ok(()) }
 

	
 
    fn visit_component_definition(&mut self, ctx: &mut Ctx, id: ComponentDefinitionId) -> VisitorResult {
 
        self.definition_type = DefinitionType::Component(id);
 

	
 
        let comp_def = &ctx.heap[id];
 
        debug_assert_eq!(comp_def.poly_vars.len(), self.poly_vars.len(), "component polyvars do not match imposed polyvars");
 

	
 
        debug_log!("{}", "-".repeat(50));
 
        debug_log!("Visiting component '{}': {}", comp_def.identifier.value.as_str(), id.0.index);
 
        debug_log!("{}", "-".repeat(50));
 

	
 
        // Reserve data for expression types
 
        debug_assert!(self.infer_nodes.is_empty());
 
        self.infer_nodes.resize(comp_def.num_expressions_in_body as usize, Default::default());
 

	
 
        // Visit parameters
 
        let section = self.var_buffer.start_section_initialized(comp_def.parameters.as_slice());
 
        for param_id in section.iter_copied() {
 
            let param = &ctx.heap[param_id];
 
            let var_type = self.determine_inference_type_from_parser_type_elements(&param.parser_type.elements, true);
 
            debug_assert!(var_type.is_done, "expected component arguments to be concrete types");
 
            self.var_types.insert(param_id, VarData::new_local(var_type));
 
        }
 
        section.forget();
 

	
 
        // Visit the body and all of its expressions
 
        let body_stmt_id = ctx.heap[id].body;
 
@@ -1673,200 +1699,200 @@ impl PassTyping {
 
    }
 

	
 
    fn visit_literal_expr(&mut self, ctx: &mut Ctx, id: LiteralExpressionId) -> VisitExprResult {
 
        let upcast_id = id.upcast();
 
        let self_index = self.insert_initial_inference_node(ctx, upcast_id)?;
 

	
 
        let old_parent = self.parent_index.replace(self_index);
 

	
 
        let literal_expr = &ctx.heap[id];
 
        match &literal_expr.value {
 
            Literal::Null => {
 
                let node = &mut self.infer_nodes[self_index];
 
                node.inference_rule = InferenceRule::MonoTemplate(InferenceRuleTemplate::new_template(&MESSAGE_TEMPLATE));
 
            },
 
            Literal::Integer(_) => {
 
                let node = &mut self.infer_nodes[self_index];
 
                node.inference_rule = InferenceRule::MonoTemplate(InferenceRuleTemplate::new_template(&INTEGERLIKE_TEMPLATE));
 
            },
 
            Literal::True | Literal::False => {
 
                let node = &mut self.infer_nodes[self_index];
 
                node.inference_rule = InferenceRule::MonoTemplate(InferenceRuleTemplate::new_forced(&BOOL_TEMPLATE));
 
            },
 
            Literal::Character(_) => {
 
                let node = &mut self.infer_nodes[self_index];
 
                node.inference_rule = InferenceRule::MonoTemplate(InferenceRuleTemplate::new_forced(&CHARACTER_TEMPLATE));
 
            },
 
            Literal::String(_) => {
 
                let node = &mut self.infer_nodes[self_index];
 
                node.inference_rule = InferenceRule::MonoTemplate(InferenceRuleTemplate::new_forced(&STRING_TEMPLATE));
 
            },
 
            Literal::Struct(literal) => {
 
                // Visit field expressions
 
                let mut expr_ids = self.expr_buffer.start_section();
 
                for field in &literal.fields {
 
                    expr_ids.push(field.value);
 
                }
 

	
 
                let mut expr_indices = self.index_buffer.start_section();
 
                for expr_id in expr_ids.iter_copied() {
 
                    let expr_index = self.visit_expr(ctx, expr_id)?;
 
                    expr_indices.push(expr_index);
 
                }
 
                expr_ids.forget();
 
                let element_indices = expr_indices.into_vec();
 

	
 
                // Assign rule and extra data index to inference node
 
                let extra_index = self.insert_initial_struct_polymorph_data(ctx, id);
 
                let node = &mut self.infer_nodes[self_index];
 
                node.extra_index = extra_index;
 
                node.poly_data_index = extra_index;
 
                node.inference_rule = InferenceRule::LiteralStruct(InferenceRuleLiteralStruct{
 
                    element_indices,
 
                });
 
            },
 
            Literal::Enum(_) => {
 
                // Enumerations do not carry any subexpressions, but may still
 
                // have a user-defined polymorphic marker variable. For this 
 
                // reason we may still have to apply inference to this 
 
                // polymorphic variable
 
                let extra_index = self.insert_initial_enum_polymorph_data(ctx, id);
 
                let node = &mut self.infer_nodes[self_index];
 
                node.extra_index = extra_index;
 
                node.poly_data_index = extra_index;
 
                node.inference_rule = InferenceRule::LiteralEnum(InferenceRuleLiteralEnum{
 

	
 
                });
 
            },
 
            Literal::Union(literal) => {
 
                // May carry subexpressions and polymorphic arguments
 
                let expr_ids = self.expr_buffer.start_section_initialized(literal.values.as_slice());
 
                let extra_index = self.insert_initial_union_polymorph_data(ctx, id);
 

	
 
                let mut expr_indices = self.index_buffer.start_section();
 
                for expr_id in expr_ids.iter_copied() {
 
                    let expr_index = self.visit_expr(ctx, expr_id)?;
 
                    expr_indices.push(expr_index);
 
                }
 
                expr_ids.forget();
 
                let element_indices = expr_indices.into_vec();
 

	
 
                let node = &mut self.infer_nodes[self_index];
 
                node.extra_index = extra_index;
 
                node.poly_data_index = extra_index;
 
                node.inference_rule = InferenceRule::LiteralUnion(InferenceRuleLiteralUnion{
 
                    element_indices,
 
                });
 
            },
 
            Literal::Array(expressions) | Literal::Tuple(expressions) => {
 
                let expr_ids = self.expr_buffer.start_section_initialized(expressions.as_slice());
 
                let mut expr_indices = self.index_buffer.start_section();
 

	
 
                for expr_id in expr_ids.iter_copied() {
 
                    let expr_index = self.visit_expr(ctx, expr_id)?;
 
                    expr_indices.push(expr_index);
 
                }
 
                expr_ids.forget();
 
                let element_indices = expr_indices.into_vec();
 

	
 
                let node = &mut self.infer_nodes[self_index];
 
                node.extra_index = extra_index;
 
                node.poly_data_index = extra_index;
 
                node.inference_rule = InferenceRule::LiteralArray(InferenceRuleLiteralArray{
 
                    element_indices,
 
                })
 
            }
 
        }
 

	
 
        self.parent_index = old_parent;
 
        self.progress_literal_expr(ctx, id)
 
    }
 

	
 
    fn visit_cast_expr(&mut self, ctx: &mut Ctx, id: CastExpressionId) -> VisitExprResult {
 
        let upcast_id = id.upcast();
 
        let self_index = self.insert_initial_inference_node(ctx, upcast_id)?;
 

	
 
        let cast_expr = &ctx.heap[id];
 
        let subject_expr_id = cast_expr.subject;
 

	
 
        let old_parent = self.parent_index.replace(self_index);
 
        let subject_index = self.visit_expr(ctx, subject_expr_id)?;
 

	
 
        let node = &mut self.infer_nodes[self_index];
 
        node.inference_rule = InferenceRule::CastExpr(InferenceRuleCastExpr{
 
            subject_index,
 
        });
 

	
 
        self.parent_index = old_parent;
 
        self.progress_cast_expr(ctx, id)
 
    }
 

	
 
    fn visit_call_expr(&mut self, ctx: &mut Ctx, id: CallExpressionId) -> VisitExprResult {
 
        let upcast_id = id.upcast();
 
        let self_index = self.insert_initial_inference_node(ctx, upcast_id)?;
 
        let extra_index = self.insert_initial_call_polymorph_data(ctx, id);
 

	
 
        // By default we set the polymorph idx for calls to 0. If the call
 
        // refers to a non-polymorphic function, then it will be "monomorphed"
 
        // once, hence we end up pointing to the correct instance.
 
        self.infer_nodes[self_index].field_or_monomorph_index = 0;
 

	
 
        // Visit all arguments
 
        let old_parent = self.parent_index.replace(self_index);
 

	
 
        let call_expr = &ctx.heap[id];
 
        let expr_ids = self.expr_buffer.start_section_initialized(call_expr.arguments.as_slice());
 
        let mut expr_indices = self.index_buffer.start_section();
 

	
 
        for arg_expr_id in expr_ids.iter_copied() {
 
            let expr_index = self.visit_expr(ctx, arg_expr_id)?;
 
            expr_indices.push(expr_index);
 
        }
 
        expr_ids.forget();
 
        let argument_indices = expr_indices.into_vec();
 

	
 
        let node = &mut self.infer_nodes[self_index];
 
        node.extra_index = extra_index;
 
        node.poly_data_index = extra_index;
 
        node.inference_rule = InferenceRule::CallExpr(InferenceRuleCallExpr{
 
            argument_indices,
 
        });
 

	
 
        self.parent_index = old_parent;
 
        self.progress_call_expr(ctx, id)
 
    }
 

	
 
    fn visit_variable_expr(&mut self, ctx: &mut Ctx, id: VariableExpressionId) -> VisitExprResult {
 
        let upcast_id = id.upcast();
 
        self.insert_initial_inference_node(ctx, upcast_id)?;
 

	
 
        let var_expr = &ctx.heap[id];
 
        debug_assert!(var_expr.declaration.is_some());
 

	
 
        // Not pretty: if a binding expression, then this is the first time we
 
        // encounter the variable, so we still need to insert the variable data.
 
        let declaration = &ctx.heap[var_expr.declaration.unwrap()];
 
        if !self.var_types.contains_key(&declaration.this)  {
 
            debug_assert!(declaration.kind == VariableKind::Binding);
 
            let var_type = self.determine_inference_type_from_parser_type_elements(
 
                &declaration.parser_type.elements, true
 
            );
 
            self.var_types.insert(declaration.this, VarData{
 
                var_type,
 
                used_at: vec![upcast_id],
 
                linked_var: None
 
            });
 
        } else {
 
            let var_data = self.var_types.get_mut(&declaration.this).unwrap();
 
            var_data.used_at.push(upcast_id);
 
        }
 

	
 
        self.progress_variable_expr(ctx, id)
 
    }
 
}
 

	
 
// -----------------------------------------------------------------------------
 
// PassTyping - Type-inference progression
 
// -----------------------------------------------------------------------------
 

	
 
impl PassTyping {
 
    #[allow(dead_code)] // used when debug flag at the top of this file is true.
 
    fn debug_get_display_name(&self, ctx: &Ctx, expr_id: ExpressionId) -> String {
 
        let expr_idx = ctx.heap[expr_id].get_unique_id_in_definition();
 
        let expr_type = &self.infer_nodes[expr_idx as usize].expr_type;
 
        expr_type.display_name(&ctx.heap)
 
    }
 
@@ -1922,97 +1948,97 @@ impl PassTyping {
 
            // Go through all polymorphic arguments and add them to the concrete
 
            // types.
 
            for (poly_idx, poly_type) in inference.iter().enumerate() {
 
                if !poly_type.is_done {
 
                    let expr = &ctx.heap[expr_id];
 
                    let definition = match expr {
 
                        Expression::Call(expr) => expr.definition,
 
                        Expression::Literal(expr) => match &expr.value {
 
                            Literal::Enum(lit) => lit.definition,
 
                            Literal::Union(lit) => lit.definition,
 
                            Literal::Struct(lit) => lit.definition,
 
                            _ => unreachable!()
 
                        },
 
                        _ => unreachable!(),
 
                    };
 
                    let poly_vars = ctx.heap[definition].poly_vars();
 
                    return Err(ParseError::new_error_at_span(
 
                        &ctx.module().source, expr.operation_span(), format!(
 
                            "could not fully infer the type of polymorphic variable '{}' of this expression (got '{}')",
 
                            poly_vars[poly_idx].value.as_str(), poly_type.display_name(&ctx.heap)
 
                        )
 
                    ));
 
                }
 

	
 
                poly_type.write_concrete_type(&mut concrete_type);
 
            }
 

	
 
            Ok(concrete_type)
 
        }
 

	
 
        // Inference is now done. But we may still have uninferred types. So we
 
        // check for these.
 
        for infer_expr in self.infer_nodes.iter_mut() {
 
            if !infer_expr.expr_type.is_done {
 
                let expr = &ctx.heap[infer_expr.expr_id];
 
                return Err(ParseError::new_error_at_span(
 
                    &ctx.module().source, expr.full_span(), format!(
 
                        "could not fully infer the type of this expression (got '{}')",
 
                        infer_expr.expr_type.display_name(&ctx.heap)
 
                    )
 
                ));
 
            }
 

	
 
            // Expression is fine, check if any extra data is attached
 
            if infer_expr.extra_data_idx < 0 { continue; }
 

	
 
            // Extra data is attached, perform typechecking and transfer
 
            // resolved information to the expression
 
            let extra_data = &self.extra_data[infer_expr.extra_data_idx as usize];
 
            let extra_data = &self.poly_data[infer_expr.extra_data_idx as usize];
 

	
 
            // Note that only call and literal expressions need full inference.
 
            // Select expressions also use `extra_data`, but only for temporary
 
            // storage of the struct type whose field it is selecting.
 
            match &ctx.heap[extra_data.expr_id] {
 
                Expression::Call(expr) => {
 
                    // Check if it is not a builtin function. If not, then
 
                    // construct the first part of the concrete type.
 
                    let first_concrete_part = if expr.method == Method::UserFunction {
 
                        ConcreteTypePart::Function(expr.definition, extra_data.poly_vars.len() as u32)
 
                    } else if expr.method == Method::UserComponent {
 
                        ConcreteTypePart::Component(expr.definition, extra_data.poly_vars.len() as u32)
 
                    } else {
 
                        // Builtin function
 
                        continue;
 
                    };
 

	
 
                    let definition_id = expr.definition;
 
                    let concrete_type = inference_type_to_concrete_type(
 
                        ctx, extra_data.expr_id, &extra_data.poly_vars, first_concrete_part
 
                    )?;
 

	
 
                    match ctx.types.get_procedure_monomorph_type_id(&definition_id, &concrete_type.parts) {
 
                        Some(type_id) => {
 
                            // Already typechecked, or already put into the resolve queue
 
                            infer_expr.type_id = type_id;
 
                        },
 
                        None => {
 
                            // Not typechecked yet, so add an entry in the queue
 
                            let reserved_type_id = ctx.types.reserve_procedure_monomorph_type_id(&definition_id, concrete_type);
 
                            infer_expr.type_id = reserved_type_id;
 
                            queue.push(ResolveQueueElement {
 
                                root_id: ctx.heap[definition_id].defined_in(),
 
                                definition_id,
 
                                reserved_type_id,
 
                            });
 
                        }
 
                    }
 
                },
 
                Expression::Literal(expr) => {
 
                    let definition_id = match &expr.value {
 
                        Literal::Enum(lit) => lit.definition,
 
                        Literal::Union(lit) => lit.definition,
 
                        Literal::Struct(lit) => lit.definition,
 
                        _ => unreachable!(),
 
                    };
 
                    let first_concrete_part = ConcreteTypePart::Instance(definition_id, extra_data.poly_vars.len() as u32);
 
                    let concrete_type = inference_type_to_concrete_type(
 
@@ -2264,151 +2290,151 @@ impl PassTyping {
 
        }
 

	
 
        if node.field_or_monomorph_index < 0 {
 
            // Don't know the subject definition, hence the field yet. Try to
 
            // determine it.
 
            let subject_node = &self.infer_nodes[subject_index];
 
            match get_definition_id_from_inference_type(&subject_node.expr_type) {
 
                Ok(Some(definition_id)) => {
 
                    // Determined definition of subject for the first time.
 
                    let base_definition = ctx.types.get_base_definition(&definition_id).unwrap();
 
                    let struct_definition = if let DefinedTypeVariant::Struct(struct_definition) = &base_definition.definition {
 
                        struct_definition
 
                    } else {
 
                        return Err(ParseError::new_error_at_span(
 
                            &ctx.module().source, rule.selected_field.span, format!(
 
                                "Can only apply field access to structs, got a subject of type '{}'",
 
                                subject_type.display_name(&ctx.heap)
 
                            )
 
                        ));
 
                    };
 

	
 
                    // Seek the field that is referenced by the select
 
                    // expression
 
                    let mut field_found = false;
 
                    for (field_index, field) in struct_definition.fields.iter().enumerate() {
 
                        if field.identifier.value == rule.selected_field.value {
 
                            // Found the field of interest
 
                            field_found = true;
 
                            let node = &mut self.infer_nodes[node_index];
 
                            node.field_or_monomorph_index = field_index as i32;
 
                            break;
 
                        }
 
                    }
 

	
 
                    if !field_found {
 
                        let struct_definition = ctx.heap[definition_id].as_struct();
 
                        return Err(ParseError::new_error_at_span(
 
                            &ctx.module().source, rule.selected_field.span, format!(
 
                                "this field does not exist on the struct '{}'",
 
                                ast_struct_def.identifier.value.as_str()
 
                            )
 
                        ));
 
                    }
 

	
 
                    // Insert the initial data needed to infer polymorphic
 
                    // fields
 
                    let extra_index = self.insert_initial_select_polymorph_data(ctx, node_index, definition_id);
 
                    let node = &mut self.infer_nodes[node_index];
 
                    node.extra_index = extra_index;
 
                    node.poly_data_index = extra_index;
 
                },
 
                Ok(None) => {
 
                    // We don't know what to do yet, because we don't know the
 
                    // subject type yet.
 
                    return Ok(())
 
                },
 
                Err(()) => {
 
                    return Err(ParseError::new_error_at_span(
 
                        &ctx.module().source, rule.selected_field.span, format!(
 
                            "Can only apply field access to structs, got a subject of type '{}'",
 
                            subject_type.display_name(&ctx.heap)
 
                        )
 
                    ));
 
                },
 
            }
 
        }
 

	
 
        // If here then the field index is known, hence we can start inferring
 
        // the type of the selected field
 
        let node = &self.infer_nodes[node_index];
 
        let poly_data = &mut self.extra_data[node.extra_index as usize];
 
        let poly_data = &mut self.poly_data[node.poly_data_index as usize];
 
        let mut poly_progress = HashSet::new(); // TODO: @Performance
 

	
 
        // Apply to struct's type
 
        let signature_type: *mut _ = &mut poly_data.embedded[0];
 
        let signature_type: *mut _ = &mut poly_data.associated[0];
 
        let subject_type: *mut _ = &mut self.infer_nodes[subject_expr_idx as usize].expr_type;
 

	
 
        let (_, progress_subject) = Self::apply_equal2_signature_constraint(
 
            ctx, upcast_id, Some(subject_id), poly_data, &mut poly_progress,
 
            signature_type, 0, subject_type, 0
 
        )?;
 

	
 
        if progress_subject {
 
            self.expr_queued.push_back(subject_expr_idx);
 
        }
 

	
 
        // Apply to field's type
 
        let signature_type: *mut _ = &mut poly_data.returned;
 
        let expr_type: *mut _ = &mut self.infer_nodes[expr_idx as usize].expr_type;
 

	
 
        let (_, progress_expr) = Self::apply_equal2_signature_constraint(
 
            ctx, upcast_id, None, poly_data, &mut poly_progress,
 
            signature_type, 0, expr_type, 0
 
        )?;
 
         )?;
 

	
 
        if progress_expr {
 
            if let Some(parent_id) = ctx.heap[upcast_id].parent_expr_id() {
 
                let parent_idx = ctx.heap[parent_id].get_unique_id_in_definition();
 
                self.expr_queued.push_back(parent_idx);
 
            }
 
        }
 

	
 
        // Reapply progress in polymorphic variables to struct's type
 
        let signature_type: *mut _ = &mut poly_data.embedded[0];
 
        let signature_type: *mut _ = &mut poly_data.associated[0];
 
        let subject_type: *mut _ = &mut self.infer_nodes[subject_expr_idx as usize].expr_type;
 

	
 
        let progress_subject = Self::apply_equal2_polyvar_constraint(
 
            poly_data, &poly_progress, signature_type, subject_type
 
        );
 

	
 
        let signature_type: *mut _ = &mut poly_data.returned;
 
        let expr_type: *mut _ = &mut self.infer_nodes[expr_idx as usize].expr_type;
 

	
 
        let progress_expr = Self::apply_equal2_polyvar_constraint(
 
            poly_data, &poly_progress, signature_type, expr_type
 
        );
 

	
 
        (progress_subject, progress_expr)
 

	
 
        return Ok(())
 
    }
 

	
 
    fn progress_inference_rule_select_tuple_member(&mut self, ctx: &mut Ctx, node_index: InferNodeIndex) -> Result<(), ParseError> {
 

	
 
    }
 

	
 
    fn progress_template(&mut self, ctx: &Ctx, node_index: InferNodeIndex, application: InferenceRuleTemplateApplication, template: &[InferenceTypePart]) -> Result<bool, ParseError> {
 
        use InferenceRuleTemplateApplication as TA;
 

	
 
        match application {
 
            TA::None => Ok(false),
 
            TA::Template => self.apply_template_constraint(ctx, node_index, template),
 
            TA::Forced => self.apply_forced_constraint(ctx, node_index, template),
 
        }
 
    }
 

	
 
    fn progress_expr(&mut self, ctx: &mut Ctx, idx: i32) -> Result<(), ParseError> {
 
        let id = self.infer_nodes[idx as usize].expr_id;
 
        match &ctx.heap[id] {
 
            Expression::Assignment(expr) => {
 
                let id = expr.this;
 
                self.progress_assignment_expr(ctx, id)
 
            },
 
            Expression::Binding(expr) => {
 
                let id = expr.this;
 
                self.progress_binding_expr(ctx, id)
 
            },
 
            Expression::Conditional(expr) => {
 
                let id = expr.this;
 
                self.progress_conditional_expr(ctx, id)
 
            },
 
            Expression::Binary(expr) => {
 
@@ -2872,130 +2898,130 @@ impl PassTyping {
 
                                ));
 
                            };
 

	
 
                            let mut struct_def_id = None;
 

	
 
                            for (field_def_idx, field_def) in struct_def.fields.iter().enumerate() {
 
                                if field_def.identifier == *field_name {
 
                                    // Set field definition and index
 
                                    let infer_expr = &mut self.infer_nodes[expr_idx as usize];
 
                                    infer_expr.field_or_monomorph_index = field_def_idx as i32;
 
                                    struct_def_id = Some(type_def.ast_definition);
 
                                    break;
 
                                }
 
                            }
 

	
 
                            if struct_def_id.is_none() {
 
                                let ast_struct_def = ctx.heap[type_def.ast_definition].as_struct();
 
                                return Err(ParseError::new_error_at_span(
 
                                    &ctx.module().source, field_name.span, format!(
 
                                        "this field does not exist on the struct '{}'",
 
                                        ast_struct_def.identifier.value.as_str()
 
                                    )
 
                                ))
 
                            }
 

	
 
                            // Encountered definition and field index for the
 
                            // first time
 
                            self.insert_initial_select_polymorph_data(ctx, id, struct_def_id.unwrap());
 
                        },
 
                        Ok(None) => {
 
                            // Type of subject is not yet known, so we
 
                            // cannot make any progress yet
 
                            return Ok(())
 
                        },
 
                        Err(()) => {
 
                            return Err(ParseError::new_error_at_span(
 
                                &ctx.module().source, field_name.span, format!(
 
                                    "Can only apply field access to structs, got a subject of type '{}'",
 
                                    subject_type.display_name(&ctx.heap)
 
                                )
 
                            ));
 
                        }
 
                    }
 
                }
 

	
 
                // If here then field index is known, and the referenced struct type
 
                // information is inserted into `extra_data`. Check to see if we can
 
                // do some mutual inference.
 
                let poly_data = &mut self.extra_data[extra_idx as usize];
 
                let poly_data = &mut self.poly_data[extra_idx as usize];
 
                let mut poly_progress = HashSet::new(); // TODO: @Performance
 

	
 
                // Apply to struct's type
 
                let signature_type: *mut _ = &mut poly_data.embedded[0];
 
                let signature_type: *mut _ = &mut poly_data.associated[0];
 
                let subject_type: *mut _ = &mut self.infer_nodes[subject_expr_idx as usize].expr_type;
 

	
 
                let (_, progress_subject) = Self::apply_equal2_signature_constraint(
 
                    ctx, upcast_id, Some(subject_id), poly_data, &mut poly_progress,
 
                    signature_type, 0, subject_type, 0
 
                )?;
 

	
 
                if progress_subject {
 
                    self.expr_queued.push_back(subject_expr_idx);
 
                }
 

	
 
                // Apply to field's type
 
                let signature_type: *mut _ = &mut poly_data.returned;
 
                let expr_type: *mut _ = &mut self.infer_nodes[expr_idx as usize].expr_type;
 

	
 
                let (_, progress_expr) = Self::apply_equal2_signature_constraint(
 
                    ctx, upcast_id, None, poly_data, &mut poly_progress,
 
                    signature_type, 0, expr_type, 0
 
                )?;
 

	
 
                if progress_expr {
 
                    if let Some(parent_id) = ctx.heap[upcast_id].parent_expr_id() {
 
                        let parent_idx = ctx.heap[parent_id].get_unique_id_in_definition();
 
                        self.expr_queued.push_back(parent_idx);
 
                    }
 
                }
 

	
 
                // Reapply progress in polymorphic variables to struct's type
 
                let signature_type: *mut _ = &mut poly_data.embedded[0];
 
                let signature_type: *mut _ = &mut poly_data.associated[0];
 
                let subject_type: *mut _ = &mut self.infer_nodes[subject_expr_idx as usize].expr_type;
 

	
 
                let progress_subject = Self::apply_equal2_polyvar_constraint(
 
                    poly_data, &poly_progress, signature_type, subject_type
 
                );
 

	
 
                let signature_type: *mut _ = &mut poly_data.returned;
 
                let expr_type: *mut _ = &mut self.infer_nodes[expr_idx as usize].expr_type;
 

	
 
                let progress_expr = Self::apply_equal2_polyvar_constraint(
 
                    poly_data, &poly_progress, signature_type, expr_type
 
                );
 

	
 
                (progress_subject, progress_expr)
 
            },
 
            SelectKind::TupleMember(member_index) => {
 
                let member_index = *member_index;
 

	
 
                if infer_expr.field_or_monomorph_index < 0 {
 
                    // We don't know what kind of tuple we're accessing yet
 
                    let subject_type = &self.infer_nodes[subject_expr_idx as usize].expr_type;
 
                    let tuple_size = try_get_tuple_size_from_inference_type(subject_type);
 

	
 
                    match tuple_size {
 
                        Ok(Some(enum_size)) => {
 
                            // Make sure we don't access an element outside of
 
                            // the tuple's bounds
 
                            if member_index >= enum_size as u64 {
 
                                return Err(ParseError::new_error_at_span(
 
                                    &ctx.module().source, select_expr.full_span, format!(
 
                                        "element index {} is out of bounds, tuple has {} elements",
 
                                        member_index, enum_size
 
                                    )
 
                                ));
 
                            }
 

	
 
                            // Within bounds, so set the index (such that we
 
                            // will not perform this lookup again)
 
                            let infer_expr = &mut self.infer_nodes[expr_idx as usize];
 
                            infer_expr.field_or_monomorph_index = member_index as i32;
 
                        },
 
                        Ok(None) => {
 
                            // Nothing is known about the tuple yet
 
                            return Ok(());
 
                        },
 
                        Err(()) => {
 
                            return Err(ParseError::new_error_at_span(
 
                                &ctx.module().source, select_expr.full_span, format!(
 
@@ -3015,291 +3041,291 @@ impl PassTyping {
 
                    member_start_idx = InferenceType::find_subtree_end_idx(&subject_type.parts, member_start_idx);
 
                }
 

	
 
                let (progress_expr, progress_subject) = self.apply_equal2_constraint(
 
                    ctx, upcast_id, upcast_id, 0, subject_id, member_start_idx
 
                )?;
 

	
 
                (progress_subject, progress_expr)
 
            },
 
        };
 

	
 
        if progress_subject { self.queue_expr(ctx, subject_id); }
 
        if progress_expr { self.queue_node_parent(ctx, upcast_id); }
 

	
 
        debug_log!(" * After:");
 
        debug_log!("   - Subject type [{}]: {}", progress_subject, self.debug_get_display_name(ctx, subject_id));
 
        debug_log!("   - Expr    type [{}]: {}", progress_expr, self.debug_get_display_name(ctx, upcast_id));
 

	
 
        Ok(())
 
    }
 

	
 
    fn progress_literal_expr(&mut self, ctx: &mut Ctx, id: LiteralExpressionId) -> Result<(), ParseError> {
 
        let upcast_id = id.upcast();
 
        let expr = &ctx.heap[id];
 
        let expr_idx = expr.unique_id_in_definition;
 
        let extra_idx = self.infer_nodes[expr_idx as usize].extra_data_idx;
 

	
 
        debug_log!("Literal expr: {}", upcast_id.index);
 
        debug_log!(" * Before:");
 
        debug_log!("   - Expr type: {}", self.debug_get_display_name(ctx, upcast_id));
 

	
 
        let progress_expr = match &expr.value {
 
            Literal::Null => {
 
                self.apply_template_constraint(ctx, upcast_id, &MESSAGE_TEMPLATE)?
 
            },
 
            Literal::Integer(_) => {
 
                self.apply_template_constraint(ctx, upcast_id, &INTEGERLIKE_TEMPLATE)?
 
            },
 
            Literal::True | Literal::False => {
 
                self.apply_forced_constraint(ctx, upcast_id, &BOOL_TEMPLATE)?
 
            },
 
            Literal::Character(_) => {
 
                self.apply_forced_constraint(ctx, upcast_id, &CHARACTER_TEMPLATE)?
 
            },
 
            Literal::String(_) => {
 
                self.apply_forced_constraint(ctx, upcast_id, &STRING_TEMPLATE)?
 
            },
 
            Literal::Struct(data) => {
 
                let extra = &mut self.extra_data[extra_idx as usize];
 
                let extra = &mut self.poly_data[extra_idx as usize];
 
                for _poly in &extra.poly_vars {
 
                    debug_log!(" * Poly: {}", _poly.display_name(&ctx.heap));
 
                }
 
                let mut poly_progress = HashSet::new();
 
                debug_assert_eq!(extra.embedded.len(), data.fields.len());
 
                debug_assert_eq!(extra.associated.len(), data.fields.len());
 

	
 
                debug_log!(" * During (inferring types from fields and struct type):");
 

	
 
                // Mutually infer field signature/expression types
 
                for (field_idx, field) in data.fields.iter().enumerate() {
 
                    let field_expr_id = field.value;
 
                    let field_expr_idx = ctx.heap[field_expr_id].get_unique_id_in_definition();
 
                    let signature_type: *mut _ = &mut extra.embedded[field_idx];
 
                    let signature_type: *mut _ = &mut extra.associated[field_idx];
 
                    let field_type: *mut _ = &mut self.infer_nodes[field_expr_idx as usize].expr_type;
 
                    let (_, progress_arg) = Self::apply_equal2_signature_constraint(
 
                        ctx, upcast_id, Some(field_expr_id), extra, &mut poly_progress,
 
                        signature_type, 0, field_type, 0
 
                    )?;
 

	
 
                    debug_log!(
 
                        "   - Field {} type | sig: {}, field: {}", field_idx,
 
                        unsafe{&*signature_type}.display_name(&ctx.heap),
 
                        unsafe{&*field_type}.display_name(&ctx.heap)
 
                    );
 

	
 
                    if progress_arg {
 
                        self.expr_queued.push_back(field_expr_idx);
 
                    }
 
                }
 

	
 
                debug_log!("   - Field poly progress | {:?}", poly_progress);
 

	
 
                // Same for the type of the struct itself
 
                let signature_type: *mut _ = &mut extra.returned;
 
                let expr_type: *mut _ = &mut self.infer_nodes[expr_idx as usize].expr_type;
 
                let (_, progress_expr) = Self::apply_equal2_signature_constraint(
 
                    ctx, upcast_id, None, extra, &mut poly_progress,
 
                    signature_type, 0, expr_type, 0
 
                )?;
 

	
 
                debug_log!(
 
                    "   - Ret type | sig: {}, expr: {}",
 
                    unsafe{&*signature_type}.display_name(&ctx.heap),
 
                    unsafe{&*expr_type}.display_name(&ctx.heap)
 
                );
 
                debug_log!("   - Ret poly progress | {:?}", poly_progress);
 

	
 
                if progress_expr {
 
                    // TODO: @cleanup, cannot call utility self.queue_parent thingo
 
                    if let Some(parent_id) = ctx.heap[upcast_id].parent_expr_id() {
 
                        let parent_idx = ctx.heap[parent_id].get_unique_id_in_definition();
 
                        self.expr_queued.push_back(parent_idx);
 
                    }
 
                }
 

	
 
                // Check which expressions use the polymorphic arguments. If the
 
                // polymorphic variables have been progressed then we try to 
 
                // progress them inside the expression as well.
 
                debug_log!(" * During (reinferring from progressed polyvars):");
 

	
 
                // For all field expressions
 
                for field_idx in 0..extra.embedded.len() {
 
                for field_idx in 0..extra.associated.len() {
 
                    // Note: fields in extra.embedded are in the same order as
 
                    // they are specified in the literal. Whereas
 
                    // `data.fields[...].field_idx` points to the field in the
 
                    // struct definition.
 
                    let signature_type: *mut _ = &mut extra.embedded[field_idx];
 
                    let signature_type: *mut _ = &mut extra.associated[field_idx];
 
                    let field_expr_id = data.fields[field_idx].value;
 
                    let field_expr_idx = ctx.heap[field_expr_id].get_unique_id_in_definition();
 
                    let field_type: *mut _ = &mut self.infer_nodes[field_expr_idx as usize].expr_type;
 

	
 
                    let progress_arg = Self::apply_equal2_polyvar_constraint(
 
                        extra, &poly_progress, signature_type, field_type
 
                    );
 

	
 
                    debug_log!(
 
                        "   - Field {} type | sig: {}, field: {}", field_idx,
 
                        unsafe{&*signature_type}.display_name(&ctx.heap),
 
                        unsafe{&*field_type}.display_name(&ctx.heap)
 
                    );
 
                    if progress_arg {
 
                        self.expr_queued.push_back(field_expr_idx);
 
                    }
 
                }
 
                
 
                // For the return type
 
                let signature_type: *mut _ = &mut extra.returned;
 
                let expr_type: *mut _ = &mut self.infer_nodes[expr_idx as usize].expr_type;
 

	
 
                let progress_expr = Self::apply_equal2_polyvar_constraint(
 
                    extra, &poly_progress, signature_type, expr_type
 
                );
 

	
 
                progress_expr
 
            },
 
            Literal::Enum(_) => {
 
                let extra = &mut self.extra_data[extra_idx as usize];
 
                let extra = &mut self.poly_data[extra_idx as usize];
 
                for _poly in &extra.poly_vars {
 
                    debug_log!(" * Poly: {}", _poly.display_name(&ctx.heap));
 
                }
 
                let mut poly_progress = HashSet::new();
 
                
 
                debug_log!(" * During (inferring types from return type)");
 

	
 
                let signature_type: *mut _ = &mut extra.returned;
 
                let expr_type: *mut _ = &mut self.infer_nodes[expr_idx as usize].expr_type;
 
                let (_, progress_expr) = Self::apply_equal2_signature_constraint(
 
                    ctx, upcast_id, None, extra, &mut poly_progress,
 
                    signature_type, 0, expr_type, 0
 
                )?;
 

	
 
                debug_log!(
 
                    "   - Ret type | sig: {}, expr: {}",
 
                    unsafe{&*signature_type}.display_name(&ctx.heap),
 
                    unsafe{&*expr_type}.display_name(&ctx.heap)
 
                );
 

	
 
                if progress_expr {
 
                    // TODO: @cleanup
 
                    if let Some(parent_id) = ctx.heap[upcast_id].parent_expr_id() {
 
                        let parent_idx = ctx.heap[parent_id].get_unique_id_in_definition();
 
                        self.expr_queued.push_back(parent_idx);
 
                    }
 
                }
 

	
 
                debug_log!(" * During (reinferring from progress polyvars):");
 
                let progress_expr = Self::apply_equal2_polyvar_constraint(
 
                    extra, &poly_progress, signature_type, expr_type
 
                );
 

	
 
                progress_expr
 
            },
 
            Literal::Union(data) => {
 
                let extra = &mut self.extra_data[extra_idx as usize];
 
                let extra = &mut self.poly_data[extra_idx as usize];
 
                for _poly in &extra.poly_vars {
 
                    debug_log!(" * Poly: {}", _poly.display_name(&ctx.heap));
 
                }
 
                let mut poly_progress = HashSet::new();
 
                debug_assert_eq!(extra.embedded.len(), data.values.len());
 
                debug_assert_eq!(extra.associated.len(), data.values.len());
 

	
 
                debug_log!(" * During (inferring types from variant values and union type):");
 

	
 
                // Mutually infer union variant values
 
                for (value_idx, value_expr_id) in data.values.iter().enumerate() {
 
                    let value_expr_id = *value_expr_id;
 
                    let value_expr_idx = ctx.heap[value_expr_id].get_unique_id_in_definition();
 
                    let signature_type: *mut _ = &mut extra.embedded[value_idx];
 
                    let signature_type: *mut _ = &mut extra.associated[value_idx];
 
                    let value_type: *mut _ = &mut self.infer_nodes[value_expr_idx as usize].expr_type;
 
                    let (_, progress_arg) = Self::apply_equal2_signature_constraint(
 
                        ctx, upcast_id, Some(value_expr_id), extra, &mut poly_progress,
 
                        signature_type, 0, value_type, 0 
 
                    )?;
 

	
 
                    debug_log!(
 
                        "   - Value {} type | sig: {}, field: {}", value_idx,
 
                        unsafe{&*signature_type}.display_name(&ctx.heap),
 
                        unsafe{&*value_type}.display_name(&ctx.heap)
 
                    );
 

	
 
                    if progress_arg {
 
                        self.expr_queued.push_back(value_expr_idx);
 
                    }
 
                }
 

	
 
                debug_log!("   - Field poly progress | {:?}", poly_progress);
 

	
 
                // Infer type of union itself
 
                let signature_type: *mut _ = &mut extra.returned;
 
                let expr_type: *mut _ = &mut self.infer_nodes[expr_idx as usize].expr_type;
 
                let (_, progress_expr) = Self::apply_equal2_signature_constraint(
 
                    ctx, upcast_id, None, extra, &mut poly_progress,
 
                    signature_type, 0, expr_type, 0
 
                )?;
 

	
 
                debug_log!(
 
                    "   - Ret type | sig: {}, expr: {}",
 
                    unsafe{&*signature_type}.display_name(&ctx.heap),
 
                    unsafe{&*expr_type}.display_name(&ctx.heap)
 
                );
 
                debug_log!("   - Ret poly progress | {:?}", poly_progress);
 

	
 
                if progress_expr {
 
                    // TODO: @cleanup, borrowing rules
 
                    if let Some(parent_id) = ctx.heap[upcast_id].parent_expr_id() {
 
                        let parent_idx = ctx.heap[parent_id].get_unique_id_in_definition();
 
                        self.expr_queued.push_back(parent_idx);
 
                    }
 
                }
 

	
 
                debug_log!(" * During (reinferring from progress polyvars):");
 
            
 
                // For all embedded values of the union variant
 
                for value_idx in 0..extra.embedded.len() {
 
                    let signature_type: *mut _ = &mut extra.embedded[value_idx];
 
                for value_idx in 0..extra.associated.len() {
 
                    let signature_type: *mut _ = &mut extra.associated[value_idx];
 
                    let value_expr_id = data.values[value_idx];
 
                    let value_expr_idx = ctx.heap[value_expr_id].get_unique_id_in_definition();
 
                    let value_type: *mut _ = &mut self.infer_nodes[value_expr_idx as usize].expr_type;
 
                    
 
                    let progress_arg = Self::apply_equal2_polyvar_constraint(
 
                        extra, &poly_progress, signature_type, value_type
 
                    );
 

	
 
                    debug_log!(
 
                        "   - Value {} type | sig: {}, value: {}", value_idx,
 
                        unsafe{&*signature_type}.display_name(&ctx.heap),
 
                        unsafe{&*value_type}.display_name(&ctx.heap)
 
                    );
 
                    if progress_arg {
 
                        self.expr_queued.push_back(value_expr_idx);
 
                    }
 
                }
 

	
 
                // And for the union type itself
 
                let signature_type: *mut _ = &mut extra.returned;
 
                let expr_type: *mut _ = &mut self.infer_nodes[expr_idx as usize].expr_type;
 

	
 
                let progress_expr = Self::apply_equal2_polyvar_constraint(
 
                    extra, &poly_progress, signature_type, expr_type
 
                );
 

	
 
                progress_expr
 
            },
 
            Literal::Array(data) => {
 
                let expr_elements = self.expr_buffer.start_section_initialized(data.as_slice());
 
                debug_log!("Array expr ({} elements): {}", expr_elements.len(), upcast_id.index);
 
                debug_log!(" * Before:");
 
                debug_log!("   - Expr type: {}", self.debug_get_display_name(ctx, upcast_id));
 

	
 
                // All elements should have an equal type
 
                let mut bool_buffer = self.bool_buffer.start_section();
 
                self.apply_equal_n_constraint(ctx, upcast_id, &expr_elements, &mut bool_buffer)?;
 
                for (progress_arg, arg_id) in bool_buffer.iter_copied().zip(expr_elements.iter_copied()) {
 
                    if progress_arg {
 
                        self.queue_expr(ctx, arg_id);
 
                    }
 
                }
 

	
 
                // And the output should be an array of the element types
 
                let mut progress_expr = self.apply_template_constraint(ctx, upcast_id, &ARRAY_TEMPLATE)?;
 
                if expr_elements.len() != 0 {
 
                    let first_arg_id = expr_elements[0];
 
                    let (inner_expr_progress, arg_progress) = self.apply_equal2_constraint(
 
@@ -3418,157 +3444,157 @@ impl PassTyping {
 
            return parts.len() == 1 && (
 
                parts[0] == InferenceTypePart::Bool ||
 
                parts[0] == InferenceTypePart::Character ||
 
                parts[0].is_concrete_integer()
 
            );
 
        }
 

	
 
        let is_valid = if is_bool_int_or_char(&expr_type.parts) && is_bool_int_or_char(&subject_type.parts) {
 
            true
 
        } else if expr_type.parts == subject_type.parts {
 
            true
 
        } else {
 
            false
 
        };
 

	
 
        debug_log!("   - Casting is valid: {}", is_valid);
 

	
 
        if !is_valid {
 
            let cast_expr = &ctx.heap[id];
 
            let subject_expr = &ctx.heap[cast_expr.subject];
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module().source, cast_expr.full_span, "invalid casting operation"
 
            ).with_info_at_span(
 
                &ctx.module().source, subject_expr.full_span(), format!(
 
                    "cannot cast the argument type '{}' to the cast type '{}'",
 
                    subject_type.display_name(&ctx.heap),
 
                    expr_type.display_name(&ctx.heap)
 
                )
 
            ));
 
        }
 

	
 
        Ok(())
 
    }
 

	
 
    // TODO: @cleanup, see how this can be cleaned up once I implement
 
    //  polymorphic struct/enum/union literals. These likely follow the same
 
    //  pattern as here.
 
    fn progress_call_expr(&mut self, ctx: &mut Ctx, id: CallExpressionId) -> Result<(), ParseError> {
 
        let upcast_id = id.upcast();
 
        let expr = &ctx.heap[id];
 
        let expr_idx = expr.unique_id_in_definition;
 
        let extra_idx = self.infer_nodes[expr_idx as usize].extra_data_idx;
 

	
 
        debug_log!("Call expr '{}': {}", ctx.heap[expr.definition].identifier().value.as_str(), upcast_id.index);
 
        debug_log!(" * Before:");
 
        debug_log!("   - Expr type: {}", self.debug_get_display_name(ctx, upcast_id));
 
        debug_log!(" * During (inferring types from arguments and return type):");
 

	
 
        let extra = &mut self.extra_data[extra_idx as usize];
 
        let extra = &mut self.poly_data[extra_idx as usize];
 

	
 
        // Check if we can make progress using the arguments and/or return types
 
        // while keeping track of the polyvars we've extended
 
        let mut poly_progress = HashSet::new();
 
        debug_assert_eq!(extra.embedded.len(), expr.arguments.len());
 
        debug_assert_eq!(extra.associated.len(), expr.arguments.len());
 

	
 
        for (call_arg_idx, arg_id) in expr.arguments.clone().into_iter().enumerate() {
 
            let arg_expr_idx = ctx.heap[arg_id].get_unique_id_in_definition();
 
            let signature_type: *mut _ = &mut extra.embedded[call_arg_idx];
 
            let signature_type: *mut _ = &mut extra.associated[call_arg_idx];
 
            let argument_type: *mut _ = &mut self.infer_nodes[arg_expr_idx as usize].expr_type;
 
            let (_, progress_arg) = Self::apply_equal2_signature_constraint(
 
                ctx, upcast_id, Some(arg_id), extra, &mut poly_progress,
 
                signature_type, 0, argument_type, 0
 
            )?;
 

	
 
            debug_log!(
 
                "   - Arg {} type | sig: {}, arg: {}", call_arg_idx,
 
                unsafe{&*signature_type}.display_name(&ctx.heap), 
 
                unsafe{&*argument_type}.display_name(&ctx.heap));
 

	
 
            if progress_arg {
 
                // Progressed argument expression
 
                self.expr_queued.push_back(arg_expr_idx);
 
            }
 
        }
 

	
 
        // Do the same for the return type
 
        let signature_type: *mut _ = &mut extra.returned;
 
        let expr_type: *mut _ = &mut self.infer_nodes[expr_idx as usize].expr_type;
 
        let (_, progress_expr) = Self::apply_equal2_signature_constraint(
 
            ctx, upcast_id, None, extra, &mut poly_progress,
 
            signature_type, 0, expr_type, 0
 
        )?;
 

	
 
        debug_log!(
 
            "   - Ret type | sig: {}, expr: {}", 
 
            unsafe{&*signature_type}.display_name(&ctx.heap), 
 
            unsafe{&*expr_type}.display_name(&ctx.heap)
 
        );
 

	
 
        if progress_expr {
 
            // TODO: @cleanup, cannot call utility self.queue_parent thingo
 
            if let Some(parent_id) = ctx.heap[upcast_id].parent_expr_id() {
 
                let parent_idx = ctx.heap[parent_id].get_unique_id_in_definition();
 
                self.expr_queued.push_back(parent_idx);
 
            }
 
        }
 

	
 
        // If we did not have an error in the polymorph inference above, then
 
        // reapplying the polymorph type to each argument type and the return
 
        // type should always succeed.
 
        debug_log!(" * During (reinferring from progressed polyvars):");
 
        for (_poly_idx, _poly_var) in extra.poly_vars.iter().enumerate() {
 
            debug_log!("   - Poly {} | sig: {}", _poly_idx, _poly_var.display_name(&ctx.heap));
 
        }
 
        // TODO: @performance If the algorithm is changed to be more "on demand
 
        //  argument re-evaluation", instead of "all-argument re-evaluation",
 
        //  then this is no longer true
 
        for arg_idx in 0..extra.embedded.len() {
 
            let signature_type: *mut _ = &mut extra.embedded[arg_idx];
 
        for arg_idx in 0..extra.associated.len() {
 
            let signature_type: *mut _ = &mut extra.associated[arg_idx];
 
            let arg_expr_id = expr.arguments[arg_idx];
 
            let arg_expr_idx = ctx.heap[arg_expr_id].get_unique_id_in_definition();
 
            let arg_type: *mut _ = &mut self.infer_nodes[arg_expr_idx as usize].expr_type;
 
            
 
            let progress_arg = Self::apply_equal2_polyvar_constraint(
 
                extra, &poly_progress,
 
                signature_type, arg_type
 
            );
 
            
 
            debug_log!(
 
                "   - Arg {} type | sig: {}, arg: {}", arg_idx, 
 
                unsafe{&*signature_type}.display_name(&ctx.heap), 
 
                unsafe{&*arg_type}.display_name(&ctx.heap)
 
            );
 
            if progress_arg {
 
                self.expr_queued.push_back(arg_expr_idx);
 
            }
 
        }
 

	
 
        // Once more for the return type
 
        let signature_type: *mut _ = &mut extra.returned;
 
        let ret_type: *mut _ = &mut self.infer_nodes[expr_idx as usize].expr_type;
 

	
 
        let progress_ret = Self::apply_equal2_polyvar_constraint(
 
            extra, &poly_progress, signature_type, ret_type
 
        );
 
        debug_log!(
 
            "   - Ret type | sig: {}, arg: {}", 
 
            unsafe{&*signature_type}.display_name(&ctx.heap), 
 
            unsafe{&*ret_type}.display_name(&ctx.heap)
 
        );
 
        if progress_ret {
 
            self.queue_node_parent(ctx, upcast_id);
 
        }
 

	
 
        debug_log!(" * After:");
 
        debug_log!("   - Expr type: {}", self.debug_get_display_name(ctx, upcast_id));
 

	
 
        Ok(())
 
    }
 

	
 
    fn progress_variable_expr(&mut self, ctx: &mut Ctx, id: VariableExpressionId) -> Result<(), ParseError> {
 
        let upcast_id = id.upcast();
 
        let var_expr = &ctx.heap[id];
 
        let var_expr_idx = var_expr.unique_id_in_definition;
 
        let var_id = var_expr.declaration.unwrap();
 

	
 
        debug_log!("Variable expr '{}': {}", ctx.heap[var_id].identifier.value.as_str(), upcast_id.index);
 
@@ -3718,180 +3744,227 @@ impl PassTyping {
 
            unsafe{ &mut *to_infer }, to_infer_start_idx,
 
            template, template_start_idx, false
 
        ) {
 
            SingleInferenceResult::Modified => Ok(true),
 
            SingleInferenceResult::Unmodified => Ok(false),
 
            SingleInferenceResult::Incompatible => Err(()),
 
        }
 
    }
 

	
 
    /// Applies a forced constraint: the supplied expression's type MUST be
 
    /// inferred from the template, the other way around is considered invalid.
 
    fn apply_forced_constraint(
 
        &mut self, ctx: &Ctx, node_index: InferNodeIndex, template: &[InferenceTypePart]
 
    ) -> Result<bool, ParseError> {
 
        let expr_type = &mut self.infer_nodes[node_index].expr_type;
 

	
 
        match InferenceType::infer_subtree_for_single_type(expr_type, 0, template, 0, true) {
 
            SingleInferenceResult::Modified => Ok(true),
 
            SingleInferenceResult::Unmodified => Ok(false),
 
            SingleInferenceResult::Incompatible => Err(
 
                self.construct_template_type_error(ctx, node_index, template)
 
            )
 
        }
 
    }
 

	
 
    /// Applies a type constraint that expects the two provided types to be
 
    /// equal. We attempt to make progress in inferring the types. If the call
 
    /// is successful then the composition of all types are made equal.
 
    /// The "parent" `expr_id` is provided to construct errors.
 
    fn apply_equal2_constraint(
 
        &mut self, ctx: &Ctx, node_index: InferNodeIndex,
 
        arg1_index: InferNodeIndex, arg1_start_idx: usize,
 
        arg2_index: InferNodeIndex, arg2_start_idx: usize
 
    ) -> Result<(bool, bool), ParseError> {
 
        let arg1_type: *mut _ = &mut self.infer_nodes[arg1_index].expr_type;
 
        let arg2_type: *mut _ = &mut self.infer_nodes[arg2_index].expr_type;
 

	
 
        let infer_res = unsafe{ InferenceType::infer_subtrees_for_both_types(
 
            arg1_type, arg1_start_idx,
 
            arg2_type, arg2_start_idx
 
        ) };
 
        if infer_res == DualInferenceResult::Incompatible {
 
            return Err(self.construct_arg_type_error(ctx, node_index, arg1_index, arg2_index));
 
        }
 

	
 
        Ok((infer_res.modified_lhs(), infer_res.modified_rhs()))
 
    }
 

	
 
    /// Applies an equal2 constraint between a member of the `PolyData` struct,
 
    /// and another inferred type. If any progress is made in the `PolyData`
 
    /// struct then the affected polymorphic variables are updated as well.
 
    ///
 
    /// Because a lot of types/expressions are involved in polymorphic type
 
    /// inference, some explanation: "outer_node" refers to the main expression
 
    /// that is the root cause of type inference (e.g. a struct literal
 
    /// expression, or a tuple member select expression). Associated with that
 
    /// outer node is `PolyData`, so that is what the "poly_data" variables
 
    /// are referring to. We are applying equality between a "poly_data" type
 
    /// and an associated expression (not necessarily the "outer_node", e.g.
 
    /// the expression that constructs the value of a struct field). Hence the
 
    /// "associated" variables.
 
    ///
 
    /// Again a special case: the `associated_expr_id` is only used
 
    fn apply_polydata_equal2_constraint(
 
        &mut self, ctx: &Ctx,
 
        outer_node_index: InferNodeIndex, error_location_expr_id: ExpressionId, error_location_name: &str
 
        poly_data_type: PolyDataTypeIndex, poly_data_start_index: usize,
 
        associated_node_index: InferNodeIndex, associated_node_start_index: usize
 
    ) -> Result<(bool, bool), ParseError> {
 
        let poly_index = self.infer_nodes[outer_node_index].poly_data_index;
 
        let poly_data_type: *mut _ = self.poly_data[poly_index].get_type_mut(poly_data_type);
 
        let associated_type: *mut _ = &mut self.infer_nodes[associated_node_index].expr_type;
 

	
 
        let inference_result = unsafe{
 
            // Safety: pointers originate from different vectors, so cannot
 
            // alias.
 
            InferenceType::infer_subtrees_for_both_types(
 
                poly_data_type, poly_data_start_index,
 
                associated_type, associated_node_start_index
 
            )
 
        };
 

	
 
        let modified_poly_data = inference_result.modified_lhs();
 
        let modified_associated = inference_result.modified_rhs();
 
        if inference_result == DualInferenceResult::Incompatible {
 
            let outer_node_expr_id = self.infer_nodes[outer_node_index].expr_id;
 
            let outer_node_span = ctx.heap[outer_node_expr_id].full_span();
 
            let detailed_span = ctx.heap[error_location_expr_id].
 
        }
 
    }
 

	
 
    fn apply_polydata_polyvar_constraint() -> bool {
 

	
 
    }
 

	
 
    /// Applies an equal2 constraint between a signature type (e.g. a function
 
    /// argument or struct field) and an expression whose type should match that
 
    /// expression. If we make progress on the signature, then we try to see if
 
    /// any of the embedded polymorphic types can be progressed.
 
    ///
 
    /// `outer_expr_id` is the main expression we're progressing (e.g. a 
 
    /// function call), while `expr_id` is the embedded expression we're 
 
    /// matching against the signature. `expression_type` and 
 
    /// `expression_start_idx` belong to `expr_id`.
 
    fn apply_equal2_signature_constraint(
 
        ctx: &Ctx, outer_expr_id: ExpressionId, expr_id: Option<ExpressionId>,
 
        polymorph_data: &mut ExtraData, polymorph_progress: &mut HashSet<u32>,
 
        polymorph_data: &mut PolyData, polymorph_progress: &mut HashSet<u32>,
 
        signature_type: *mut InferenceType, signature_start_idx: usize,
 
        expression_type: *mut InferenceType, expression_start_idx: usize
 
    ) -> Result<(bool, bool), ParseError> {
 
        // Safety: all pointers distinct
 

	
 
        // Infer the signature and expression type
 
        let infer_res = unsafe { 
 
            InferenceType::infer_subtrees_for_both_types(
 
                signature_type, signature_start_idx,
 
                expression_type, expression_start_idx
 
            ) 
 
            )
 
        };
 

	
 
        if infer_res == DualInferenceResult::Incompatible {
 
            // TODO: Check if I still need to use this
 
            let outer_span = ctx.heap[outer_expr_id].full_span();
 
            let (span_name, span) = match expr_id {
 
                Some(expr_id) => ("argument's", ctx.heap[expr_id].full_span()),
 
                None => ("type's", outer_span)
 
            };
 
            let (signature_display_type, expression_display_type) = unsafe { (
 
                (&*signature_type).display_name(&ctx.heap),
 
                (&*expression_type).display_name(&ctx.heap)
 
            ) };
 

	
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module().source, outer_span,
 
                "failed to fully resolve the types of this expression"
 
            ).with_info_at_span(
 
                &ctx.module().source, span, format!(
 
                    "because the {} signature has been resolved to '{}', but the expression has been resolved to '{}'",
 
                    span_name, signature_display_type, expression_display_type
 
                )
 
            ));
 
        }
 

	
 
        // Try to see if we can progress any of the polymorphic variables
 
        let progress_sig = infer_res.modified_lhs();
 
        let progress_expr = infer_res.modified_rhs();
 

	
 
        if progress_sig {
 
            let signature_type = unsafe{&mut *signature_type};
 
            debug_assert!(
 
                signature_type.has_marker,
 
                "made progress on signature type, but it doesn't have a marker"
 
            );
 
            for (poly_idx, poly_section) in signature_type.marker_iter() {
 
                let polymorph_type = &mut polymorph_data.poly_vars[poly_idx as usize];
 
                match Self::apply_template_constraint_to_types(
 
                    polymorph_type, 0, poly_section, 0
 
                ) {
 
                    Ok(true) => { polymorph_progress.insert(poly_idx); },
 
                    Ok(false) => {},
 
                    Err(()) => { return Err(Self::construct_poly_arg_error(ctx, polymorph_data, outer_expr_id))}
 
                }
 
            }
 
        }
 
        Ok((progress_sig, progress_expr))
 
    }
 

	
 
    /// Applies equal2 constraints on the signature type for each of the 
 
    /// polymorphic variables. If the signature type is progressed then we 
 
    /// progress the expression type as well.
 
    ///
 
    /// This function assumes that the polymorphic variables have already been
 
    /// progressed as far as possible by calling 
 
    /// `apply_equal2_signature_constraint`. As such, we expect to not encounter
 
    /// any errors.
 
    ///
 
    /// This function returns true if the expression's type has been progressed
 
    fn apply_equal2_polyvar_constraint(
 
        polymorph_data: &ExtraData, _polymorph_progress: &HashSet<u32>,
 
        polymorph_data: &PolyData, _polymorph_progress: &HashSet<u32>,
 
        signature_type: *mut InferenceType, expr_type: *mut InferenceType
 
    ) -> bool {
 
        // Safety: all pointers should be distinct
 
        //         polymorph_data containers may not be modified
 
        let signature_type = unsafe{&mut *signature_type};
 
        let expr_type = unsafe{&mut *expr_type};
 

	
 
        // Iterate through markers in signature type to try and make progress
 
        // on the polymorphic variable        
 
        let mut seek_idx = 0;
 
        let mut modified_sig = false;
 
        
 
        while let Some((poly_idx, start_idx)) = signature_type.find_marker(seek_idx) {
 
            let end_idx = InferenceType::find_subtree_end_idx(&signature_type.parts, start_idx);
 
            // if polymorph_progress.contains(&poly_idx) {
 
                // Need to match subtrees
 
                let polymorph_type = &polymorph_data.poly_vars[poly_idx as usize];
 
                let modified_at_marker = Self::apply_template_constraint_to_types(
 
                    signature_type, start_idx, 
 
                    &polymorph_type.parts, 0
 
                ).expect("no failure when applying polyvar constraints");
 

	
 
                modified_sig = modified_sig || modified_at_marker;
 
            // }
 

	
 
            seek_idx = end_idx;
 
        }
 

	
 
        // If we made any progress on the signature's type, then we also need to
 
        // apply it to the expression that is supposed to match the signature.
 
        if modified_sig {
 
            match InferenceType::infer_subtree_for_single_type(
 
                expr_type, 0, &signature_type.parts, 0, true
 
            ) {
 
                SingleInferenceResult::Modified => true,
 
                SingleInferenceResult::Unmodified => false,
 
                SingleInferenceResult::Incompatible =>
 
                    unreachable!("encountered failure while reapplying modified signature to expression after polyvar inference")
 
            }
 
        } else {
 
            false
 
        }
 
    }
 

	
 
    /// Applies a type constraint that expects all three provided types to be
 
    /// equal. In case we can make progress in inferring the types then we
 
    /// attempt to do so. If the call is successful then the composition of all
 
    /// types is made equal.
 
@@ -4037,365 +4110,365 @@ impl PassTyping {
 
                // If we are the test expression of a conditional expression,
 
                // then we must resolve to a boolean
 
                let is_conditional = if let Expression::Conditional(_) = &ctx.heap[*parent_id] {
 
                    true
 
                } else {
 
                    false
 
                };
 

	
 
                if is_conditional && *idx_in_parent == 0 {
 
                    InferenceType::new(false, true, vec![ITP::Bool])
 
                } else {
 
                    InferenceType::new(false, false, vec![ITP::Unknown])
 
                }
 
            },
 
            EP::If(_) | EP::While(_) =>
 
                // Must be a boolean
 
                InferenceType::new(false, true, vec![ITP::Bool]),
 
            EP::Return(_) =>
 
                // Must match the return type of the function
 
                if let DefinitionType::Function(func_id) = self.definition_type {
 
                    let returned = &ctx.heap[func_id].return_type;
 
                    self.determine_inference_type_from_parser_type_elements(&returned.elements, true)
 
                } else {
 
                    // Cannot happen: definition always set upon body traversal
 
                    // and "return" calls in components are illegal.
 
                    unreachable!();
 
                },
 
            EP::New(_) =>
 
                // Must be a component call, which we assign a "Void" return
 
                // type
 
                InferenceType::new(false, true, vec![ITP::Void]),
 
        };
 

	
 
        let infer_index = self.infer_nodes.len() as InferNodeIndex;
 
        self.infer_nodes.push(InferenceNode {
 
            expr_type: inference_type,
 
            expr_id,
 
            parent_index: self.parent_index,
 
            field_or_monomorph_index: -1,
 
            extra_data_idx: -1,
 
            type_id: TypeId::new_invalid(),
 
        });
 

	
 
        return Ok(infer_index);
 
    }
 

	
 
    fn insert_initial_call_polymorph_data(
 
        &mut self, ctx: &mut Ctx, call_id: CallExpressionId
 
    ) -> ExtraIndex {
 
    ) -> PolyDataIndex {
 
        // Note: the polymorph variables may be partially specified and may
 
        // contain references to the wrapping definition's (i.e. the proctype
 
        // we are currently visiting) polymorphic arguments.
 
        //
 
        // The arguments of the call may refer to polymorphic variables in the
 
        // definition of the function we're calling, not of the wrapping
 
        // definition. We insert markers in these inferred types to be able to
 
        // map them back and forth to the polymorphic arguments of the function
 
        // we are calling.
 
        let call = &ctx.heap[call_id];
 

	
 
        // Handle the polymorphic arguments (if there are any)
 
        let num_poly_args = call.parser_type.elements[0].variant.num_embedded();
 
        let mut poly_args = Vec::with_capacity(num_poly_args);
 
        for embedded_elements in call.parser_type.iter_embedded(0) {
 
            poly_args.push(self.determine_inference_type_from_parser_type_elements(embedded_elements, true));
 
        }
 

	
 
        // Handle the arguments and return types
 
        let definition = &ctx.heap[call.definition];
 
        let (parameters, returned) = match definition {
 
            Definition::Component(definition) => {
 
                debug_assert_eq!(poly_args.len(), definition.poly_vars.len());
 
                (&definition.parameters, None)
 
            },
 
            Definition::Function(definition) => {
 
                debug_assert_eq!(poly_args.len(), definition.poly_vars.len());
 
                (&definition.parameters, Some(&definition.return_type))
 
            },
 
            Definition::Struct(_) | Definition::Enum(_) | Definition::Union(_) => {
 
                unreachable!("insert_initial_call_polymorph data for non-procedure type");
 
            },
 
        };
 

	
 
        let mut parameter_types = Vec::with_capacity(parameters.len());
 
        for parameter_id in parameters.clone().into_iter() { // TODO: @Performance @Now
 
            let param = &ctx.heap[parameter_id];
 
            parameter_types.push(self.determine_inference_type_from_parser_type_elements(&param.parser_type.elements, false));
 
        }
 

	
 
        let return_type = match returned {
 
            None => {
 
                // Component, so returns a "Void"
 
                InferenceType::new(false, true, vec![InferenceTypePart::Void])
 
            },
 
            Some(returned) => {
 
                self.determine_inference_type_from_parser_type_elements(&returned.elements, false)
 
            }
 
        };
 

	
 
        let extra_data_idx = self.extra_data.len() as ExtraIndex;
 
        self.extra_data.push(ExtraData{
 
        let extra_data_idx = self.poly_data.len() as PolyDataIndex;
 
        self.poly_data.push(PolyData {
 
            expr_id: call_id.upcast(),
 
            definition_id: call.definition,
 
            poly_vars: poly_args,
 
            embedded: parameter_types,
 
            associated: parameter_types,
 
            returned: return_type
 
        });
 
        return extra_data_idx
 
    }
 

	
 
    fn insert_initial_struct_polymorph_data(
 
        &mut self, ctx: &mut Ctx, lit_id: LiteralExpressionId,
 
    ) -> ExtraIndex {
 
    ) -> PolyDataIndex {
 
        use InferenceTypePart as ITP;
 
        let literal = ctx.heap[lit_id].value.as_struct();
 

	
 
        // Handle polymorphic arguments
 
        let num_embedded = literal.parser_type.elements[0].variant.num_embedded();
 
        let mut total_num_poly_parts = 0;
 
        let mut poly_args = Vec::with_capacity(num_embedded);
 

	
 
        for embedded_elements in literal.parser_type.iter_embedded(0) {
 
            let poly_type = self.determine_inference_type_from_parser_type_elements(embedded_elements, true);
 
            total_num_poly_parts += poly_type.parts.len();
 
            poly_args.push(poly_type);
 
        }
 

	
 
        // Handle parser types on struct definition
 
        let defined_type = ctx.types.get_base_definition(&literal.definition).unwrap();
 
        let struct_type = defined_type.definition.as_struct();
 
        debug_assert_eq!(poly_args.len(), defined_type.poly_vars.len());
 

	
 
        // Note: programmer is capable of specifying fields in a struct literal
 
        // in a different order than on the definition. We take the literal-
 
        // specified order to be leading.
 
        let mut embedded_types = Vec::with_capacity(struct_type.fields.len());
 
        for lit_field in literal.fields.iter() {
 
            let def_field = &struct_type.fields[lit_field.field_idx];
 
            let inference_type = self.determine_inference_type_from_parser_type_elements(&def_field.parser_type.elements, false);
 
            embedded_types.push(inference_type);
 
        }
 

	
 
        // Return type is the struct type itself, with the appropriate 
 
        // polymorphic variables. So:
 
        // - 1 part for definition
 
        // - N_poly_arg marker parts for each polymorphic argument
 
        // - all the parts for the currently known polymorphic arguments 
 
        let parts_reserved = 1 + poly_args.len() + total_num_poly_parts;
 
        let mut parts = Vec::with_capacity(parts_reserved);
 
        parts.push(ITP::Instance(literal.definition, poly_args.len() as u32));
 
        let mut return_type_done = true;
 
        for (poly_var_idx, poly_var) in poly_args.iter().enumerate() {
 
            if !poly_var.is_done { return_type_done = false; }
 

	
 
            parts.push(ITP::Marker(poly_var_idx as u32));
 
            parts.extend(poly_var.parts.iter().cloned());
 
        }
 

	
 
        debug_assert_eq!(parts.len(), parts_reserved);
 
        let return_type = InferenceType::new(!poly_args.is_empty(), return_type_done, parts);
 

	
 
        let extra_data_index = self.extra_data.len() as ExtraIndex;
 
        self.extra_data.push(ExtraData{
 
        let extra_data_index = self.poly_data.len() as PolyDataIndex;
 
        self.poly_data.push(PolyData {
 
            expr_id: lit_id.upcast(),
 
            definition_id: literal.definition,
 
            poly_vars: poly_args,
 
            embedded: embedded_types,
 
            associated: embedded_types,
 
            returned: return_type,
 
        });
 

	
 
        return extra_data_index
 
    }
 

	
 
    /// Inserts the extra polymorphic data struct for enum expressions. These
 
    /// can never be determined from the enum itself, but may be inferred from
 
    /// the use of the enum.
 
    fn insert_initial_enum_polymorph_data(
 
        &mut self, ctx: &Ctx, lit_id: LiteralExpressionId
 
    ) -> ExtraIndex {
 
    ) -> PolyDataIndex {
 
        use InferenceTypePart as ITP;
 
        let literal = ctx.heap[lit_id].value.as_enum();
 

	
 
        // Handle polymorphic arguments to the enum
 
        let num_poly_args = literal.parser_type.elements[0].variant.num_embedded();
 
        let mut total_num_poly_parts = 0;
 
        let mut poly_args = Vec::with_capacity(num_poly_args);
 

	
 
        for embedded_elements in literal.parser_type.iter_embedded(0) {
 
            let poly_type = self.determine_inference_type_from_parser_type_elements(embedded_elements, true);
 
            total_num_poly_parts += poly_type.parts.len();
 
            poly_args.push(poly_type);
 
        }
 

	
 
        // Handle enum type itself
 
        let parts_reserved = 1 + poly_args.len() + total_num_poly_parts;
 
        let mut parts = Vec::with_capacity(parts_reserved);
 
        parts.push(ITP::Instance(literal.definition, poly_args.len() as u32));
 
        let mut enum_type_done = true;
 
        for (poly_var_idx, poly_var) in poly_args.iter().enumerate() {
 
            if !poly_var.is_done { enum_type_done = false; }
 

	
 
            parts.push(ITP::Marker(poly_var_idx as u32));
 
            parts.extend(poly_var.parts.iter().cloned());
 
        }
 

	
 
        debug_assert_eq!(parts.len(), parts_reserved);
 
        let enum_type = InferenceType::new(!poly_args.is_empty(), enum_type_done, parts);
 

	
 
        let extra_data_index = self.extra_data.len() as ExtraIndex;
 
        self.extra_data.push(ExtraData{
 
        let extra_data_index = self.poly_data.len() as PolyDataIndex;
 
        self.poly_data.push(PolyData {
 
            expr_id: lit_id.upcast(),
 
            definition_id: literal.definition,
 
            poly_vars: poly_args,
 
            embedded: Vec::new(),
 
            associated: Vec::new(),
 
            returned: enum_type,
 
        });
 

	
 
        return extra_data_index;
 
    }
 

	
 
    /// Inserts the extra polymorphic data struct for unions. The polymorphic
 
    /// arguments may be partially determined from embedded values in the union.
 
    fn insert_initial_union_polymorph_data(
 
        &mut self, ctx: &Ctx, lit_id: LiteralExpressionId
 
    ) -> ExtraIndex {
 
    ) -> PolyDataIndex {
 
        use InferenceTypePart as ITP;
 
        let literal = ctx.heap[lit_id].value.as_union();
 

	
 
        // Construct the polymorphic variables
 
        let num_poly_args = literal.parser_type.elements[0].variant.num_embedded();
 
        let mut total_num_poly_parts = 0;
 
        let mut poly_args = Vec::with_capacity(num_poly_args);
 

	
 
        for embedded_elements in literal.parser_type.iter_embedded(0) {
 
            let poly_type = self.determine_inference_type_from_parser_type_elements(embedded_elements, true);
 
            total_num_poly_parts += poly_type.parts.len();
 
            poly_args.push(poly_type);
 
        }
 

	
 
        // Handle any of the embedded values in the variant, if specified
 
        let definition_id = literal.definition;
 
        let type_definition = ctx.types.get_base_definition(&definition_id).unwrap();
 
        let union_definition = type_definition.definition.as_union();
 
        debug_assert_eq!(poly_args.len(), type_definition.poly_vars.len());
 

	
 
        let variant_definition = &union_definition.variants[literal.variant_idx];
 
        debug_assert_eq!(variant_definition.embedded.len(), literal.values.len());
 

	
 
        let mut embedded = Vec::with_capacity(variant_definition.embedded.len());
 
        for embedded_parser_type in &variant_definition.embedded {
 
            let inference_type = self.determine_inference_type_from_parser_type_elements(&embedded_parser_type.elements, false);
 
            embedded.push(inference_type);
 
        }
 

	
 
        // Handle the type of the union itself
 
        let parts_reserved = 1 + poly_args.len() + total_num_poly_parts;
 
        let mut parts = Vec::with_capacity(parts_reserved);
 
        parts.push(ITP::Instance(definition_id, poly_args.len() as u32));
 
        let mut union_type_done = true;
 
        for (poly_var_idx, poly_var) in poly_args.iter().enumerate() {
 
            if !poly_var.is_done { union_type_done = false; }
 

	
 
            parts.push(ITP::Marker(poly_var_idx as u32));
 
            parts.extend(poly_var.parts.iter().cloned());
 
        }
 

	
 
        debug_assert_eq!(parts_reserved, parts.len());
 
        let union_type = InferenceType::new(!poly_args.is_empty(), union_type_done, parts);
 

	
 
        let extra_data_index = self.extra_data.len();
 
        self.extra_data.push(ExtraData{
 
        let extra_data_index = self.poly_data.len();
 
        self.poly_data.push(PolyData {
 
            expr_id: lit_id.upcast(),
 
            definition_id: literal.definition,
 
            poly_vars: poly_args,
 
            embedded,
 
            associated: embedded,
 
            returned: union_type
 
        });
 

	
 
        return extra_data_index;
 
    }
 

	
 
    /// Inserts the extra polymorphic data struct. Assumes that the select
 
    /// expression's referenced (definition_id, field_idx) has been resolved.
 
    fn insert_initial_select_polymorph_data(
 
        &mut self, ctx: &Ctx, node_index: InferNodeIndex, struct_def_id: DefinitionId
 
    ) -> ExtraIndex {
 
    ) -> PolyDataIndex {
 
        use InferenceTypePart as ITP;
 

	
 
        let definition = ctx.heap[struct_def_id].as_struct();
 
        let node = &self.infer_nodes[node_index];
 
        let field_index = node.field_or_monomorph_index as usize;
 

	
 
        // Generate initial polyvar types and struct type
 
        // TODO: @Performance: we can immediately set the polyvars of the subject's struct type
 
        let num_poly_vars = definition.poly_vars.len();
 
        let mut poly_vars = Vec::with_capacity(num_poly_vars);
 
        let struct_parts_reserved = 1 + 2 * num_poly_vars;
 
        let mut struct_parts = Vec::with_capacity(struct_parts_reserved);
 
        struct_parts.push(ITP::Instance(struct_def_id, num_poly_vars as u32));
 

	
 
        for poly_idx in 0..num_poly_vars {
 
            poly_vars.push(InferenceType::new(true, false, vec![
 
                ITP::Marker(poly_idx as u32), ITP::Unknown,
 
            ]));
 
            struct_parts.push(ITP::Marker(poly_idx as u32));
 
            struct_parts.push(ITP::Unknown);
 
        }
 
        debug_assert_eq!(struct_parts.len(), struct_parts_reserved);
 

	
 
        // Generate initial field type
 
        let field_type = self.determine_inference_type_from_parser_type_elements(&definition.fields[field_index].parser_type.elements, false);
 

	
 
        let extra_data_index = self.extra_data.len() as ExtraIndex;
 
        self.extra_data.push(ExtraData{
 
        let extra_data_index = self.poly_data.len() as PolyDataIndex;
 
        self.poly_data.push(PolyData {
 
            definition_id: struct_def_id,
 
            poly_vars,
 
            embedded: vec![InferenceType::new(num_poly_vars != 0, num_poly_vars == 0, struct_parts)],
 
            associated: vec![InferenceType::new(num_poly_vars != 0, num_poly_vars == 0, struct_parts)],
 
            returned: field_type
 
        });
 

	
 
        return extra_data_index;
 
    }
 

	
 
    /// Determines the initial InferenceType from the provided ParserType. This
 
    /// may be called with two kinds of intentions:
 
    /// 1. To resolve a ParserType within the body of a function, or on
 
    ///     polymorphic arguments to calls/instantiations within that body. This
 
    ///     means that the polymorphic variables are known and can be replaced
 
    ///     with the monomorph we're instantiating.
 
    /// 2. To resolve a ParserType on a called function's definition or on
 
    ///     an instantiated datatype's members. This means that the polymorphic
 
    ///     arguments inside those ParserTypes refer to the polymorphic
 
    ///     variables in the called/instantiated type's definition.
 
    /// In the second case we place InferenceTypePart::Marker instances such
 
    /// that we can perform type inference on the polymorphic variables.
 
    fn determine_inference_type_from_parser_type_elements(
 
        &mut self, elements: &[ParserTypeElement],
 
        use_definitions_known_poly_args: bool
 
    ) -> InferenceType {
 
        use ParserTypeVariant as PTV;
 
        use InferenceTypePart as ITP;
 

	
 
        let mut infer_type = Vec::with_capacity(elements.len());
 
        let mut has_inferred = false;
 
        let mut has_markers = false;
 

	
 
        for element in elements {
 
            match &element.variant {
 
                // Compiler-only types
 
                PTV::Void => { infer_type.push(ITP::Void); },
 
                PTV::InputOrOutput => { infer_type.push(ITP::PortLike); has_inferred = true },
 
                PTV::ArrayLike => { infer_type.push(ITP::ArrayLike); has_inferred = true },
 
                PTV::IntegerLike => { infer_type.push(ITP::IntegerLike); has_inferred = true },
 
                // Builtins
 
                PTV::Message => {
 
                    // TODO: @types Remove the Message -> Byte hack at some point...
 
                    infer_type.push(ITP::Message);
 
                    infer_type.push(ITP::UInt8);
 
                },
 
                PTV::Bool => { infer_type.push(ITP::Bool); },
 
                PTV::UInt8 => { infer_type.push(ITP::UInt8); },
 
                PTV::UInt16 => { infer_type.push(ITP::UInt16); },
 
                PTV::UInt32 => { infer_type.push(ITP::UInt32); },
 
                PTV::UInt64 => { infer_type.push(ITP::UInt64); },
 
                PTV::SInt8 => { infer_type.push(ITP::SInt8); },
 
@@ -4524,290 +4597,290 @@ impl PassTyping {
 
        let expr_id = self.infer_nodes[expr_index].expr_id;
 
        let expr = &ctx.heap[expr_id];
 
        let arg1 = &ctx.heap[arg1_node.expr_id];
 
        let arg2 = &ctx.heap[arg2_node.expr_id];
 

	
 
        return ParseError::new_error_str_at_span(
 
            &ctx.module().source, expr.operation_span(),
 
            "incompatible types: cannot apply this expression"
 
        ).with_info_at_span(
 
            &ctx.module().source, arg1.full_span(), format!(
 
                "Because this expression has type '{}'",
 
                arg1_node.expr_type.display_name(&ctx.heap)
 
            )
 
        ).with_info_at_span(
 
            &ctx.module().source, arg2.full_span(), format!(
 
                "But this expression has type '{}'",
 
                arg2_node.expr_type.display_name(&ctx.heap)
 
            )
 
        )
 
    }
 

	
 
    fn construct_template_type_error(
 
        &self, ctx: &Ctx, node_index: InferNodeIndex, template: &[InferenceTypePart]
 
    ) -> ParseError {
 
        let node = &self.infer_nodes[node_index];
 
        let expr = &ctx.heap[node.expr_id];
 
        let expr_type = &node.expr_type;
 

	
 
        return ParseError::new_error_at_span(
 
            &ctx.module().source, expr.full_span(), format!(
 
                "incompatible types: got a '{}' but expected a '{}'",
 
                expr_type.display_name(&ctx.heap), 
 
                InferenceType::partial_display_name(&ctx.heap, template)
 
            )
 
        )
 
    }
 

	
 
    /// Constructs a human interpretable error in the case that type inference
 
    /// on a polymorphic variable to a function call or literal construction 
 
    /// failed. This may only be caused by a pair of inference types (which may 
 
    /// come from arguments or the return type) having two different inferred 
 
    /// values for that polymorphic variable.
 
    ///
 
    /// So we find this pair and construct the error using it.
 
    ///
 
    /// We assume that the expression is a function call or a struct literal,
 
    /// and that an actual error has occurred.
 
    fn construct_poly_arg_error(
 
        ctx: &Ctx, poly_data: &ExtraData, expr_id: ExpressionId
 
        ctx: &Ctx, poly_data: &PolyData, expr_id: ExpressionId
 
    ) -> ParseError {
 
        // Helper function to check for polymorph mismatch between two inference
 
        // types.
 
        fn has_poly_mismatch<'a>(type_a: &'a InferenceType, type_b: &'a InferenceType) -> Option<(u32, &'a [InferenceTypePart], &'a [InferenceTypePart])> {
 
            if !type_a.has_marker || !type_b.has_marker {
 
                return None
 
            }
 

	
 
            for (marker_a, section_a) in type_a.marker_iter() {
 
                for (marker_b, section_b) in type_b.marker_iter() {
 
                    if marker_a != marker_b {
 
                        // Not the same polymorphic variable
 
                        continue;
 
                    }
 

	
 
                    if !InferenceType::check_subtrees(section_a, 0, section_b, 0) {
 
                        // Not compatible
 
                        return Some((marker_a, section_a, section_b))
 
                    }
 
                }
 
            }
 

	
 
            None
 
        }
 

	
 
        // Helper function to check for polymorph mismatch between an inference
 
        // type and the polymorphic variables in the poly_data struct.
 
        fn has_explicit_poly_mismatch<'a>(
 
            poly_vars: &'a [InferenceType], arg: &'a InferenceType
 
        ) -> Option<(u32, &'a [InferenceTypePart], &'a [InferenceTypePart])> {
 
            for (marker, section) in arg.marker_iter() {
 
                debug_assert!((marker as usize) < poly_vars.len());
 
                let poly_section = &poly_vars[marker as usize].parts;
 
                if !InferenceType::check_subtrees(poly_section, 0, section, 0) {
 
                    return Some((marker, poly_section, section))
 
                }
 
            }
 

	
 
            None
 
        }
 

	
 
        // Helpers function to retrieve polyvar name and definition name
 
        fn get_poly_var_and_definition_name<'a>(ctx: &'a Ctx, poly_var_idx: u32, definition_id: DefinitionId) -> (&'a str, &'a str) {
 
            let definition = &ctx.heap[definition_id];
 
            let poly_var = definition.poly_vars()[poly_var_idx as usize].value.as_str();
 
            let func_name = definition.identifier().value.as_str();
 

	
 
            (poly_var, func_name)
 
        }
 

	
 
        // Helper function to construct initial error
 
        fn construct_main_error(ctx: &Ctx, poly_data: &ExtraData, poly_var_idx: u32, expr: &Expression) -> ParseError {
 
        fn construct_main_error(ctx: &Ctx, poly_data: &PolyData, poly_var_idx: u32, expr: &Expression) -> ParseError {
 
            match expr {
 
                Expression::Call(expr) => {
 
                    let (poly_var, func_name) = get_poly_var_and_definition_name(ctx, poly_var_idx, poly_data.definition_id);
 
                    return ParseError::new_error_at_span(
 
                        &ctx.module().source, expr.func_span, format!(
 
                            "Conflicting type for polymorphic variable '{}' of '{}'",
 
                            poly_var, func_name
 
                        )
 
                    )
 
                },
 
                Expression::Literal(expr) => {
 
                    let (poly_var, type_name) = get_poly_var_and_definition_name(ctx, poly_var_idx, poly_data.definition_id);
 
                    return ParseError::new_error_at_span(
 
                        &ctx.module().source, expr.span, format!(
 
                            "Conflicting type for polymorphic variable '{}' of instantiation of '{}'",
 
                            poly_var, type_name
 
                        )
 
                    );
 
                },
 
                Expression::Select(expr) => {
 
                    let (poly_var, struct_name) = get_poly_var_and_definition_name(ctx, poly_var_idx, poly_data.definition_id);
 
                    let field_name = match &expr.kind {
 
                        SelectKind::StructField(v) => v,
 
                        SelectKind::TupleMember(_) => unreachable!(), // because we're constructing a polymorph error, and tuple access does not deal with polymorphs
 
                    };
 
                    return ParseError::new_error_at_span(
 
                        &ctx.module().source, expr.full_span, format!(
 
                            "Conflicting type for polymorphic variable '{}' while accessing field '{}' of '{}'",
 
                            poly_var, field_name.value.as_str(), struct_name
 
                        )
 
                    )
 
                }
 
                _ => unreachable!("called construct_poly_arg_error without an expected expression, got: {:?}", expr)
 
            }
 
        }
 

	
 
        // Actual checking
 
        let expr = &ctx.heap[expr_id];
 
        let (expr_args, expr_return_name) = match expr {
 
            Expression::Call(expr) => 
 
                (
 
                    expr.arguments.clone(),
 
                    "return type"
 
                ),
 
            Expression::Literal(expr) => {
 
                let expressions = match &expr.value {
 
                    Literal::Struct(v) => v.fields.iter()
 
                        .map(|f| f.value)
 
                        .collect(),
 
                    Literal::Enum(_) => Vec::new(),
 
                    Literal::Union(v) => v.values.clone(),
 
                    _ => unreachable!()
 
                };
 

	
 
                ( expressions, "literal" )
 
            },
 
            Expression::Select(expr) =>
 
                // Select expression uses the polymorphic variables of the 
 
                // struct it is accessing, so get the subject expression.
 
                (
 
                    vec![expr.subject],
 
                    "selected field"
 
                ),
 
            _ => unreachable!(),
 
        };
 

	
 
        // - check return type with itself
 
        if let Some((poly_idx, section_a, section_b)) = has_poly_mismatch(
 
            &poly_data.returned, &poly_data.returned
 
        ) {
 
            return construct_main_error(ctx, poly_data, poly_idx, expr)
 
                .with_info_at_span(
 
                    &ctx.module().source, expr.full_span(), format!(
 
                        "The {} inferred the conflicting types '{}' and '{}'",
 
                        expr_return_name,
 
                        InferenceType::partial_display_name(&ctx.heap, section_a),
 
                        InferenceType::partial_display_name(&ctx.heap, section_b)
 
                    )
 
                );
 
        }
 

	
 
        // - check arguments with each other argument and with return type
 
        for (arg_a_idx, arg_a) in poly_data.embedded.iter().enumerate() {
 
            for (arg_b_idx, arg_b) in poly_data.embedded.iter().enumerate() {
 
        for (arg_a_idx, arg_a) in poly_data.associated.iter().enumerate() {
 
            for (arg_b_idx, arg_b) in poly_data.associated.iter().enumerate() {
 
                if arg_b_idx > arg_a_idx {
 
                    break;
 
                }
 

	
 
                if let Some((poly_idx, section_a, section_b)) = has_poly_mismatch(&arg_a, &arg_b) {
 
                    let error = construct_main_error(ctx, poly_data, poly_idx, expr);
 
                    if arg_a_idx == arg_b_idx {
 
                        // Same argument
 
                        let arg = &ctx.heap[expr_args[arg_a_idx]];
 
                        return error.with_info_at_span(
 
                            &ctx.module().source, arg.full_span(), format!(
 
                                "This argument inferred the conflicting types '{}' and '{}'",
 
                                InferenceType::partial_display_name(&ctx.heap, section_a),
 
                                InferenceType::partial_display_name(&ctx.heap, section_b)
 
                            )
 
                        );
 
                    } else {
 
                        let arg_a = &ctx.heap[expr_args[arg_a_idx]];
 
                        let arg_b = &ctx.heap[expr_args[arg_b_idx]];
 
                        return error.with_info_at_span(
 
                            &ctx.module().source, arg_a.full_span(), format!(
 
                                "This argument inferred it to '{}'",
 
                                InferenceType::partial_display_name(&ctx.heap, section_a)
 
                            )
 
                        ).with_info_at_span(
 
                            &ctx.module().source, arg_b.full_span(), format!(
 
                                "While this argument inferred it to '{}'",
 
                                InferenceType::partial_display_name(&ctx.heap, section_b)
 
                            )
 
                        )
 
                    }
 
                }
 
            }
 

	
 
            // Check with return type
 
            if let Some((poly_idx, section_arg, section_ret)) = has_poly_mismatch(arg_a, &poly_data.returned) {
 
                let arg = &ctx.heap[expr_args[arg_a_idx]];
 
                return construct_main_error(ctx, poly_data, poly_idx, expr)
 
                    .with_info_at_span(
 
                        &ctx.module().source, arg.full_span(), format!(
 
                            "This argument inferred it to '{}'",
 
                            InferenceType::partial_display_name(&ctx.heap, section_arg)
 
                        )
 
                    )
 
                    .with_info_at_span(
 
                        &ctx.module().source, expr.full_span(), format!(
 
                            "While the {} inferred it to '{}'",
 
                            expr_return_name,
 
                            InferenceType::partial_display_name(&ctx.heap, section_ret)
 
                        )
 
                    );
 
            }
 
        }
 

	
 
        // Now check against the explicitly specified polymorphic variables (if
 
        // any).
 
        for (arg_idx, arg) in poly_data.embedded.iter().enumerate() {
 
        for (arg_idx, arg) in poly_data.associated.iter().enumerate() {
 
            if let Some((poly_idx, poly_section, arg_section)) = has_explicit_poly_mismatch(&poly_data.poly_vars, arg) {
 
                let arg = &ctx.heap[expr_args[arg_idx]];
 
                return construct_main_error(ctx, poly_data, poly_idx, expr)
 
                    .with_info_at_span(
 
                        &ctx.module().source, arg.full_span(), format!(
 
                            "The polymorphic variable has type '{}' (which might have been partially inferred) while the argument inferred it to '{}'",
 
                            InferenceType::partial_display_name(&ctx.heap, poly_section),
 
                            InferenceType::partial_display_name(&ctx.heap, arg_section)
 
                        )
 
                    );
 
            }
 
        }
 

	
 
        if let Some((poly_idx, poly_section, ret_section)) = has_explicit_poly_mismatch(&poly_data.poly_vars, &poly_data.returned) {
 
            return construct_main_error(ctx, poly_data, poly_idx, expr)
 
                .with_info_at_span(
 
                    &ctx.module().source, expr.full_span(), format!(
 
                        "The polymorphic variable has type '{}' (which might have been partially inferred) while the {} inferred it to '{}'",
 
                        InferenceType::partial_display_name(&ctx.heap, poly_section),
 
                        expr_return_name,
 
                        InferenceType::partial_display_name(&ctx.heap, ret_section)
 
                    )
 
                )
 
        }
 

	
 
        unreachable!("construct_poly_arg_error without actual error found?")
 
    }
 
}
 

	
 
#[cfg(test)]
 
mod tests {
 
    use super::*;
 
    use crate::protocol::arena::Id;
 
    use InferenceTypePart as ITP;
 
    use InferenceType as IT;
 

	
 
    #[test]
 
    fn test_single_part_inference() {
 
        // lhs argument inferred from rhs
 
        let pairs = [
 
            (ITP::NumberLike, ITP::UInt8),
 
            (ITP::IntegerLike, ITP::SInt32),
 
            (ITP::Unknown, ITP::UInt64),
 
            (ITP::Unknown, ITP::Bool)
 
        ];
 
        for (lhs, rhs) in pairs.iter() {
 
            // Using infer-both
 
            let mut lhs_type = IT::new(false, false, vec![lhs.clone()]);
0 comments (0 inline, 0 general)