Changeset - b6805cfb30e1
[Not reviewed]
0 1 0
mh - 3 years ago 2022-02-16 16:50:38
contact@maxhenger.nl
WIP: Refactor typing pass to simplify adding expr types
1 file changed with 385 insertions and 182 deletions:
0 comments (0 inline, 0 general)
src/protocol/parser/pass_typing.rs
Show inline comments
 
@@ -834,32 +834,130 @@ pub(crate) struct ResolveQueueElement {
 
    pub(crate) reserved_type_id: TypeId,
 
}
 

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

	
 
#[derive(Clone)]
 
struct InferenceExpression {
 
struct InferenceNode {
 
    expr_type: InferenceType,       // result type from expression
 
    expr_id: ExpressionId,          // expression that is evaluated
 
    inference_rule: InferenceRule,
 
    field_or_monomorph_idx: i32,    // index of field
 
    extra_data_idx: i32,            // index of extra data needed for inference
 
    type_id: TypeId,                // when applicable indexes into type table
 
}
 

	
 
impl Default for InferenceExpression {
 
impl Default for InferenceNode {
 
    fn default() -> Self {
 
        Self{
 
            expr_type: InferenceType::default(),
 
            expr_id: ExpressionId::new_invalid(),
 
            inference_rule: InferenceRule::Noop,
 
            field_or_monomorph_idx: -1,
 
            extra_data_idx: -1,
 
            type_id: TypeId::new_invalid(),
 
        }
 
    }
 
}
 

	
 
/// 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),
 
    SelectExpr(InferenceRuleSelectExpr),
 
    LiteralStruct,
 
    LiteralEnum,
 
    LiteralUnion,
 
    LiteralArray,
 
    LiteralTuple,
 
    CastExpr,
 
    CallExpr,
 
    VariableExpr,
 
}
 

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

	
 
impl InferenceRuleTemplate {
 
    fn new_none() -> Self {
 
        return Self{
 
            template: &[],
 
            application: InferenceRuleTemplateApplication::None,
 
        };
 
    }
 

	
 
    fn new_forced(template: &'static [InferenceTypePart]) -> Self {
 
        return Self{
 
            template,
 
            application: InferenceRuleTemplateApplication::Forced,
 
        };
 
    }
 

	
 
    fn new_template(template: &'static [InferenceTypePart]) -> Self {
 
        return Self{
 
            template,
 
            application: InferenceRuleTemplateApplication::Template,
 
        }
 
    }
 
}
 

	
 
enum InferenceRuleTemplateApplication {
 
    None, // do not apply template, silly, but saves some bytes
 
    Forced,
 
    Template,
 
}
 

	
 
struct InferenceRuleBiEqual {
 
    template: InferenceRuleTemplate,
 
    argument_index: InferIndex,
 
}
 

	
 
struct InferenceRuleTriEqualArgs {
 
    argument_template: InferenceRuleTemplate,
 
    result_template: InferenceRuleTemplate,
 
    argument1_index: InferIndex,
 
    argument2_index: InferIndex,
 
}
 

	
 
struct InferenceRuleTriEqualAll {
 
    template: InferenceRuleTemplate,
 
    argument1_index: InferIndex,
 
    argument2_index: InferIndex,
 
}
 

	
 
// generic two-argument (excluding expression itself) inference rule arguments
 
struct InferenceRuleTwoArgs {
 
    argument1_index: InferIndex,
 
    argument2_index: InferIndex,
 
}
 

	
 
struct InferenceRuleIndexingExpr {
 
    subject_index: InferIndex,
 
    index_index: InferIndex,
 
}
 

	
 
struct InferenceRuleSlicingExpr {
 
    subject_index: InferIndex,
 
    from_index: InferIndex,
 
    to_index: InferIndex,
 
}
 

	
 
struct InferenceRuleSelectExpr {
 
    subject_index: InferIndex,
 
}
 

	
 
/// 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,
 
@@ -869,13 +967,13 @@ pub(crate) struct PassTyping {
 
    expr_buffer: ScopedBuffer<ExpressionId>,
 
    stmt_buffer: ScopedBuffer<StatementId>,
 
    bool_buffer: ScopedBuffer<bool>,
 
    // 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
 
    expr_types: Vec<InferenceExpression>,                     // will be transferred to type table at end
 
    infer_nodes: Vec<InferenceNode>,                     // will be transferred to type table at end
 
    extra_data: Vec<ExtraData>,       // 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>,
 
}
 

	
 
@@ -930,13 +1028,13 @@ impl PassTyping {
 
            poly_vars: Vec::new(),
 
            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(),
 
            expr_types: Vec::new(),
 
            infer_nodes: Vec::new(),
 
            extra_data: Vec::new(),
 
            expr_queued: DequeSet::new(),
 
        }
 
    }
 

	
 
    pub(crate) fn queue_module_definitions(ctx: &mut Ctx, queue: &mut ResolveQueue) {
 
@@ -1003,24 +1101,24 @@ impl PassTyping {
 

	
 
    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.expr_types.clear();
 
        self.infer_nodes.clear();
 
        self.extra_data.clear();
 
        self.expr_queued.clear();
 
    }
 
}
 

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

	
 
type VisitorResult = Result<(), ParseError>;
 
type VisitStmtResult = Result<>
 
type VisitExprResult = Result<InferIndex, 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);
 
@@ -1038,14 +1136,14 @@ impl PassTyping {
 

	
 
        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.expr_types.is_empty());
 
        self.expr_types.resize(comp_def.num_expressions_in_body as usize, Default::default());
 
        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);
 
@@ -1078,14 +1176,14 @@ impl PassTyping {
 
                debug_log!(" - [{:03}] {:?}", _idx, _infer_type.display_name(&ctx.heap));
 
            }
 
        }
 
        debug_log!("{}", "-".repeat(50));
 

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

	
 
        // Visit parameters
 
        let section = self.var_buffer.start_section_initialized(func_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);
 
@@ -1234,158 +1332,297 @@ impl PassTyping {
 

	
 
    fn visit_return_stmt(&mut self, ctx: &mut Ctx, id: ReturnStatementId) -> VisitorResult {
 
        let return_stmt = &ctx.heap[id];
 
        debug_assert_eq!(return_stmt.expressions.len(), 1);
 
        let expr_id = return_stmt.expressions[0];
 

	
 
        self.visit_expr(ctx, expr_id)
 
        self.visit_expr(ctx, expr_id)?;
 
        return Ok(());
 
    }
 

	
 
    fn visit_goto_stmt(&mut self, _: &mut Ctx, _: GotoStatementId) -> VisitorResult { return Ok(()) }
 

	
 
    fn visit_new_stmt(&mut self, ctx: &mut Ctx, id: NewStatementId) -> VisitorResult {
 
        let new_stmt = &ctx.heap[id];
 
        let call_expr_id = new_stmt.expression;
 

	
 
        self.visit_call_expr(ctx, call_expr_id)
 
        self.visit_call_expr(ctx, call_expr_id)?;
 
        return Ok(());
 
    }
 

	
 
    fn visit_expr_stmt(&mut self, ctx: &mut Ctx, id: ExpressionStatementId) -> VisitorResult {
 
        let expr_stmt = &ctx.heap[id];
 
        let subexpr_id = expr_stmt.expression;
 

	
 
        self.visit_expr(ctx, subexpr_id)
 
        self.visit_expr(ctx, subexpr_id)?;
 
        return Ok(());
 
    }
 

	
 
    // Expressions
 

	
 
    fn visit_expr(&mut self, ctx: &mut Ctx, id: ExpressionId) -> VisitorResult {
 
    fn visit_expr(&mut self, ctx: &mut Ctx, id: ExpressionId) -> VisitExprResult {
 
        return visitor_recursive_expression_impl!(self, &ctx.heap[id], ctx);
 
    }
 

	
 
    fn visit_assignment_expr(&mut self, ctx: &mut Ctx, id: AssignmentExpressionId) -> VisitorResult {
 
    fn visit_assignment_expr(&mut self, ctx: &mut Ctx, id: AssignmentExpressionId) -> VisitExprResult {
 
        use AssignmentOperator as AO;
 

	
 
        let upcast_id = id.upcast();
 
        self.insert_initial_expr_inference_type(ctx, upcast_id)?;
 
        let self_index = self.insert_initial_expr_inference_type(ctx, upcast_id)?;
 

	
 
        let assign_expr = &ctx.heap[id];
 
        let assign_op = assign_expr.operation;
 
        let left_expr_id = assign_expr.left;
 
        let right_expr_id = assign_expr.right;
 

	
 
        self.visit_expr(ctx, left_expr_id)?;
 
        self.visit_expr(ctx, right_expr_id)?;
 
        let left_index = self.visit_expr(ctx, left_expr_id)?;
 
        let right_index = self.visit_expr(ctx, right_expr_id)?;
 

	
 
        let node = &mut self.infer_nodes[self_index];
 
        let argument_template = match assign_op {
 
            AO::Set =>
 
                InferenceRuleTemplate::new_none(),
 
            AO::Concatenated =>
 
                InferenceRuleTemplate::new_template(&ARRAYLIKE_TEMPLATE),
 
            AO::Multiplied | AO::Divided | AO::Added | AO::Subtracted =>
 
                InferenceRuleTemplate::new_template(&NUMBERLIKE_TEMPLATE),
 
            AO::Remained | AO::ShiftedLeft | AO::ShiftedRight |
 
            AO::BitwiseAnded | AO::BitwiseXored | AO::BitwiseOred =>
 
                InferenceRuleTemplate::new_template(&INTEGERLIKE_TEMPLATE),
 
        };
 

	
 
        node.inference_rule = InferenceRule::TriEqualArgs(InferenceRuleTriEqualArgs{
 
            argument_template,
 
            result_template: InferenceRuleTemplate::new_forced(&VOID_TEMPLATE),
 
            argument1_index: left_index,
 
            argument2_index: right_index,
 
        });
 

	
 
        self.progress_assignment_expr(ctx, id)
 
    }
 

	
 
    fn visit_binding_expr(&mut self, ctx: &mut Ctx, id: BindingExpressionId) -> VisitorResult {
 
    fn visit_binding_expr(&mut self, ctx: &mut Ctx, id: BindingExpressionId) -> VisitExprResult {
 
        let upcast_id = id.upcast();
 
        self.insert_initial_expr_inference_type(ctx, upcast_id)?;
 
        let self_index = self.insert_initial_expr_inference_type(ctx, upcast_id)?;
 

	
 
        let binding_expr = &ctx.heap[id];
 
        let bound_to_id = binding_expr.bound_to;
 
        let bound_from_id = binding_expr.bound_from;
 

	
 
        self.visit_expr(ctx, bound_to_id)?;
 
        self.visit_expr(ctx, bound_from_id)?;
 
        let arg_to_index = self.visit_expr(ctx, bound_to_id)?;
 
        let arg_from_index = self.visit_expr(ctx, bound_from_id)?;
 

	
 
        let node = &mut self.infer_nodes[self_index];
 
        node.inference_rule = InferenceRule::TriEqualArgs(InferenceRuleTriEqualArgs{
 
            argument_template: InferenceRuleTemplate::new_none(),
 
            result_template: InferenceRuleTemplate::new_forced(&BOOL_TEMPLATE),
 
            argument1_index: arg_to_index,
 
            argument2_index: arg_from_index,
 
        });
 

	
 
        self.progress_binding_expr(ctx, id)
 
    }
 

	
 
    fn visit_conditional_expr(&mut self, ctx: &mut Ctx, id: ConditionalExpressionId) -> VisitorResult {
 
    fn visit_conditional_expr(&mut self, ctx: &mut Ctx, id: ConditionalExpressionId) -> VisitExprResult {
 
        let upcast_id = id.upcast();
 
        self.insert_initial_expr_inference_type(ctx, upcast_id)?;
 
        let self_index = self.insert_initial_expr_inference_type(ctx, upcast_id)?;
 

	
 
        let conditional_expr = &ctx.heap[id];
 
        let test_expr_id = conditional_expr.test;
 
        let true_expr_id = conditional_expr.true_expression;
 
        let false_expr_id = conditional_expr.false_expression;
 

	
 
        self.visit_expr(ctx, test_expr_id)?;
 
        self.visit_expr(ctx, true_expr_id)?;
 
        self.visit_expr(ctx, false_expr_id)?;
 
        let true_index = self.visit_expr(ctx, true_expr_id)?;
 
        let false_index = self.visit_expr(ctx, false_expr_id)?;
 

	
 
        // Note: the test to the conditional expression has already been forced
 
        // to the boolean type. So the only thing we need to do while progressing
 
        // is to apply an equal3 constraint to the arguments and the result of
 
        // the expression.
 
        let node = &mut self.infer_nodes[self_index];
 
        node.inference_rule = InferenceRule::TriEqualAll(InferenceRuleTriEqualAll{
 
            template: InferenceRuleTemplate::new_none(),
 
            argument1_index: true_index,
 
            argument2_index: false_index,
 
        });
 

	
 
        self.progress_conditional_expr(ctx, id)
 
    }
 

	
 
    fn visit_binary_expr(&mut self, ctx: &mut Ctx, id: BinaryExpressionId) -> VisitorResult {
 
    fn visit_binary_expr(&mut self, ctx: &mut Ctx, id: BinaryExpressionId) -> VisitExprResult {
 
        use BinaryOperator as BO;
 

	
 
        let upcast_id = id.upcast();
 
        self.insert_initial_expr_inference_type(ctx, upcast_id)?;
 
        let self_index = self.insert_initial_expr_inference_type(ctx, upcast_id)?;
 

	
 
        let binary_expr = &ctx.heap[id];
 
        let binary_op = binary_expr.operation;
 
        let lhs_expr_id = binary_expr.left;
 
        let rhs_expr_id = binary_expr.right;
 

	
 
        self.visit_expr(ctx, lhs_expr_id)?;
 
        self.visit_expr(ctx, rhs_expr_id)?;
 
        let left_index = self.visit_expr(ctx, lhs_expr_id)?;
 
        let right_index = self.visit_expr(ctx, rhs_expr_id)?;
 

	
 
        let inference_rule = match binary_op {
 
            BO::Concatenate =>
 
                InferenceRule::Concatenate(InferenceRuleTwoArgs{
 
                    argument1_index: left_index,
 
                    argument2_index: right_index,
 
                }),
 
            BO::LogicalAnd | BO::LogicalOr =>
 
                InferenceRule::TriEqualAll(InferenceRuleTriEqualAll{
 
                    template: InferenceRuleTemplate::new_forced(&BOOL_TEMPLATE),
 
                    argument1_index: left_index,
 
                    argument2_index: right_index,
 
                }),
 
            BO::BitwiseOr | BO::BitwiseXor | BO::BitwiseAnd | BO::Remainder | BO::ShiftLeft | BO::ShiftRight =>
 
                InferenceRule::TriEqualAll(InferenceRuleTriEqualAll{
 
                    template: InferenceRuleTemplate::new_template(&INTEGERLIKE_TEMPLATE),
 
                    argument1_index: left_index,
 
                    argument2_index: right_index,
 
                }),
 
            BO::Equality | BO::Inequality =>
 
                InferenceRule::TriEqualArgs(InferenceRuleTriEqualArgs{
 
                    argument_template: InferenceRuleTemplate::new_none(),
 
                    result_template: InferenceRuleTemplate::new_forced(&BOOL_TEMPLATE),
 
                    argument1_index: left_index,
 
                    argument2_index: right_index,
 
                }),
 
            BO::LessThan | BO::GreaterThan | BO::LessThanEqual | BO::GreaterThanEqual =>
 
                InferenceRule::TriEqualArgs(InferenceRuleTriEqualArgs{
 
                    argument_template: InferenceRuleTemplate::new_template(&NUMBERLIKE_TEMPLATE),
 
                    result_template: InferenceRuleTemplate::new_forced(&BOOL_TEMPLATE),
 
                    argument1_index: left_index,
 
                    argument2_index: right_index,
 
                }),
 
            BO::Add | BO::Subtract | BO::Multiply | BO::Divide =>
 
                InferenceRule::TriEqualAll(InferenceRuleTriEqualAll{
 
                    template: InferenceRuleTemplate::new_template(&NUMBERLIKE_TEMPLATE),
 
                    argument1_index: left_index,
 
                    argument2_index: right_index,
 
                }),
 
        };
 

	
 
        let node = &mut self.infer_nodes[self_index];
 
        node.inference_rule = inference_rule;
 

	
 
        self.progress_binary_expr(ctx, id)
 
    }
 

	
 
    fn visit_unary_expr(&mut self, ctx: &mut Ctx, id: UnaryExpressionId) -> VisitorResult {
 
    fn visit_unary_expr(&mut self, ctx: &mut Ctx, id: UnaryExpressionId) -> VisitExprResult {
 
        use UnaryOperator as UO;
 

	
 
        let upcast_id = id.upcast();
 
        self.insert_initial_expr_inference_type(ctx, upcast_id)?;
 
        let self_index = self.insert_initial_expr_inference_type(ctx, upcast_id)?;
 

	
 
        let unary_expr = &ctx.heap[id];
 
        let operation = unary_expr.operation;
 
        let arg_expr_id = unary_expr.expression;
 

	
 
        self.visit_expr(ctx, arg_expr_id)?;
 
        let argument_index = self.visit_expr(ctx, arg_expr_id)?;
 

	
 
        let template = match operation {
 
            UO::Positive | UO::Negative =>
 
                InferenceRuleTemplate::new_template(&NUMBERLIKE_TEMPLATE),
 
            UO::BitwiseNot =>
 
                InferenceRuleTemplate::new_template(&INTEGERLIKE_TEMPLATE),
 
            UO::LogicalNot =>
 
                InferenceRuleTemplate::new_forced(&BOOL_TEMPLATE),
 
        };
 

	
 
        let node = &mut self.infer_nodes[self_index];
 
        node.inference_rule = InferenceRule::BiEqual(InferenceRuleBiEqual{
 
            template, argument_index,
 
        });
 

	
 
        self.progress_unary_expr(ctx, id)
 
    }
 

	
 
    fn visit_indexing_expr(&mut self, ctx: &mut Ctx, id: IndexingExpressionId) -> VisitorResult {
 
    fn visit_indexing_expr(&mut self, ctx: &mut Ctx, id: IndexingExpressionId) -> VisitExprResult {
 
        let upcast_id = id.upcast();
 
        self.insert_initial_expr_inference_type(ctx, upcast_id)?;
 
        let self_index = self.insert_initial_expr_inference_type(ctx, upcast_id)?;
 

	
 
        let indexing_expr = &ctx.heap[id];
 
        let subject_expr_id = indexing_expr.subject;
 
        let index_expr_id = indexing_expr.index;
 

	
 
        self.visit_expr(ctx, subject_expr_id)?;
 
        self.visit_expr(ctx, index_expr_id)?;
 
        let subject_index = self.visit_expr(ctx, subject_expr_id)?;
 
        let index_index = self.visit_expr(ctx, index_expr_id)?; // cool name, bro
 

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

	
 
        self.progress_indexing_expr(ctx, id)
 
    }
 

	
 
    fn visit_slicing_expr(&mut self, ctx: &mut Ctx, id: SlicingExpressionId) -> VisitorResult {
 
    fn visit_slicing_expr(&mut self, ctx: &mut Ctx, id: SlicingExpressionId) -> VisitExprResult {
 
        let upcast_id = id.upcast();
 
        self.insert_initial_expr_inference_type(ctx, upcast_id)?;
 
        let self_index = self.insert_initial_expr_inference_type(ctx, upcast_id)?;
 

	
 
        let slicing_expr = &ctx.heap[id];
 
        let subject_expr_id = slicing_expr.subject;
 
        let from_expr_id = slicing_expr.from_index;
 
        let to_expr_id = slicing_expr.to_index;
 

	
 
        self.visit_expr(ctx, subject_expr_id)?;
 
        self.visit_expr(ctx, from_expr_id)?;
 
        self.visit_expr(ctx, to_expr_id)?;
 
        let subject_index = self.visit_expr(ctx, subject_expr_id)?;
 
        let from_index = self.visit_expr(ctx, from_expr_id)?;
 
        let to_index = self.visit_expr(ctx, to_expr_id)?;
 

	
 
        let node = &mut self.infer_nodes[self_index];
 
        node.inference_rule = InferenceRule::SlicingExpr(InferenceRuleSlicingExpr{
 
            subject_index, from_index, to_index,
 
        });
 

	
 
        self.progress_slicing_expr(ctx, id)
 
    }
 

	
 
    fn visit_select_expr(&mut self, ctx: &mut Ctx, id: SelectExpressionId) -> VisitorResult {
 
    fn visit_select_expr(&mut self, ctx: &mut Ctx, id: SelectExpressionId) -> VisitExprResult {
 
        let upcast_id = id.upcast();
 
        self.insert_initial_expr_inference_type(ctx, upcast_id)?;
 
        let self_index = self.insert_initial_expr_inference_type(ctx, upcast_id)?;
 

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

	
 
        self.visit_expr(ctx, subject_expr_id)?;
 
        let subject_index = self.visit_expr(ctx, subject_expr_id)?;
 

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

	
 
        self.progress_select_expr(ctx, id)
 
    }
 

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

	
 
        let literal_expr = &ctx.heap[id];
 
        match &literal_expr.value {
 
            Literal::Null | Literal::False | Literal::True |
 
            Literal::Integer(_) | Literal::Character(_) | Literal::String(_) => {
 
                // No subexpressions
 
            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) => {
 
                let mut expr_ids = self.expr_buffer.start_section();
 
                for field in &literal.fields {
 
                    expr_ids.push(field.value);
 
                }
 
@@ -1422,46 +1659,46 @@ impl PassTyping {
 
            }
 
        }
 

	
 
        self.progress_literal_expr(ctx, id)
 
    }
 

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

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

	
 
        self.visit_expr(ctx, subject_expr_id)?;
 

	
 
        self.progress_cast_expr(ctx, id)
 
    }
 

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

	
 
        // By default we set the polymorph idx for calls to 0. If the call ends
 
        // up not being a polymorphic one, then we will select the default
 
        // expression types in the type table
 
        let call_expr = &ctx.heap[id];
 
        self.expr_types[call_expr.unique_id_in_definition as usize].field_or_monomorph_idx = 0;
 
        self.infer_nodes[call_expr.unique_id_in_definition as usize].field_or_monomorph_idx = 0;
 

	
 
        // Visit all arguments
 
        let expr_ids = self.expr_buffer.start_section_initialized(call_expr.arguments.as_slice());
 
        for arg_expr_id in expr_ids.iter_copied() {
 
            self.visit_expr(ctx, arg_expr_id)?;
 
        }
 
        expr_ids.forget();
 

	
 
        self.progress_call_expr(ctx, id)
 
    }
 

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

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

	
 
@@ -1492,13 +1729,13 @@ impl PassTyping {
 
// -----------------------------------------------------------------------------
 

	
 
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.expr_types[expr_idx as usize].expr_type;
 
        let expr_type = &self.infer_nodes[expr_idx as usize].expr_type;
 
        expr_type.display_name(&ctx.heap)
 
    }
 

	
 
    fn resolve_types(&mut self, ctx: &mut Ctx, queue: &mut ResolveQueue) -> Result<(), ParseError> {
 
        // Keep inferring until we can no longer make any progress
 
        while !self.expr_queued.is_empty() {
 
@@ -1509,13 +1746,13 @@ impl PassTyping {
 
                self.progress_expr(ctx, next_expr_idx)?;
 
            }
 

	
 
            // Nothing is queued anymore. However we might have integer literals
 
            // whose type cannot be inferred. For convenience's sake we'll
 
            // infer these to be s32.
 
            for (infer_expr_idx, infer_expr) in self.expr_types.iter_mut().enumerate() {
 
            for (infer_expr_idx, infer_expr) in self.infer_nodes.iter_mut().enumerate() {
 
                let expr_type = &mut infer_expr.expr_type;
 
                if !expr_type.is_done && expr_type.parts.len() == 1 && expr_type.parts[0] == InferenceTypePart::IntegerLike {
 
                    // Force integer type to s32
 
                    expr_type.parts[0] = InferenceTypePart::SInt32;
 
                    expr_type.is_done = true;
 

	
 
@@ -1576,13 +1813,13 @@ impl PassTyping {
 

	
 
            Ok(concrete_type)
 
        }
 

	
 
        // Inference is now done. But we may still have uninferred types. So we
 
        // check for these.
 
        for infer_expr in self.expr_types.iter_mut() {
 
        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)
 
@@ -1682,14 +1919,14 @@ impl PassTyping {
 
            let argument_type = self.var_types.get(argument_id).unwrap();
 
            argument_type.var_type.write_concrete_type(&mut concrete);
 
            target.arg_types.push(concrete);
 
        }
 

	
 
        // - Write the expression data
 
        target.expr_data.reserve(self.expr_types.len());
 
        for infer_expr in self.expr_types.iter() {
 
        target.expr_data.reserve(self.infer_nodes.len());
 
        for infer_expr in self.infer_nodes.iter() {
 
            let mut concrete = ConcreteType::default();
 
            infer_expr.expr_type.write_concrete_type(&mut concrete);
 
            target.expr_data.push(MonomorphExpression{
 
                expr_type: concrete,
 
                field_or_monomorph_idx: infer_expr.field_or_monomorph_idx,
 
                type_id: infer_expr.type_id,
 
@@ -1697,13 +1934,13 @@ impl PassTyping {
 
        }
 

	
 
        Ok(())
 
    }
 

	
 
    fn progress_expr(&mut self, ctx: &mut Ctx, idx: i32) -> Result<(), ParseError> {
 
        let id = self.expr_types[idx as usize].expr_id;
 
        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) => {
 
@@ -1750,16 +1987,15 @@ impl PassTyping {
 
                let id = expr.this;
 
                self.progress_variable_expr(ctx, id)
 
            }
 
        }
 
    }
 

	
 
    fn progress_assignment_expr(&mut self, ctx: &mut Ctx, id: AssignmentExpressionId) -> Result<(), ParseError> {
 
    fn progress_assignment_expr(&mut self, ctx: &mut Ctx, infer_index: InferIndex) -> Result<(), ParseError> {
 
        use AssignmentOperator as AO;
 

	
 
        let upcast_id = id.upcast();
 

	
 
        let expr = &ctx.heap[id];
 
        let arg1_expr_id = expr.left;
 
        let arg2_expr_id = expr.right;
 

	
 
        debug_log!("Assignment expr '{:?}': {}", expr.operation, upcast_id.index);
 
@@ -1869,13 +2105,13 @@ impl PassTyping {
 
        debug_log!("   - Expr type: {}", self.debug_get_display_name(ctx, upcast_id));
 

	
 
        let (progress_expr, progress_arg1, progress_arg2) = match expr.operation {
 
            BO::Concatenate => {
 
                // Two cases: if one of the arguments or the output type is a
 
                // string, then all must be strings. Otherwise the arguments
 
                // must be arraylike and the output will be a array.
 
                // must be arraylike and the output will be an array.
 
                let (expr_is_str, expr_is_not_str) = self.type_is_certainly_or_certainly_not_string(ctx, upcast_id);
 
                let (arg1_is_str, arg1_is_not_str) = self.type_is_certainly_or_certainly_not_string(ctx, arg1_id);
 
                let (arg2_is_str, arg2_is_not_str) = self.type_is_certainly_or_certainly_not_string(ctx, arg2_id);
 

	
 
                let someone_is_str = expr_is_str || arg1_is_str || arg2_is_str;
 
                let someone_is_not_str = expr_is_not_str || arg1_is_not_str || arg2_is_not_str;
 
@@ -1900,21 +2136,13 @@ impl PassTyping {
 
                    let (subtype_expr, subtype_arg1, subtype_arg2) =
 
                        self.apply_equal3_constraint(ctx, upcast_id, arg1_id, arg2_id, 1)?;
 

	
 
                    (progress_expr || subtype_expr, progress_arg1 || subtype_arg1, progress_arg2 || subtype_arg2)
 
                }
 
            },
 
            BO::LogicalAnd => {
 
                // Forced boolean on all
 
                let progress_expr = self.apply_forced_constraint(ctx, upcast_id, &BOOL_TEMPLATE)?;
 
                let progress_arg1 = self.apply_forced_constraint(ctx, upcast_id, &BOOL_TEMPLATE)?;
 
                let progress_arg2 = self.apply_forced_constraint(ctx, upcast_id, &BOOL_TEMPLATE)?;
 

	
 
                (progress_expr, progress_arg1, progress_arg2)
 
            },
 
            BO::LogicalOr => {
 
            BO::LogicalAnd | BO::LogicalOr => {
 
                // Forced boolean on all
 
                let progress_expr = self.apply_forced_constraint(ctx, upcast_id, &BOOL_TEMPLATE)?;
 
                let progress_arg1 = self.apply_forced_constraint(ctx, arg1_id, &BOOL_TEMPLATE)?;
 
                let progress_arg2 = self.apply_forced_constraint(ctx, arg2_id, &BOOL_TEMPLATE)?;
 

	
 
                (progress_expr, progress_arg1, progress_arg2)
 
@@ -2111,13 +2339,13 @@ impl PassTyping {
 

	
 
        let subject_id = ctx.heap[id].subject;
 
        let subject_expr_idx = ctx.heap[subject_id].get_unique_id_in_definition();
 
        let select_expr = &ctx.heap[id];
 
        let expr_idx = select_expr.unique_id_in_definition;
 

	
 
        let infer_expr = &self.expr_types[expr_idx as usize];
 
        let infer_expr = &self.infer_nodes[expr_idx as usize];
 
        let extra_idx = infer_expr.extra_data_idx;
 

	
 
        fn try_get_definition_id_from_inference_type<'a>(types: &'a TypeTable, infer_type: &InferenceType) -> Result<Option<&'a DefinedType>, ()> {
 
            for part in &infer_type.parts {
 
                if part.is_marker() || !part.is_concrete() {
 
                    continue;
 
@@ -2163,13 +2391,13 @@ impl PassTyping {
 
        let (progress_subject, progress_expr) = match &select_expr.kind {
 
            SelectKind::StructField(field_name) => {
 
                // Handle select of a struct's field
 
                if infer_expr.field_or_monomorph_idx < 0 {
 
                    // We don't know the field or the definition it is pointing to yet
 
                    // Not yet known, check if we can determine it
 
                    let subject_type = &self.expr_types[subject_expr_idx as usize].expr_type;
 
                    let subject_type = &self.infer_nodes[subject_expr_idx as usize].expr_type;
 
                    let type_def = try_get_definition_id_from_inference_type(&ctx.types, subject_type);
 

	
 
                    match type_def {
 
                        Ok(Some(type_def)) => {
 
                            // Subject type is known, check if it is a
 
                            // struct and the field exists on the struct
 
@@ -2186,13 +2414,13 @@ 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.expr_types[expr_idx as usize];
 
                                    let infer_expr = &mut self.infer_nodes[expr_idx as usize];
 
                                    infer_expr.field_or_monomorph_idx = field_def_idx as i32;
 
                                    struct_def_id = Some(type_def.ast_definition);
 
                                    break;
 
                                }
 
                            }
 

	
 
@@ -2231,26 +2459,26 @@ impl PassTyping {
 
                // do some mutual inference.
 
                let poly_data = &mut self.extra_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 subject_type: *mut _ = &mut self.expr_types[subject_expr_idx as usize].expr_type;
 
                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.expr_types[expr_idx as usize].expr_type;
 
                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
 
                )?;
 

	
 
@@ -2260,33 +2488,33 @@ impl PassTyping {
 
                        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 subject_type: *mut _ = &mut self.expr_types[subject_expr_idx as usize].expr_type;
 
                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.expr_types[expr_idx as usize].expr_type;
 
                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_idx < 0 {
 
                    // We don't know what kind of tuple we're accessing yet
 
                    let subject_type = &self.expr_types[subject_expr_idx as usize].expr_type;
 
                    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
 
@@ -2298,13 +2526,13 @@ impl PassTyping {
 
                                    )
 
                                ));
 
                            }
 

	
 
                            // Within bounds, so set the index (such that we
 
                            // will not perform this lookup again)
 
                            let infer_expr = &mut self.expr_types[expr_idx as usize];
 
                            let infer_expr = &mut self.infer_nodes[expr_idx as usize];
 
                            infer_expr.field_or_monomorph_idx = member_index as i32;
 
                        },
 
                        Ok(None) => {
 
                            // Nothing is known about the tuple yet
 
                            return Ok(());
 
                        },
 
@@ -2318,13 +2546,13 @@ impl PassTyping {
 
                        }
 
                    }
 
                }
 

	
 
                // If here then we know which member we're accessing. So seek
 
                // that member in the subject type and apply inference.
 
                let subject_type = &self.expr_types[subject_expr_idx as usize].expr_type;
 
                let subject_type = &self.infer_nodes[subject_expr_idx as usize].expr_type;
 
                let mut member_start_idx = 1;
 
                for _ in 0..member_index {
 
                    member_start_idx = InferenceType::find_subtree_end_idx(&subject_type.parts, member_start_idx);
 
                }
 

	
 
                let (progress_expr, progress_subject) = self.apply_equal2_constraint(
 
@@ -2346,13 +2574,13 @@ impl PassTyping {
 
    }
 

	
 
    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.expr_types[expr_idx as usize].extra_data_idx;
 
        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 {
 
@@ -2383,13 +2611,13 @@ impl PassTyping {
 

	
 
                // 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 field_type: *mut _ = &mut self.expr_types[field_expr_idx as usize].expr_type;
 
                    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!(
 
@@ -2404,13 +2632,13 @@ impl PassTyping {
 
                }
 

	
 
                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.expr_types[expr_idx as usize].expr_type;
 
                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!(
 
@@ -2439,13 +2667,13 @@ impl PassTyping {
 
                    // 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 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.expr_types[field_expr_idx as usize].expr_type;
 
                    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!(
 
@@ -2457,13 +2685,13 @@ impl PassTyping {
 
                        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.expr_types[expr_idx as usize].expr_type;
 
                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
 
@@ -2475,13 +2703,13 @@ impl PassTyping {
 
                }
 
                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.expr_types[expr_idx as usize].expr_type;
 
                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!(
 
@@ -2517,13 +2745,13 @@ impl PassTyping {
 

	
 
                // 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 value_type: *mut _ = &mut self.expr_types[value_expr_idx as usize].expr_type;
 
                    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!(
 
@@ -2538,13 +2766,13 @@ impl PassTyping {
 
                }
 

	
 
                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.expr_types[expr_idx as usize].expr_type;
 
                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!(
 
@@ -2566,13 +2794,13 @@ impl PassTyping {
 
            
 
                // 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];
 
                    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.expr_types[value_expr_idx as usize].expr_type;
 
                    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!(
 
@@ -2584,13 +2812,13 @@ impl PassTyping {
 
                        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.expr_types[expr_idx as usize].expr_type;
 
                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
 
@@ -2653,13 +2881,13 @@ impl PassTyping {
 
                for (member_expr_index, member_expr_id) in expr_elements.iter_copied().enumerate() {
 
                    // For the current expression index, (re)compute the
 
                    // position in the tuple type where the types should match.
 
                    let mut start_index = 1; // first element is Tuple type, second is the first child
 
                    for _ in 0..member_expr_index {
 
                        let tuple_expr_index = ctx.heap[id].unique_id_in_definition;
 
                        let tuple_type = &self.expr_types[tuple_expr_index as usize].expr_type;
 
                        let tuple_type = &self.infer_nodes[tuple_expr_index as usize].expr_type;
 
                        start_index = InferenceType::find_subtree_end_idx(&tuple_type.parts, start_index);
 
                        debug_assert_ne!(start_index, tuple_type.parts.len()); // would imply less tuple type children than member expressions
 
                    }
 

	
 
                    // Apply the constraint
 
                    let (member_progress_expr, member_progress) = self.apply_equal2_constraint(
 
@@ -2713,14 +2941,14 @@ impl PassTyping {
 
        debug_log!(" * After:");
 
        debug_log!("   - Expr type [{}]: {}", expr_progress, self.debug_get_display_name(ctx, upcast_id));
 
        debug_log!("   - Note that the subject type can never be inferred");
 
        debug_log!(" * Decision:");
 

	
 
        let subject_idx = ctx.heap[expr.subject].get_unique_id_in_definition();
 
        let expr_type = &self.expr_types[expr_idx as usize].expr_type;
 
        let subject_type = &self.expr_types[subject_idx as usize].expr_type;
 
        let expr_type = &self.infer_nodes[expr_idx as usize].expr_type;
 
        let subject_type = &self.infer_nodes[subject_idx as usize].expr_type;
 
        if !expr_type.is_done || !subject_type.is_done {
 
            // Not yet done
 
            debug_log!("   - Casting is valid: unknown as the types are not yet complete");
 
            return Ok(())
 
        }
 

	
 
@@ -2765,13 +2993,13 @@ impl PassTyping {
 
    //  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.expr_types[expr_idx as usize].extra_data_idx;
 
        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):");
 

	
 
@@ -2782,13 +3010,13 @@ impl PassTyping {
 
        let mut poly_progress = HashSet::new();
 
        debug_assert_eq!(extra.embedded.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 argument_type: *mut _ = &mut self.expr_types[arg_expr_idx as usize].expr_type;
 
            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!(
 
@@ -2801,13 +3029,13 @@ impl PassTyping {
 
                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.expr_types[expr_idx as usize].expr_type;
 
        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!(
 
@@ -2835,13 +3063,13 @@ impl PassTyping {
 
        //  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];
 
            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.expr_types[arg_expr_idx as usize].expr_type;
 
            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
 
            );
 
            
 
@@ -2854,13 +3082,13 @@ impl PassTyping {
 
                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.expr_types[expr_idx as usize].expr_type;
 
        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: {}", 
 
@@ -2887,13 +3115,13 @@ impl PassTyping {
 
        debug_log!(" * Before:");
 
        debug_log!("   - Var  type: {}", self.var_types.get(&var_id).unwrap().var_type.display_name(&ctx.heap));
 
        debug_log!("   - Expr type: {}", self.debug_get_display_name(ctx, upcast_id));
 

	
 
        // Retrieve shared variable type and expression type and apply inference
 
        let var_data = self.var_types.get_mut(&var_id).unwrap();
 
        let expr_type = &mut self.expr_types[var_expr_idx as usize].expr_type;
 
        let expr_type = &mut self.infer_nodes[var_expr_idx as usize].expr_type;
 

	
 
        let infer_res = unsafe{ InferenceType::infer_subtrees_for_both_types(
 
            &mut var_data.var_type as *mut _, 0, expr_type, 0
 
        ) };
 
        if infer_res == DualInferenceResult::Incompatible {
 
            let var_decl = &ctx.heap[var_id];
 
@@ -2990,13 +3218,13 @@ impl PassTyping {
 
    }
 

	
 

	
 
    // first returned is certainly string, second is certainly not
 
    fn type_is_certainly_or_certainly_not_string(&self, ctx: &Ctx, expr_id: ExpressionId) -> (bool, bool) {
 
        let expr_idx = ctx.heap[expr_id].get_unique_id_in_definition();
 
        let expr_type = &self.expr_types[expr_idx as usize].expr_type;
 
        let expr_type = &self.infer_nodes[expr_idx as usize].expr_type;
 
        if expr_type.is_done {
 
            if expr_type.parts[0] == InferenceTypePart::String {
 
                return (true, false);
 
            } else {
 
                return (false, true);
 
            }
 
@@ -3011,13 +3239,13 @@ impl PassTyping {
 
    /// expression type as well. Hence the template may be fully specified (e.g.
 
    /// a bool) or contain "inference" variables (e.g. an array of T)
 
    fn apply_template_constraint(
 
        &mut self, ctx: &Ctx, expr_id: ExpressionId, template: &[InferenceTypePart]
 
    ) -> Result<bool, ParseError> {
 
        let expr_idx = ctx.heap[expr_id].get_unique_id_in_definition(); // TODO: @Temp
 
        let expr_type = &mut self.expr_types[expr_idx as usize].expr_type;
 
        let expr_type = &mut self.infer_nodes[expr_idx as usize].expr_type;
 
        match InferenceType::infer_subtree_for_single_type(expr_type, 0, template, 0, false) {
 
            SingleInferenceResult::Modified => Ok(true),
 
            SingleInferenceResult::Unmodified => Ok(false),
 
            SingleInferenceResult::Incompatible => Err(
 
                self.construct_template_type_error(ctx, expr_id, template)
 
            )
 
@@ -3041,13 +3269,13 @@ impl PassTyping {
 
    /// 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, expr_id: ExpressionId, template: &[InferenceTypePart]
 
    ) -> Result<bool, ParseError> {
 
        let expr_idx = ctx.heap[expr_id].get_unique_id_in_definition();
 
        let expr_type = &mut self.expr_types[expr_idx as usize].expr_type;
 
        let expr_type = &mut self.infer_nodes[expr_idx as usize].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, expr_id, template)
 
            )
 
@@ -3062,14 +3290,14 @@ impl PassTyping {
 
        &mut self, ctx: &Ctx, expr_id: ExpressionId,
 
        arg1_id: ExpressionId, arg1_start_idx: usize,
 
        arg2_id: ExpressionId, arg2_start_idx: usize
 
    ) -> Result<(bool, bool), ParseError> {
 
        let arg1_expr_idx = ctx.heap[arg1_id].get_unique_id_in_definition(); // TODO: @Temp
 
        let arg2_expr_idx = ctx.heap[arg2_id].get_unique_id_in_definition();
 
        let arg1_type: *mut _ = &mut self.expr_types[arg1_expr_idx as usize].expr_type;
 
        let arg2_type: *mut _ = &mut self.expr_types[arg2_expr_idx as usize].expr_type;
 
        let arg1_type: *mut _ = &mut self.infer_nodes[arg1_expr_idx as usize].expr_type;
 
        let arg2_type: *mut _ = &mut self.infer_nodes[arg2_expr_idx as usize].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 {
 
@@ -3219,15 +3447,15 @@ impl PassTyping {
 
        // Safety: all points are unique
 
        //         containers may not be modified
 
        let expr_expr_idx = ctx.heap[expr_id].get_unique_id_in_definition(); // TODO: @Temp
 
        let arg1_expr_idx = ctx.heap[arg1_id].get_unique_id_in_definition();
 
        let arg2_expr_idx = ctx.heap[arg2_id].get_unique_id_in_definition();
 

	
 
        let expr_type: *mut _ = &mut self.expr_types[expr_expr_idx as usize].expr_type;
 
        let arg1_type: *mut _ = &mut self.expr_types[arg1_expr_idx as usize].expr_type;
 
        let arg2_type: *mut _ = &mut self.expr_types[arg2_expr_idx as usize].expr_type;
 
        let expr_type: *mut _ = &mut self.infer_nodes[expr_expr_idx as usize].expr_type;
 
        let arg1_type: *mut _ = &mut self.infer_nodes[arg1_expr_idx as usize].expr_type;
 
        let arg2_type: *mut _ = &mut self.infer_nodes[arg2_expr_idx as usize].expr_type;
 

	
 
        let expr_res = unsafe{
 
            InferenceType::infer_subtrees_for_both_types(expr_type, start_idx, arg1_type, start_idx)
 
        };
 
        if expr_res == DualInferenceResult::Incompatible {
 
            return Err(self.construct_expr_type_error(ctx, expr_id, arg1_id));
 
@@ -3292,14 +3520,14 @@ impl PassTyping {
 
        let mut last_lhs_progressed = 0;
 
        let mut lhs_arg_idx = 0;
 

	
 
        while let Some(next_arg_id) = arg_iter.next() {
 
            let last_expr_idx = ctx.heap[last_arg_id].get_unique_id_in_definition(); // TODO: @Temp
 
            let next_expr_idx = ctx.heap[next_arg_id].get_unique_id_in_definition();
 
            let last_type: *mut _ = &mut self.expr_types[last_expr_idx as usize].expr_type;
 
            let next_type: *mut _ = &mut self.expr_types[next_expr_idx as usize].expr_type;
 
            let last_type: *mut _ = &mut self.infer_nodes[last_expr_idx as usize].expr_type;
 
            let next_type: *mut _ = &mut self.infer_nodes[next_expr_idx as usize].expr_type;
 

	
 
            let res = unsafe {
 
                InferenceType::infer_subtrees_for_both_types(last_type, 0, next_type, 0)
 
            };
 

	
 
            if res == DualInferenceResult::Incompatible {
 
@@ -3318,16 +3546,16 @@ impl PassTyping {
 
            lhs_arg_idx += 1;
 
        }
 

	
 
        // Re-infer everything. Note that we do not need to re-infer the type
 
        // exactly at `last_lhs_progressed`, but only everything up to it.
 
        let last_arg_expr_idx = ctx.heap[last_arg_id].get_unique_id_in_definition();
 
        let last_type: *mut _ = &mut self.expr_types[last_arg_expr_idx as usize].expr_type;
 
        let last_type: *mut _ = &mut self.infer_nodes[last_arg_expr_idx as usize].expr_type;
 
        for arg_idx in 0..last_lhs_progressed {
 
            let other_arg_expr_idx = ctx.heap[args[arg_idx]].get_unique_id_in_definition();
 
            let arg_type: *mut _ = &mut self.expr_types[other_arg_expr_idx as usize].expr_type;
 
            let arg_type: *mut _ = &mut self.infer_nodes[other_arg_expr_idx as usize].expr_type;
 
            unsafe{
 
                (*arg_type).replace_subtree(0, &(*last_type).parts);
 
            }
 
            progress[arg_idx] = true;
 
        }
 

	
 
@@ -3337,16 +3565,17 @@ impl PassTyping {
 
    /// Determines the `InferenceType` for the expression based on the
 
    /// expression parent. Note that if the parent is another expression, we do
 
    /// not take special action, instead we let parent expressions fix the type
 
    /// of subexpressions before they have a chance to call this function.
 
    fn insert_initial_expr_inference_type(
 
        &mut self, ctx: &mut Ctx, expr_id: ExpressionId
 
    ) -> Result<(), ParseError> {
 
    ) -> Result<InferIndex, ParseError> {
 
        use ExpressionParent as EP;
 
        use InferenceTypePart as ITP;
 

	
 
        // Set the initial inference type based on the expression parent.
 
        let expr = &ctx.heap[expr_id];
 
        let inference_type = match expr.parent() {
 
            EP::None =>
 
                // Should have been set by linker
 
                unreachable!(),
 
            EP::Memory(_) | EP::ExpressionStmt(_) =>
 
@@ -3383,65 +3612,37 @@ impl PassTyping {
 
            EP::New(_) =>
 
                // Must be a component call, which we assign a "Void" return
 
                // type
 
                InferenceType::new(false, true, vec![ITP::Void]),
 
        };
 

	
 
        let infer_expr = &mut self.expr_types[expr.get_unique_id_in_definition() as usize];
 
        let needs_extra_data = match expr {
 
            Expression::Call(_) => true,
 
            Expression::Literal(expr) => match expr.value {
 
                Literal::Enum(_) | Literal::Union(_) | Literal::Struct(_) => true,
 
                _ => false,
 
            },
 
            Expression::Select(expr) => match expr.kind {
 
                SelectKind::StructField(_) => true,
 
                SelectKind::TupleMember(_) => false,
 
            },
 
            _ => false,
 
        };
 

	
 
        if infer_expr.expr_id.is_invalid() {
 
            // Nothing is set yet
 
            infer_expr.expr_type = inference_type;
 
            infer_expr.expr_id = expr_id;
 
            if needs_extra_data {
 
                let extra_idx = self.extra_data.len() as i32;
 
                self.extra_data.push(ExtraData::default());
 
                infer_expr.extra_data_idx = extra_idx;
 
            }
 
        } else {
 
            // We already have an entry
 
            debug_assert!(false, "does this ever happen?");
 
            if let SingleInferenceResult::Incompatible = InferenceType::infer_subtree_for_single_type(
 
                &mut infer_expr.expr_type, 0, &inference_type.parts, 0, false
 
            ) {
 
                return Err(self.construct_expr_type_error(ctx, expr_id, expr_id));
 
            }
 

	
 
            debug_assert!((infer_expr.extra_data_idx != -1) == needs_extra_data);
 
        }
 
        let infer_index = self.infer_nodes.len() as InferIndex;
 
        self.infer_nodes.push(InferenceNode {
 
            expr_type: inference_type,
 
            expr_id,
 
            field_or_monomorph_idx: -1,
 
            extra_data_idx: -1,
 
            type_id: TypeId::new_invalid(),
 
        });
 

	
 
        Ok(())
 
        return Ok(infer_index);
 
    }
 

	
 
    fn insert_initial_call_polymorph_data(
 
        &mut self, ctx: &mut Ctx, call_id: CallExpressionId
 
    ) {
 
    ) -> ExtraIndex {
 
        // 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];
 
        let extra_data_idx = self.expr_types[call.unique_id_in_definition as usize].extra_data_idx; // TODO: @Temp
 
        debug_assert!(extra_data_idx != -1, "insert initial call polymorph data, no preallocated ExtraData");
 

	
 
        // 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));
 
@@ -3476,28 +3677,27 @@ impl PassTyping {
 
            },
 
            Some(returned) => {
 
                self.determine_inference_type_from_parser_type_elements(&returned.elements, false)
 
            }
 
        };
 

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

	
 
    fn insert_initial_struct_polymorph_data(
 
        &mut self, ctx: &mut Ctx, lit_id: LiteralExpressionId,
 
    ) {
 
    ) -> ExtraIndex {
 
        use InferenceTypePart as ITP;
 
        let literal = &ctx.heap[lit_id];
 
        let extra_data_idx = self.expr_types[literal.unique_id_in_definition as usize].extra_data_idx; // TODO: @Temp
 
        debug_assert!(extra_data_idx != -1, "initial struct polymorph data, but no preallocated ExtraData");
 
        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);
 
@@ -3539,31 +3739,31 @@ impl PassTyping {
 
            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);
 

	
 
        self.extra_data[extra_data_idx as usize] = ExtraData{
 
        let extra_data_index = self.extra_data.len() as ExtraIndex;
 
        self.extra_data.push(ExtraData{
 
            expr_id: lit_id.upcast(),
 
            definition_id: literal.definition,
 
            poly_vars: poly_args,
 
            embedded: 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 {
 
        use InferenceTypePart as ITP;
 
        let literal = &ctx.heap[lit_id];
 
        let extra_data_idx = self.expr_types[literal.unique_id_in_definition as usize].extra_data_idx; // TODO: @Temp
 
        debug_assert!(extra_data_idx != -1, "initial enum polymorph data, but no preallocated ExtraData");
 
        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);
 
@@ -3586,30 +3786,30 @@ impl PassTyping {
 
            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);
 

	
 
        self.extra_data[extra_data_idx as usize] = ExtraData{
 
        let extra_data_index = self.extra_data.len() as ExtraIndex;
 
        self.extra_data.push(ExtraData{
 
            expr_id: lit_id.upcast(),
 
            definition_id: literal.definition,
 
            poly_vars: poly_args,
 
            embedded: 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
 
    ) {
 
        use InferenceTypePart as ITP;
 
        let literal = &ctx.heap[lit_id];
 
        let extra_data_idx = self.expr_types[literal.unique_id_in_definition as usize].extra_data_idx; // TODO: @Temp
 
        debug_assert!(extra_data_idx != -1, "initial union polymorph data, but no preallocated ExtraData");
 
        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);
 
@@ -3647,34 +3847,33 @@ impl PassTyping {
 
            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);
 

	
 
        self.extra_data[extra_data_idx as usize] = ExtraData{
 
        let extra_data_index = self.extra_data.len();
 
        self.extra_data.push(ExtraData{
 
            expr_id: lit_id.upcast(),
 
            definition_id: literal.definition,
 
            poly_vars: poly_args,
 
            embedded,
 
            returned: union_type
 
        };
 
        });
 
    }
 

	
 
    /// 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, select_id: SelectExpressionId, struct_def_id: DefinitionId
 
    ) {
 
    ) -> ExtraIndex {
 
        use InferenceTypePart as ITP;
 

	
 
        // Retrieve relevant data
 
        let expr = &ctx.heap[select_id];
 
        let expr_type = &self.expr_types[expr.unique_id_in_definition as usize];
 
        let expr_type = &self.infer_nodes[expr.unique_id_in_definition as usize];
 
        let field_idx = expr_type.field_or_monomorph_idx as usize;
 
        let extra_data_idx = expr_type.extra_data_idx; // TODO: @Temp
 
        debug_assert!(extra_data_idx != -1, "initial select polymorph data, but no preallocated ExtraData");
 

	
 
        let definition = ctx.heap[struct_def_id].as_struct();
 

	
 
        // 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();
 
@@ -3691,19 +3890,23 @@ impl PassTyping {
 
            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_idx].parser_type.elements, false);
 
        self.extra_data[extra_data_idx as usize] = ExtraData{
 

	
 
        let extra_data_index = self.extra_data.len() as ExtraIndex;
 
        self.extra_data.push(ExtraData{
 
            expr_id: select_id.upcast(),
 
            definition_id: struct_def_id,
 
            poly_vars,
 
            embedded: 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
 
@@ -3842,14 +4045,14 @@ impl PassTyping {
 
    ) -> ParseError {
 
        // TODO: Expand and provide more meaningful information for humans
 
        let expr = &ctx.heap[expr_id];
 
        let arg_expr = &ctx.heap[arg_id];
 
        let expr_idx = expr.get_unique_id_in_definition();
 
        let arg_expr_idx = arg_expr.get_unique_id_in_definition();
 
        let expr_type = &self.expr_types[expr_idx as usize].expr_type;
 
        let arg_type = &self.expr_types[arg_expr_idx as usize].expr_type;
 
        let expr_type = &self.infer_nodes[expr_idx as usize].expr_type;
 
        let arg_type = &self.infer_nodes[arg_expr_idx as usize].expr_type;
 

	
 
        return ParseError::new_error_at_span(
 
            &ctx.module().source, expr.operation_span(), format!(
 
                "incompatible types: this expression expected a '{}'",
 
                expr_type.display_name(&ctx.heap)
 
            )
 
@@ -3867,15 +4070,15 @@ impl PassTyping {
 
    ) -> ParseError {
 
        let expr = &ctx.heap[expr_id];
 
        let arg1 = &ctx.heap[arg1_id];
 
        let arg2 = &ctx.heap[arg2_id];
 

	
 
        let arg1_idx = arg1.get_unique_id_in_definition();
 
        let arg1_type = &self.expr_types[arg1_idx as usize].expr_type;
 
        let arg1_type = &self.infer_nodes[arg1_idx as usize].expr_type;
 
        let arg2_idx = arg2.get_unique_id_in_definition();
 
        let arg2_type = &self.expr_types[arg2_idx as usize].expr_type;
 
        let arg2_type = &self.infer_nodes[arg2_idx as usize].expr_type;
 

	
 
        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!(
 
@@ -3892,13 +4095,13 @@ impl PassTyping {
 

	
 
    fn construct_template_type_error(
 
        &self, ctx: &Ctx, expr_id: ExpressionId, template: &[InferenceTypePart]
 
    ) -> ParseError {
 
        let expr = &ctx.heap[expr_id];
 
        let expr_idx = expr.get_unique_id_in_definition();
 
        let expr_type = &self.expr_types[expr_idx as usize].expr_type;
 
        let expr_type = &self.infer_nodes[expr_idx as usize].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)
0 comments (0 inline, 0 general)