Changeset - 94751a00b9b7
[Not reviewed]
0 8 0
mh - 4 years ago 2021-05-31 10:43:41
contact@maxhenger.nl
Better error spans for expressions
8 files changed with 184 insertions and 127 deletions:
0 comments (0 inline, 0 general)
src/protocol/ast.rs
Show inline comments
 
@@ -1215,40 +1215,62 @@ pub enum Expression {
 
    Call(CallExpression),
 
    Variable(VariableExpression),
 
}
 

	
 
impl Expression {
 
    pub fn as_variable(&self) -> &VariableExpression {
 
        match self {
 
            Expression::Variable(result) => result,
 
            _ => panic!("Unable to cast `Expression` to `VariableExpression`"),
 
        }
 
    }
 

	
 
    pub fn span(&self) -> InputSpan {
 
    /// Returns operator span, function name, a binding's "let" span, etc. An
 
    /// indicator for the kind of expression that is being applied.
 
    pub fn operation_span(&self) -> InputSpan {
 
        match self {
 
            Expression::Assignment(expr) => expr.operator_span,
 
            Expression::Binding(expr) => expr.operator_span,
 
            Expression::Conditional(expr) => expr.operator_span,
 
            Expression::Binary(expr) => expr.operator_span,
 
            Expression::Unary(expr) => expr.operator_span,
 
            Expression::Indexing(expr) => expr.operator_span,
 
            Expression::Slicing(expr) => expr.slicing_span,
 
            Expression::Select(expr) => expr.operator_span,
 
            Expression::Literal(expr) => expr.span,
 
            Expression::Cast(expr) => expr.cast_span,
 
            Expression::Call(expr) => expr.func_span,
 
            Expression::Variable(expr) => expr.identifier.span,
 
        }
 
    }
 

	
 
    /// Returns the span covering the entire expression (i.e. including the
 
    /// spans of the arguments as well).
 
    pub fn full_span(&self) -> InputSpan {
 
        match self {
 
            Expression::Assignment(expr) => expr.span,
 
            Expression::Binding(expr) => expr.span,
 
            Expression::Conditional(expr) => expr.span,
 
            Expression::Binary(expr) => expr.span,
 
            Expression::Unary(expr) => expr.span,
 
            Expression::Indexing(expr) => expr.span,
 
            Expression::Slicing(expr) => expr.span,
 
            Expression::Select(expr) => expr.span,
 
            Expression::Assignment(expr) => expr.full_span,
 
            Expression::Binding(expr) => expr.full_span,
 
            Expression::Conditional(expr) => expr.full_span,
 
            Expression::Binary(expr) => expr.full_span,
 
            Expression::Unary(expr) => expr.full_span,
 
            Expression::Indexing(expr) => expr.full_span,
 
            Expression::Slicing(expr) => expr.full_span,
 
            Expression::Select(expr) => expr.full_span,
 
            Expression::Literal(expr) => expr.span,
 
            Expression::Cast(expr) => expr.span,
 
            Expression::Call(expr) => expr.span,
 
            Expression::Cast(expr) => expr.full_span,
 
            Expression::Call(expr) => expr.full_span,
 
            Expression::Variable(expr) => expr.identifier.span,
 
        }
 
    }
 

	
 
    // TODO: @cleanup
 
    pub fn parent(&self) -> &ExpressionParent {
 
        match self {
 
            Expression::Assignment(expr) => &expr.parent,
 
            Expression::Binding(expr) => &expr.parent,
 
            Expression::Conditional(expr) => &expr.parent,
 
            Expression::Binary(expr) => &expr.parent,
 
            Expression::Unary(expr) => &expr.parent,
 
            Expression::Indexing(expr) => &expr.parent,
 
            Expression::Slicing(expr) => &expr.parent,
 
            Expression::Select(expr) => &expr.parent,
 
            Expression::Literal(expr) => &expr.parent,
 
@@ -1295,50 +1317,53 @@ pub enum AssignmentOperator {
 
    Subtracted,
 
    ShiftedLeft,
 
    ShiftedRight,
 
    BitwiseAnded,
 
    BitwiseXored,
 
    BitwiseOred,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct AssignmentExpression {
 
    pub this: AssignmentExpressionId,
 
    // Parsing
 
    pub span: InputSpan, // of the operator
 
    pub operator_span: InputSpan,
 
    pub full_span: InputSpan,
 
    pub left: ExpressionId,
 
    pub operation: AssignmentOperator,
 
    pub right: ExpressionId,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    pub unique_id_in_definition: i32,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct BindingExpression {
 
    pub this: BindingExpressionId,
 
    // Parsing
 
    pub span: InputSpan, // of the binding keyword
 
    pub operator_span: InputSpan,
 
    pub full_span: InputSpan,
 
    pub bound_to: ExpressionId,
 
    pub bound_from: ExpressionId,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    pub unique_id_in_definition: i32,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct ConditionalExpression {
 
    pub this: ConditionalExpressionId,
 
    // Parsing
 
    pub span: InputSpan, // of question mark operator
 
    pub operator_span: InputSpan,
 
    pub full_span: InputSpan,
 
    pub test: ExpressionId,
 
    pub true_expression: ExpressionId,
 
    pub false_expression: ExpressionId,
 
    // Validator/Linking
 
    pub parent: ExpressionParent,
 
    pub unique_id_in_definition: i32,
 
}
 

	
 
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 
pub enum BinaryOperator {
 
    Concatenate,
 
    LogicalOr,
 
@@ -1356,107 +1381,114 @@ pub enum BinaryOperator {
 
    ShiftRight,
 
    Add,
 
    Subtract,
 
    Multiply,
 
    Divide,
 
    Remainder,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct BinaryExpression {
 
    pub this: BinaryExpressionId,
 
    // Parsing
 
    pub span: InputSpan, // of the operator
 
    pub operator_span: InputSpan,
 
    pub full_span: InputSpan,
 
    pub left: ExpressionId,
 
    pub operation: BinaryOperator,
 
    pub right: ExpressionId,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    pub unique_id_in_definition: i32,
 
}
 

	
 
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 
pub enum UnaryOperator {
 
    Positive,
 
    Negative,
 
    BitwiseNot,
 
    LogicalNot,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct UnaryExpression {
 
    pub this: UnaryExpressionId,
 
    // Parsing
 
    pub span: InputSpan, // of the operator
 
    pub operator_span: InputSpan,
 
    pub full_span: InputSpan,
 
    pub operation: UnaryOperator,
 
    pub expression: ExpressionId,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    pub unique_id_in_definition: i32,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct IndexingExpression {
 
    pub this: IndexingExpressionId,
 
    // Parsing
 
    pub span: InputSpan,
 
    pub operator_span: InputSpan,
 
    pub full_span: InputSpan,
 
    pub subject: ExpressionId,
 
    pub index: ExpressionId,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    pub unique_id_in_definition: i32,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct SlicingExpression {
 
    pub this: SlicingExpressionId,
 
    // Parsing
 
    pub span: InputSpan, // from '[' to ']';
 
    pub slicing_span: InputSpan, // from '[' to ']'
 
    pub full_span: InputSpan, // includes subject
 
    pub subject: ExpressionId,
 
    pub from_index: ExpressionId,
 
    pub to_index: ExpressionId,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    pub unique_id_in_definition: i32,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct SelectExpression {
 
    pub this: SelectExpressionId,
 
    // Parsing
 
    pub span: InputSpan, // of the '.'
 
    pub operator_span: InputSpan, // of the '.'
 
    pub full_span: InputSpan, // includes subject and field
 
    pub subject: ExpressionId,
 
    pub field_name: Identifier,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    pub unique_id_in_definition: i32,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct CastExpression {
 
    pub this: CastExpressionId,
 
    // Parsing
 
    pub span: InputSpan, // of the "cast" keyword,
 
    pub cast_span: InputSpan, // of the "cast" keyword,
 
    pub full_span: InputSpan, // includes the cast subject
 
    pub to_type: ParserType,
 
    pub subject: ExpressionId,
 
    // Validator/linker
 
    pub parent: ExpressionParent,
 
    pub unique_id_in_definition: i32,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct CallExpression {
 
    pub this: CallExpressionId,
 
    // Parsing
 
    pub span: InputSpan,
 
    pub func_span: InputSpan, // of the function name
 
    pub full_span: InputSpan, // includes the arguments and parentheses
 
    pub parser_type: ParserType, // of the function call, not the return type
 
    pub method: Method,
 
    pub arguments: Vec<ExpressionId>,
 
    pub definition: DefinitionId,
 
    // Validator/Linker
 
    pub parent: ExpressionParent,
 
    pub unique_id_in_definition: i32,
 
}
 

	
 
#[derive(Debug, Clone, PartialEq, Eq)]
 
pub enum Method {
 
    // Builtin
src/protocol/eval/error.rs
Show inline comments
 
@@ -72,25 +72,25 @@ impl EvalError {
 

	
 
            last_module_source = &module.source;
 
            frames.push(EvalFrame{
 
                line: statement_span.begin.line,
 
                module_name,
 
                procedure,
 
                is_func
 
            });
 
        }
 

	
 
        let expr = &heap[expr_id];
 
        let statements = vec![
 
            ErrorStatement::from_source_at_span(StatementKind::Error, last_module_source, expr.span(), msg)
 
            ErrorStatement::from_source_at_span(StatementKind::Error, last_module_source, expr.full_span(), msg)
 
        ];
 

	
 
        EvalError{ statements, frames }
 
    }
 
}
 

	
 
impl fmt::Display for EvalError {
 
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 
        // Display error statement(s)
 
        self.statements[0].fmt(f)?;
 
        for statement in self.statements.iter().skip(1) {
 
            writeln!(f)?;
src/protocol/eval/executor.rs
Show inline comments
 
@@ -578,24 +578,34 @@ impl Prompt {
 
                                            cur_frame.expr_values.push_front(value.clone());
 
                                            cur_frame.expr_stack.push_back(ExprInstruction::EvalExpr(expr_id));
 
                                            return Ok(EvalContinuation::BlockGet(value));
 
                                        }
 
                                    }
 
                                },
 
                                Method::Put => {
 
                                    let port_value = cur_frame.expr_values.pop_front().unwrap();
 
                                    let deref_port_value = self.store.maybe_read_ref(&port_value).clone();
 
                                    let msg_value = cur_frame.expr_values.pop_front().unwrap();
 
                                    let deref_msg_value = self.store.maybe_read_ref(&msg_value).clone();
 

	
 
                                    match deref_msg_value {
 
                                        Value::Message(_) => {},
 
                                        _ => {
 
                                            return Err(EvalError::new_error_at_expr(
 
                                                self, modules, heap, expr_id,
 
                                                String::from("Calls to `put` are currently restricted to only send instances of `msg` types. This will change in the future")
 
                                            ));
 
                                        }
 
                                    }
 

	
 
                                    if ctx.did_put(deref_port_value.clone()) {
 
                                        // We're fine, deallocate in case the expression value stack
 
                                        // held an owned value
 
                                        self.store.drop_value(msg_value.get_heap_pos());
 
                                    } else {
 
                                        cur_frame.expr_values.push_front(msg_value);
 
                                        cur_frame.expr_values.push_front(port_value);
 
                                        cur_frame.expr_stack.push_back(ExprInstruction::EvalExpr(expr_id));
 
                                        return Ok(EvalContinuation::Put(deref_port_value, deref_msg_value));
 
                                    }
 
                                },
 
                                Method::Fires => {
src/protocol/parser/pass_definitions.rs
Show inline comments
 
@@ -852,25 +852,26 @@ impl PassDefinitions {
 

	
 
                // Allocate the initial assignment
 
                let variable_expr_id = ctx.heap.alloc_variable_expression(|this| VariableExpression{
 
                    this,
 
                    identifier,
 
                    declaration: None,
 
                    used_as_binding_target: false,
 
                    parent: ExpressionParent::None,
 
                    unique_id_in_definition: -1,
 
                });
 
                let assignment_expr_id = ctx.heap.alloc_assignment_expression(|this| AssignmentExpression{
 
                    this,
 
                    span: assign_span,
 
                    operator_span: assign_span,
 
                    full_span: InputSpan::from_positions(memory_span.begin, initial_expr_end_pos),
 
                    left: variable_expr_id.upcast(),
 
                    operation: AssignmentOperator::Set,
 
                    right: initial_expr_id,
 
                    parent: ExpressionParent::None,
 
                    unique_id_in_definition: -1,
 
                });
 
                let assignment_stmt_id = ctx.heap.alloc_expression_statement(|this| ExpressionStatement{
 
                    this,
 
                    span: InputSpan::from_positions(initial_expr_begin_pos, initial_expr_end_pos),
 
                    expression: assignment_expr_id.upcast(),
 
                    next: StatementId::new_invalid(),
 
                });
 
@@ -935,54 +936,65 @@ impl PassDefinitions {
 
                TK::MinusEquals         => Some(AO::Subtracted),
 
                TK::ShiftLeftEquals     => Some(AO::ShiftedLeft),
 
                TK::ShiftRightEquals    => Some(AO::ShiftedRight),
 
                TK::AndEquals           => Some(AO::BitwiseAnded),
 
                TK::CaretEquals         => Some(AO::BitwiseXored),
 
                TK::OrEquals            => Some(AO::BitwiseOred),
 
                _                       => None
 
            }
 
        }
 

	
 
        let expr = self.consume_conditional_expression(module, iter, ctx)?;
 
        if let Some(operation) = parse_assignment_operator(iter.next()) {
 
            let span = iter.next_span();
 
            let operator_span = iter.next_span();
 
            iter.consume();
 

	
 
            let left = expr;
 
            let right = self.consume_expression(module, iter, ctx)?;
 

	
 
            let full_span = InputSpan::from_positions(
 
                ctx.heap[left].full_span().begin,
 
                ctx.heap[right].full_span().end,
 
            );
 

	
 
            Ok(ctx.heap.alloc_assignment_expression(|this| AssignmentExpression{
 
                this, span, left, operation, right,
 
                this, operator_span, full_span, left, operation, right,
 
                parent: ExpressionParent::None,
 
                unique_id_in_definition: -1,
 
            }).upcast())
 
        } else {
 
            Ok(expr)
 
        }
 
    }
 

	
 
    fn consume_conditional_expression(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<ExpressionId, ParseError> {
 
        let result = self.consume_concat_expression(module, iter, ctx)?;
 
        if let Some(TokenKind::Question) = iter.next() {
 
            let span = iter.next_span();
 
            let operator_span = iter.next_span();
 
            iter.consume();
 

	
 
            let test = result;
 
            let true_expression = self.consume_expression(module, iter, ctx)?;
 
            consume_token(&module.source, iter, TokenKind::Colon)?;
 
            let false_expression = self.consume_expression(module, iter, ctx)?;
 

	
 
            let full_span = InputSpan::from_positions(
 
                ctx.heap[test].full_span().begin,
 
                ctx.heap[false_expression].full_span().end,
 
            );
 

	
 
            Ok(ctx.heap.alloc_conditional_expression(|this| ConditionalExpression{
 
                this, span, test, true_expression, false_expression,
 
                this, operator_span, full_span, test, true_expression, false_expression,
 
                parent: ExpressionParent::None,
 
                unique_id_in_definition: -1,
 
            }).upcast())
 
        } else {
 
            Ok(result)
 
        }
 
    }
 

	
 
    fn consume_concat_expression(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<ExpressionId, ParseError> {
 
        self.consume_generic_binary_expression(
 
@@ -1141,33 +1153,33 @@ impl PassDefinitions {
 
            use UnaryOperator as UO;
 
            match token {
 
                Some(TK::Plus) => Some(UO::Positive),
 
                Some(TK::Minus) => Some(UO::Negative),
 
                Some(TK::Tilde) => Some(UO::BitwiseNot),
 
                Some(TK::Exclamation) => Some(UO::LogicalNot),
 
                _ => None
 
            }
 
        }
 

	
 
        let next = iter.next();
 
        if let Some(operation) = parse_prefix_token(next) {
 
            let span = iter.next_span();
 
            let operator_span = iter.next_span();
 
            iter.consume();
 

	
 
            let expression = self.consume_prefix_expression(module, iter, ctx)?;
 
            let full_span = InputSpan::from_positions(
 
                operator_span.begin, ctx.heap[expression].full_span().end,
 
            );
 
            Ok(ctx.heap.alloc_unary_expression(|this| UnaryExpression {
 
                this,
 
                span,
 
                operation,
 
                expression,
 
                this, operator_span, full_span, operation, expression,
 
                parent: ExpressionParent::None,
 
                unique_id_in_definition: -1,
 
            }).upcast())
 
        } else if next == Some(TokenKind::PlusPlus) {
 
            return Err(ParseError::new_error_str_at_span(
 
                &module.source, iter.next_span(), "prefix increment is not supported in the language"
 
            ));
 
        } else if next == Some(TokenKind::MinusMinus) {
 
            return Err(ParseError::new_error_str_at_span(
 
                &module.source, iter.next_span(), "prefix decrement is not supported in this language"
 
            ));
 
        } else {
 
@@ -1183,75 +1195,87 @@ impl PassDefinitions {
 

	
 
            if token.is_none() { return false; }
 
            match token.unwrap() {
 
                TK::PlusPlus | TK::MinusMinus | TK::OpenSquare | TK::Dot => true,
 
                _ => false
 
            }
 
        }
 

	
 
        let mut result = self.consume_primary_expression(module, iter, ctx)?;
 
        let mut next = iter.next();
 
        while has_matching_postfix_token(next) {
 
            let token = next.unwrap();
 
            let mut span = iter.next_span();
 
            let mut operator_span = iter.next_span();
 
            iter.consume();
 

	
 
            if token == TokenKind::PlusPlus {
 
                return Err(ParseError::new_error_str_at_span(
 
                    &module.source, span, "postfix increment is not supported in this language"
 
                    &module.source, operator_span, "postfix increment is not supported in this language"
 
                ));
 
            } else if token == TokenKind::MinusMinus {
 
                return Err(ParseError::new_error_str_at_span(
 
                    &module.source, span, "prefix increment is not supported in this language"
 
                    &module.source, operator_span, "prefix increment is not supported in this language"
 
                ));
 
            } else if token == TokenKind::OpenSquare {
 
                let subject = result;
 
                let from_index = self.consume_expression(module, iter, ctx)?;
 

	
 
                // Check if we have an indexing or slicing operation
 
                next = iter.next();
 
                if Some(TokenKind::DotDot) == next {
 
                    iter.consume();
 

	
 
                    let to_index = self.consume_expression(module, iter, ctx)?;
 
                    let end_span = consume_token(&module.source, iter, TokenKind::CloseSquare)?;
 
                    span.end = end_span.end;
 
                    operator_span.end = end_span.end;
 
                    let full_span = InputSpan::from_positions(
 
                        ctx.heap[subject].full_span().begin, operator_span.end
 
                    );
 

	
 
                    result = ctx.heap.alloc_slicing_expression(|this| SlicingExpression{
 
                        this, span, subject, from_index, to_index,
 
                        this,
 
                        slicing_span: operator_span,
 
                        full_span, subject, from_index, to_index,
 
                        parent: ExpressionParent::None,
 
                        unique_id_in_definition: -1,
 
                    }).upcast();
 
                } else if Some(TokenKind::CloseSquare) == next {
 
                    let end_span = consume_token(&module.source, iter, TokenKind::CloseSquare)?;
 
                    span.end = end_span.end;
 
                    operator_span.end = end_span.end;
 

	
 
                    let full_span = InputSpan::from_positions(
 
                        ctx.heap[subject].full_span().begin, operator_span.end
 
                    );
 

	
 
                    result = ctx.heap.alloc_indexing_expression(|this| IndexingExpression{
 
                        this, span, subject,
 
                        this, operator_span, full_span, subject,
 
                        index: from_index,
 
                        parent: ExpressionParent::None,
 
                        unique_id_in_definition: -1,
 
                    }).upcast();
 
                } else {
 
                    return Err(ParseError::new_error_str_at_pos(
 
                        &module.source, iter.last_valid_pos(), "unexpected token: expected ']' or '..'"
 
                    ));
 
                }
 
            } else {
 
                debug_assert_eq!(token, TokenKind::Dot);
 
                let subject = result;
 
                let field_name = consume_ident_interned(&module.source, iter, ctx)?;
 

	
 
                let full_span = InputSpan::from_positions(
 
                    ctx.heap[subject].full_span().begin, field_name.span.end
 
                );
 
                result = ctx.heap.alloc_select_expression(|this| SelectExpression{
 
                    this, span, subject, field_name,
 
                    this, operator_span, full_span, subject, field_name,
 
                    parent: ExpressionParent::None,
 
                    unique_id_in_definition: -1,
 
                }).upcast();
 
            }
 

	
 
            next = iter.next();
 
        }
 

	
 
        Ok(result)
 
    }
 

	
 
    fn consume_primary_expression(
 
@@ -1399,29 +1423,32 @@ impl PassDefinitions {
 
                                    span: InputSpan::from_positions(ident_span.begin, end_pos),
 
                                    value: Literal::Union(LiteralUnion{
 
                                        parser_type, variant, values,
 
                                        definition: target_definition_id,
 
                                        variant_idx: 0,
 
                                    }),
 
                                    parent: ExpressionParent::None,
 
                                    unique_id_in_definition: -1,
 
                                }).upcast()
 
                            },
 
                            Definition::Component(_) => {
 
                                // Component instantiation
 
                                let arguments = self.consume_expression_list(module, iter, ctx, None)?;
 
                                let func_span = parser_type.full_span;
 
                                let mut full_span = func_span;
 
                                let arguments = self.consume_expression_list(
 
                                    module, iter, ctx, Some(&mut full_span.end)
 
                                )?;
 

	
 
                                ctx.heap.alloc_call_expression(|this| CallExpression{
 
                                    this,
 
                                    span: parser_type.full_span,
 
                                    this, func_span, full_span,
 
                                    parser_type,
 
                                    method: Method::UserComponent,
 
                                    arguments,
 
                                    definition: target_definition_id,
 
                                    parent: ExpressionParent::None,
 
                                    unique_id_in_definition: -1,
 
                                }).upcast()
 
                            },
 
                            Definition::Function(function_definition) => {
 
                                // Check whether it is a builtin function
 
                                let method = if function_definition.builtin {
 
                                    match function_definition.identifier.value.as_str() {
 
@@ -1429,32 +1456,32 @@ impl PassDefinitions {
 
                                        "put" => Method::Put,
 
                                        "fires" => Method::Fires,
 
                                        "create" => Method::Create,
 
                                        "length" => Method::Length,
 
                                        "assert" => Method::Assert,
 
                                        _ => unreachable!(),
 
                                    }
 
                                } else {
 
                                    Method::UserFunction
 
                                };
 

	
 
                                // Function call: consume the arguments
 
                                let arguments = self.consume_expression_list(module, iter, ctx, None)?;
 
                                let func_span = parser_type.full_span;
 
                                let mut full_span = func_span;
 
                                let arguments = self.consume_expression_list(
 
                                    module, iter, ctx, Some(&mut full_span.end)
 
                                )?;
 

	
 
                                ctx.heap.alloc_call_expression(|this| CallExpression{
 
                                    this,
 
                                    span: parser_type.full_span,
 
                                    parser_type,
 
                                    method,
 
                                    arguments,
 
                                    this, func_span, full_span, parser_type, method, arguments,
 
                                    definition: target_definition_id,
 
                                    parent: ExpressionParent::None,
 
                                    unique_id_in_definition: -1,
 
                                }).upcast()
 
                            }
 
                        }
 
                    },
 
                    _ => {
 
                        return Err(ParseError::new_error_str_at_span(
 
                            &module.source, parser_type.full_span, "unexpected type in expression"
 
                        ))
 
                    }
 
@@ -1472,36 +1499,37 @@ impl PassDefinitions {
 
                        _ => unreachable!(),
 
                    };
 

	
 
                    ctx.heap.alloc_literal_expression(|this| LiteralExpression {
 
                        this,
 
                        span: ident_span,
 
                        value,
 
                        parent: ExpressionParent::None,
 
                        unique_id_in_definition: -1,
 
                    }).upcast()
 
                } else if ident_text == KW_LET {
 
                    // Binding expression
 
                    let keyword_span = iter.next_span();
 
                    let operator_span = iter.next_span();
 
                    iter.consume();
 

	
 
                    let bound_to = self.consume_prefix_expression(module, iter, ctx)?;
 
                    consume_token(&module.source, iter, TokenKind::Equal)?;
 
                    let bound_from = self.consume_prefix_expression(module, iter, ctx)?;
 

	
 
                    let full_span = InputSpan::from_positions(
 
                        operator_span.begin, ctx.heap[bound_from].full_span().end,
 
                    );
 

	
 
                    ctx.heap.alloc_binding_expression(|this| BindingExpression{
 
                        this,
 
                        span: keyword_span,
 
                        bound_to,
 
                        bound_from,
 
                        this, operator_span, full_span, bound_to, bound_from,
 
                        parent: ExpressionParent::None,
 
                        unique_id_in_definition: -1,
 
                    }).upcast()
 
                } else if ident_text == KW_CAST {
 
                    // Casting expression
 
                    iter.consume();
 
                    let to_type = if Some(TokenKind::OpenAngle) == iter.next() {
 
                        iter.consume();
 
                        let definition_id = self.cur_definition;
 
                        let poly_vars = ctx.heap[definition_id].poly_vars();
 
                        consume_parser_type(
 
                            &module.source, iter, &ctx.symbols, &ctx.heap,
 
@@ -1512,31 +1540,32 @@ impl PassDefinitions {
 
                        // Automatic casting with inferred target type
 
                        ParserType{
 
                            elements: vec![ParserTypeElement{
 
                                element_span: ident_span,
 
                                variant: ParserTypeVariant::Inferred,
 
                            }],
 
                            full_span: ident_span
 
                        }
 
                    };
 

	
 
                    consume_token(&module.source, iter, TokenKind::OpenParen)?;
 
                    let subject = self.consume_expression(module, iter, ctx)?;
 
                    let mut full_span = iter.next_span();
 
                    full_span.begin = to_type.full_span.begin;
 
                    consume_token(&module.source, iter, TokenKind::CloseParen)?;
 

	
 
                    ctx.heap.alloc_cast_expression(|this| CastExpression{
 
                        this,
 
                        span: ident_span,
 
                        to_type,
 
                        subject,
 
                        cast_span: to_type.full_span,
 
                        full_span, to_type, subject,
 
                        parent: ExpressionParent::None,
 
                        unique_id_in_definition: -1,
 
                    }).upcast()
 
                } else {
 
                    // Not a builtin literal, but also not a known type. So we
 
                    // assume it is a variable expression. Although if we do,
 
                    // then if a programmer mistyped a struct/function name the
 
                    // error messages will be rather cryptic. For polymorphic
 
                    // arguments we can't really do anything at all (because it
 
                    // uses the '<' token). In the other cases we try to provide
 
                    // a better error message.
 
                    iter.consume();
 
@@ -1581,32 +1610,37 @@ impl PassDefinitions {
 
    // Expression Utilities
 
    //--------------------------------------------------------------------------
 

	
 
    #[inline]
 
    fn consume_generic_binary_expression<
 
        M: Fn(Option<TokenKind>) -> Option<BinaryOperator>,
 
        F: Fn(&mut PassDefinitions, &Module, &mut TokenIter, &mut PassCtx) -> Result<ExpressionId, ParseError>
 
    >(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx, match_fn: M, higher_precedence_fn: F
 
    ) -> Result<ExpressionId, ParseError> {
 
        let mut result = higher_precedence_fn(self, module, iter, ctx)?;
 
        while let Some(operation) = match_fn(iter.next()) {
 
            let span = iter.next_span();
 
            let operator_span = iter.next_span();
 
            iter.consume();
 

	
 
            let left = result;
 
            let right = higher_precedence_fn(self, module, iter, ctx)?;
 

	
 
            let full_span = InputSpan::from_positions(
 
                ctx.heap[left].full_span().begin,
 
                ctx.heap[right].full_span().end,
 
            );
 

	
 
            result = ctx.heap.alloc_binary_expression(|this| BinaryExpression{
 
                this, span, left, operation, right,
 
                this, operator_span, full_span, left, operation, right,
 
                parent: ExpressionParent::None,
 
                unique_id_in_definition: -1,
 
            }).upcast();
 
        }
 

	
 
        Ok(result)
 
    }
 

	
 
    #[inline]
 
    fn consume_expression_list(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx, end_pos: Option<&mut InputPosition>
 
    ) -> Result<Vec<ExpressionId>, ParseError> {
src/protocol/parser/pass_typing.rs
Show inline comments
 
@@ -1420,25 +1420,25 @@ impl PassTyping {
 
                    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.span(), format!(
 
                        &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)
 
                        )
 
                    ));
 
                }
 

	
 
                let mut concrete_type = ConcreteType::default();
 
                poly_type.write_concrete_type(&mut concrete_type);
 
                concrete.push(concrete_type);
 
            }
 

	
 
            Ok(concrete)
 
@@ -1447,25 +1447,25 @@ impl PassTyping {
 
        // Inference is now done. But we may still have uninferred types. So we
 
        // check for these.
 
        for (infer_expr_idx, infer_expr) in self.expr_types.iter_mut().enumerate() {
 
            let expr_type = &mut infer_expr.expr_type;
 
            if !expr_type.is_done {
 
                // Auto-infer numberlike/integerlike types to a regular int
 
                if expr_type.parts.len() == 1 && expr_type.parts[0] == InferenceTypePart::IntegerLike {
 
                    expr_type.parts[0] = InferenceTypePart::SInt32;
 
                    self.expr_queued.push_back(infer_expr_idx as i32);
 
                } else {
 
                    let expr = &ctx.heap[infer_expr.expr_id];
 
                    return Err(ParseError::new_error_at_span(
 
                        &ctx.module.source, expr.span(), format!(
 
                        &ctx.module.source, expr.full_span(), format!(
 
                            "could not fully infer the type of this expression (got '{}')",
 
                            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
 
@@ -2430,28 +2430,28 @@ impl PassTyping {
 
        } 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.span, "invalid casting operation"
 
                &ctx.module.source, cast_expr.full_span, "invalid casting operation"
 
            ).with_info_at_span(
 
                &ctx.module.source, subject_expr.span(), format!(
 
                    "cannot cast this type '{}' to the cast type '{}'",
 
                &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.
 
@@ -2772,27 +2772,27 @@ impl PassTyping {
 
        // 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].span();
 
            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].span()),
 
                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!(
 
@@ -3459,74 +3459,74 @@ impl PassTyping {
 
    fn construct_expr_type_error(
 
        &self, ctx: &Ctx, expr_id: ExpressionId, arg_id: ExpressionId
 
    ) -> 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;
 

	
 
        return ParseError::new_error_at_span(
 
            &ctx.module.source, expr.span(), format!(
 
            &ctx.module.source, expr.operation_span(), format!(
 
                "incompatible types: this expression expected a '{}'",
 
                expr_type.display_name(&ctx.heap)
 
            )
 
        ).with_info_at_span(
 
            &ctx.module.source, arg_expr.span(), format!(
 
            &ctx.module.source, arg_expr.full_span(), format!(
 
                "but this expression yields a '{}'",
 
                arg_type.display_name(&ctx.heap)
 
            )
 
        )
 
    }
 

	
 
    fn construct_arg_type_error(
 
        &self, ctx: &Ctx, expr_id: ExpressionId,
 
        arg1_id: ExpressionId, arg2_id: ExpressionId
 
    ) -> 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 arg2_idx = arg2.get_unique_id_in_definition();
 
        let arg2_type = &self.expr_types[arg2_idx as usize].expr_type;
 

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

	
 
    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;
 

	
 
        return ParseError::new_error_at_span(
 
            &ctx.module.source, expr.span(), format!(
 
            &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.
 
@@ -3584,43 +3584,43 @@ impl PassTyping {
 
            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 {
 
            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.span, format!(
 
                        &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);
 
                    return ParseError::new_error_at_span(
 
                        &ctx.module.source, expr.span, format!(
 
                        &ctx.module.source, expr.full_span, format!(
 
                            "Conflicting type for polymorphic variable '{}' while accessing field '{}' of '{}'",
 
                            poly_var, expr.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 {
 
@@ -3648,110 +3648,110 @@ impl PassTyping {
 
                    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.span(), format!(
 
                    &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() {
 
                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.span(), format!(
 
                            &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.span(), format!(
 
                            &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.span(), format!(
 
                            &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.span(), format!(
 
                        &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.span(), format!(
 
                        &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() {
 
            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.span(), format!(
 
                        &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.span(), format!(
 
                    &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?")
 
    }
 
}
 

	
src/protocol/parser/pass_validation_linking.rs
Show inline comments
 
@@ -454,74 +454,75 @@ impl Visitor for PassValidationLinking {
 
    //--------------------------------------------------------------------------
 
    // Expression visitors
 
    //--------------------------------------------------------------------------
 

	
 
    fn visit_assignment_expr(&mut self, ctx: &mut Ctx, id: AssignmentExpressionId) -> VisitorResult {
 
        let upcast_id = id.upcast();
 

	
 
        let assignment_expr = &mut ctx.heap[id];
 

	
 
        // Although we call assignment an expression to simplify the compiler's
 
        // code (mainly typechecking), we disallow nested use in expressions
 
        match self.expr_parent {
 
            // Look at us: lying through our teeth while providing error messages.
 
            ExpressionParent::ExpressionStmt(_) => {},
 
            _ => return Err(ParseError::new_error_str_at_span(
 
                &ctx.module.source, assignment_expr.span,
 
                "assignments may only appear at the statement level"
 
                &ctx.module.source, assignment_expr.full_span,
 
                "assignments are statements, and cannot be used in expressions"
 
            )),
 
        }
 

	
 
        let left_expr_id = assignment_expr.left;
 
        let right_expr_id = assignment_expr.right;
 
        let old_expr_parent = self.expr_parent;
 
        assignment_expr.parent = old_expr_parent;
 
        assignment_expr.unique_id_in_definition = self.next_expr_index;
 
        self.next_expr_index += 1;
 

	
 
        self.expr_parent = ExpressionParent::Expression(upcast_id, 0);
 
        self.must_be_assignable = Some(assignment_expr.span);
 
        self.must_be_assignable = Some(assignment_expr.operator_span);
 
        self.visit_expr(ctx, left_expr_id)?;
 
        self.expr_parent = ExpressionParent::Expression(upcast_id, 1);
 
        self.must_be_assignable = None;
 
        self.visit_expr(ctx, right_expr_id)?;
 
        self.expr_parent = old_expr_parent;
 
        Ok(())
 
    }
 

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

	
 
        // Check for valid context of binding expression
 
        if let Some(span) = self.must_be_assignable {
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module.source, span, "cannot assign to the result from a binding expression"
 
            ));
 
        }
 

	
 
        if self.in_test_expr.is_invalid() {
 
            let binding_expr = &ctx.heap[id];
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module.source, binding_expr.span,
 
                &ctx.module.source, binding_expr.full_span,
 
                "binding expressions can only be used inside the testing expression of 'if' and 'while' statements"
 
            ));
 
        }
 

	
 
        if !self.in_binding_expr.is_invalid() {
 
            let binding_expr = &ctx.heap[id];
 
            let previous_expr = &ctx.heap[self.in_binding_expr];
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module.source, binding_expr.span,
 
                &ctx.module.source, binding_expr.full_span,
 
                "nested binding expressions are not allowed"
 
            ).with_info_str_at_span(
 
                &ctx.module.source, previous_expr.span,
 
                &ctx.module.source, previous_expr.operator_span,
 
                "the outer binding expression is found here"
 
            ));
 
        }
 

	
 
        let mut seeking_parent = self.expr_parent;
 
        loop {
 
            // Perform upward search to make sure only LogicalAnd is applied to
 
            // the binding expression
 
            let valid = match seeking_parent {
 
                ExpressionParent::If(_) | ExpressionParent::While(_) => {
 
                    // Every parent expression (if any) were LogicalAnd.
 
                    break;
 
@@ -541,28 +542,28 @@ impl Visitor for PassValidationLinking {
 
                            }
 
                        },
 
                        _ => false,
 
                    }
 
                },
 
                _ => unreachable!(), // nested under if/while, so always expressions as parents
 
            };
 

	
 
            if !valid {
 
                let binding_expr = &ctx.heap[id];
 
                let parent_expr = &ctx.heap[seeking_parent.as_expression()];
 
                return Err(ParseError::new_error_str_at_span(
 
                    &ctx.module.source, binding_expr.span,
 
                    &ctx.module.source, binding_expr.full_span,
 
                    "only the logical-and operator (&&) may be applied to binding expressions"
 
                ).with_info_str_at_span(
 
                    &ctx.module.source, parent_expr.span(),
 
                    &ctx.module.source, parent_expr.operation_span(),
 
                    "this was the disallowed operation applied to the result from a binding expression"
 
                ));
 
            }
 
        }
 

	
 
        // Perform all of the index/parent assignment magic
 
        let binding_expr = &mut ctx.heap[id];
 

	
 
        let old_expr_parent = self.expr_parent;
 
        binding_expr.parent = old_expr_parent;
 
        binding_expr.unique_id_in_definition = self.next_expr_index;
 
        self.next_expr_index += 1;
 
@@ -574,25 +575,25 @@ impl Visitor for PassValidationLinking {
 
        let bound_to_id = binding_expr.bound_to;
 
        let bound_from_id = binding_expr.bound_from;
 

	
 
        match &ctx.heap[bound_to_id] {
 
            // Variables may not be binding variables, and literals may
 
            // actually not contain binding variables. But in that case we just
 
            // perform an equality check.
 
            Expression::Variable(_) => {}
 
            Expression::Literal(_) => {},
 
            _ => {
 
                let binding_expr = &ctx.heap[id];
 
                return Err(ParseError::new_error_str_at_span(
 
                    &ctx.module.source, binding_expr.span,
 
                    &ctx.module.source, binding_expr.operator_span,
 
                    "the left hand side of a binding expression may only be a variable or a literal expression"
 
                ));
 
            },
 
        }
 

	
 
        // Visit the children themselves
 
        self.in_binding_expr_lhs = true;
 
        self.expr_parent = ExpressionParent::Expression(upcast_id, 0);
 
        self.visit_expr(ctx, bound_to_id)?;
 
        self.in_binding_expr_lhs = false;
 
        self.expr_parent = ExpressionParent::Expression(upcast_id, 1);
 
        self.visit_expr(ctx, bound_from_id)?;
 
@@ -971,114 +972,114 @@ impl Visitor for PassValidationLinking {
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module.source, span, "cannot assign to the result from a call expression"
 
            ))
 
        }
 

	
 
        // Check whether the method is allowed to be called within the code's
 
        // context (in sync, definition type, etc.)
 
        let mut expected_wrapping_new_stmt = false;
 
        match &mut call_expr.method {
 
            Method::Get => {
 
                if !self.def_type.is_primitive() {
 
                    return Err(ParseError::new_error_str_at_span(
 
                        &ctx.module.source, call_expr.span,
 
                        &ctx.module.source, call_expr.func_span,
 
                        "a call to 'get' may only occur in primitive component definitions"
 
                    ));
 
                }
 
                if self.in_sync.is_invalid() {
 
                    return Err(ParseError::new_error_str_at_span(
 
                        &ctx.module.source, call_expr.span,
 
                        &ctx.module.source, call_expr.func_span,
 
                        "a call to 'get' may only occur inside synchronous blocks"
 
                    ));
 
                }
 
            },
 
            Method::Put => {
 
                if !self.def_type.is_primitive() {
 
                    return Err(ParseError::new_error_str_at_span(
 
                        &ctx.module.source, call_expr.span,
 
                        &ctx.module.source, call_expr.func_span,
 
                        "a call to 'put' may only occur in primitive component definitions"
 
                    ));
 
                }
 
                if self.in_sync.is_invalid() {
 
                    return Err(ParseError::new_error_str_at_span(
 
                        &ctx.module.source, call_expr.span,
 
                        &ctx.module.source, call_expr.func_span,
 
                        "a call to 'put' may only occur inside synchronous blocks"
 
                    ));
 
                }
 
            },
 
            Method::Fires => {
 
                if !self.def_type.is_primitive() {
 
                    return Err(ParseError::new_error_str_at_span(
 
                        &ctx.module.source, call_expr.span,
 
                        &ctx.module.source, call_expr.func_span,
 
                        "a call to 'fires' may only occur in primitive component definitions"
 
                    ));
 
                }
 
                if self.in_sync.is_invalid() {
 
                    return Err(ParseError::new_error_str_at_span(
 
                        &ctx.module.source, call_expr.span,
 
                        &ctx.module.source, call_expr.func_span,
 
                        "a call to 'fires' may only occur inside synchronous blocks"
 
                    ));
 
                }
 
            },
 
            Method::Create => {},
 
            Method::Length => {},
 
            Method::Assert => {
 
                if self.def_type.is_function() {
 
                    return Err(ParseError::new_error_str_at_span(
 
                        &ctx.module.source, call_expr.span,
 
                        &ctx.module.source, call_expr.func_span,
 
                        "assert statement may only occur in components"
 
                    ));
 
                }
 
                if self.in_sync.is_invalid() {
 
                    return Err(ParseError::new_error_str_at_span(
 
                        &ctx.module.source, call_expr.span,
 
                        &ctx.module.source, call_expr.func_span,
 
                        "assert statements may only occur inside synchronous blocks"
 
                    ));
 
                }
 
            },
 
            Method::UserFunction => {},
 
            Method::UserComponent => {
 
                expected_wrapping_new_stmt = true;
 
            },
 
        }
 

	
 
        if expected_wrapping_new_stmt {
 
            if !self.expr_parent.is_new() {
 
                return Err(ParseError::new_error_str_at_span(
 
                    &ctx.module.source, call_expr.span,
 
                    &ctx.module.source, call_expr.func_span,
 
                    "cannot call a component, it can only be instantiated by using 'new'"
 
                ));
 
            }
 
        } else {
 
            if self.expr_parent.is_new() {
 
                return Err(ParseError::new_error_str_at_span(
 
                    &ctx.module.source, call_expr.span,
 
                    &ctx.module.source, call_expr.func_span,
 
                    "only components can be instantiated, this is a function"
 
                ));
 
            }
 
        }
 

	
 
        // Check the number of arguments
 
        let call_definition = ctx.types.get_base_definition(&call_expr.definition).unwrap();
 
        let num_expected_args = match &call_definition.definition {
 
            DefinedTypeVariant::Function(definition) => definition.arguments.len(),
 
            DefinedTypeVariant::Component(definition) => definition.arguments.len(),
 
            v => unreachable!("encountered {} type in call expression", v.type_class()),
 
        };
 

	
 
        let num_provided_args = call_expr.arguments.len();
 
        if num_provided_args != num_expected_args {
 
            let argument_text = if num_expected_args == 1 { "argument" } else { "arguments" };
 
            return Err(ParseError::new_error_at_span(
 
                &ctx.module.source, call_expr.span, format!(
 
                &ctx.module.source, call_expr.full_span, format!(
 
                    "expected {} {}, but {} were provided",
 
                    num_expected_args, argument_text, num_provided_args
 
                )
 
            ));
 
        }
 

	
 
        // Recurse into all of the arguments and set the expression's parent
 
        let upcast_id = id.upcast();
 

	
 
        let section = self.expression_buffer.start_section_initialized(&call_expr.arguments);
 
        let old_expr_parent = self.expr_parent;
 
        call_expr.parent = old_expr_parent;
 
@@ -1144,25 +1145,25 @@ impl Visitor for PassValidationLinking {
 
                    },
 
                    _ => {
 
                        false
 
                    }
 
                };
 

	
 
                if !is_valid_binding {
 
                    let binding_expr = &ctx.heap[self.in_binding_expr];
 
                    return Err(ParseError::new_error_str_at_span(
 
                        &ctx.module.source, var_expr.identifier.span,
 
                        "illegal location for binding variable: binding variables may only be nested under a binding expression, or a struct, union or array literal"
 
                    ).with_info_at_span(
 
                        &ctx.module.source, binding_expr.span, format!(
 
                        &ctx.module.source, binding_expr.operator_span, format!(
 
                            "'{}' was interpreted as a binding variable because the variable is not declared and it is nested under this binding expression",
 
                            var_expr.identifier.value.as_str()
 
                        )
 
                    ));
 
                }
 

	
 
                // By now we know that this is a valid binding expression. Given
 
                // that a binding expression must be nested under an if/while
 
                // statement, we now add the variable to the (implicit) block
 
                // statement following the if/while statement.
 
                let bound_identifier = var_expr.identifier.clone();
 
                let bound_variable_id = ctx.heap.alloc_variable(|this| Variable{
src/protocol/tests/parser_inference.rs
Show inline comments
 
@@ -379,42 +379,42 @@ fn test_failed_polymorph_inference() {
 
        "
 
        enum Uninteresting<T>{ Variant }
 
        func fix_t<T>(Uninteresting<T> arg) -> s32 { return 0; }
 
        func call() -> s32 {
 
            auto a = Uninteresting::Variant;
 
            fix_t<s8>(a);
 
            fix_t<s32>(a);
 
            return 4;
 
        }
 
        "
 
    ).error(|e| { e
 
        .assert_num(2)
 
        .assert_any_msg_has("type 'Uninteresting<s8>'")
 
        .assert_any_msg_has("type 'Uninteresting<s32>'");
 
        .assert_msg_has(0, "type 'Uninteresting<s8>'")
 
        .assert_msg_has(1, "type 'Uninteresting<s32>'");
 
    });
 

	
 
    Tester::new_single_source_expect_err(
 
        "field access inference mismatch",
 
        "
 
        struct Holder<Shazam>{ Shazam a }
 
        func call() -> s32 {
 
            s8 to_hold = 0;
 
            auto holder = Holder{ a: to_hold };
 
            return holder.a;
 
        }
 
        "
 
    ).error(|e| { e
 
        .assert_num(3)
 
        .assert_ctx_has(0, "holder.a")
 
        .assert_occurs_at(0, ".")
 
        .assert_occurs_at(0, "holder.a")
 
        .assert_msg_has(0, "Conflicting type for polymorphic variable 'Shazam'")
 
        .assert_msg_has(1, "inferred it to 's8'")
 
        .assert_msg_has(2, "inferred it to 's32'");
 
    });
 

	
 
    // Silly regression test
 
    Tester::new_single_source_expect_err(
 
        "nested field access inference mismatch",
 
        "
 
        struct Node<T1, T2>{ T1 l, T2 r }
 
        func construct<T1, T2>(T1 l, T2 r) -> Node<T1, T2> { return Node{ l: l, r: r }; }
 
        func fix_poly<T>(Node<T, T> a) -> s32 { return 0; }
src/protocol/tests/utils.rs
Show inline comments
 
@@ -560,25 +560,25 @@ impl<'a> FunctionTester<'a> {
 
        }
 

	
 
        assert!(
 
            outer_match_idx < module.source.input.len(),
 
            "[{}] Failed to find '{}' within the source that contains {}",
 
            self.ctx.test_name, outer_match, self.assert_postfix()
 
        );
 
        let inner_match_idx = outer_match_idx + outer_match.find(inner_match).unwrap();
 

	
 
        // Use the inner match index to find the expression
 
        let expr_id = seek_expr_in_stmt(
 
            &self.ctx.heap, self.def.body.upcast(),
 
            &|expr| expr.span().begin.offset as usize == inner_match_idx
 
            &|expr| expr.operation_span().begin.offset as usize == inner_match_idx
 
        );
 
        assert!(
 
            expr_id.is_some(),
 
            "[{}] Failed to find '{}' within the source that contains {} \
 
            (note: expression was found, but not within the specified function",
 
            self.ctx.test_name, outer_match, self.assert_postfix()
 
        );
 
        let expr_id = expr_id.unwrap();
 

	
 
        // We have the expression, call the testing function
 
        let tester = ExpressionTester::new(
 
            self.ctx, self.def.this.upcast(), &self.ctx.heap[expr_id]
 
@@ -805,44 +805,24 @@ impl<'a> ErrorTester<'a> {
 
    }
 

	
 
    pub(crate) fn assert_msg_has(self, idx: usize, msg: &str) -> Self {
 
        assert!(
 
            self.error.statements[idx].message.contains(msg),
 
            "[{}] expected error statement {}'s message to contain '{}' for {}",
 
            self.test_name, idx, msg, self.assert_postfix()
 
        );
 

	
 
        self
 
    }
 

	
 
    // TODO: @tokenizer This should really be removed, as compilation should be
 
    //  deterministic, but we're currently using rather inefficient hashsets for
 
    //  the type inference, so remove once compiler architecture has changed.
 
    pub(crate) fn assert_any_msg_has(self, msg: &str) -> Self {
 
        let mut is_present = false;
 
        for statement in &self.error.statements {
 
            if statement.message.contains(msg) {
 
                is_present = true;
 
                break;
 
            }
 
        }
 

	
 
        assert!(
 
            is_present, "[{}] Expected an error statement to contain '{}' for {}",
 
            self.test_name, msg, self.assert_postfix()
 
        );
 
        
 
        self
 
    }
 

	
 
    /// Seeks the index of the pattern in the context message, then checks if
 
    /// the input position corresponds to that index.
 
    pub (crate) fn assert_occurs_at(self, idx: usize, pattern: &str) -> Self {
 
        let pos = self.error.statements[idx].context.find(pattern);
 
        assert!(
 
            pos.is_some(),
 
            "[{}] incorrect occurs_at: '{}' could not be found in the context for {}",
 
            self.test_name, pattern, self.assert_postfix()
 
        );
 
        let pos = pos.unwrap();
 
        let col = self.error.statements[idx].start_column as usize;
 
        assert_eq!(
0 comments (0 inline, 0 general)