Changeset - f39f9c5e5873
[Not reviewed]
0 12 0
mh - 3 years ago 2022-02-10 09:35:28
contact@maxhenger.nl
WIP: Refactored scopes in AST, pending bugfixes
12 files changed with 633 insertions and 656 deletions:
0 comments (0 inline, 0 general)
src/protocol/ast.rs
Show inline comments
 
@@ -137,73 +137,77 @@ define_new_ast_id!(SynchronousStatementId, StatementId, index(SynchronousStateme
 
define_new_ast_id!(EndSynchronousStatementId, StatementId, index(EndSynchronousStatement, Statement::EndSynchronous, statements), alloc(alloc_end_synchronous_statement));
 
define_new_ast_id!(ForkStatementId, StatementId, index(ForkStatement, Statement::Fork, statements), alloc(alloc_fork_statement));
 
define_new_ast_id!(EndForkStatementId, StatementId, index(EndForkStatement, Statement::EndFork, statements), alloc(alloc_end_fork_statement));
 
define_new_ast_id!(SelectStatementId, StatementId, index(SelectStatement, Statement::Select, statements), alloc(alloc_select_statement));
 
define_new_ast_id!(EndSelectStatementId, StatementId, index(EndSelectStatement, Statement::EndSelect, statements), alloc(alloc_end_select_statement));
 
define_new_ast_id!(ReturnStatementId, StatementId, index(ReturnStatement, Statement::Return, statements), alloc(alloc_return_statement));
 
define_new_ast_id!(GotoStatementId, StatementId, index(GotoStatement, Statement::Goto, statements), alloc(alloc_goto_statement));
 
define_new_ast_id!(NewStatementId, StatementId, index(NewStatement, Statement::New, statements), alloc(alloc_new_statement));
 
define_new_ast_id!(ExpressionStatementId, StatementId, index(ExpressionStatement, Statement::Expression, statements), alloc(alloc_expression_statement));
 

	
 
define_aliased_ast_id!(ExpressionId, Id<Expression>, index(Expression, expressions));
 
define_new_ast_id!(AssignmentExpressionId, ExpressionId, index(AssignmentExpression, Expression::Assignment, expressions), alloc(alloc_assignment_expression));
 
define_new_ast_id!(BindingExpressionId, ExpressionId, index(BindingExpression, Expression::Binding, expressions), alloc(alloc_binding_expression));
 
define_new_ast_id!(ConditionalExpressionId, ExpressionId, index(ConditionalExpression, Expression::Conditional, expressions), alloc(alloc_conditional_expression));
 
define_new_ast_id!(BinaryExpressionId, ExpressionId, index(BinaryExpression, Expression::Binary, expressions), alloc(alloc_binary_expression));
 
define_new_ast_id!(UnaryExpressionId, ExpressionId, index(UnaryExpression, Expression::Unary, expressions), alloc(alloc_unary_expression));
 
define_new_ast_id!(IndexingExpressionId, ExpressionId, index(IndexingExpression, Expression::Indexing, expressions), alloc(alloc_indexing_expression));
 
define_new_ast_id!(SlicingExpressionId, ExpressionId, index(SlicingExpression, Expression::Slicing, expressions), alloc(alloc_slicing_expression));
 
define_new_ast_id!(SelectExpressionId, ExpressionId, index(SelectExpression, Expression::Select, expressions), alloc(alloc_select_expression));
 
define_new_ast_id!(LiteralExpressionId, ExpressionId, index(LiteralExpression, Expression::Literal, expressions), alloc(alloc_literal_expression));
 
define_new_ast_id!(CastExpressionId, ExpressionId, index(CastExpression, Expression::Cast, expressions), alloc(alloc_cast_expression));
 
define_new_ast_id!(CallExpressionId, ExpressionId, index(CallExpression, Expression::Call, expressions), alloc(alloc_call_expression));
 
define_new_ast_id!(VariableExpressionId, ExpressionId, index(VariableExpression, Expression::Variable, expressions), alloc(alloc_variable_expression));
 

	
 
define_aliased_ast_id!(ScopeId, Id<Scope>, index(Scope, scopes), alloc(alloc_scope));
 

	
 
#[derive(Debug)]
 
pub struct Heap {
 
    // Root arena, contains the entry point for different modules. Each root
 
    // contains lists of IDs that correspond to the other arenas.
 
    pub(crate) protocol_descriptions: Arena<Root>,
 
    // Contents of a file, these are the elements the `Root` elements refer to
 
    pragmas: Arena<Pragma>,
 
    pub(crate) imports: Arena<Import>,
 
    pub(crate) variables: Arena<Variable>,
 
    pub(crate) definitions: Arena<Definition>,
 
    pub(crate) statements: Arena<Statement>,
 
    pub(crate) expressions: Arena<Expression>,
 
    pub(crate) scopes: Arena<Scope>,
 
}
 

	
 
impl Heap {
 
    pub fn new() -> Heap {
 
        Heap {
 
            // string_alloc: StringAllocator::new(),
 
            protocol_descriptions: Arena::new(),
 
            pragmas: Arena::new(),
 
            imports: Arena::new(),
 
            variables: Arena::new(),
 
            definitions: Arena::new(),
 
            statements: Arena::new(),
 
            expressions: Arena::new(),
 
            scopes: Arena::new(),
 
        }
 
    }
 
    pub fn alloc_memory_statement(
 
        &mut self,
 
        f: impl FnOnce(MemoryStatementId) -> MemoryStatement,
 
    ) -> MemoryStatementId {
 
        MemoryStatementId(LocalStatementId(self.statements.alloc_with_id(|id| {
 
            Statement::Local(LocalStatement::Memory(
 
                f(MemoryStatementId(LocalStatementId(id)))
 
            ))
 
        })))
 
    }
 
    pub fn alloc_channel_statement(
 
        &mut self,
 
        f: impl FnOnce(ChannelStatementId) -> ChannelStatement,
 
    ) -> ChannelStatementId {
 
        ChannelStatementId(LocalStatementId(self.statements.alloc_with_id(|id| {
 
            Statement::Local(LocalStatement::Channel(
 
                f(ChannelStatementId(LocalStatementId(id)))
 
            ))
 
        })))
 
    }
 
}
 

	
 
@@ -684,118 +688,108 @@ impl<'a> ConcreteTypeIter<'a> {
 
        }
 
    }
 
}
 

	
 
impl<'a> Iterator for ConcreteTypeIter<'a> {
 
    type Item = &'a [ConcreteTypePart];
 

	
 
    fn next(&mut self) -> Option<Self::Item> {
 
        if self.idx_embedded == self.num_embedded {
 
            return None;
 
        }
 

	
 
        // Retrieve the subtree of interest
 
        let start_idx = self.part_idx;
 
        let end_idx = ConcreteType::type_parts_subtree_end_idx(&self.parts, start_idx);
 

	
 
        self.idx_embedded += 1;
 
        self.part_idx = end_idx;
 

	
 
        return Some(&self.parts[start_idx..end_idx]);
 
    }
 
}
 

	
 
#[derive(Debug, Clone, Copy)]
 
pub enum Scope {
 
pub enum ScopeAssociation {
 
    Definition(DefinitionId),
 
    Regular(BlockStatementId),
 
    Synchronous(SynchronousStatementId, BlockStatementId),
 
}
 

	
 
impl Scope {
 
    pub(crate) fn new_invalid() -> Scope {
 
        return Scope::Definition(DefinitionId::new_invalid());
 
    }
 

	
 
    pub(crate) fn is_invalid(&self) -> bool {
 
        match self {
 
            Scope::Definition(id) => id.is_invalid(),
 
            _ => false,
 
        }
 
    }
 

	
 
    pub fn is_block(&self) -> bool {
 
        match &self {
 
            Scope::Definition(_) => false,
 
            Scope::Regular(_) => true,
 
            Scope::Synchronous(_, _) => true,
 
        }
 
    }
 
    pub fn to_block(&self) -> BlockStatementId {
 
        match &self {
 
            Scope::Regular(id) => *id,
 
            Scope::Synchronous(_, id) => *id,
 
            _ => panic!("unable to get BlockStatement from Scope")
 
        }
 
    }
 
    Block(BlockStatementId),
 
    If(IfStatementId, bool), // if true, then body of "if", otherwise body of "else"
 
    While(WhileStatementId),
 
    Synchronous(SynchronousStatementId),
 
    SelectCase(SelectStatementId, u32), // index is select case
 
}
 

	
 
/// `ScopeNode` is a helper that links scopes in two directions. It doesn't
 
/// actually contain any information associated with the scope, this may be
 
/// found on the AST elements that `Scope` points to.
 
#[derive(Debug, Clone)]
 
pub struct ScopeNode {
 
    pub parent: Scope,
 
    pub nested: Vec<Scope>,
 
pub struct Scope {
 
    // Relation to other scopes
 
    pub this: ScopeId,
 
    pub parent: Option<ScopeId>,
 
    pub nested: Vec<ScopeId>,
 
    // Locally available variables/labels
 
    pub association: ScopeAssociation,
 
    pub variables: Vec<VariableId>,
 
    pub labels: Vec<LabeledStatementId>,
 
    // Location trackers/counters
 
    pub relative_pos_in_parent: i32,
 
    pub first_unique_id_in_scope: i32,
 
    pub next_unique_id_in_scope: i32,
 
}
 

	
 
impl ScopeNode {
 
    pub(crate) fn new_invalid() -> Self {
 
        ScopeNode{
 
            parent: Scope::new_invalid(),
 
impl Scope {
 
    pub(crate) fn new_invalid(this: ScopeId) -> Self {
 
        return Self{
 
            this,
 
            parent: None,
 
            nested: Vec::new(),
 
            association: ScopeAssociation::Definition(DefinitionId::new_invalid()),
 
            variables: Vec::new(),
 
            labels: Vec::new(),
 
            relative_pos_in_parent: -1,
 
        }
 
            first_unique_id_in_scope: -1,
 
            next_unique_id_in_scope: -1,
 
        };
 
    }
 
}
 

	
 
#[derive(Debug, Clone, PartialEq, Eq)]
 
pub enum VariableKind {
 
    Parameter,      // in parameter list of function/component
 
    Local,          // declared in function/component body
 
    Binding,        // may be bound to in a binding expression (determined in validator/linker)
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct Variable {
 
    pub this: VariableId,
 
    // Parsing
 
    pub kind: VariableKind,
 
    pub parser_type: ParserType,
 
    pub identifier: Identifier,
 
    // Validator/linker
 
    pub relative_pos_in_block: i32,
 
    pub relative_pos_in_parent: i32,
 
    pub unique_id_in_scope: i32, // Temporary fix until proper bytecode/asm is generated
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub enum Definition {
 
    Struct(StructDefinition),
 
    Enum(EnumDefinition),
 
    Union(UnionDefinition),
 
    Component(ComponentDefinition),
 
    Function(FunctionDefinition),
 
}
 

	
 
impl Definition {
 
    pub fn is_struct(&self) -> bool {
 
        match self {
 
            Definition::Struct(_) => true,
 
            _ => false
 
        }
 
    }
 
    pub(crate) fn as_struct(&self) -> &StructDefinition {
 
        match self {
 
            Definition::Struct(result) => result,
 
            _ => panic!("Unable to cast 'Definition' to 'StructDefinition'"),
 
        }
 
@@ -999,98 +993,102 @@ impl UnionDefinition {
 
        this: UnionDefinitionId, defined_in: RootId, span: InputSpan,
 
        identifier: Identifier, poly_vars: Vec<Identifier>
 
    ) -> Self {
 
        Self{ this, defined_in, span, identifier, poly_vars, variants: Vec::new() }
 
    }
 
}
 

	
 
#[derive(Debug, Clone, Copy)]
 
pub enum ComponentVariant {
 
    Primitive,
 
    Composite,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct ComponentDefinition {
 
    pub this: ComponentDefinitionId,
 
    pub defined_in: RootId,
 
    // Symbol scanning
 
    pub span: InputSpan,
 
    pub variant: ComponentVariant,
 
    pub identifier: Identifier,
 
    pub poly_vars: Vec<Identifier>,
 
    // Parsing
 
    pub parameters: Vec<VariableId>,
 
    pub scope: ScopeId,
 
    pub body: BlockStatementId,
 
    // Validation/linking
 
    pub num_expressions_in_body: i32,
 
}
 

	
 
impl ComponentDefinition {
 
    // Used for preallocation during symbol scanning
 
    pub(crate) fn new_empty(
 
        this: ComponentDefinitionId, defined_in: RootId, span: InputSpan,
 
        variant: ComponentVariant, identifier: Identifier, poly_vars: Vec<Identifier>
 
    ) -> Self {
 
        Self{ 
 
            this, defined_in, span, variant, identifier, poly_vars,
 
            parameters: Vec::new(), 
 
            parameters: Vec::new(),
 
            scope: ScopeId::new_invalid(),
 
            body: BlockStatementId::new_invalid(),
 
            num_expressions_in_body: -1,
 
        }
 
    }
 
}
 

	
 
// Note that we will have function definitions for builtin functions as well. In
 
// that case the span, the identifier span and the body are all invalid.
 
#[derive(Debug, Clone)]
 
pub struct FunctionDefinition {
 
    pub this: FunctionDefinitionId,
 
    pub defined_in: RootId,
 
    // Symbol scanning
 
    pub builtin: bool,
 
    pub span: InputSpan,
 
    pub identifier: Identifier,
 
    pub poly_vars: Vec<Identifier>,
 
    // Parser
 
    pub return_types: Vec<ParserType>,
 
    pub return_type: ParserType,
 
    pub parameters: Vec<VariableId>,
 
    pub scope: ScopeId,
 
    pub body: BlockStatementId,
 
    // Validation/linking
 
    pub num_expressions_in_body: i32,
 
}
 

	
 
impl FunctionDefinition {
 
    pub(crate) fn new_empty(
 
        this: FunctionDefinitionId, defined_in: RootId, span: InputSpan,
 
        identifier: Identifier, poly_vars: Vec<Identifier>
 
    ) -> Self {
 
        Self {
 
            this, defined_in,
 
            builtin: false,
 
            span, identifier, poly_vars,
 
            return_types: Vec::new(),
 
            return_type: ParserType{ elements: Vec::new(), full_span: InputSpan::new() },
 
            parameters: Vec::new(),
 
            scope: ScopeId::new_invalid(),
 
            body: BlockStatementId::new_invalid(),
 
            num_expressions_in_body: -1,
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub enum Statement {
 
    Block(BlockStatement),
 
    EndBlock(EndBlockStatement),
 
    Local(LocalStatement),
 
    Labeled(LabeledStatement),
 
    If(IfStatement),
 
    EndIf(EndIfStatement),
 
    While(WhileStatement),
 
    EndWhile(EndWhileStatement),
 
    Break(BreakStatement),
 
    Continue(ContinueStatement),
 
    Synchronous(SynchronousStatement),
 
    EndSynchronous(EndSynchronousStatement),
 
    Fork(ForkStatement),
 
    EndFork(EndForkStatement),
 
    Select(SelectStatement),
 
    EndSelect(EndSelectStatement),
 
@@ -1157,64 +1155,61 @@ impl Statement {
 
            Statement::EndBlock(stmt) => stmt.next = next,
 
            Statement::Local(stmt) => match stmt {
 
                LocalStatement::Channel(stmt) => stmt.next = next,
 
                LocalStatement::Memory(stmt) => stmt.next = next,
 
            },
 
            Statement::EndIf(stmt) => stmt.next = next,
 
            Statement::EndWhile(stmt) => stmt.next = next,
 
            Statement::EndSynchronous(stmt) => stmt.next = next,
 
            Statement::EndFork(stmt) => stmt.next = next,
 
            Statement::EndSelect(stmt) => stmt.next = next,
 
            Statement::New(stmt) => stmt.next = next,
 
            Statement::Expression(stmt) => stmt.next = next,
 
            Statement::Return(_)
 
            | Statement::Break(_)
 
            | Statement::Continue(_)
 
            | Statement::Synchronous(_)
 
            | Statement::Fork(_)
 
            | Statement::Select(_)
 
            | Statement::Goto(_)
 
            | Statement::While(_)
 
            | Statement::Labeled(_)
 
            | Statement::If(_) => unreachable!(),
 
        }
 
    }
 

	
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct BlockStatement {
 
    pub this: BlockStatementId,
 
    // Phase 1: parser
 
    pub is_implicit: bool,
 
    pub span: InputSpan, // of the complete block
 
    pub statements: Vec<StatementId>,
 
    pub end_block: EndBlockStatementId,
 
    // Phase 2: linker
 
    pub scope_node: ScopeNode,
 
    pub first_unique_id_in_scope: i32, // Temporary fix until proper bytecode/asm is generated
 
    pub next_unique_id_in_scope: i32, // Temporary fix until proper bytecode/asm is generated
 
    pub locals: Vec<VariableId>,
 
    pub labels: Vec<LabeledStatementId>,
 
    pub scope: ScopeId,
 
    pub next: StatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct EndBlockStatement {
 
    pub this: EndBlockStatementId,
 
    // Parser
 
    pub start_block: BlockStatementId,
 
    // Validation/Linking
 
    pub next: StatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub enum LocalStatement {
 
    Memory(MemoryStatement),
 
    Channel(ChannelStatement),
 
}
 

	
 
impl LocalStatement {
 
    pub fn this(&self) -> LocalStatementId {
 
        match self {
 
            LocalStatement::Memory(stmt) => stmt.this.upcast(),
 
            LocalStatement::Channel(stmt) => stmt.this.upcast(),
 
        }
 
@@ -1242,168 +1237,177 @@ impl LocalStatement {
 
#[derive(Debug, Clone)]
 
pub struct MemoryStatement {
 
    pub this: MemoryStatementId,
 
    // Phase 1: parser
 
    pub span: InputSpan,
 
    pub variable: VariableId,
 
    pub initial_expr: AssignmentExpressionId,
 
    // Phase 2: linker
 
    pub next: StatementId,
 
}
 

	
 
/// ChannelStatement is the declaration of an input and output port associated
 
/// with the same channel. Note that the polarity of the ports are from the
 
/// point of view of the component. So an output port is something that a
 
/// component uses to send data over (i.e. it is the "input end" of the
 
/// channel), and vice versa.
 
#[derive(Debug, Clone)]
 
pub struct ChannelStatement {
 
    pub this: ChannelStatementId,
 
    // Phase 1: parser
 
    pub span: InputSpan, // of the "channel" keyword
 
    pub from: VariableId, // output
 
    pub to: VariableId,   // input
 
    // Phase 2: linker
 
    pub relative_pos_in_block: i32,
 
    pub relative_pos_in_parent: i32,
 
    pub next: StatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct LabeledStatement {
 
    pub this: LabeledStatementId,
 
    // Phase 1: parser
 
    pub label: Identifier,
 
    pub body: StatementId,
 
    // Phase 2: linker
 
    pub relative_pos_in_block: i32,
 
    pub relative_pos_in_parent: i32,
 
    pub in_sync: SynchronousStatementId, // may be invalid
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct IfStatement {
 
    pub this: IfStatementId,
 
    // Phase 1: parser
 
    pub span: InputSpan, // of the "if" keyword
 
    pub test: ExpressionId,
 
    pub true_body: BlockStatementId,
 
    pub false_body: Option<BlockStatementId>,
 
    pub true_case: IfStatementCase,
 
    pub false_case: Option<IfStatementCase>,
 
    pub end_if: EndIfStatementId,
 
}
 

	
 
#[derive(Debug, Clone, Copy)]
 
pub struct IfStatementCase {
 
    pub body: StatementId,
 
    pub scope: ScopeId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct EndIfStatement {
 
    pub this: EndIfStatementId,
 
    pub start_if: IfStatementId,
 
    pub next: StatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct WhileStatement {
 
    pub this: WhileStatementId,
 
    // Phase 1: parser
 
    pub span: InputSpan, // of the "while" keyword
 
    pub test: ExpressionId,
 
    pub body: BlockStatementId,
 
    pub scope: ScopeId,
 
    pub body: StatementId,
 
    pub end_while: EndWhileStatementId,
 
    pub in_sync: SynchronousStatementId, // may be invalid
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct EndWhileStatement {
 
    pub this: EndWhileStatementId,
 
    pub start_while: WhileStatementId,
 
    // Phase 2: linker
 
    pub next: StatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct BreakStatement {
 
    pub this: BreakStatementId,
 
    // Phase 1: parser
 
    pub span: InputSpan, // of the "break" keyword
 
    pub label: Option<Identifier>,
 
    // Phase 2: linker
 
    pub target: EndWhileStatementId, // invalid if not yet set
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct ContinueStatement {
 
    pub this: ContinueStatementId,
 
    // Phase 1: parser
 
    pub span: InputSpan, // of the "continue" keyword
 
    pub label: Option<Identifier>,
 
    // Phase 2: linker
 
    pub target: WhileStatementId, // invalid if not yet set
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct SynchronousStatement {
 
    pub this: SynchronousStatementId,
 
    // Phase 1: parser
 
    pub span: InputSpan, // of the "sync" keyword
 
    pub body: BlockStatementId,
 
    pub scope: ScopeId,
 
    pub body: StatementId,
 
    pub end_sync: EndSynchronousStatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct EndSynchronousStatement {
 
    pub this: EndSynchronousStatementId,
 
    pub start_sync: SynchronousStatementId,
 
    // Phase 2: linker
 
    pub next: StatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct ForkStatement {
 
    pub this: ForkStatementId,
 
    // Phase 1: parser
 
    pub span: InputSpan, // of the "fork" keyword
 
    pub left_body: BlockStatementId,
 
    pub right_body: Option<BlockStatementId>,
 
    pub left_body: StatementId,
 
    pub right_body: Option<StatementId>,
 
    pub end_fork: EndForkStatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct EndForkStatement {
 
    pub this: EndForkStatementId,
 
    pub start_fork: ForkStatementId,
 
    pub next: StatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct SelectStatement {
 
    pub this: SelectStatementId,
 
    pub span: InputSpan, // of the "select" keyword
 
    pub cases: Vec<SelectCase>,
 
    pub end_select: EndSelectStatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct SelectCase {
 
    // The guard statement of a `select` is either a MemoryStatement or an
 
    // ExpressionStatement. Nothing else is allowed by the initial parsing
 
    pub guard: StatementId,
 
    pub block: BlockStatementId,
 
    pub body: StatementId,
 
    pub scope: ScopeId,
 
    // Phase 2: Validation and Linking
 
    pub involved_ports: Vec<(CallExpressionId, ExpressionId)>, // call to `get` and its port argument
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct EndSelectStatement {
 
    pub this: EndSelectStatementId,
 
    pub start_select: SelectStatementId,
 
    pub next: StatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct ReturnStatement {
 
    pub this: ReturnStatementId,
 
    // Phase 1: parser
 
    pub span: InputSpan, // of the "return" keyword
 
    pub expressions: Vec<ExpressionId>,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct GotoStatement {
 
    pub this: GotoStatementId,
 
    // Phase 1: parser
 
    pub span: InputSpan, // of the "goto" keyword
src/protocol/ast_printer.rs
Show inline comments
 
@@ -333,98 +333,93 @@ impl ASTWriter {
 
                    self.kv(indent3).with_s_key("Variant");
 
                    self.kv(indent4).with_s_key("Name")
 
                        .with_identifier_val(&variant.identifier);
 
                        
 
                    if variant.value.is_empty() {
 
                        self.kv(indent4).with_s_key("Value").with_s_val("None");
 
                    } else {
 
                        self.kv(indent4).with_s_key("Values");
 
                        for embedded in &variant.value {
 
                            self.kv(indent4+1).with_s_key("Value")
 
                                .with_custom_val(|v| write_parser_type(v, heap, embedded));
 
                        }
 
                    }
 
                }
 
            }
 
            Definition::Function(def) => {
 
                self.kv(indent).with_id(PREFIX_FUNCTION_ID, def.this.0.index)
 
                    .with_s_key("DefinitionFunction");
 

	
 
                self.kv(indent2).with_s_key("Name").with_identifier_val(&def.identifier);
 
                for poly_var_id in &def.poly_vars {
 
                    self.kv(indent3).with_s_key("PolyVar").with_identifier_val(&poly_var_id);
 
                }
 

	
 
                self.kv(indent2).with_s_key("ReturnParserTypes");
 
                for return_type in &def.return_types {
 
                    self.kv(indent3).with_s_key("ReturnParserType")
 
                        .with_custom_val(|s| write_parser_type(s, heap, return_type));
 
                }
 
                self.kv(indent2).with_s_key("ReturnParserType")
 
                    .with_custom_val(|s| write_parser_type(s, heap, &def.return_type));
 

	
 
                self.kv(indent2).with_s_key("Parameters");
 
                for variable_id in &def.parameters {
 
                    self.write_variable(heap, *variable_id, indent3);
 
                }
 

	
 
                self.kv(indent2).with_s_key("Body");
 
                self.write_stmt(heap, def.body.upcast(), indent3);
 
            },
 
            Definition::Component(def) => {
 
                self.kv(indent).with_id(PREFIX_COMPONENT_ID,def.this.0.index)
 
                    .with_s_key("DefinitionComponent");
 

	
 
                self.kv(indent2).with_s_key("Name").with_identifier_val(&def.identifier);
 
                self.kv(indent2).with_s_key("Variant").with_debug_val(&def.variant);
 

	
 
                self.kv(indent2).with_s_key("PolymorphicVariables");
 
                for poly_var_id in &def.poly_vars {
 
                    self.kv(indent3).with_s_key("PolyVar").with_identifier_val(&poly_var_id);
 
                }
 

	
 
                self.kv(indent2).with_s_key("Parameters");
 
                for variable_id in &def.parameters {
 
                    self.write_variable(heap, *variable_id, indent3)
 
                }
 

	
 
                self.kv(indent2).with_s_key("Body");
 
                self.write_stmt(heap, def.body.upcast(), indent3);
 
            }
 
        }
 
    }
 

	
 
    fn write_stmt(&mut self, heap: &Heap, stmt_id: StatementId, indent: usize) {
 
        let stmt = &heap[stmt_id];
 
        let indent2 = indent + 1;
 
        let indent3 = indent2 + 1;
 

	
 
        match stmt {
 
            Statement::Block(stmt) => {
 
                self.kv(indent).with_id(PREFIX_BLOCK_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("Block");
 
                self.kv(indent2).with_s_key("EndBlockID").with_disp_val(&stmt.end_block.0.index);
 
                self.kv(indent2).with_s_key("FirstUniqueScopeID").with_disp_val(&stmt.first_unique_id_in_scope);
 
                self.kv(indent2).with_s_key("NextUniqueScopeID").with_disp_val(&stmt.next_unique_id_in_scope);
 
                self.kv(indent2).with_s_key("RelativePos").with_disp_val(&stmt.scope_node.relative_pos_in_parent);
 
                self.kv(indent2).with_s_key("ScopeID").with_disp_val(&stmt.scope.index);
 

	
 
                self.kv(indent2).with_s_key("Statements");
 
                for stmt_id in &stmt.statements {
 
                    self.write_stmt(heap, *stmt_id, indent3);
 
                }
 
            },
 
            Statement::EndBlock(stmt) => {
 
                self.kv(indent).with_id(PREFIX_ENDBLOCK_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("EndBlock");
 
                self.kv(indent2).with_s_key("StartBlockID").with_disp_val(&stmt.start_block.0.index);
 
            }
 
            Statement::Local(stmt) => {
 
                match stmt {
 
                    LocalStatement::Channel(stmt) => {
 
                        self.kv(indent).with_id(PREFIX_CHANNEL_STMT_ID, stmt.this.0.0.index)
 
                            .with_s_key("LocalChannel");
 

	
 
                        self.kv(indent2).with_s_key("From");
 
                        self.write_variable(heap, stmt.from, indent3);
 
                        self.kv(indent2).with_s_key("To");
 
                        self.write_variable(heap, stmt.to, indent3);
 
                        self.kv(indent2).with_s_key("Next").with_disp_val(&stmt.next.index);
 
                    },
 
                    LocalStatement::Memory(stmt) => {
 
@@ -436,139 +431,139 @@ impl ASTWriter {
 
                        self.kv(indent2).with_s_key("InitialValue");
 
                        self.write_expr(heap, stmt.initial_expr.upcast(), indent3);
 
                        self.kv(indent2).with_s_key("Next").with_disp_val(&stmt.next.index);
 
                    }
 
                }
 
            },
 
            Statement::Labeled(stmt) => {
 
                self.kv(indent).with_id(PREFIX_LABELED_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("Labeled");
 

	
 
                self.kv(indent2).with_s_key("Label").with_identifier_val(&stmt.label);
 
                self.kv(indent2).with_s_key("Statement");
 
                self.write_stmt(heap, stmt.body, indent3);
 
            },
 
            Statement::If(stmt) => {
 
                self.kv(indent).with_id(PREFIX_IF_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("If");
 

	
 
                self.kv(indent2).with_s_key("EndIf").with_disp_val(&stmt.end_if.0.index);
 

	
 
                self.kv(indent2).with_s_key("Condition");
 
                self.write_expr(heap, stmt.test, indent3);
 

	
 
                self.kv(indent2).with_s_key("TrueBody");
 
                self.write_stmt(heap, stmt.true_body.upcast(), indent3);
 
                self.write_stmt(heap, stmt.true_case.body, indent3);
 

	
 
                if let Some(false_body) = stmt.false_body {
 
                if let Some(false_body) = stmt.false_case {
 
                    self.kv(indent2).with_s_key("FalseBody");
 
                    self.write_stmt(heap, false_body.upcast(), indent3);
 
                    self.write_stmt(heap, false_body.body, indent3);
 
                }
 
            },
 
            Statement::EndIf(stmt) => {
 
                self.kv(indent).with_id(PREFIX_ENDIF_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("EndIf");
 
                self.kv(indent2).with_s_key("StartIf").with_disp_val(&stmt.start_if.0.index);
 
                self.kv(indent2).with_s_key("Next").with_disp_val(&stmt.next.index);
 
            },
 
            Statement::While(stmt) => {
 
                self.kv(indent).with_id(PREFIX_WHILE_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("While");
 

	
 
                self.kv(indent2).with_s_key("EndWhile").with_disp_val(&stmt.end_while.0.index);
 
                self.kv(indent2).with_s_key("InSync")
 
                    .with_disp_val(&stmt.in_sync.0.index);
 
                self.kv(indent2).with_s_key("Condition");
 
                self.write_expr(heap, stmt.test, indent3);
 
                self.kv(indent2).with_s_key("Body");
 
                self.write_stmt(heap, stmt.body.upcast(), indent3);
 
                self.write_stmt(heap, stmt.body, indent3);
 
            },
 
            Statement::EndWhile(stmt) => {
 
                self.kv(indent).with_id(PREFIX_ENDWHILE_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("EndWhile");
 
                self.kv(indent2).with_s_key("StartWhile").with_disp_val(&stmt.start_while.0.index);
 
                self.kv(indent2).with_s_key("Next").with_disp_val(&stmt.next.index);
 
            },
 
            Statement::Break(stmt) => {
 
                self.kv(indent).with_id(PREFIX_BREAK_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("Break");
 
                self.kv(indent2).with_s_key("Label")
 
                    .with_opt_identifier_val(stmt.label.as_ref());
 
                self.kv(indent2).with_s_key("Target")
 
                    .with_disp_val(&stmt.target.0.index);
 
            },
 
            Statement::Continue(stmt) => {
 
                self.kv(indent).with_id(PREFIX_CONTINUE_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("Continue");
 
                self.kv(indent2).with_s_key("Label")
 
                    .with_opt_identifier_val(stmt.label.as_ref());
 
                self.kv(indent2).with_s_key("Target")
 
                    .with_disp_val(&stmt.target.0.index);
 
            },
 
            Statement::Synchronous(stmt) => {
 
                self.kv(indent).with_id(PREFIX_SYNC_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("Synchronous");
 
                self.kv(indent2).with_s_key("EndSync").with_disp_val(&stmt.end_sync.0.index);
 
                self.kv(indent2).with_s_key("Body");
 
                self.write_stmt(heap, stmt.body.upcast(), indent3);
 
                self.write_stmt(heap, stmt.body, indent3);
 
            },
 
            Statement::EndSynchronous(stmt) => {
 
                self.kv(indent).with_id(PREFIX_ENDSYNC_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("EndSynchronous");
 
                self.kv(indent2).with_s_key("StartSync").with_disp_val(&stmt.start_sync.0.index);
 
                self.kv(indent2).with_s_key("Next").with_disp_val(&stmt.next.index);
 
            },
 
            Statement::Fork(stmt) => {
 
                self.kv(indent).with_id(PREFIX_FORK_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("Fork");
 
                self.kv(indent2).with_s_key("EndFork").with_disp_val(&stmt.end_fork.0.index);
 
                self.kv(indent2).with_s_key("LeftBody");
 
                self.write_stmt(heap, stmt.left_body.upcast(), indent3);
 
                self.write_stmt(heap, stmt.left_body, indent3);
 

	
 
                if let Some(right_body_id) = stmt.right_body {
 
                    self.kv(indent2).with_s_key("RightBody");
 
                    self.write_stmt(heap, right_body_id.upcast(), indent3);
 
                    self.write_stmt(heap, right_body_id, indent3);
 
                }
 
            },
 
            Statement::EndFork(stmt) => {
 
                self.kv(indent).with_id(PREFIX_END_FORK_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("EndFork");
 
                self.kv(indent2).with_s_key("StartFork").with_disp_val(&stmt.start_fork.0.index);
 
                self.kv(indent2).with_s_key("Next").with_disp_val(&stmt.next.index);
 
            },
 
            Statement::Select(stmt) => {
 
                self.kv(indent).with_id(PREFIX_SELECT_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("Select");
 
                self.kv(indent2).with_s_key("EndSelect").with_disp_val(&stmt.end_select.0.index);
 
                self.kv(indent2).with_s_key("Cases");
 
                let indent3 = indent2 + 1;
 
                let indent4 = indent3 + 1;
 
                for case in &stmt.cases {
 
                    self.kv(indent3).with_s_key("Guard");
 
                    self.write_stmt(heap, case.guard, indent4);
 

	
 
                    self.kv(indent3).with_s_key("Block");
 
                    self.write_stmt(heap, case.block.upcast(), indent4);
 
                    self.write_stmt(heap, case.body, indent4);
 
                }
 
            },
 
            Statement::EndSelect(stmt) => {
 
                self.kv(indent).with_id(PREFIX_END_SELECT_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("EndSelect");
 
                self.kv(indent2).with_s_key("StartSelect").with_disp_val(&stmt.start_select.0.index);
 
                self.kv(indent2).with_s_key("Next").with_disp_val(&stmt.next.index);
 
            }
 
            Statement::Return(stmt) => {
 
                self.kv(indent).with_id(PREFIX_RETURN_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("Return");
 
                self.kv(indent2).with_s_key("Expressions");
 
                for expr_id in &stmt.expressions {
 
                    self.write_expr(heap, *expr_id, indent3);
 
                }
 
            },
 
            Statement::Goto(stmt) => {
 
                self.kv(indent).with_id(PREFIX_GOTO_STMT_ID, stmt.this.0.index)
 
                    .with_s_key("Goto");
 
                self.kv(indent2).with_s_key("Label").with_identifier_val(&stmt.label);
 
                self.kv(indent2).with_s_key("Target")
 
                    .with_disp_val(&stmt.target.0.index);
 
            },
 
            Statement::New(stmt) => {
 
@@ -804,49 +799,49 @@ impl ASTWriter {
 
            },
 
            Expression::Variable(expr) => {
 
                self.kv(indent).with_id(PREFIX_VARIABLE_EXPR_ID, expr.this.0.index)
 
                    .with_s_key("VariableExpr");
 
                self.kv(indent2).with_s_key("Name").with_identifier_val(&expr.identifier);
 
                self.kv(indent2).with_s_key("Definition")
 
                    .with_opt_disp_val(expr.declaration.as_ref().map(|v| &v.index));
 
                self.kv(indent2).with_s_key("Parent")
 
                    .with_custom_val(|v| write_expression_parent(v, &expr.parent));
 
            }
 
        }
 
    }
 

	
 
    fn write_variable(&mut self, heap: &Heap, variable_id: VariableId, indent: usize) {
 
        let var = &heap[variable_id];
 
        let indent2 = indent + 1;
 

	
 
        self.kv(indent).with_id(PREFIX_VARIABLE_ID, variable_id.index)
 
            .with_s_key("Variable");
 

	
 
        self.kv(indent2).with_s_key("Name").with_identifier_val(&var.identifier);
 
        self.kv(indent2).with_s_key("Kind").with_debug_val(&var.kind);
 
        self.kv(indent2).with_s_key("ParserType")
 
            .with_custom_val(|w| write_parser_type(w, heap, &var.parser_type));
 
        self.kv(indent2).with_s_key("RelativePos").with_disp_val(&var.relative_pos_in_block);
 
        self.kv(indent2).with_s_key("RelativePos").with_disp_val(&var.relative_pos_in_parent);
 
        self.kv(indent2).with_s_key("UniqueScopeID").with_disp_val(&var.unique_id_in_scope);
 
    }
 

	
 
    //--------------------------------------------------------------------------
 
    // Printing Utilities
 
    //--------------------------------------------------------------------------
 

	
 
    fn kv(&mut self, indent: usize) -> KV {
 
        KV::new(&mut self.buffer, &mut self.temp1, &mut self.temp2, indent)
 
    }
 

	
 
    fn flush<W: IOWrite>(&mut self, w: &mut W) {
 
        w.write(self.buffer.as_bytes()).unwrap();
 
        self.buffer.clear()
 
    }
 
}
 

	
 
fn write_option<V: Display>(target: &mut String, value: Option<V>) {
 
    target.clear();
 
    match &value {
 
        Some(v) => target.push_str(&format!("Some({})", v)),
 
        None => target.push_str("None")
 
    };
 
}
src/protocol/eval/executor.rs
Show inline comments
 
@@ -17,77 +17,76 @@ macro_rules! debug_log {
 
        enabled_debug_print!(false, "exec", $format, $($args),*);
 
    };
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub(crate) enum ExprInstruction {
 
    EvalExpr(ExpressionId),
 
    PushValToFront,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub(crate) struct Frame {
 
    pub(crate) definition: DefinitionId,
 
    pub(crate) monomorph_idx: i32,
 
    pub(crate) position: StatementId,
 
    pub(crate) expr_stack: VecDeque<ExprInstruction>, // hack for expression evaluation, evaluated by popping from back
 
    pub(crate) expr_values: VecDeque<Value>, // hack for expression results, evaluated by popping from front/back
 
    pub(crate) max_stack_size: u32,
 
}
 

	
 
impl Frame {
 
    /// Creates a new execution frame. Does not modify the stack in any way.
 
    pub fn new(heap: &Heap, definition_id: DefinitionId, monomorph_idx: i32) -> Self {
 
        let definition = &heap[definition_id];
 
        let first_statement = match definition {
 
            Definition::Component(definition) => definition.body,
 
            Definition::Function(definition) => definition.body,
 
        let (outer_scope_id, first_statement_id) = match definition {
 
            Definition::Component(definition) => (definition.scope, definition.body),
 
            Definition::Function(definition) => (definition.scope, definition.body),
 
            _ => unreachable!("initializing frame with {:?} instead of a function/component", definition),
 
        };
 

	
 
        // Another not-so-pretty thing that has to be replaced somewhere in the
 
        // future...
 
        fn determine_max_stack_size(heap: &Heap, block_id: BlockStatementId, max_size: &mut u32) {
 
            let block_stmt = &heap[block_id];
 
            debug_assert!(block_stmt.next_unique_id_in_scope >= 0);
 
        fn determine_max_stack_size(heap: &Heap, scope_id: ScopeId, max_size: &mut u32) {
 
            let scope = &heap[scope_id];
 

	
 
            // Check current block
 
            let cur_size = block_stmt.next_unique_id_in_scope as u32;
 
            let cur_size = scope.next_unique_id_in_scope as u32;
 
            if cur_size > *max_size { *max_size = cur_size; }
 

	
 
            // And child blocks
 
            for child_scope in &block_stmt.scope_node.nested {
 
                determine_max_stack_size(heap, child_scope.to_block(), max_size);
 
            for child_scope in &scope.nested {
 
                determine_max_stack_size(heap, *child_scope, max_size);
 
            }
 
        }
 

	
 
        let mut max_stack_size = 0;
 
        determine_max_stack_size(heap, first_statement, &mut max_stack_size);
 
        determine_max_stack_size(heap, outer_scope_id, &mut max_stack_size);
 

	
 
        Frame{
 
            definition: definition_id,
 
            monomorph_idx,
 
            position: first_statement.upcast(),
 
            position: first_statement_id.upcast(),
 
            expr_stack: VecDeque::with_capacity(128),
 
            expr_values: VecDeque::with_capacity(128),
 
            max_stack_size,
 
        }
 
    }
 

	
 
    /// Prepares a single expression for execution. This involves walking the
 
    /// expression tree and putting them in the `expr_stack` such that
 
    /// continuously popping from its back will evaluate the expression. The
 
    /// results of each expression will be stored by pushing onto `expr_values`.
 
    pub fn prepare_single_expression(&mut self, heap: &Heap, expr_id: ExpressionId) {
 
        debug_assert!(self.expr_stack.is_empty());
 
        self.expr_values.clear(); // May not be empty if last expression result(s) were discarded
 

	
 
        self.serialize_expression(heap, expr_id);
 
    }
 

	
 
    /// Prepares multiple expressions for execution (i.e. evaluating all
 
    /// function arguments or all elements of an array/union literal). Per
 
    /// expression this works the same as `prepare_single_expression`. However
 
    /// after each expression is evaluated we insert a `PushValToFront`
 
    /// instruction
 
    pub fn prepare_multiple_expressions(&mut self, heap: &Heap, expr_ids: &[ExpressionId]) {
 
        debug_assert!(self.expr_stack.is_empty());
 
@@ -805,178 +804,196 @@ impl Prompt {
 
            }
 

	
 
            debug_log!("Stack (size = {}):", self.store.stack.len());
 
            for (_stack_idx, _stack_val) in self.store.stack.iter().enumerate() {
 
                debug_log!("  [{:03}] {:?}", _stack_idx, _stack_val);
 
            }
 

	
 
            debug_log!("Heap:");
 
            for (_heap_idx, _heap_region) in self.store.heap_regions.iter().enumerate() {
 
                let _is_free = self.store.free_regions.iter().any(|idx| *idx as usize == _heap_idx);
 
                debug_log!("  [{:03}] in_use: {}, len: {}, vals: {:?}", _heap_idx, !_is_free, _heap_region.values.len(), &_heap_region.values);
 
            }
 
        }
 
        // No (more) expressions to evaluate. So evaluate statement (that may
 
        // depend on the result on the last evaluated expression(s))
 
        let stmt = &heap[cur_frame.position];
 
        let return_value = match stmt {
 
            Statement::Block(stmt) => {
 
                debug_assert!(stmt.statements.is_empty() || stmt.next == stmt.statements[0]);
 
                cur_frame.position = stmt.next;
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::EndBlock(stmt) => {
 
                let block = &heap[stmt.start_block];
 
                self.store.clear_stack(block.first_unique_id_in_scope as usize);
 
                let scope = &heap[block.scope];
 
                self.store.clear_stack(scope.first_unique_id_in_scope as usize);
 
                cur_frame.position = stmt.next;
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::Local(stmt) => {
 
                match stmt {
 
                    LocalStatement::Memory(stmt) => {
 
                        if cfg!(debug_assertions) {
 
                            let variable = &heap[stmt.variable];
 
                            debug_assert!(match self.store.read_ref(ValueId::Stack(variable.unique_id_in_scope as u32)) {
 
                                Value::Unassigned => false,
 
                                _ => true,
 
                            });
 
                        }
 

	
 
                        cur_frame.position = stmt.next;
 
                        Ok(EvalContinuation::Stepping)
 
                    },
 
                    LocalStatement::Channel(stmt) => {
 
                        // Need to create a new channel by requesting it from
 
                        // the runtime.
 
                        match ctx.created_channel() {
 
                            None => {
 
                                // No channel is pending. So request one
 
                                    Ok(EvalContinuation::NewChannel)
 
                            },
 
                            Some((put_port, get_port)) => {
 
                                self.store.write(ValueId::Stack(heap[stmt.from].unique_id_in_scope as u32), put_port);
 
                                self.store.write(ValueId::Stack(heap[stmt.to].unique_id_in_scope as u32), get_port);
 
                                cur_frame.position = stmt.next;
 
                                Ok(EvalContinuation::Stepping)
 
                            }
 
                        }
 
                    }
 
                }
 
            },
 
            Statement::Labeled(stmt) => {
 
                cur_frame.position = stmt.body;
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::If(stmt) => {
 
                debug_assert_eq!(cur_frame.expr_values.len(), 1, "expected one expr value for if statement");
 
                let test_value = cur_frame.expr_values.pop_back().unwrap();
 
                let test_value = self.store.maybe_read_ref(&test_value).as_bool();
 
                if test_value {
 
                    cur_frame.position = stmt.true_body.upcast();
 
                } else if let Some(false_body) = stmt.false_body {
 
                    cur_frame.position = false_body.upcast();
 
                    cur_frame.position = stmt.true_case.body;
 
                } else if let Some(false_body) = stmt.false_case {
 
                    cur_frame.position = false_body.body;
 
                } else {
 
                    // Not true, and no false body
 
                    cur_frame.position = stmt.end_if.upcast();
 
                }
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::EndIf(stmt) => {
 
                cur_frame.position = stmt.next;
 
                let if_stmt = &heap[stmt.start_if];
 
                debug_assert_eq!(
 
                    heap[if_stmt.true_case.scope].first_unique_id_in_scope,
 
                    heap[if_stmt.false_case.unwrap_or(if_stmt.true_case).scope].first_unique_id_in_scope,
 
                );
 
                let scope = &heap[if_stmt.true_case.scope];
 
                self.store.clear_stack(scope.first_unique_id_in_scope as usize);
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::While(stmt) => {
 
                debug_assert_eq!(cur_frame.expr_values.len(), 1, "expected one expr value for while statement");
 
                let test_value = cur_frame.expr_values.pop_back().unwrap();
 
                let test_value = self.store.maybe_read_ref(&test_value).as_bool();
 
                if test_value {
 
                    cur_frame.position = stmt.body.upcast();
 
                    cur_frame.position = stmt.body;
 
                } else {
 
                    cur_frame.position = stmt.end_while.upcast();
 
                }
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::EndWhile(stmt) => {
 
                cur_frame.position = stmt.next;
 

	
 
                let start_while = &heap[stmt.start_while];
 
                let scope = &heap[start_while.scope];
 
                self.store.clear_stack(scope.first_unique_id_in_scope as usize);
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::Break(stmt) => {
 
                cur_frame.position = stmt.target.upcast();
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::Continue(stmt) => {
 
                cur_frame.position = stmt.target.upcast();
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::Synchronous(stmt) => {
 
                cur_frame.position = stmt.body.upcast();
 
                cur_frame.position = stmt.body;
 

	
 
                Ok(EvalContinuation::SyncBlockStart)
 
            },
 
            Statement::EndSynchronous(stmt) => {
 
                cur_frame.position = stmt.next;
 
                let start_synchronous = &heap[stmt.start_sync];
 
                let scope = &heap[start_synchronous.scope];
 
                self.store.clear_stack(scope.first_unique_id_in_scope as usize);
 

	
 
                Ok(EvalContinuation::SyncBlockEnd)
 
            },
 
            Statement::Fork(stmt) => {
 
                if stmt.right_body.is_none() {
 
                    // No reason to fork
 
                    cur_frame.position = stmt.left_body.upcast();
 
                    cur_frame.position = stmt.left_body;
 
                } else {
 
                    // Need to fork
 
                    if let Some(go_left) = ctx.performed_fork() {
 
                        // Runtime has created a fork
 
                        if go_left {
 
                            cur_frame.position = stmt.left_body.upcast();
 
                            cur_frame.position = stmt.left_body;
 
                        } else {
 
                            cur_frame.position = stmt.right_body.unwrap().upcast();
 
                            cur_frame.position = stmt.right_body.unwrap();
 
                        }
 
                    } else {
 
                        // Request the runtime to create a fork of the current
 
                        // branch
 
                        return Ok(EvalContinuation::NewFork);
 
                    }
 
                }
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::EndFork(stmt) => {
 
                cur_frame.position = stmt.next;
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::Select(_stmt) => {
 
                todo!("implement select evaluation")
 
            },
 
            Statement::EndSelect(stmt) => {
 
                cur_frame.position = stmt.next;
 
                let start_select = &heap[stmt.start_select];
 
                if let Some(select_case) = start_select.cases.first() {
 
                    let scope = &heap[select_case.scope];
 
                    self.store.clear_stack(scope.first_unique_id_in_scope as usize);
 
                }
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::Return(_stmt) => {
 
                debug_assert!(heap[cur_frame.definition].is_function());
 
                debug_assert_eq!(cur_frame.expr_values.len(), 1, "expected one expr value for return statement");
 

	
 
                // The preceding frame has executed a call, so is expecting the
 
                // return expression on its expression value stack. Note that
 
                // we may be returning a reference to something on our stack,
 
                // so we need to read that value and clone it.
 
                let return_value = cur_frame.expr_values.pop_back().unwrap();
 
                let return_value = match return_value {
 
                    Value::Ref(value_id) => self.store.read_copy(value_id),
 
                    _ => return_value,
 
                };
 

	
 
                // Pre-emptively pop our stack frame
 
                self.frames.pop();
 

	
 
                // Clean up our section of the stack
 
                self.store.clear_stack(0);
 
                self.store.stack.truncate(self.store.cur_stack_boundary + 1);
 
                let prev_stack_idx = self.store.stack.pop().unwrap().as_stack_boundary();
src/protocol/eval/value.rs
Show inline comments
 
@@ -499,71 +499,71 @@ pub(crate) fn apply_binary_operator(store: &mut Store, lhs: &Value, op: BinaryOp
 
        BO::ShiftRight       => { apply_int_op_and_return_self!(lhs, >>, op, rhs); },
 
        BO::Add              => { apply_int_op_and_return_self!(lhs, +,  op, rhs); },
 
        BO::Subtract         => { apply_int_op_and_return_self!(lhs, -,  op, rhs); },
 
        BO::Multiply         => { apply_int_op_and_return_self!(lhs, *,  op, rhs); },
 
        BO::Divide           => { apply_int_op_and_return_self!(lhs, /,  op, rhs); },
 
        BO::Remainder        => { apply_int_op_and_return_self!(lhs, %,  op, rhs); }
 
    }
 
}
 

	
 
pub(crate) fn apply_unary_operator(store: &mut Store, op: UnaryOperator, value: &Value) -> Value {
 
    use UnaryOperator as UO;
 

	
 
    macro_rules! apply_int_expr_and_return {
 
        ($value:ident, $apply:tt, $op:ident) => {
 
            return match $value {
 
                Value::UInt8(v)  => Value::UInt8($apply *v),
 
                Value::UInt16(v) => Value::UInt16($apply *v),
 
                Value::UInt32(v) => Value::UInt32($apply *v),
 
                Value::UInt64(v) => Value::UInt64($apply *v),
 
                Value::SInt8(v)  => Value::SInt8($apply *v),
 
                Value::SInt16(v) => Value::SInt16($apply *v),
 
                Value::SInt32(v) => Value::SInt32($apply *v),
 
                Value::SInt64(v) => Value::SInt64($apply *v),
 
                _ => unreachable!("apply_unary_operator {:?} on value {:?}", $op, $value),
 
            };
 
            }
 
        }
 
    }
 

	
 
    // If the value is a reference, retrieve the thing it is referring to
 
    let value = store.maybe_read_ref(value);
 

	
 
    match op {
 
        UO::Positive => {
 
            debug_assert!(value.is_integer());
 
            return value.clone();
 
        },
 
        UO::Negative => {
 
            // TODO: Error on negating unsigned integers
 
            return match value {
 
                Value::SInt8(v) => Value::SInt8(-*v),
 
                Value::SInt16(v) => Value::SInt16(-*v),
 
                Value::SInt32(v) => Value::SInt32(-*v),
 
                Value::SInt64(v) => Value::SInt64(-*v),
 
                _ => unreachable!("apply_unary_operator {:?} on value {:?}", op, value),
 
            }
 
        },
 
        UO::BitwiseNot => { apply_int_expr_and_return!(value, !, op)},
 
        UO::BitwiseNot => { apply_int_expr_and_return!(value, !, op); },
 
        UO::LogicalNot => { return Value::Bool(!value.as_bool()); },
 
    }
 
}
 

	
 
pub(crate) fn apply_casting(store: &mut Store, output_type: &ConcreteType, subject: &Value) -> Result<Value, String> {
 
    // To simplify the casting logic: if the output type is not a simple
 
    // integer/boolean/character, then the type checker made sure that the two
 
    // types must be equal, hence we can do a simple clone.
 
    use ConcreteTypePart as CTP;
 
    let part = &output_type.parts[0];
 
    match part {
 
        CTP::Bool | CTP::Character |
 
        CTP::UInt8 | CTP::UInt16 | CTP::UInt32 | CTP::UInt64 |
 
        CTP::SInt8 | CTP::SInt16 | CTP::SInt32 | CTP::SInt64 => {
 
            // Do the checking of these below
 
            debug_assert_eq!(output_type.parts.len(), 1);
 
        },
 
        _ => {
 
            return Ok(store.clone_value(subject.clone()));
 
        },
 
    }
 

	
 
    // Note: character is not included, needs per-type checking
 
    macro_rules! unchecked_cast {
src/protocol/parser/mod.rs
Show inline comments
 
@@ -261,63 +261,64 @@ impl Parser {
 
            writer.write_ast(&mut file, &self.heap);
 
        }
 

	
 
        Ok(())
 
    }
 
}
 

	
 
// Note: args and return type need to be a function because we need to know the function ID.
 
fn insert_builtin_function<T: Fn(FunctionDefinitionId) -> (Vec<(&'static str, ParserType)>, ParserType)> (
 
    p: &mut Parser, func_name: &str, polymorphic: &[&str], arg_and_return_fn: T) {
 

	
 
    let mut poly_vars = Vec::with_capacity(polymorphic.len());
 
    for poly_var in polymorphic {
 
        poly_vars.push(Identifier{ span: InputSpan::new(), value: p.string_pool.intern(poly_var.as_bytes()) });
 
    }
 

	
 
    let func_ident_ref = p.string_pool.intern(func_name.as_bytes());
 
    let func_id = p.heap.alloc_function_definition(|this| FunctionDefinition{
 
        this,
 
        defined_in: RootId::new_invalid(),
 
        builtin: true,
 
        span: InputSpan::new(),
 
        identifier: Identifier{ span: InputSpan::new(), value: func_ident_ref.clone() },
 
        poly_vars,
 
        return_types: Vec::new(),
 
        return_type: ParserType{ elements: Vec::new(), full_span: InputSpan::new() },
 
        parameters: Vec::new(),
 
        scope: ScopeId::new_invalid(),
 
        body: BlockStatementId::new_invalid(),
 
        num_expressions_in_body: -1,
 
    });
 

	
 
    let (args, ret) = arg_and_return_fn(func_id);
 
    let (arguments, return_type) = arg_and_return_fn(func_id);
 

	
 
    let mut parameters = Vec::with_capacity(args.len());
 
    for (arg_name, arg_type) in args {
 
    let mut parameters = Vec::with_capacity(arguments.len());
 
    for (arg_name, arg_type) in arguments {
 
        let identifier = Identifier{ span: InputSpan::new(), value: p.string_pool.intern(arg_name.as_bytes()) };
 
        let param_id = p.heap.alloc_variable(|this| Variable{
 
            this,
 
            kind: VariableKind::Parameter,
 
            parser_type: arg_type.clone(),
 
            identifier,
 
            relative_pos_in_block: 0,
 
            relative_pos_in_parent: 0,
 
            unique_id_in_scope: 0
 
        });
 
        parameters.push(param_id);
 
    }
 

	
 
    let func = &mut p.heap[func_id];
 
    func.parameters = parameters;
 
    func.return_types.push(ret);
 
    func.return_type = return_type;
 

	
 
    p.symbol_table.insert_symbol(SymbolScope::Global, Symbol{
 
        name: func_ident_ref,
 
        variant: SymbolVariant::Definition(SymbolDefinition{
 
            defined_in_module: RootId::new_invalid(),
 
            defined_in_scope: SymbolScope::Global,
 
            definition_span: InputSpan::new(),
 
            identifier_span: InputSpan::new(),
 
            imported_at: None,
 
            class: DefinitionClass::Function,
 
            definition_id: func_id.upcast(),
 
        })
 
    }).unwrap();
 
}
 
\ No newline at end of file
src/protocol/parser/pass_definitions.rs
Show inline comments
 
@@ -252,488 +252,435 @@ impl PassDefinitions {
 
    fn visit_function_definition(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<(), ParseError> {
 
        // Retrieve function name
 
        consume_exact_ident(&module.source, iter, KW_FUNCTION)?;
 
        let (ident_text, _) = consume_ident(&module.source, iter)?;
 

	
 
        // Retrieve preallocated DefinitionId
 
        let module_scope = SymbolScope::Module(module.root_id);
 
        let definition_id = ctx.symbols.get_symbol_by_name_defined_in_scope(module_scope, ident_text)
 
            .unwrap().variant.as_definition().definition_id;
 
        self.cur_definition = definition_id;
 

	
 
        consume_polymorphic_vars_spilled(&module.source, iter, ctx)?;
 

	
 
        // Parse function's argument list
 
        let mut parameter_section = self.variables.start_section();
 
        consume_parameter_list(
 
            &mut self.type_parser, &module.source, iter, ctx, &mut parameter_section, module_scope, definition_id
 
        )?;
 
        let parameters = parameter_section.into_vec();
 

	
 
        // Consume return types
 
        consume_token(&module.source, iter, TokenKind::ArrowRight)?;
 
        let mut return_types = self.parser_types.start_section();
 
        let mut open_curly_pos = iter.last_valid_pos(); // bogus value
 
        consume_comma_separated_until(
 
            TokenKind::OpenCurly, &module.source, iter, ctx,
 
            |source, iter, ctx| {
 
                let poly_vars = ctx.heap[definition_id].poly_vars();
 
                self.type_parser.consume_parser_type(
 
                    iter, &ctx.heap, source, &ctx.symbols, poly_vars, definition_id,
 
                    module_scope, false, None
 
                )
 
            },
 
            &mut return_types, "a return type", Some(&mut open_curly_pos)
 
        let poly_vars = ctx.heap[definition_id].poly_vars();
 
        let parser_type = self.type_parser.consume_parser_type(
 
            iter, &ctx.heap, &module.source, &ctx.symbols, poly_vars, definition_id,
 
            module_scope, false, None
 
        )?;
 
        let return_types = return_types.into_vec();
 

	
 
        match return_types.len() {
 
            0 => return Err(ParseError::new_error_str_at_pos(&module.source, open_curly_pos, "expected a return type")),
 
            1 => {},
 
            _ => return Err(ParseError::new_error_str_at_pos(&module.source, open_curly_pos, "multiple return types are not (yet) allowed")),
 
        }
 

	
 
        // Consume block
 
        let body = self.consume_block_statement_without_leading_curly(module, iter, ctx, open_curly_pos)?;
 
        // Consume block and the definition's scope
 
        let body = self.consume_block_statement(module, iter, ctx)?;
 

	
 
        // Assign everything in the preallocated AST node
 
        let function = ctx.heap[definition_id].as_function_mut();
 
        function.return_types = return_types;
 
        function.return_type = parser_type;
 
        function.parameters = parameters;
 
        function.body = body;
 

	
 
        Ok(())
 
    }
 

	
 
    fn visit_component_definition(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<(), ParseError> {
 
        // Consume component variant and name
 
        let (_variant_text, _) = consume_any_ident(&module.source, iter)?;
 
        debug_assert!(_variant_text == KW_PRIMITIVE || _variant_text == KW_COMPOSITE);
 
        let (ident_text, _) = consume_ident(&module.source, iter)?;
 

	
 
        // Retrieve preallocated definition
 
        let module_scope = SymbolScope::Module(module.root_id);
 
        let definition_id = ctx.symbols.get_symbol_by_name_defined_in_scope(module_scope, ident_text)
 
            .unwrap().variant.as_definition().definition_id;
 
        self.cur_definition = definition_id;
 

	
 
        consume_polymorphic_vars_spilled(&module.source, iter, ctx)?;
 

	
 
        // Parse component's argument list
 
        let mut parameter_section = self.variables.start_section();
 
        consume_parameter_list(
 
            &mut self.type_parser, &module.source, iter, ctx, &mut parameter_section, module_scope, definition_id
 
        )?;
 
        let parameters = parameter_section.into_vec();
 

	
 
        // Consume block
 
        let body = self.consume_block_statement(module, iter, ctx)?;
 

	
 
        // Assign everything in the AST node
 
        let component = ctx.heap[definition_id].as_component_mut();
 
        component.parameters = parameters;
 
        component.body = body;
 

	
 
        Ok(())
 
    }
 

	
 
    /// Consumes a block statement. If the resulting statement is not a block
 
    /// (e.g. for a shorthand "if (expr) single_statement") then it will be
 
    /// wrapped in one
 
    fn consume_block_or_wrapped_statement(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<BlockStatementId, ParseError> {
 
        if Some(TokenKind::OpenCurly) == iter.next() {
 
            // This is a block statement
 
            self.consume_block_statement(module, iter, ctx)
 
        } else {
 
            // Not a block statement, so wrap it in one
 
            let mut statements = self.statements.start_section();
 
            let wrap_begin_pos = iter.last_valid_pos();
 
            self.consume_statement(module, iter, ctx, &mut statements)?;
 
            let wrap_end_pos = iter.last_valid_pos();
 

	
 
            let statements = statements.into_vec();
 

	
 
            let id = ctx.heap.alloc_block_statement(|this| BlockStatement{
 
                this,
 
                is_implicit: true,
 
                span: InputSpan::from_positions(wrap_begin_pos, wrap_end_pos),
 
                statements,
 
                end_block: EndBlockStatementId::new_invalid(),
 
                scope_node: ScopeNode::new_invalid(),
 
                first_unique_id_in_scope: -1,
 
                next_unique_id_in_scope: -1,
 
                locals: Vec::new(),
 
                labels: Vec::new(),
 
                next: StatementId::new_invalid(),
 
            });
 

	
 
            let end_block = ctx.heap.alloc_end_block_statement(|this| EndBlockStatement{
 
                this, start_block: id, next: StatementId::new_invalid()
 
            });
 

	
 
            let block_stmt = &mut ctx.heap[id];
 
            block_stmt.end_block = end_block;
 

	
 
            Ok(id)
 
        }
 
    }
 

	
 
    /// Consumes a statement and returns a boolean indicating whether it was a
 
    /// block or not.
 
    fn consume_statement(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx, section: &mut ScopedSection<StatementId>
 
    ) -> Result<(), ParseError> {
 
    fn consume_statement(&mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx) -> Result<StatementId, ParseError> {
 
        let next = iter.next().expect("consume_statement has a next token");
 

	
 
        if next == TokenKind::OpenCurly {
 
            let id = self.consume_block_statement(module, iter, ctx)?;
 
            section.push(id.upcast());
 
            return Ok(id.upcast());
 
        } else if next == TokenKind::Ident {
 
            let ident = peek_ident(&module.source, iter).unwrap();
 
            if ident == KW_STMT_IF {
 
                // Consume if statement and place end-if statement directly
 
                // after it.
 
                let id = self.consume_if_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 

	
 
                let end_if = ctx.heap.alloc_end_if_statement(|this| EndIfStatement {
 
                    this,
 
                    start_if: id,
 
                    next: StatementId::new_invalid()
 
                });
 
                section.push(end_if.upcast());
 

	
 
                let if_stmt = &mut ctx.heap[id];
 
                if_stmt.end_if = end_if;
 

	
 
                return Ok(id.upcast());
 
            } else if ident == KW_STMT_WHILE {
 
                let id = self.consume_while_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 

	
 
                let end_while = ctx.heap.alloc_end_while_statement(|this| EndWhileStatement {
 
                    this,
 
                    start_while: id,
 
                    next: StatementId::new_invalid()
 
                });
 
                section.push(end_while.upcast());
 

	
 
                let while_stmt = &mut ctx.heap[id];
 
                while_stmt.end_while = end_while;
 

	
 
                return Ok(id.upcast());
 
            } else if ident == KW_STMT_BREAK {
 
                let id = self.consume_break_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 
                return Ok(id.upcast());
 
            } else if ident == KW_STMT_CONTINUE {
 
                let id = self.consume_continue_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 
                return Ok(id.upcast());
 
            } else if ident == KW_STMT_SYNC {
 
                let id = self.consume_synchronous_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 

	
 
                let end_sync = ctx.heap.alloc_end_synchronous_statement(|this| EndSynchronousStatement {
 
                    this,
 
                    start_sync: id,
 
                    next: StatementId::new_invalid()
 
                });
 
                section.push(end_sync.upcast());
 

	
 
                let sync_stmt = &mut ctx.heap[id];
 
                sync_stmt.end_sync = end_sync;
 

	
 
                return Ok(id.upcast());
 
            } else if ident == KW_STMT_FORK {
 
                let id = self.consume_fork_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 

	
 
                let end_fork = ctx.heap.alloc_end_fork_statement(|this| EndForkStatement {
 
                    this,
 
                    start_fork: id,
 
                    next: StatementId::new_invalid(),
 
                });
 
                section.push(end_fork.upcast());
 

	
 
                let fork_stmt = &mut ctx.heap[id];
 
                fork_stmt.end_fork = end_fork;
 

	
 
                return Ok(id.upcast());
 
            } else if ident == KW_STMT_SELECT {
 
                let id = self.consume_select_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 

	
 
                let end_select = ctx.heap.alloc_end_select_statement(|this| EndSelectStatement{
 
                    this,
 
                    start_select: id,
 
                    next: StatementId::new_invalid(),
 
                });
 
                section.push(end_select.upcast());
 

	
 
                let select_stmt = &mut ctx.heap[id];
 
                select_stmt.end_select = end_select;
 

	
 
                return Ok(id.upcast());
 
            } else if ident == KW_STMT_RETURN {
 
                let id = self.consume_return_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 
                return Ok(id.upcast());
 
            } else if ident == KW_STMT_GOTO {
 
                let id = self.consume_goto_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 
                return Ok(id.upcast());
 
            } else if ident == KW_STMT_NEW {
 
                let id = self.consume_new_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 
                return Ok(id.upcast());
 
            } else if ident == KW_STMT_CHANNEL {
 
                let id = self.consume_channel_statement(module, iter, ctx)?;
 
                section.push(id.upcast().upcast());
 
                return Ok(id.upcast().upcast());
 
            } else if iter.peek() == Some(TokenKind::Colon) {
 
                self.consume_labeled_statement(module, iter, ctx, section)?;
 
                let id = self.consume_labeled_statement(module, iter, ctx)?;
 
                return Ok(id.upcast());
 
            } else {
 
                // Two fallback possibilities: the first one is a memory
 
                // declaration, the other one is to parse it as a normal
 
                // expression. This is a bit ugly.
 
                if let Some(memory_stmt_id) = self.maybe_consume_memory_statement_without_semicolon(module, iter, ctx)? {
 
                    consume_token(&module.source, iter, TokenKind::SemiColon)?;
 
                    section.push(memory_stmt_id.upcast().upcast());
 
                    return Ok(memory_stmt_id.upcast().upcast());
 
                } else {
 
                    let id = self.consume_expression_statement(module, iter, ctx)?;
 
                    section.push(id.upcast());
 
                    return Ok(id.upcast());
 
                }
 
            }
 
        } else if next == TokenKind::OpenParen {
 
            // Same as above: memory statement or normal expression
 
            if let Some(memory_stmt_id) = self.maybe_consume_memory_statement_without_semicolon(module, iter, ctx)? {
 
                consume_token(&module.source, iter, TokenKind::SemiColon)?;
 
                section.push(memory_stmt_id.upcast().upcast());
 
                return Ok(memory_stmt_id.upcast().upcast());
 
            } else {
 
                let id = self.consume_expression_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 
                return Ok(id.upcast());
 
            }
 
        } else {
 
            let id = self.consume_expression_statement(module, iter, ctx)?;
 
            section.push(id.upcast());
 
            return Ok(id.upcast());
 
        }
 

	
 
        return Ok(());
 
    }
 

	
 
    fn consume_block_statement(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<BlockStatementId, ParseError> {
 
        let open_span = consume_token(&module.source, iter, TokenKind::OpenCurly)?;
 
        self.consume_block_statement_without_leading_curly(module, iter, ctx, open_span.begin)
 
    }
 
        let open_curly_span = consume_token(&module.source, iter, TokenKind::OpenCurly)?;
 

	
 
    fn consume_block_statement_without_leading_curly(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx, open_curly_pos: InputPosition
 
    ) -> Result<BlockStatementId, ParseError> {
 
        let mut stmt_section = self.statements.start_section();
 
        let mut next = iter.next();
 
        while next != Some(TokenKind::CloseCurly) {
 
            if next.is_none() {
 
                return Err(ParseError::new_error_str_at_pos(
 
                    &module.source, iter.last_valid_pos(), "expected a statement or '}'"
 
                ));
 
            }
 
            self.consume_statement(module, iter, ctx, &mut stmt_section)?;
 
            let stmt_id = self.consume_statement(module, iter, ctx)?;
 
            stmt_section.push(stmt_id);
 
            next = iter.next();
 
        }
 

	
 
        let statements = stmt_section.into_vec();
 
        let mut block_span = consume_token(&module.source, iter, TokenKind::CloseCurly)?;
 
        block_span.begin = open_curly_pos;
 
        block_span.begin = open_curly_span.begin;
 

	
 
        let id = ctx.heap.alloc_block_statement(|this| BlockStatement{
 
            this,
 
            is_implicit: false,
 
            span: block_span,
 
            statements,
 
            end_block: EndBlockStatementId::new_invalid(),
 
            scope_node: ScopeNode::new_invalid(),
 
            first_unique_id_in_scope: -1,
 
            next_unique_id_in_scope: -1,
 
            locals: Vec::new(),
 
            labels: Vec::new(),
 
            scope: ScopeId::new_invalid(),
 
            next: StatementId::new_invalid(),
 
        });
 

	
 
        let end_block = ctx.heap.alloc_end_block_statement(|this| EndBlockStatement{
 
            this, start_block: id, next: StatementId::new_invalid()
 
        });
 

	
 
        let block_stmt = &mut ctx.heap[id];
 
        block_stmt.end_block = end_block;
 

	
 
        Ok(id)
 
    }
 

	
 
    fn consume_if_statement(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<IfStatementId, ParseError> {
 
        let if_span = consume_exact_ident(&module.source, iter, KW_STMT_IF)?;
 
        consume_token(&module.source, iter, TokenKind::OpenParen)?;
 
        let test = self.consume_expression(module, iter, ctx)?;
 
        consume_token(&module.source, iter, TokenKind::CloseParen)?;
 
        let true_body = self.consume_block_or_wrapped_statement(module, iter, ctx)?;
 

	
 
        let false_body = if has_ident(&module.source, iter, KW_STMT_ELSE) {
 
        let true_body = IfStatementCase{
 
            body: self.consume_statement(module, iter, ctx)?,
 
            scope: ScopeId::new_invalid(),
 
        };
 
        let true_body_scope_id = true_body.scope;
 

	
 
        let (false_body, false_body_scope_id) = if has_ident(&module.source, iter, KW_STMT_ELSE) {
 
            iter.consume();
 
            let false_body = self.consume_block_or_wrapped_statement(module, iter, ctx)?;
 
            Some(false_body)
 
            let false_body = IfStatementCase{
 
                body: self.consume_statement(module, iter, ctx)?,
 
                scope: ScopeId::new_invalid(),
 
            };
 

	
 
            let false_body_scope_id = false_body.scope;
 
            (Some(false_body), Some(false_body_scope_id))
 
        } else {
 
            None
 
            (None, None)
 
        };
 

	
 
        Ok(ctx.heap.alloc_if_statement(|this| IfStatement{
 
        let if_stmt_id = ctx.heap.alloc_if_statement(|this| IfStatement{
 
            this,
 
            span: if_span,
 
            test,
 
            true_body,
 
            false_body,
 
            true_case: true_body,
 
            false_case: false_body,
 
            end_if: EndIfStatementId::new_invalid(),
 
        }))
 
        });
 

	
 
        return Ok(if_stmt_id);
 
    }
 

	
 
    fn consume_while_statement(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<WhileStatementId, ParseError> {
 
        let while_span = consume_exact_ident(&module.source, iter, KW_STMT_WHILE)?;
 
        consume_token(&module.source, iter, TokenKind::OpenParen)?;
 
        let test = self.consume_expression(module, iter, ctx)?;
 
        consume_token(&module.source, iter, TokenKind::CloseParen)?;
 
        let body = self.consume_block_or_wrapped_statement(module, iter, ctx)?;
 
        let body = self.consume_statement(module, iter, ctx)?;
 

	
 
        Ok(ctx.heap.alloc_while_statement(|this| WhileStatement{
 
            this,
 
            span: while_span,
 
            test,
 
            scope: ScopeId::new_invalid(),
 
            body,
 
            end_while: EndWhileStatementId::new_invalid(),
 
            in_sync: SynchronousStatementId::new_invalid(),
 
        }))
 
    }
 

	
 
    fn consume_break_statement(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<BreakStatementId, ParseError> {
 
        let break_span = consume_exact_ident(&module.source, iter, KW_STMT_BREAK)?;
 
        let label = if Some(TokenKind::Ident) == iter.next() {
 
            let label = consume_ident_interned(&module.source, iter, ctx)?;
 
            Some(label)
 
        } else {
 
            None
 
        };
 
        consume_token(&module.source, iter, TokenKind::SemiColon)?;
 
        Ok(ctx.heap.alloc_break_statement(|this| BreakStatement{
 
            this,
 
            span: break_span,
 
            label,
 
            target: EndWhileStatementId::new_invalid(),
 
        }))
 
    }
 

	
 
    fn consume_continue_statement(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<ContinueStatementId, ParseError> {
 
        let continue_span = consume_exact_ident(&module.source, iter, KW_STMT_CONTINUE)?;
 
        let label=  if Some(TokenKind::Ident) == iter.next() {
 
            let label = consume_ident_interned(&module.source, iter, ctx)?;
 
            Some(label)
 
        } else {
 
            None
 
        };
 
        consume_token(&module.source, iter, TokenKind::SemiColon)?;
 
        Ok(ctx.heap.alloc_continue_statement(|this| ContinueStatement{
 
            this,
 
            span: continue_span,
 
            label,
 
            target: WhileStatementId::new_invalid(),
 
        }))
 
    }
 

	
 
    fn consume_synchronous_statement(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<SynchronousStatementId, ParseError> {
 
        let synchronous_span = consume_exact_ident(&module.source, iter, KW_STMT_SYNC)?;
 
        let body = self.consume_block_or_wrapped_statement(module, iter, ctx)?;
 
        let body = self.consume_statement(module, iter, ctx)?;
 

	
 
        Ok(ctx.heap.alloc_synchronous_statement(|this| SynchronousStatement{
 
            this,
 
            span: synchronous_span,
 
            scope: ScopeId::new_invalid(),
 
            body,
 
            end_sync: EndSynchronousStatementId::new_invalid(),
 
        }))
 
    }
 

	
 
    fn consume_fork_statement(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<ForkStatementId, ParseError> {
 
        let fork_span = consume_exact_ident(&module.source, iter, KW_STMT_FORK)?;
 
        let left_body = self.consume_block_or_wrapped_statement(module, iter, ctx)?;
 
        let left_body = self.consume_statement(module, iter, ctx)?;
 

	
 
        let right_body = if has_ident(&module.source, iter, KW_STMT_OR) {
 
            iter.consume();
 
            let right_body = self.consume_block_or_wrapped_statement(module, iter, ctx)?;
 
            let right_body = self.consume_statement(module, iter, ctx)?;
 
            Some(right_body)
 
        } else {
 
            None
 
        };
 

	
 
        Ok(ctx.heap.alloc_fork_statement(|this| ForkStatement{
 
            this,
 
            span: fork_span,
 
            left_body,
 
            right_body,
 
            end_fork: EndForkStatementId::new_invalid(),
 
        }))
 
    }
 

	
 
    fn consume_select_statement(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<SelectStatementId, ParseError> {
 
        let select_span = consume_exact_ident(&module.source, iter, KW_STMT_SELECT)?;
 
        consume_token(&module.source, iter, TokenKind::OpenCurly)?;
 

	
 
        let mut cases = Vec::new();
 
        let mut next = iter.next();
 

	
 
        while Some(TokenKind::CloseCurly) != next {
 
            let guard = match self.maybe_consume_memory_statement_without_semicolon(module, iter, ctx)? {
 
                Some(guard_mem_stmt) => guard_mem_stmt.upcast().upcast(),
 
                None => {
 
                    let start_pos = iter.last_valid_pos();
 
                    let expr = self.consume_expression(module, iter, ctx)?;
 
                    let end_pos = iter.last_valid_pos();
 

	
 
                    let guard_expr_stmt = ctx.heap.alloc_expression_statement(|this| ExpressionStatement{
 
                        this,
 
                        span: InputSpan::from_positions(start_pos, end_pos),
 
                        expression: expr,
 
                        next: StatementId::new_invalid(),
 
                    });
 

	
 
                    guard_expr_stmt.upcast()
 
                },
 
            };
 
            consume_token(&module.source, iter, TokenKind::ArrowRight)?;
 
            let block = self.consume_block_or_wrapped_statement(module, iter, ctx)?;
 
            let block = self.consume_statement(module, iter, ctx)?;
 
            cases.push(SelectCase{
 
                guard, block,
 
                guard,
 
                body: block,
 
                scope: ScopeId::new_invalid(),
 
                involved_ports: Vec::with_capacity(1)
 
            });
 

	
 
            next = iter.next();
 
        }
 

	
 
        consume_token(&module.source, iter, TokenKind::CloseCurly)?;
 

	
 
        Ok(ctx.heap.alloc_select_statement(|this| SelectStatement{
 
            this,
 
            span: select_span,
 
            cases,
 
            end_select: EndSelectStatementId::new_invalid(),
 
        }))
 
    }
 

	
 
    fn consume_return_statement(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<ReturnStatementId, ParseError> {
 
        let return_span = consume_exact_ident(&module.source, iter, KW_STMT_RETURN)?;
 
        let mut scoped_section = self.expressions.start_section();
 

	
 
        consume_comma_separated_until(
 
            TokenKind::SemiColon, &module.source, iter, ctx,
 
@@ -834,154 +781,130 @@ impl PassDefinitions {
 
                }],
 
                channel_span.end
 
            )
 
        };
 

	
 
        let from_identifier = consume_ident_interned(&module.source, iter, ctx)?;
 
        consume_token(&module.source, iter, TokenKind::ArrowRight)?;
 
        let to_identifier = consume_ident_interned(&module.source, iter, ctx)?;
 
        consume_token(&module.source, iter, TokenKind::SemiColon)?;
 

	
 
        // Construct ports
 
        let port_type_span = InputSpan::from_positions(channel_span.begin, end_pos);
 
        let port_type_len = inner_port_type.len() + 1;
 
        let mut from_port_type = ParserType{ elements: Vec::with_capacity(port_type_len), full_span: port_type_span };
 
        from_port_type.elements.push(ParserTypeElement{
 
            element_span: channel_span,
 
            variant: ParserTypeVariant::Output,
 
        });
 
        from_port_type.elements.extend_from_slice(&inner_port_type);
 
        let from = ctx.heap.alloc_variable(|this| Variable{
 
            this,
 
            kind: VariableKind::Local,
 
            identifier: from_identifier,
 
            parser_type: from_port_type,
 
            relative_pos_in_block: 0,
 
            relative_pos_in_parent: 0,
 
            unique_id_in_scope: -1,
 
        });
 

	
 
        let mut to_port_type = ParserType{ elements: Vec::with_capacity(port_type_len), full_span: port_type_span };
 
        to_port_type.elements.push(ParserTypeElement{
 
            element_span: channel_span,
 
            variant: ParserTypeVariant::Input
 
        });
 
        to_port_type.elements.extend_from_slice(&inner_port_type);
 
        let to = ctx.heap.alloc_variable(|this|Variable{
 
            this,
 
            kind: VariableKind::Local,
 
            identifier: to_identifier,
 
            parser_type: to_port_type,
 
            relative_pos_in_block: 0,
 
            relative_pos_in_parent: 0,
 
            unique_id_in_scope: -1,
 
        });
 

	
 
        // Construct the channel
 
        Ok(ctx.heap.alloc_channel_statement(|this| ChannelStatement{
 
            this,
 
            span: channel_span,
 
            from, to,
 
            relative_pos_in_block: 0,
 
            relative_pos_in_parent: 0,
 
            next: StatementId::new_invalid(),
 
        }))
 
    }
 

	
 
    fn consume_labeled_statement(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx, section: &mut ScopedSection<StatementId>
 
    ) -> Result<(), ParseError> {
 
    fn consume_labeled_statement(&mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx) -> Result<LabeledStatementId, ParseError> {
 
        let label = consume_ident_interned(&module.source, iter, ctx)?;
 
        consume_token(&module.source, iter, TokenKind::Colon)?;
 

	
 
        // Not pretty: consume_statement may produce more than one statement.
 
        // The values in the section need to be in the correct order if some
 
        // kind of outer block is consumed, so we take another section, push
 
        // the expressions in that one, and then allocate the labeled statement.
 
        let mut inner_section = self.statements.start_section();
 
        self.consume_statement(module, iter, ctx, &mut inner_section)?;
 
        debug_assert!(inner_section.len() >= 1);
 

	
 
        let inner_stmt_id = self.consume_statement(module, iter, ctx)?;
 
        let stmt_id = ctx.heap.alloc_labeled_statement(|this| LabeledStatement {
 
            this,
 
            label,
 
            body: inner_section[0],
 
            relative_pos_in_block: 0,
 
            body: inner_stmt_id,
 
            relative_pos_in_parent: 0,
 
            in_sync: SynchronousStatementId::new_invalid(),
 
        });
 

	
 
        if inner_section.len() == 1 {
 
            // Produce the labeled statement pointing to the first statement.
 
            // This is by far the most common case.
 
            inner_section.forget();
 
            section.push(stmt_id.upcast());
 
        } else {
 
            // Produce the labeled statement using the first statement, and push
 
            // the remaining ones at the end.
 
            let inner_statements = inner_section.into_vec();
 
            section.push(stmt_id.upcast());
 
            for idx in 1..inner_statements.len() {
 
                section.push(inner_statements[idx])
 
            }
 
        }
 

	
 
        Ok(())
 
        return Ok(stmt_id);
 
    }
 

	
 
    /// Attempts to consume a memory statement (a statement along the lines of
 
    /// `type var_name = initial_expr`). Will return `Ok(None)` if it didn't
 
    /// seem like there was a memory statement, `Ok(Some(...))` if there was
 
    /// one, and `Err(...)` if its reasonable to assume that there was a memory
 
    /// statement, but we failed to parse it.
 
    fn maybe_consume_memory_statement_without_semicolon(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<Option<MemoryStatementId>, ParseError> {
 
        // This is a bit ugly. It would be nicer if we could somehow
 
        // consume the expression with a type hint if we do get a valid
 
        // type, but we don't get an identifier following it
 
        let iter_state = iter.save();
 
        let definition_id = self.cur_definition;
 
        let poly_vars = ctx.heap[definition_id].poly_vars();
 

	
 
        let parser_type = self.type_parser.consume_parser_type(
 
            iter, &ctx.heap, &module.source, &ctx.symbols, poly_vars,
 
            definition_id, SymbolScope::Definition(definition_id), true, None
 
        );
 

	
 
        if let Ok(parser_type) = parser_type {
 
            if Some(TokenKind::Ident) == iter.next() {
 
                // Assume this is a proper memory statement
 
                let identifier = consume_ident_interned(&module.source, iter, ctx)?;
 
                let memory_span = InputSpan::from_positions(parser_type.full_span.begin, identifier.span.end);
 
                let assign_span = consume_token(&module.source, iter, TokenKind::Equal)?;
 

	
 
                let initial_expr_id = self.consume_expression(module, iter, ctx)?;
 
                let initial_expr_end_pos = iter.last_valid_pos();
 

	
 
                // Create the AST variable
 
                let local_id = ctx.heap.alloc_variable(|this| Variable{
 
                    this,
 
                    kind: VariableKind::Local,
 
                    identifier: identifier.clone(),
 
                    parser_type,
 
                    relative_pos_in_block: 0,
 
                    relative_pos_in_parent: 0,
 
                    unique_id_in_scope: -1,
 
                });
 

	
 
                // Create the initial assignment expression
 
                // Note: we set the initial variable declaration here
 
                let variable_expr_id = ctx.heap.alloc_variable_expression(|this| VariableExpression{
 
                    this,
 
                    identifier,
 
                    declaration: Some(local_id),
 
                    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,
 
                    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,
 
                });
 

	
 
@@ -1862,32 +1785,32 @@ fn consume_polymorphic_vars_spilled(source: &InputSource, iter: &mut TokenIter,
 
    )?;
 
    Ok(())
 
}
 

	
 
/// Consumes the parameter list to functions/components
 
fn consume_parameter_list(
 
    parser: &mut ParserTypeParser, source: &InputSource, iter: &mut TokenIter,
 
    ctx: &mut PassCtx, target: &mut ScopedSection<VariableId>,
 
    scope: SymbolScope, definition_id: DefinitionId
 
) -> Result<(), ParseError> {
 
    consume_comma_separated(
 
        TokenKind::OpenParen, TokenKind::CloseParen, source, iter, ctx,
 
        |source, iter, ctx| {
 
            let poly_vars = ctx.heap[definition_id].poly_vars(); // Rust being rust, multiple lookups
 
            let parser_type = parser.consume_parser_type(
 
                iter, &ctx.heap, source, &ctx.symbols, poly_vars, definition_id,
 
                scope, false, None
 
            )?;
 
            let identifier = consume_ident_interned(source, iter, ctx)?;
 
            let parameter_id = ctx.heap.alloc_variable(|this| Variable{
 
                this,
 
                kind: VariableKind::Parameter,
 
                parser_type,
 
                identifier,
 
                relative_pos_in_block: 0,
 
                relative_pos_in_parent: 0,
 
                unique_id_in_scope: -1,
 
            });
 
            Ok(parameter_id)
 
        },
 
        target, "a parameter", "a parameter list", None
 
    )
 
}
 
\ No newline at end of file
src/protocol/parser/pass_rewriting.rs
Show inline comments
 
@@ -38,102 +38,103 @@ impl Visitor for PassRewriting {
 

	
 
    // --- Visiting statements (that are not the select statement)
 

	
 
    fn visit_block_stmt(&mut self, ctx: &mut Ctx, id: BlockStatementId) -> VisitorResult {
 
        let block_stmt = &ctx.heap[id];
 
        let stmt_section = self.statement_buffer.start_section_initialized(&block_stmt.statements);
 

	
 
        self.current_scope = id;
 
        for stmt_idx in 0..stmt_section.len() {
 
            self.visit_stmt(ctx, stmt_section[stmt_idx])?;
 
        }
 

	
 
        stmt_section.forget();
 
        return Ok(())
 
    }
 

	
 
    fn visit_labeled_stmt(&mut self, ctx: &mut Ctx, id: LabeledStatementId) -> VisitorResult {
 
        let labeled_stmt = &ctx.heap[id];
 
        let body_id = labeled_stmt.body;
 
        return self.visit_stmt(ctx, body_id);
 
    }
 

	
 
    fn visit_if_stmt(&mut self, ctx: &mut Ctx, id: IfStatementId) -> VisitorResult {
 
        let if_stmt = &ctx.heap[id];
 
        let true_body_id = if_stmt.true_body;
 
        let false_body_id = if_stmt.false_body;
 
        let true_body_id = if_stmt.true_case;
 
        let false_body_id = if_stmt.false_case;
 

	
 
        self.visit_block_stmt(ctx, true_body_id)?;
 
        self.visit_stmt(ctx, true_body_id.body)?;
 
        if let Some(false_body_id) = false_body_id {
 
            self.visit_block_stmt(ctx, false_body_id)?;
 
            self.visit_stmt(ctx, false_body_id.body)?;
 
        }
 

	
 
        return Ok(())
 
    }
 

	
 
    fn visit_while_stmt(&mut self, ctx: &mut Ctx, id: WhileStatementId) -> VisitorResult {
 
        let while_stmt = &ctx.heap[id];
 
        let body_id = while_stmt.body;
 
        return self.visit_block_stmt(ctx, body_id);
 
        return self.visit_stmt(ctx, body_id);
 
    }
 

	
 
    fn visit_synchronous_stmt(&mut self, ctx: &mut Ctx, id: SynchronousStatementId) -> VisitorResult {
 
        let sync_stmt = &ctx.heap[id];
 
        let body_id = sync_stmt.body;
 
        return self.visit_block_stmt(ctx, body_id);
 
        return self.visit_stmt(ctx, body_id);
 
    }
 

	
 
    // --- Visiting the select statement
 

	
 
    fn visit_select_stmt(&mut self, ctx: &mut Ctx, id: SelectStatementId) -> VisitorResult {
 
        // Utility for the last stage of rewriting process
 
        // Utility for the last stage of rewriting process. Note that caller
 
        // still needs to point the end of the if-statement to the end of the
 
        // replacement statement of the select statement.
 
        fn transform_select_case_code(ctx: &mut Ctx, select_id: SelectStatementId, case_index: usize, select_var_id: VariableId) -> (IfStatementId, EndIfStatementId) {
 
            // Retrieve statement IDs associated with case
 
            let case = &ctx.heap[select_id].cases[case_index];
 
            let case_guard_id = case.guard;
 
            let case_body_id = case.block;
 
            let case_body_id = case.body;
 
            let case_scope_id = case.scope;
 

	
 
            // Create the if-statement for the result of the select statement
 
            let compare_expr_id = create_ast_equality_comparison_expr(ctx, select_var_id, case_index as u64);
 
            let (if_stmt_id, end_if_stmt_id) = create_ast_if_stmt(ctx, compare_expr_id.upcast(), case_body_id, None);
 

	
 
            // Modify body of case to link up to the surrounding statements
 
            // correctly
 
            let case_body = &mut ctx.heap[case_body_id];
 
            let case_end_body_id = case_body.end_block;
 
            case_body.statements.insert(0, case_guard_id);
 
            let true_case = IfStatementCase{
 
                body: case_guard_id, // which is linked up to the body
 
                scope: case_scope_id,
 
            };
 
            let (if_stmt_id, end_if_stmt_id) = create_ast_if_stmt(ctx, compare_expr_id.upcast(), true_case, None);
 

	
 
            let case_end_body = &mut ctx.heap[case_end_body_id];
 
            case_end_body.next = end_if_stmt_id.upcast();
 
            // Link up body statement to end-if
 
            set_ast_statement_next(ctx, case_body_id, end_if_stmt_id.upcast());
 

	
 
            return (if_stmt_id, end_if_stmt_id)
 
        }
 

	
 
        // We're going to transform the select statement by a block statement
 
        // containing builtin runtime-calls. And to do so we create temporary
 
        // variables and move some other statements around.
 
        let select_stmt = &ctx.heap[id];
 
        let mut total_num_cases = select_stmt.cases.len();
 
        let total_num_cases = select_stmt.cases.len();
 
        let mut total_num_ports = 0;
 
        let end_select_stmt_id = select_stmt.end_select;
 
        let end_select = &ctx.heap[end_select_stmt_id];
 
        let stmt_id_after_select_stmt = end_select.next;
 

	
 
        // Put heap IDs into temporary buffers to handle borrowing rules
 
        let mut call_id_section = self.call_expr_buffer.start_section();
 
        let mut expr_id_section = self.expression_buffer.start_section();
 

	
 
        for case in select_stmt.cases.iter() {
 
            total_num_ports += case.involved_ports.len();
 
            for (call_id, expr_id) in case.involved_ports.iter().copied() {
 
                call_id_section.push(call_id);
 
                expr_id_section.push(expr_id);
 
            }
 
        }
 

	
 
        // Transform all of the call expressions by takings its argument (the
 
        // port from which we `get`) and turning it into a temporary variable.
 
        let mut transformed_stmts = Vec::with_capacity(total_num_ports); // TODO: Recompute this preallocated length, put assert at the end
 
        let mut locals = Vec::with_capacity(total_num_ports);
 

	
 
        for port_var_idx in 0..call_id_section.len() {
 
            let get_call_expr_id = call_id_section[port_var_idx];
 
@@ -144,132 +145,128 @@ impl Visitor for PassRewriting {
 
            let variable_decl_stmt_id = create_ast_variable_declaration_stmt(ctx, variable_id, port_expr_id);
 

	
 
            // Replace the original port expression in the call with a reference
 
            // to the replacement variable
 
            let variable_expr_id = create_ast_variable_expr(ctx, variable_id);
 
            let call_expr = &mut ctx.heap[get_call_expr_id];
 
            call_expr.arguments[0] = variable_expr_id.upcast();
 

	
 
            transformed_stmts.push(variable_decl_stmt_id.upcast().upcast());
 
            locals.push(variable_id);
 
        }
 

	
 
        // Insert runtime calls that facilitate the semantics of the select
 
        // block.
 

	
 
        // Create the call that indicates the start of the select block
 
        {
 
            let num_cases_expression_id = create_ast_literal_integer_expr(ctx, total_num_cases as u64);
 
            let num_ports_expression_id = create_ast_literal_integer_expr(ctx, total_num_ports as u64);
 
            let arguments = vec![
 
                num_cases_expression_id.upcast(),
 
                num_ports_expression_id.upcast()
 
            ];
 

	
 
            let call_expression_id = create_ast_call_expr(ctx, Method::SelectStart, arguments);
 
            let call_expression_id = create_ast_call_expr(ctx, Method::SelectStart, &mut self.expression_buffer, arguments);
 
            let call_statement_id = create_ast_expression_stmt(ctx, call_expression_id.upcast());
 

	
 
            transformed_stmts.push(call_statement_id.upcast());
 
        }
 

	
 
        // Create calls for each select case that will register the ports that
 
        // we are waiting on at the runtime.
 
        {
 
            let mut total_port_index = 0;
 
            for case_index in 0..total_num_cases {
 
                let case = &ctx.heap[id].cases[case_index];
 
                let case_num_ports = case.involved_ports.len();
 

	
 
                for case_port_index in 0..case_num_ports {
 
                    // Arguments to runtime call
 
                    let port_variable_id = locals[total_port_index]; // so far this variable contains the temporary variables for the port expressions
 
                    let case_index_expr_id = create_ast_literal_integer_expr(ctx, case_index as u64);
 
                    let port_index_expr_id = create_ast_literal_integer_expr(ctx, case_port_index as u64);
 
                    let port_variable_expr_id = create_ast_variable_expr(ctx, port_variable_id);
 
                    let runtime_call_arguments = vec![
 
                        case_index_expr_id.upcast(),
 
                        port_index_expr_id.upcast(),
 
                        port_variable_expr_id.upcast()
 
                    ];
 

	
 
                    // Create runtime call, then store it
 
                    let runtime_call_expr_id = create_ast_call_expr(ctx, Method::SelectRegisterCasePort, runtime_call_arguments);
 
                    let runtime_call_expr_id = create_ast_call_expr(ctx, Method::SelectRegisterCasePort, &mut self.expression_buffer, runtime_call_arguments);
 
                    let runtime_call_stmt_id = create_ast_expression_stmt(ctx, runtime_call_expr_id.upcast());
 

	
 
                    transformed_stmts.push(runtime_call_stmt_id.upcast());
 

	
 
                    total_port_index += 1;
 
                }
 
            }
 
        }
 

	
 
        // Create the variable that will hold the result of a completed select
 
        // block. Then create the runtime call that will produce this result
 
        let select_variable_id = create_ast_variable(ctx);
 
        locals.push(select_variable_id);
 

	
 
        {
 
            let runtime_call_expr_id = create_ast_call_expr(ctx, Method::SelectWait, Vec::new());
 
            let runtime_call_expr_id = create_ast_call_expr(ctx, Method::SelectWait, &mut self.expression_buffer, Vec::new());
 
            let variable_stmt_id = create_ast_variable_declaration_stmt(ctx, select_variable_id, runtime_call_expr_id.upcast());
 
            transformed_stmts.push(variable_stmt_id.upcast().upcast());
 
        }
 

	
 
        call_id_section.forget();
 
        expr_id_section.forget();
 

	
 
        // Precreate the block statement that will be the replacement of the
 
        // select statement. Do not set its members yet.
 
        let replacement_stmt_id = ctx.heap.alloc_block_statement(|this| BlockStatement{
 
            this,
 
            is_implicit: true,
 
            span: InputSpan::new(),
 
            statements: Vec::new(),
 
            end_block: EndBlockStatementId::new_invalid(),
 
            scope_node: ScopeNode::new_invalid(),
 
            first_unique_id_in_scope: -1,
 
            next_unique_id_in_scope: -1,
 
            locals: Vec::new(),
 
            labels: Vec::new(),
 
            scope: ScopeId::new_invalid(),
 
            next: StatementId::new_invalid(),
 
        });
 
        let end_block_id = ctx.heap.alloc_end_block_statement(|this| EndBlockStatement{
 
            this,
 
            start_block: replacement_stmt_id,
 
            next: stmt_id_after_select_stmt,
 
        });
 

	
 
        // Now we transform each of the select block case's guard and code into
 
        // a chained if-else statement.
 
        if total_num_cases > 0 {
 
            let (if_stmt_id, end_if_stmt_id) = transform_select_case_code(ctx, id, 0, select_variable_id);
 
            let mut last_if_stmt_id = if_stmt_id;
 
            let mut last_end_if_stmt_id = end_if_stmt_id;
 
            transformed_stmts.push(last_if_stmt_id.upcast());
 

	
 
            for case_index in 1..total_num_cases {
 
                let (if_stmt_id, end_if_stmt_id) = transform_select_case_code(ctx, id, case_index, select_variable_id);
 
                let last_if_stmt = &mut ctx.heap[last_if_stmt_id];
 
                last_if_stmt.false_body = Some(if_stmt_id.upcast());
 
                // last_if_stmt.false_case = Some(if_stmt_id.upcast());
 

	
 
                // TODO:
 
                //  1. Change scoping such that it is a separate datastructure with separate IDs
 
                //  2. Change statements that contain "implicit scopes" to explicitly point to the appropriate scopes
 
                //  3. Continue here setting the true-body and false-body.
 
                //  4. Figure out how we're going to link everything up again
 
            }
 
        }
 

	
 
        // let block = ctx.heap.alloc_block_statement(|this| BlockStatement{
 
        //     this,
 
        //     is_implicit: true,
 
        //     span: stmt.span,
 
        //     statements: vec![],
 
        //     end_block: EndBlockStatementId(),
 
        //     scope_node: ScopeNode {},
 
        //     first_unique_id_in_scope: 0,
 
        //     next_unique_id_in_scope: 0,
 
        //     locals,
 
        //     labels: vec![],
 
        //     next: ()
 
        // });
 

	
 
        return Ok(())
 
@@ -344,83 +341,85 @@ impl PassRewriting {
 
        let variable_expr = &mut ctx.heap[variable_expr_id];
 
        variable_expr.parent = ExpressionParent::Expression(initial_expr_id.upcast(), 0);
 
        let runtime_call_expr = &mut ctx.heap[runtime_call_expr_id];
 
        runtime_call_expr.parent = ExpressionParent::Expression(initial_expr_id.upcast(), 1);
 
        let initial_expr = &mut ctx.heap[initial_expr_id];
 
        initial_expr.parent = ExpressionParent::Memory(variable_statement_id);
 

	
 
        return (variable_id, variable_statement_id);
 
    }
 
}
 

	
 
// -----------------------------------------------------------------------------
 
// Utilities to create compiler-generated AST nodes
 
// -----------------------------------------------------------------------------
 

	
 
fn create_ast_variable(ctx: &mut Ctx) -> VariableId {
 
    return ctx.heap.alloc_variable(|this| Variable{
 
        this,
 
        kind: VariableKind::Local,
 
        parser_type: ParserType{
 
            elements: Vec::new(),
 
            full_span: InputSpan::new(),
 
        },
 
        identifier: Identifier::new_empty(InputSpan::new()),
 
        relative_pos_in_block: -1,
 
        relative_pos_in_parent: -1,
 
        unique_id_in_scope: -1,
 
    });
 
}
 

	
 
fn create_ast_variable_expr(ctx: &mut Ctx, variable_id: VariableId) -> VariableExpressionId {
 
    return ctx.heap.alloc_variable_expression(|this| VariableExpression{
 
        this,
 
        identifier: Identifier::new_empty(InputSpan::new()),
 
        declaration: Some(variable_id),
 
        used_as_binding_target: false,
 
        parent: ExpressionParent::None,
 
        unique_id_in_definition: -1
 
    });
 
}
 

	
 
fn create_ast_call_expr(ctx: &mut Ctx, method: Method, arguments: Vec<ExpressionId>) -> CallExpressionId {
 
fn create_ast_call_expr(ctx: &mut Ctx, method: Method, buffer: &mut ScopedBuffer<ExpressionId>, arguments: Vec<ExpressionId>) -> CallExpressionId {
 
    let expression_ids = buffer.start_section_initialized(&arguments);
 
    let call_expression_id = ctx.heap.alloc_call_expression(|this| CallExpression{
 
        this,
 
        func_span: InputSpan::new(),
 
        full_span: InputSpan::new(),
 
        parser_type: ParserType{
 
            elements: Vec::new(),
 
            full_span: InputSpan::new(),
 
        },
 
        method,
 
        arguments,
 
        definition: DefinitionId::new_invalid(),
 
        parent: ExpressionParent::None,
 
        unique_id_in_definition: -1,
 
    });
 

	
 
    for (argument_index, argument_id) in arguments.iter().cloned().enumerate() {
 
    for argument_index in 0..expression_ids.len() {
 
        let argument_id = expression_ids[argument_index];
 
        let argument_expr = &mut ctx.heap[argument_id];
 
        *argument_expr.parent_mut() = ExpressionParent::Expression(call_expression_id.upcat(), argument_index as u32);
 
        *argument_expr.parent_mut() = ExpressionParent::Expression(call_expression_id.upcast(), argument_index as u32);
 
    }
 

	
 
    return call_expression_id;
 
}
 

	
 
fn create_ast_literal_integer_expr(ctx: &mut Ctx, unsigned_value: u64) -> LiteralExpressionId {
 
    return ctx.heap.alloc_literal_expression(|this| LiteralExpression{
 
        this,
 
        span: InputSpan::new(),
 
        value: Literal::Integer(LiteralInteger{
 
            unsigned_value,
 
            negated: false,
 
        }),
 
        parent: ExpressionParent::None,
 
        unique_id_in_definition: -1
 
    });
 
}
 

	
 
fn create_ast_equality_comparison_expr(ctx: &mut Ctx, variable_id: VariableId, value: u64) -> BinaryExpressionId {
 
    let var_expr_id = create_ast_variable_expr(ctx, variable_id);
 
    let int_expr_id = create_ast_literal_integer_expr(ctx, value);
 
    let cmp_expr_id = ctx.heap.alloc_binary_expression(|this| BinaryExpression{
 
        this,
 
        operator_span: InputSpan::new(),
 
@@ -467,72 +466,121 @@ fn create_ast_variable_declaration_stmt(ctx: &mut Ctx, variable_id: VariableId,
 
        parent: ExpressionParent::None,
 
        unique_id_in_definition: -1,
 
    });
 

	
 
    // Create the memory statement
 
    let memory_stmt_id = ctx.heap.alloc_memory_statement(|this| MemoryStatement{
 
        this,
 
        span: InputSpan::new(),
 
        variable: variable_id,
 
        initial_expr: assignment_expr_id,
 
        next: StatementId::new_invalid(),
 
    });
 

	
 
    // Set all parents which we can access
 
    let variable_expr = &mut ctx.heap[variable_expr_id];
 
    variable_expr.parent = ExpressionParent::Expression(assignment_expr_id.upcast(), 0);
 
    let value_expr = &mut ctx.heap[initial_value_expr_id];
 
    *value_expr.parent_mut() = ExpressionParent::Expression(assignment_expr_id.upcast(), 1);
 
    let assignment_expr = &mut ctx.heap[assignment_expr_id];
 
    assignment_expr.parent = ExpressionParent::Memory(memory_stmt_id);
 

	
 
    return memory_stmt_id;
 
}
 

	
 
fn create_ast_if_stmt(ctx: &mut Ctx, condition_expression_id: ExpressionId, true_body: BlockStatementId, false_body: Option<BlockStatementId>) -> (IfStatementId, EndIfStatementId) {
 
fn create_ast_if_stmt(ctx: &mut Ctx, condition_expression_id: ExpressionId, true_case: IfStatementCase, false_case: Option<IfStatementCase>) -> (IfStatementId, EndIfStatementId) {
 
    // Create if statement and the end-if statement
 
    let if_stmt_id = ctx.heap.alloc_if_statement(|this| IfStatement{
 
        this,
 
        span: InputSpan::new(),
 
        test: condition_expression_id,
 
        true_body,
 
        false_body,
 
        true_case,
 
        false_case,
 
        end_if: EndIfStatementId::new_invalid()
 
    });
 

	
 
    let end_if_stmt_id = ctx.heap.alloc_end_if_statement(|this| EndIfStatement{
 
        this,
 
        start_if: if_stmt_id,
 
        next: StatementId::new_invalid(),
 
    });
 

	
 
    // Link the statements up as much as we can
 
    let if_stmt = &mut ctx.heap[if_stmt_id];
 
    if_stmt.end_if = end_if_stmt_id;
 

	
 
    let condition_expr = &mut ctx.heap[condition_expression_id];
 
    *condition_expr.parent_mut() = ExpressionParent::If(if_stmt_id);
 

	
 
    let true_body_stmt = &ctx.heap[true_body];
 
    let true_body_end_stmt = &mut ctx.heap[true_body_stmt.end_block];
 
    true_body_end_stmt.next = end_if_stmt_id.upcast();
 

	
 
    if let Some(false_body) = false_body {
 
        let false_body_stmt = &ctx.heap[false_body];
 
        let false_body_end_stmt = &mut ctx.heap[false_body_stmt.end_block];
 
        false_body_end_stmt.next = end_if_stmt_id.upcast();
 
    }
 

	
 
    return (if_stmt_id, end_if_stmt_id);
 
}
 

	
 
fn set_ast_if_statement_false_body(ctx: &mut Ctx, if_statement_id: IfStatementId, end_if_statement_id: EndIfStatementId, false_body: BlockStatementId) {
 
fn set_ast_if_statement_false_body(ctx: &mut Ctx, if_statement_id: IfStatementId, end_if_statement_id: EndIfStatementId, false_body_id: StatementId) {
 
    // Point if-statement to "false body"
 
    todo!("set scopes");
 
    let if_stmt = &mut ctx.heap[if_statement_id];
 
    debug_assert!(if_stmt.false_body.is_none()); // simplifies logic, not necessary
 
    if_stmt.false_body = Some(false_body);
 
    debug_assert!(if_stmt.false_case.is_none()); // simplifies logic, not necessary
 
    if_stmt.false_case = Some(IfStatementCase{
 
        body: false_body_id,
 
        scope: ScopeId::new_invalid(),
 
    });
 

	
 
    // Point end of false body to the end of the if statement
 
    let false_body_stmt = &ctx.heap[false_body];
 
    let false_body_end_stmt = &mut ctx.heap[false_body_stmt.end_block];
 
    false_body_end_stmt.next = end_if_statement_id.upcast();
 
    set_ast_statement_next(ctx, false_body_id, end_if_statement_id.upcast());
 
}
 

	
 
/// Sets the specified AST statement's control flow such that it will be
 
/// followed by the target statement. This may seem obvious, but may imply that
 
/// a statement associated with, but different from, the source statement is
 
/// modified.
 
fn set_ast_statement_next(ctx: &mut Ctx, source_stmt_id: StatementId, target_stmt_id: StatementId) {
 
    let source_stmt = &mut ctx.heap[source_stmt_id];
 
    match source_stmt {
 
        Statement::Block(stmt) => {
 
            let end_id = stmt.end_block;
 
            ctx.heap[end_id].next = target_stmt_id
 
        },
 
        Statement::EndBlock(stmt) => stmt.next = target_stmt_id,
 
        Statement::Local(stmt) => {
 
            match stmt {
 
                LocalStatement::Memory(stmt) => stmt.next = target_stmt_id,
 
                LocalStatement::Channel(stmt) => stmt.next = target_stmt_id,
 
            }
 
        },
 
        Statement::Labeled(stmt) => {
 
            let body_id = stmt.body;
 
            set_ast_statement_next(ctx, body_id, target_stmt_id);
 
        },
 
        Statement::If(stmt) => {
 
            let end_id = stmt.end_if;
 
            ctx.heap[end_id].next = target_stmt_id;
 
        },
 
        Statement::EndIf(stmt) => stmt.next = target_stmt_id,
 
        Statement::While(stmt) => {
 
            let end_id = stmt.end_while;
 
            ctx.heap[end_id].next = target_stmt_id;
 
        },
 
        Statement::EndWhile(stmt) => stmt.next = target_stmt_id,
 

	
 
        Statement::Break(_stmt) => {},
 
        Statement::Continue(_stmt) => {},
 
        Statement::Synchronous(stmt) => {
 
            let end_id = stmt.end_sync;
 
            ctx.heap[end_id].next = target_stmt_id;
 
        },
 
        Statement::EndSynchronous(stmt) => {
 
            stmt.next = target_stmt_id;
 
        },
 
        Statement::Fork(_) | Statement::EndFork(_) => {
 
            todo!("remove fork from language");
 
        },
 
        Statement::Select(stmt) => {
 
            let end_id = stmt.end_select;
 
            ctx.heap[end_id].next = target_stmt_id;
 
        },
 
        Statement::EndSelect(stmt) => stmt.next = target_stmt_id,
 
        Statement::Return(_stmt) => {},
 
        Statement::Goto(_stmt) => {},
 
        Statement::New(stmt) => stmt.next = target_stmt_id,
 
        Statement::Expression(stmt) => stmt.next = target_stmt_id,
 
    }
 
}
 
\ No newline at end of file
src/protocol/parser/pass_typing.rs
Show inline comments
 
@@ -35,49 +35,50 @@
 
///  4. Disallow certain types in certain operations (e.g. `Void`).
 

	
 
macro_rules! debug_log_enabled {
 
    () => { false };
 
}
 

	
 
macro_rules! debug_log {
 
    ($format:literal) => {
 
        enabled_debug_print!(false, "types", $format);
 
    };
 
    ($format:literal, $($args:expr),*) => {
 
        enabled_debug_print!(false, "types", $format, $($args),*);
 
    };
 
}
 

	
 
use std::collections::{HashMap, HashSet};
 

	
 
use crate::collections::{ScopedBuffer, ScopedSection, DequeSet};
 
use crate::protocol::ast::*;
 
use crate::protocol::input_source::ParseError;
 
use crate::protocol::parser::ModuleCompilationPhase;
 
use crate::protocol::parser::type_table::*;
 
use crate::protocol::parser::token_parsing::*;
 
use super::visitor::{
 
    BUFFER_INIT_CAPACITY,
 
    BUFFER_INIT_CAP_LARGE,
 
    BUFFER_INIT_CAP_SMALL,
 
    Ctx,
 
    Visitor,
 
    VisitorResult
 
};
 

	
 
const VOID_TEMPLATE: [InferenceTypePart; 1] = [ InferenceTypePart::Void ];
 
const MESSAGE_TEMPLATE: [InferenceTypePart; 2] = [ InferenceTypePart::Message, InferenceTypePart::UInt8 ];
 
const BOOL_TEMPLATE: [InferenceTypePart; 1] = [ InferenceTypePart::Bool ];
 
const CHARACTER_TEMPLATE: [InferenceTypePart; 1] = [ InferenceTypePart::Character ];
 
const STRING_TEMPLATE: [InferenceTypePart; 2] = [ InferenceTypePart::String, InferenceTypePart::Character ];
 
const NUMBERLIKE_TEMPLATE: [InferenceTypePart; 1] = [ InferenceTypePart::NumberLike ];
 
const INTEGERLIKE_TEMPLATE: [InferenceTypePart; 1] = [ InferenceTypePart::IntegerLike ];
 
const ARRAY_TEMPLATE: [InferenceTypePart; 2] = [ InferenceTypePart::Array, InferenceTypePart::Unknown ];
 
const SLICE_TEMPLATE: [InferenceTypePart; 2] = [ InferenceTypePart::Slice, InferenceTypePart::Unknown ];
 
const ARRAYLIKE_TEMPLATE: [InferenceTypePart; 2] = [ InferenceTypePart::ArrayLike, InferenceTypePart::Unknown ];
 

	
 
/// TODO: @performance Turn into PartialOrd+Ord to simplify checks
 
#[derive(Debug, Clone, Eq, PartialEq)]
 
pub(crate) enum InferenceTypePart {
 
    // When we infer types of AST elements that support polymorphic arguments,
 
    // then we might have the case that multiple embedded types depend on the
 
    // polymorphic type (e.g. func bla(T a, T[] b) -> T[][]). If we can infer
 
    // the type in one place (e.g. argument a), then we may propagate this
 
    // information to other types (e.g. argument b and the return type). For
 
@@ -900,52 +901,52 @@ struct VarData {
 
    /// Type of the variable
 
    var_type: InferenceType,
 
    /// VariableExpressions that use the variable
 
    used_at: Vec<ExpressionId>,
 
    /// For channel statements we link to the other variable such that when one
 
    /// channel's interior type is resolved, we can also resolve the other one.
 
    linked_var: Option<VariableId>,
 
}
 

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

	
 
impl PassTyping {
 
    pub(crate) fn new() -> Self {
 
        PassTyping {
 
            reserved_idx: -1,
 
            definition_type: DefinitionType::Function(FunctionDefinitionId::new_invalid()),
 
            poly_vars: Vec::new(),
 
            var_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAPACITY),
 
            expr_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAPACITY),
 
            stmt_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAPACITY),
 
            bool_buffer: ScopedBuffer::with_capacity(16),
 
            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(),
 
            extra_data: Vec::new(),
 
            expr_queued: DequeSet::new(),
 
        }
 
    }
 

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

	
 
            let first_concrete_part = match definition {
 
                Definition::Function(definition) => {
 
                    if definition.poly_vars.is_empty() {
 
                        Some(ConcreteTypePart::Function(*definition_id, 0))
 
                    } else {
 
                        None
 
                    }
 
                }
 
                Definition::Component(definition) => {
 
                    if definition.poly_vars.is_empty() {
 
@@ -1109,102 +1110,102 @@ impl Visitor for PassTyping {
 

	
 
    fn visit_local_channel_stmt(&mut self, ctx: &mut Ctx, id: ChannelStatementId) -> VisitorResult {
 
        let channel_stmt = &ctx.heap[id];
 

	
 
        let from_local = &ctx.heap[channel_stmt.from];
 
        let from_var_type = self.determine_inference_type_from_parser_type_elements(&from_local.parser_type.elements, true);
 
        self.var_types.insert(from_local.this, VarData::new_channel(from_var_type, channel_stmt.to));
 

	
 
        let to_local = &ctx.heap[channel_stmt.to];
 
        let to_var_type = self.determine_inference_type_from_parser_type_elements(&to_local.parser_type.elements, true);
 
        self.var_types.insert(to_local.this, VarData::new_channel(to_var_type, channel_stmt.from));
 

	
 
        Ok(())
 
    }
 

	
 
    fn visit_labeled_stmt(&mut self, ctx: &mut Ctx, id: LabeledStatementId) -> VisitorResult {
 
        let labeled_stmt = &ctx.heap[id];
 
        let substmt_id = labeled_stmt.body;
 
        self.visit_stmt(ctx, substmt_id)
 
    }
 

	
 
    fn visit_if_stmt(&mut self, ctx: &mut Ctx, id: IfStatementId) -> VisitorResult {
 
        let if_stmt = &ctx.heap[id];
 

	
 
        let true_body_id = if_stmt.true_body;
 
        let false_body_id = if_stmt.false_body;
 
        let true_body_case = if_stmt.true_case;
 
        let false_body_case = if_stmt.false_case;
 
        let test_expr_id = if_stmt.test;
 

	
 
        self.visit_expr(ctx, test_expr_id)?;
 
        self.visit_block_stmt(ctx, true_body_id)?;
 
        if let Some(false_body_id) = false_body_id {
 
            self.visit_block_stmt(ctx, false_body_id)?;
 
        self.visit_stmt(ctx, true_body_case.body)?;
 
        if let Some(false_body_case) = false_body_case {
 
            self.visit_stmt(ctx, false_body_case.body)?;
 
        }
 

	
 
        Ok(())
 
    }
 

	
 
    fn visit_while_stmt(&mut self, ctx: &mut Ctx, id: WhileStatementId) -> VisitorResult {
 
        let while_stmt = &ctx.heap[id];
 

	
 
        let body_id = while_stmt.body;
 
        let test_expr_id = while_stmt.test;
 

	
 
        self.visit_expr(ctx, test_expr_id)?;
 
        self.visit_block_stmt(ctx, body_id)?;
 
        self.visit_stmt(ctx, body_id)?;
 

	
 
        Ok(())
 
    }
 

	
 
    fn visit_synchronous_stmt(&mut self, ctx: &mut Ctx, id: SynchronousStatementId) -> VisitorResult {
 
        let sync_stmt = &ctx.heap[id];
 
        let body_id = sync_stmt.body;
 

	
 
        self.visit_block_stmt(ctx, body_id)
 
        self.visit_stmt(ctx, body_id)
 
    }
 

	
 
    fn visit_fork_stmt(&mut self, ctx: &mut Ctx, id: ForkStatementId) -> VisitorResult {
 
        let fork_stmt = &ctx.heap[id];
 
        let left_body_id = fork_stmt.left_body;
 
        let right_body_id = fork_stmt.right_body;
 

	
 
        self.visit_block_stmt(ctx, left_body_id)?;
 
        self.visit_stmt(ctx, left_body_id)?;
 
        if let Some(right_body_id) = right_body_id {
 
            self.visit_block_stmt(ctx, right_body_id)?;
 
            self.visit_stmt(ctx, right_body_id)?;
 
        }
 

	
 
        Ok(())
 
    }
 

	
 
    fn visit_select_stmt(&mut self, ctx: &mut Ctx, id: SelectStatementId) -> VisitorResult {
 
        let select_stmt = &ctx.heap[id];
 

	
 
        let mut section = self.stmt_buffer.start_section();
 
        let num_cases = select_stmt.cases.len();
 

	
 
        for case in &select_stmt.cases {
 
            section.push(case.guard);
 
            section.push(case.block.upcast());
 
            section.push(case.body);
 
        }
 

	
 
        for case_index in 0..num_cases {
 
            let base_index = 2 * case_index;
 
            let guard_stmt_id = section[base_index    ];
 
            let block_stmt_id = section[base_index + 1];
 

	
 
            self.visit_stmt(ctx, guard_stmt_id)?;
 
            self.visit_stmt(ctx, block_stmt_id)?;
 
        }
 
        section.forget();
 

	
 
        Ok(())
 
    }
 

	
 
    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)
 
    }
 

	
 
    fn visit_new_stmt(&mut self, ctx: &mut Ctx, id: NewStatementId) -> VisitorResult {
 
@@ -3308,50 +3309,49 @@ impl PassTyping {
 
            EP::Memory(_) | EP::ExpressionStmt(_) =>
 
                // Determined during type inference
 
                InferenceType::new(false, false, vec![ITP::Unknown]),
 
            EP::Expression(parent_id, idx_in_parent) => {
 
                // If we are the test expression of a conditional expression,
 
                // then we must resolve to a boolean
 
                let is_conditional = if let Expression::Conditional(_) = &ctx.heap[*parent_id] {
 
                    true
 
                } else {
 
                    false
 
                };
 

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

	
 
        let infer_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,
 
@@ -3392,69 +3392,67 @@ impl PassTyping {
 
        // 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));
 
        }
 

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

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

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

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

	
 
    fn insert_initial_struct_polymorph_data(
 
        &mut self, ctx: &mut 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 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();
src/protocol/parser/pass_validation_linking.rs
Show inline comments
 
@@ -21,394 +21,411 @@
 
 * Setting the `next` statement: the AST is a tree, but during execution we walk
 
 * a linear path through all statements. So where appropriate a statement may
 
 * set the "previous statement" field of the pass to itself. When visiting the
 
 * subsequent statement it will check this "previous statement", and if set, it
 
 * will link this previous statement up to itself. Not every statement has a
 
 * previous statement. Hence there are two patterns that occur: assigning the
 
 * `next` value, then clearing the "previous statement" field. And assigning the
 
 * `next` value, and then putting the current statement's ID in the "previous
 
 * statement" field. Because it is so common, this file contain two macros that
 
 * perform that operation.
 
 *
 
 * To make storing types for polymorphic procedures simpler and more efficient,
 
 * we assign to each expression in the procedure a unique ID. This is what the
 
 * "next expression index" field achieves. Each expression simply takes the
 
 * current value, and then increments this counter.
 
 */
 

	
 
use crate::collections::{ScopedBuffer};
 
use crate::protocol::ast::*;
 
use crate::protocol::input_source::*;
 
use crate::protocol::parser::symbol_table::*;
 
use crate::protocol::parser::type_table::*;
 

	
 
use super::visitor::{
 
    BUFFER_INIT_CAPACITY,
 
    BUFFER_INIT_CAP_SMALL,
 
    BUFFER_INIT_CAP_LARGE,
 
    Ctx,
 
    Visitor,
 
    VisitorResult
 
};
 
use crate::protocol::parser::ModuleCompilationPhase;
 

	
 
#[derive(PartialEq, Eq)]
 
enum DefinitionType {
 
    Primitive(ComponentDefinitionId),
 
    Composite(ComponentDefinitionId),
 
    Function(FunctionDefinitionId)
 
}
 

	
 
impl DefinitionType {
 
    fn is_primitive(&self) -> bool { if let Self::Primitive(_) = self { true } else { false } }
 
    fn is_composite(&self) -> bool { if let Self::Composite(_) = self { true } else { false } }
 
    fn is_function(&self) -> bool { if let Self::Function(_) = self { true } else { false } }
 
    fn definition_id(&self) -> DefinitionId {
 
        match self {
 
            DefinitionType::Primitive(v) => v.upcast(),
 
            DefinitionType::Composite(v) => v.upcast(),
 
            DefinitionType::Function(v) => v.upcast(),
 
        }
 
    }
 
}
 

	
 
struct ControlFlowStatement {
 
    in_sync: SynchronousStatementId,
 
    in_while: WhileStatementId,
 
    in_scope: Scope,
 
    in_scope: ScopeId,
 
    statement: StatementId, // of 'break', 'continue' or 'goto'
 
}
 

	
 
/// This particular visitor will go through the entire AST in a recursive manner
 
/// and check if all statements and expressions are legal (e.g. no "return"
 
/// statements in component definitions), and will link certain AST nodes to
 
/// their appropriate targets (e.g. goto statements, or function calls).
 
///
 
/// This visitor will not perform control-flow analysis (e.g. making sure that
 
/// each function actually returns) and will also not perform type checking. So
 
/// the linking of function calls and component instantiations will be checked
 
/// and linked to the appropriate definitions, but the return types and/or
 
/// arguments will not be checked for validity.
 
///
 
/// The main idea is, because we're visiting nodes in a tree, to do as much as
 
/// we can while we have the memory in cache.
 
pub(crate) struct PassValidationLinking {
 
    // Traversal state, all valid IDs if inside a certain AST element. Otherwise
 
    // `id.is_invalid()` returns true.
 
    in_sync: SynchronousStatementId,
 
    in_while: WhileStatementId, // to resolve labeled continue/break
 
    in_select_guard: SelectStatementId, // for detection/rejection of builtin calls
 
    in_select_arm: u32,
 
    in_test_expr: StatementId, // wrapping if/while stmt id
 
    in_binding_expr: BindingExpressionId, // to resolve variable expressions
 
    in_binding_expr_lhs: bool,
 
    // Traversal state, current scope (which can be used to find the parent
 
    // scope) and the definition variant we are considering.
 
    cur_scope: Scope,
 
    cur_scope: ScopeId,
 
    def_type: DefinitionType,
 
    // "Trailing" traversal state, set be child/prev stmt/expr used by next one
 
    prev_stmt: StatementId,
 
    expr_parent: ExpressionParent,
 
    // Set by parent to indicate that child expression must be assignable. The
 
    // child will throw an error if it is not assignable. The stored span is
 
    // used for the error's position
 
    must_be_assignable: Option<InputSpan>,
 
    // Keeping track of relative positions and unique IDs.
 
    relative_pos_in_block: i32, // of statements: to determine when variables are visible
 
    relative_pos_in_parent: i32, // of statements: to determine when variables are visible
 
    next_expr_index: i32, // to arrive at a unique ID for all expressions within a definition
 
    // Control flow statements that require label resolving
 
    control_flow_stmts: Vec<ControlFlowStatement>,
 
    // Various temporary buffers for traversal. Essentially working around
 
    // Rust's borrowing rules since it cannot understand we're modifying AST
 
    // members but not the AST container.
 
    variable_buffer: ScopedBuffer<VariableId>,
 
    definition_buffer: ScopedBuffer<DefinitionId>,
 
    statement_buffer: ScopedBuffer<StatementId>,
 
    expression_buffer: ScopedBuffer<ExpressionId>,
 
    scope_buffer: ScopedBuffer<ScopeId>,
 
}
 

	
 
impl PassValidationLinking {
 
    pub(crate) fn new() -> Self {
 
        Self{
 
            in_sync: SynchronousStatementId::new_invalid(),
 
            in_while: WhileStatementId::new_invalid(),
 
            in_select_guard: SelectStatementId::new_invalid(),
 
            in_select_arm: 0,
 
            in_test_expr: StatementId::new_invalid(),
 
            in_binding_expr: BindingExpressionId::new_invalid(),
 
            in_binding_expr_lhs: false,
 
            cur_scope: Scope::new_invalid(),
 
            cur_scope: ScopeId::new_invalid(),
 
            prev_stmt: StatementId::new_invalid(),
 
            expr_parent: ExpressionParent::None,
 
            def_type: DefinitionType::Function(FunctionDefinitionId::new_invalid()),
 
            must_be_assignable: None,
 
            relative_pos_in_block: 0,
 
            relative_pos_in_parent: 0,
 
            next_expr_index: 0,
 
            control_flow_stmts: Vec::with_capacity(32),
 
            variable_buffer: ScopedBuffer::with_capacity(128),
 
            definition_buffer: ScopedBuffer::with_capacity(128),
 
            statement_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAPACITY),
 
            expression_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAPACITY),
 
            control_flow_stmts: Vec::with_capacity(BUFFER_INIT_CAP_SMALL),
 
            variable_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_SMALL),
 
            definition_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_SMALL),
 
            statement_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_LARGE),
 
            expression_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_LARGE),
 
            scope_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_SMALL),
 
        }
 
    }
 

	
 
    fn reset_state(&mut self) {
 
        self.in_sync = SynchronousStatementId::new_invalid();
 
        self.in_while = WhileStatementId::new_invalid();
 
        self.in_select_guard = SelectStatementId::new_invalid();
 
        self.in_test_expr = StatementId::new_invalid();
 
        self.in_binding_expr = BindingExpressionId::new_invalid();
 
        self.in_binding_expr_lhs = false;
 
        self.cur_scope = Scope::new_invalid();
 
        self.cur_scope = ScopeId::new_invalid();
 
        self.def_type = DefinitionType::Function(FunctionDefinitionId::new_invalid());
 
        self.prev_stmt = StatementId::new_invalid();
 
        self.expr_parent = ExpressionParent::None;
 
        self.must_be_assignable = None;
 
        self.relative_pos_in_block = 0;
 
        self.relative_pos_in_parent = 0;
 
        self.next_expr_index = 0;
 
        self.control_flow_stmts.clear();
 
    }
 
}
 

	
 
macro_rules! assign_then_erase_next_stmt {
 
    ($self:ident, $ctx:ident, $stmt_id:expr) => {
 
        if !$self.prev_stmt.is_invalid() {
 
            $ctx.heap[$self.prev_stmt].link_next($stmt_id);
 
            $self.prev_stmt = StatementId::new_invalid();
 
        }
 
    }
 
}
 

	
 
macro_rules! assign_and_replace_next_stmt {
 
    ($self:ident, $ctx:ident, $stmt_id:expr) => {
 
        if !$self.prev_stmt.is_invalid() {
 
            $ctx.heap[$self.prev_stmt].link_next($stmt_id);
 
        }
 
        $self.prev_stmt = $stmt_id;
 
    }
 
}
 

	
 
impl Visitor for PassValidationLinking {
 
    fn visit_module(&mut self, ctx: &mut Ctx) -> VisitorResult {
 
        debug_assert_eq!(ctx.module().phase, ModuleCompilationPhase::TypesAddedToTable);
 

	
 
        let root = &ctx.heap[ctx.module().root_id];
 
        let section = self.definition_buffer.start_section_initialized(&root.definitions);
 
        for definition_id in section.iter_copied() {
 
            self.visit_definition(ctx, definition_id)?;
 
        }
 
        section.forget();
 

	
 
        ctx.module_mut().phase = ModuleCompilationPhase::ValidatedAndLinked;
 
        Ok(())
 
    }
 
    //--------------------------------------------------------------------------
 
    // Definition visitors
 
    //--------------------------------------------------------------------------
 

	
 
    fn visit_component_definition(&mut self, ctx: &mut Ctx, id: ComponentDefinitionId) -> VisitorResult {
 
        self.reset_state();
 

	
 
        self.def_type = match &ctx.heap[id].variant {
 
            ComponentVariant::Primitive => DefinitionType::Primitive(id),
 
            ComponentVariant::Composite => DefinitionType::Composite(id),
 
        };
 
        self.cur_scope = Scope::Definition(id.upcast());
 
        self.expr_parent = ExpressionParent::None;
 

	
 
        // Visit parameters and assign a unique scope ID
 
        let old_scope = self.push_scope(ctx, ScopeAssociation::Definition(id.upcast()));
 

	
 
        let definition = &ctx.heap[id];
 
        let body_id = definition.body;
 
        let section = self.variable_buffer.start_section_initialized(&definition.parameters);
 
        for variable_idx in 0..section.len() {
 
            let variable_id = section[variable_idx];
 
            let variable = &mut ctx.heap[variable_id];
 
            variable.unique_id_in_scope = variable_idx as i32;
 
            self.checked_at_single_scope_add_local(ctx, self.cur_scope, variable_idx as i32, variable_id)?;
 
        }
 
        self.relative_pos_in_parent = section.len() as i32;
 

	
 
        section.forget();
 

	
 
        // Visit statements in component body
 
        self.visit_block_stmt(ctx, body_id)?;
 
        self.pop_scope(old_scope);
 

	
 
        // Assign total number of expressions and assign an in-block unique ID
 
        // to each of the locals in the procedure.
 
        ctx.heap[id].num_expressions_in_body = self.next_expr_index;
 
        self.visit_definition_and_assign_local_ids(ctx, id.upcast());
 
        let definition = &mut ctx.heap[id];
 
        let definition_scope = definition.scope;
 
        definition.num_expressions_in_body = self.next_expr_index;
 

	
 
        self.visit_scope_and_assign_local_ids(ctx, definition_scope, 0);
 
        self.resolve_pending_control_flow_targets(ctx)?;
 

	
 
        Ok(())
 
    }
 

	
 
    fn visit_function_definition(&mut self, ctx: &mut Ctx, id: FunctionDefinitionId) -> VisitorResult {
 
        self.reset_state();
 

	
 
        // Set internal statement indices
 
        self.def_type = DefinitionType::Function(id);
 
        self.cur_scope = Scope::Definition(id.upcast());
 
        self.expr_parent = ExpressionParent::None;
 

	
 
        // Visit parameters and assign a unique scope ID
 
        let old_scope = self.push_scope(ctx, ScopeAssociation::Definition(id.upcast()));
 

	
 
        let definition = &ctx.heap[id];
 
        let body_id = definition.body;
 
        let section = self.variable_buffer.start_section_initialized(&definition.parameters);
 
        for variable_idx in 0..section.len() {
 
            let variable_id = section[variable_idx];
 
            let variable = &mut ctx.heap[variable_id];
 
            variable.unique_id_in_scope = variable_idx as i32;
 
            self.checked_at_single_scope_add_local(ctx, self.cur_scope, variable_idx as i32, variable_id)?;
 
        }
 
        section.forget();
 

	
 
        // Visit statements in function body
 
        self.visit_block_stmt(ctx, body_id)?;
 
        self.pop_scope(old_scope);
 

	
 
        // Assign total number of expressions and assign an in-block unique ID
 
        // to each of the locals in the procedure.
 
        ctx.heap[id].num_expressions_in_body = self.next_expr_index;
 
        self.visit_definition_and_assign_local_ids(ctx, id.upcast());
 
        let definition = &mut ctx.heap[id];
 
        let definition_scope = definition.scope;
 
        definition.num_expressions_in_body = self.next_expr_index;
 
        self.visit_scope_and_assign_local_ids(ctx, definition_scope, 0);
 
        self.resolve_pending_control_flow_targets(ctx)?;
 

	
 
        Ok(())
 
    }
 

	
 
    //--------------------------------------------------------------------------
 
    // Statement visitors
 
    //--------------------------------------------------------------------------
 

	
 
    fn visit_block_stmt(&mut self, ctx: &mut Ctx, id: BlockStatementId) -> VisitorResult {
 
        let old_scope = self.push_statement_scope(ctx, Scope::Regular(id));
 

	
 
        // Set end of block
 
        // Get end of block
 
        let block_stmt = &ctx.heap[id];
 
        let end_block_id = block_stmt.end_block;
 

	
 
        // Traverse statements in block
 
        let statement_section = self.statement_buffer.start_section_initialized(&block_stmt.statements);
 
        let old_scope = self.push_scope(ctx, ScopeAssociation::Block(id));
 
        assign_and_replace_next_stmt!(self, ctx, id.upcast());
 

	
 
        for stmt_idx in 0..statement_section.len() {
 
            self.relative_pos_in_block = stmt_idx as i32;
 
            self.relative_pos_in_parent = stmt_idx as i32;
 
            self.visit_stmt(ctx, statement_section[stmt_idx])?;
 
        }
 

	
 
        statement_section.forget();
 
        assign_and_replace_next_stmt!(self, ctx, end_block_id.upcast());
 

	
 
        self.pop_statement_scope(old_scope);
 
        self.pop_scope(old_scope);
 
        Ok(())
 
    }
 

	
 
    fn visit_local_memory_stmt(&mut self, ctx: &mut Ctx, id: MemoryStatementId) -> VisitorResult {
 
        let stmt = &ctx.heap[id];
 
        let expr_id = stmt.initial_expr;
 
        let variable_id = stmt.variable;
 

	
 
        self.checked_add_local(ctx, self.cur_scope, self.relative_pos_in_block, variable_id)?;
 
        self.checked_add_local(ctx, self.cur_scope, self.relative_pos_in_parent, variable_id)?;
 

	
 
        assign_and_replace_next_stmt!(self, ctx, id.upcast().upcast());
 
        debug_assert_eq!(self.expr_parent, ExpressionParent::None);
 
        self.expr_parent = ExpressionParent::Memory(id);
 
        self.visit_assignment_expr(ctx, expr_id)?;
 
        self.expr_parent = ExpressionParent::None;
 

	
 
        Ok(())
 
    }
 

	
 
    fn visit_local_channel_stmt(&mut self, ctx: &mut Ctx, id: ChannelStatementId) -> VisitorResult {
 
        let stmt = &ctx.heap[id];
 
        let from_id = stmt.from;
 
        let to_id = stmt.to;
 

	
 
        self.checked_add_local(ctx, self.cur_scope, self.relative_pos_in_block, from_id)?;
 
        self.checked_add_local(ctx, self.cur_scope, self.relative_pos_in_block, to_id)?;
 
        self.checked_add_local(ctx, self.cur_scope, self.relative_pos_in_parent, from_id)?;
 
        self.checked_add_local(ctx, self.cur_scope, self.relative_pos_in_parent, to_id)?;
 

	
 
        assign_and_replace_next_stmt!(self, ctx, id.upcast().upcast());
 
        Ok(())
 
    }
 

	
 
    fn visit_labeled_stmt(&mut self, ctx: &mut Ctx, id: LabeledStatementId) -> VisitorResult {
 
        let stmt = &ctx.heap[id];
 
        let body_id = stmt.body;
 

	
 
        self.checked_add_label(ctx, self.relative_pos_in_block, self.in_sync, id)?;
 
        self.checked_add_label(ctx, self.relative_pos_in_parent, self.in_sync, id)?;
 

	
 
        self.visit_stmt(ctx, body_id)?;
 
        Ok(())
 
    }
 

	
 
    fn visit_if_stmt(&mut self, ctx: &mut Ctx, id: IfStatementId) -> VisitorResult {
 
        let if_stmt = &ctx.heap[id];
 
        let end_if_id = if_stmt.end_if;
 
        let test_expr_id = if_stmt.test;
 
        let true_stmt_id = if_stmt.true_body;
 
        let false_stmt_id = if_stmt.false_body;
 
        let true_case = if_stmt.true_case;
 
        let false_case = if_stmt.false_case;
 

	
 
        // Visit test expression
 
        debug_assert_eq!(self.expr_parent, ExpressionParent::None);
 
        debug_assert!(self.in_test_expr.is_invalid());
 

	
 
        self.in_test_expr = id.upcast();
 
        self.expr_parent = ExpressionParent::If(id);
 
        self.visit_expr(ctx, test_expr_id)?;
 
        self.in_test_expr = StatementId::new_invalid();
 

	
 
        self.expr_parent = ExpressionParent::None;
 

	
 
        // Visit true and false branch. Executor chooses next statement based on
 
        // test expression, not on if-statement itself. Hence the if statement
 
        // does not have a static subsequent statement.
 
        assign_then_erase_next_stmt!(self, ctx, id.upcast());
 
        self.visit_block_stmt(ctx, true_stmt_id)?;
 
        let old_scope = self.push_scope(ctx, ScopeAssociation::If(id, true));
 
        self.visit_stmt(ctx, true_case.body)?;
 
        self.pop_scope(old_scope);
 
        assign_then_erase_next_stmt!(self, ctx, end_if_id.upcast());
 

	
 
        if let Some(false_id) = false_stmt_id {
 
            self.visit_block_stmt(ctx, false_id)?;
 
        if let Some(false_case) = false_case {
 
            let old_scope = self.push_scope(ctx, ScopeAssociation::If(id, false));
 
            self.visit_stmt(ctx, false_case.body)?;
 
            self.pop_scope(old_scope);
 
            assign_then_erase_next_stmt!(self, ctx, end_if_id.upcast());
 
        }
 

	
 
        self.prev_stmt = end_if_id.upcast();
 
        Ok(())
 
    }
 

	
 
    fn visit_while_stmt(&mut self, ctx: &mut Ctx, id: WhileStatementId) -> VisitorResult {
 
        let stmt = &ctx.heap[id];
 
        let end_while_id = stmt.end_while;
 
        let test_expr_id = stmt.test;
 
        let body_stmt_id = stmt.body;
 

	
 
        let old_while = self.in_while;
 
        self.in_while = id;
 

	
 
        // Visit test expression
 
        debug_assert_eq!(self.expr_parent, ExpressionParent::None);
 
        debug_assert!(self.in_test_expr.is_invalid());
 
        self.in_test_expr = id.upcast();
 
        self.expr_parent = ExpressionParent::While(id);
 
        self.visit_expr(ctx, test_expr_id)?;
 
        self.in_test_expr = StatementId::new_invalid();
 

	
 
        // Link up to body statement
 
        assign_then_erase_next_stmt!(self, ctx, id.upcast());
 

	
 
        self.expr_parent = ExpressionParent::None;
 
        self.visit_block_stmt(ctx, body_stmt_id)?;
 
        let old_scope = self.push_scope(ctx, ScopeAssociation::While(id));
 
        self.visit_stmt(ctx, body_stmt_id)?;
 
        self.pop_scope(old_scope);
 
        self.in_while = old_while;
 

	
 
        // Link final entry in while's block statement back to the while. The
 
        // executor will go to the end-while statement if the test expression
 
        // is false, so put that in as the new previous stmt
 
        assign_then_erase_next_stmt!(self, ctx, id.upcast());
 
        self.prev_stmt = end_while_id.upcast();
 

	
 
        Ok(())
 
    }
 

	
 
    fn visit_break_stmt(&mut self, ctx: &mut Ctx, id: BreakStatementId) -> VisitorResult {
 
        self.control_flow_stmts.push(ControlFlowStatement{
 
            in_sync: self.in_sync,
 
            in_while: self.in_while,
 
            in_scope: self.cur_scope,
 
            statement: id.upcast()
 
        });
 
        assign_then_erase_next_stmt!(self, ctx, id.upcast());
 

	
 
        Ok(())
 
    }
 

	
 
    fn visit_continue_stmt(&mut self, ctx: &mut Ctx, id: ContinueStatementId) -> VisitorResult {
 
@@ -433,141 +450,138 @@ impl Visitor for PassValidationLinking {
 
            let old_sync_span = ctx.heap[self.in_sync].span;
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module().source, cur_sync_span, "Illegal nested synchronous statement"
 
            ).with_info_str_at_span(
 
                &ctx.module().source, old_sync_span, "It is nested in this synchronous statement"
 
            ));
 
        }
 

	
 
        if !self.def_type.is_primitive() {
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module().source, cur_sync_span,
 
                "synchronous statements may only be used in primitive components"
 
            ));
 
        }
 

	
 
        // Synchronous statement implicitly moves to its block
 
        assign_then_erase_next_stmt!(self, ctx, id.upcast());
 

	
 
        // Visit block statement. Note that we explicitly push the scope here
 
        // (and the `visit_block_stmt` will also push, but without effect) to
 
        // ensure the scope contains the sync ID.
 
        let sync_body = ctx.heap[id].body;
 
        debug_assert!(self.in_sync.is_invalid());
 
        self.in_sync = id;
 
        let old_scope = self.push_statement_scope(ctx, Scope::Synchronous(id, sync_body));
 
        self.visit_block_stmt(ctx, sync_body)?;
 
        self.pop_statement_scope(old_scope);
 
        let old_scope = self.push_scope(ctx, ScopeAssociation::Synchronous(id));
 
        self.visit_stmt(ctx, sync_body)?;
 
        self.pop_scope(old_scope);
 
        assign_and_replace_next_stmt!(self, ctx, end_sync_id.upcast());
 

	
 
        self.in_sync = SynchronousStatementId::new_invalid();
 

	
 
        Ok(())
 
    }
 

	
 
    fn visit_fork_stmt(&mut self, ctx: &mut Ctx, id: ForkStatementId) -> VisitorResult {
 
        let fork_stmt = &ctx.heap[id];
 
        let end_fork_id = fork_stmt.end_fork;
 
        let left_body_id = fork_stmt.left_body;
 
        let right_body_id = fork_stmt.right_body;
 

	
 
        // Fork statements may only occur inside sync blocks
 
        if self.in_sync.is_invalid() {
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module().source, fork_stmt.span,
 
                "Forking may only occur inside sync blocks"
 
            ));
 
        }
 

	
 
        // Visit the respective bodies. Like the if statement, a fork statement
 
        // does not have a single static subsequent statement. It forks and then
 
        // each fork has a different next statement.
 
        assign_then_erase_next_stmt!(self, ctx, id.upcast());
 
        self.visit_block_stmt(ctx, left_body_id)?;
 
        self.visit_stmt(ctx, left_body_id)?;
 
        assign_then_erase_next_stmt!(self, ctx, end_fork_id.upcast());
 

	
 
        if let Some(right_body_id) = right_body_id {
 
            self.visit_block_stmt(ctx, right_body_id)?;
 
            self.visit_stmt(ctx, right_body_id)?;
 
            assign_then_erase_next_stmt!(self, ctx, end_fork_id.upcast());
 
        }
 

	
 
        self.prev_stmt = end_fork_id.upcast();
 
        Ok(())
 
    }
 

	
 
    fn visit_select_stmt(&mut self, ctx: &mut Ctx, id: SelectStatementId) -> VisitorResult {
 
        let select_stmt = &ctx.heap[id];
 
        let end_select_id = select_stmt.end_select;
 

	
 
        // Select statements may only occur inside sync blocks
 
        if self.in_sync.is_invalid() {
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module().source, select_stmt.span,
 
                "select statements may only occur inside sync blocks"
 
            ));
 
        }
 

	
 
        if !self.def_type.is_primitive() {
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module().source, select_stmt.span,
 
                "select statements may only be used in primitive components"
 
            ));
 
        }
 

	
 
        // Visit the various arms in the select block
 
        let mut case_stmt_ids = self.statement_buffer.start_section();
 
        let num_cases = select_stmt.cases.len();
 
        for case in &select_stmt.cases {
 
            // Note: we add both to the buffer, retrieve them later in indexed
 
            // fashion
 
            // We add them in pairs, so the subsequent for-loop retrieves in pairs
 
            case_stmt_ids.push(case.guard);
 
            case_stmt_ids.push(case.block.upcast());
 
            case_stmt_ids.push(case.body);
 
        }
 

	
 
        assign_then_erase_next_stmt!(self, ctx, id.upcast());
 

	
 
        for idx in 0..num_cases {
 
            let base_idx = 2 * idx;
 
            let guard_id     = case_stmt_ids[base_idx    ];
 
            let arm_block_id = case_stmt_ids[base_idx + 1];
 
            debug_assert_eq!(ctx.heap[arm_block_id].as_block().this.upcast(), arm_block_id); // backwards way of saying arm_block_id is a BlockStatementId
 
            let arm_block_id = BlockStatementId(arm_block_id);
 
            let case_body_id = case_stmt_ids[base_idx + 1];
 

	
 
            // The guard statement ends up belonging to the block statement
 
            // following the arm. The reason we parse it separately is to
 
            // extract all of the "get" calls.
 
            let old_scope = self.push_statement_scope(ctx, Scope::Regular(arm_block_id));
 
            let old_scope = self.push_scope(ctx, ScopeAssociation::SelectCase(id, idx as u32));
 

	
 
            // Visit the guard of this arm
 
            debug_assert!(self.in_select_guard.is_invalid());
 
            self.in_select_guard = id;
 
            self.in_select_arm = idx as u32;
 
            self.visit_stmt(ctx, guard_id)?;
 
            self.in_select_guard = SelectStatementId::new_invalid();
 

	
 
            // Visit the code associated with the guard
 
            self.visit_block_stmt(ctx, arm_block_id)?;
 
            self.pop_statement_scope(old_scope);
 
            self.visit_stmt(ctx, case_body_id)?;
 
            self.pop_scope(old_scope);
 

	
 
            // Link up last statement in block to EndSelect
 
            assign_then_erase_next_stmt!(self, ctx, end_select_id.upcast());
 
        }
 

	
 
        self.in_select_guard = SelectStatementId::new_invalid();
 
        self.prev_stmt = end_select_id.upcast();
 
        Ok(())
 
    }
 

	
 
    fn visit_return_stmt(&mut self, ctx: &mut Ctx, id: ReturnStatementId) -> VisitorResult {
 
        // Check if "return" occurs within a function
 
        let stmt = &ctx.heap[id];
 
        if !self.def_type.is_function() {
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module().source, stmt.span,
 
                "return statements may only appear in function bodies"
 
            ));
 
        }
 

	
 
        // If here then we are within a function
 
        assign_then_erase_next_stmt!(self, ctx, id.upcast());
 
        debug_assert_eq!(self.expr_parent, ExpressionParent::None);
 
        debug_assert_eq!(ctx.heap[id].expressions.len(), 1);
 
@@ -1306,49 +1320,49 @@ impl Visitor for PassValidationLinking {
 

	
 
        for arg_expr_idx in 0..section.len() {
 
            let arg_expr_id = section[arg_expr_idx];
 
            self.expr_parent = ExpressionParent::Expression(upcast_id, arg_expr_idx as u32);
 
            self.visit_expr(ctx, arg_expr_id)?;
 
        }
 

	
 
        section.forget();
 
        self.expr_parent = old_expr_parent;
 

	
 
        Ok(())
 
    }
 

	
 
    fn visit_variable_expr(&mut self, ctx: &mut Ctx, id: VariableExpressionId) -> VisitorResult {
 
        let var_expr = &ctx.heap[id];
 

	
 
        // Check if declaration was already resolved (this occurs for the
 
        // variable expr that is on the LHS of the assignment expr that is
 
        // associated with a variable declaration)
 
        let mut variable_id = var_expr.declaration;
 
        let mut is_binding_target = false;
 

	
 
        // Otherwise try to find it
 
        if variable_id.is_none() {
 
            variable_id = self.find_variable(ctx, self.relative_pos_in_block, &var_expr.identifier);
 
            variable_id = self.find_variable(ctx, self.relative_pos_in_parent, &var_expr.identifier);
 
        }
 

	
 
        // Otherwise try to see if is a variable introduced by a binding expr
 
        let variable_id = if let Some(variable_id) = variable_id {
 
            variable_id
 
        } else {
 
            if self.in_binding_expr.is_invalid() || !self.in_binding_expr_lhs {
 
                return Err(ParseError::new_error_str_at_span(
 
                    &ctx.module().source, var_expr.identifier.span, "unresolved variable"
 
                ));
 
            }
 

	
 
            // This is a binding variable, but it may only appear in very
 
            // specific locations.
 
            let is_valid_binding = match self.expr_parent {
 
                ExpressionParent::Expression(expr_id, idx) => {
 
                    match &ctx.heap[expr_id] {
 
                        Expression::Binding(_binding_expr) => {
 
                            // Nested binding is disallowed, and because of
 
                            // the check above we know we're directly at the
 
                            // LHS of the binding expression
 
                            debug_assert_eq!(_binding_expr.this, self.in_binding_expr);
 
                            debug_assert_eq!(idx, 0);
 
                            true
 
@@ -1367,216 +1381,232 @@ impl Visitor for PassValidationLinking {
 
                        },
 
                        _ => false,
 
                    }
 
                },
 
                _ => {
 
                    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.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.
 
            // statement, we now add the variable to the scope associated with
 
            // that statement.
 
            let bound_identifier = var_expr.identifier.clone();
 
            let bound_variable_id = ctx.heap.alloc_variable(|this| Variable {
 
                this,
 
                kind: VariableKind::Binding,
 
                parser_type: ParserType {
 
                    elements: vec![ParserTypeElement {
 
                        element_span: bound_identifier.span,
 
                        variant: ParserTypeVariant::Inferred
 
                    }],
 
                    full_span: bound_identifier.span
 
                },
 
                identifier: bound_identifier,
 
                relative_pos_in_block: 0,
 
                relative_pos_in_parent: 0,
 
                unique_id_in_scope: -1,
 
            });
 

	
 
            let body_stmt_id = match &ctx.heap[self.in_test_expr] {
 
                Statement::If(stmt) => stmt.true_body,
 
                Statement::While(stmt) => stmt.body,
 
            let scope_id = match &ctx.heap[self.in_test_expr] {
 
                Statement::If(stmt) => stmt.true_case.scope,
 
                Statement::While(stmt) => stmt.scope,
 
                _ => unreachable!(),
 
            };
 
            let body_scope = Scope::Regular(body_stmt_id);
 
            self.checked_at_single_scope_add_local(ctx, body_scope, -1, bound_variable_id)?; // add at -1 such that first statement can access
 

	
 
            self.checked_at_single_scope_add_local(ctx, scope_id, -1, bound_variable_id)?; // add at -1 such that first statement can find the variable if needed
 

	
 
            is_binding_target = true;
 
            bound_variable_id
 
        };
 

	
 
        let var_expr = &mut ctx.heap[id];
 
        var_expr.declaration = Some(variable_id);
 
        var_expr.used_as_binding_target = is_binding_target;
 
        var_expr.parent = self.expr_parent;
 
        var_expr.unique_id_in_definition = self.next_expr_index;
 
        self.next_expr_index += 1;
 

	
 
        Ok(())
 
    }
 
}
 

	
 
impl PassValidationLinking {
 
    //--------------------------------------------------------------------------
 
    // Special traversal
 
    //--------------------------------------------------------------------------
 

	
 
    /// Pushes a new scope associated with a particular statement. If that
 
    /// statement already has an associated scope (i.e. scope associated with
 
    /// sync statement or select statement's arm) then we won't do anything.
 
    /// In all cases the caller must call `pop_statement_scope` with the scope
 
    /// and relative scope position returned by this function.
 
    fn push_statement_scope(&mut self, ctx: &mut Ctx, new_scope: Scope) -> (Scope, i32) {
 
        let old_scope = self.cur_scope.clone();
 
        debug_assert!(new_scope.is_block()); // never call for Definition scope
 
        let is_new_block = if old_scope.is_block() {
 
            old_scope.to_block() != new_scope.to_block()
 
        } else {
 
            true
 
    fn push_scope(&mut self, ctx: &mut Ctx, association: ScopeAssociation) -> (ScopeId, i32) {
 
        // Create new scope and assign as ScopeId to the specified associated
 
        // statement.
 
        let is_first_scope = match association {
 
            ScopeAssociation::Definition(_) => true,
 
            _ => false,
 
        };
 

	
 
        if !is_new_block {
 
            // No need to push, but still return old scope, we pretend like we
 
            // replaced it.
 
            debug_assert!(!ctx.heap[new_scope.to_block()].scope_node.parent.is_invalid());
 
            return (old_scope, self.relative_pos_in_block);
 
        }
 
        let old_scope_id = self.cur_scope.clone();
 
        let new_scope_id = ctx.heap.alloc_scope(|this| Scope{
 
            this,
 
            parent: if is_first_scope { None } else { Some(old_scope_id) },
 
            nested: Vec::new(),
 
            association,
 
            variables: Vec::new(),
 
            labels: Vec::new(),
 
            relative_pos_in_parent: self.relative_pos_in_parent,
 
            first_unique_id_in_scope: -1,
 
            next_unique_id_in_scope: -1,
 
        });
 

	
 
        // This is a new block, so link it up
 
        if old_scope.is_block() {
 
            let parent_block = &mut ctx.heap[old_scope.to_block()];
 
            parent_block.scope_node.nested.push(new_scope);
 
        match association {
 
            ScopeAssociation::Definition(definition_id) => {
 
                let def = &mut ctx.heap[definition_id];
 
                match def {
 
                    Definition::Function(def) => def.scope = new_scope_id,
 
                    Definition::Component(def) => def.scope = new_scope_id,
 
                    _ => unreachable!(),
 
                }
 
            },
 
            ScopeAssociation::Block(stmt_id) => {
 
                ctx.heap[stmt_id].scope = new_scope_id;
 
            },
 
            ScopeAssociation::If(stmt_id, in_true_body) => {
 
                let stmt = &mut ctx.heap[stmt_id];
 
                if in_true_body {
 
                    stmt.true_case.scope = new_scope_id;
 
                } else {
 
                    let false_body = stmt.false_case.as_mut().unwrap();
 
                    false_body.scope = new_scope_id;
 
                }
 
            },
 
            ScopeAssociation::While(stmt_id) => {
 
                ctx.heap[stmt_id].scope = new_scope_id;
 
            },
 
            ScopeAssociation::Synchronous(stmt_id) => {
 
                ctx.heap[stmt_id].scope = new_scope_id;
 
            },
 
            ScopeAssociation::SelectCase(stmt_id, case_index) => {
 
                let select_stmt = &mut ctx.heap[stmt_id];
 
                select_stmt.cases[case_index as usize].scope = new_scope_id;
 
            }
 
        }
 

	
 
        self.cur_scope = new_scope;
 

	
 
        let cur_block = &mut ctx.heap[new_scope.to_block()];
 
        cur_block.scope_node.parent = old_scope;
 
        cur_block.scope_node.relative_pos_in_parent = self.relative_pos_in_block;
 
        // Link up scopes
 
        if !is_first_scope {
 
            let old_scope = &mut ctx.heap[old_scope_id];
 
            old_scope.nested.push(new_scope_id);
 
        }
 

	
 
        let old_relative_pos = self.relative_pos_in_block;
 
        self.relative_pos_in_block = -1;
 
        // Set as current traversal scope, then return old scope
 
        self.cur_scope = new_scope_id;
 

	
 
        return (old_scope, old_relative_pos)
 
        let old_relative_pos = self.relative_pos_in_parent;
 
        self.relative_pos_in_parent = 0;
 
        return (old_scope_id, old_relative_pos)
 
    }
 

	
 
    fn pop_statement_scope(&mut self, scope_to_restore: (Scope, i32)) {
 
    fn pop_scope(&mut self, scope_to_restore: (ScopeId, i32)) {
 
        self.cur_scope = scope_to_restore.0;
 
        self.relative_pos_in_block = scope_to_restore.1;
 
        self.relative_pos_in_parent = scope_to_restore.1;
 
    }
 

	
 
    fn visit_definition_and_assign_local_ids(&mut self, ctx: &mut Ctx, definition_id: DefinitionId) {
 
        let mut var_counter = 0;
 

	
 
        // Set IDs on parameters
 
        let (param_section, body_id) = match &ctx.heap[definition_id] {
 
            Definition::Function(func_def) => (
 
                self.variable_buffer.start_section_initialized(&func_def.parameters),
 
                func_def.body
 
            ),
 
            Definition::Component(comp_def) => (
 
                self.variable_buffer.start_section_initialized(&comp_def.parameters),
 
                comp_def.body
 
            ),
 
            _ => unreachable!(),
 
        } ;
 

	
 
        for var_id in param_section.iter_copied() {
 
            let var = &mut ctx.heap[var_id];
 
            var.unique_id_in_scope = var_counter;
 
            var_counter += 1;
 
        }
 

	
 
        param_section.forget();
 
    fn visit_scope_and_assign_local_ids(&mut self, ctx: &mut Ctx, scope_id: ScopeId, mut variable_counter: i32) {
 
        let scope = &mut ctx.heap[scope_id];
 
        scope.first_unique_id_in_scope = variable_counter;
 

	
 
        // Recurse into body
 
        self.visit_block_and_assign_local_ids(ctx, body_id, var_counter);
 
    }
 
        let variable_section = self.variable_buffer.start_section_initialized(&scope.variables);
 
        let child_scope_section = self.scope_buffer.start_section_initialized(&scope.nested);
 

	
 
    fn visit_block_and_assign_local_ids(&mut self, ctx: &mut Ctx, block_id: BlockStatementId, mut var_counter: i32) {
 
        let block_stmt = &mut ctx.heap[block_id];
 
        block_stmt.first_unique_id_in_scope = var_counter;
 
        let mut variable_index = 0;
 
        let mut child_scope_index = 0;
 

	
 
        let var_section = self.variable_buffer.start_section_initialized(&block_stmt.locals);
 
        let mut scope_section = self.statement_buffer.start_section();
 
        for child_scope in &block_stmt.scope_node.nested {
 
            debug_assert!(child_scope.is_block(), "found a child scope that is not a block statement");
 
            scope_section.push(child_scope.to_block().upcast());
 
        }
 

	
 
        let mut var_idx = 0;
 
        let mut scope_idx = 0;
 
        while var_idx < var_section.len() || scope_idx < scope_section.len() {
 
            let relative_var_pos = if var_idx < var_section.len() {
 
                ctx.heap[var_section[var_idx]].relative_pos_in_block
 
        loop {
 
            // Determine relative positions of variable and scope to determine
 
            // which one occurs first within the current scope.
 
            let variable_relative_pos;
 
            if variable_index < variable_section.len() {
 
                let variable_id = variable_section[variable_index];
 
                let variable = &ctx.heap[variable_id];
 
                variable_relative_pos = variable.relative_pos_in_parent;
 
            } else {
 
                i32::MAX
 
            };
 
                variable_relative_pos = i32::MAX;
 
            }
 

	
 
            let relative_scope_pos = if scope_idx < scope_section.len() {
 
                ctx.heap[scope_section[scope_idx]].as_block().scope_node.relative_pos_in_parent
 
            let child_scope_relative_pos;
 
            if child_scope_index < child_scope_section.len() {
 
                let child_scope_id = child_scope_section[child_scope_index];
 
                let child_scope = &ctx.heap[child_scope_id];
 
                child_scope_relative_pos = child_scope.relative_pos_in_parent;
 
            } else {
 
                i32::MAX
 
            };
 
                child_scope_relative_pos = i32::MAX;
 
            }
 

	
 
            debug_assert!(!(relative_var_pos == i32::MAX && relative_scope_pos == i32::MAX));
 
            if variable_relative_pos == i32::MAX && child_scope_relative_pos == i32::MAX {
 
                // Done, no more elements in the scope to consider
 
                break;
 
            }
 

	
 
            // In certain cases the relative variable position is the same as
 
            // the scope position (insertion of binding variables). In that case
 
            // the variable should be treated first
 
            if relative_var_pos <= relative_scope_pos {
 
                let var = &mut ctx.heap[var_section[var_idx]];
 
                var.unique_id_in_scope = var_counter;
 
                var_counter += 1;
 
                var_idx += 1;
 
            // Label the variable/scope, whichever comes first. When dealing
 
            // with binding variables, where both variable and scope are
 
            // considered to have the same position in the scope, we treat the
 
            // variable first.
 
            // TODO: Think about this some more, isn't it correct that we
 
            //  consider it part of the fresh scope? So we'll have to deal with
 
            //  the scope first, and add the variable to that scope?
 
            if variable_relative_pos <= child_scope_relative_pos {
 
                let variable = &mut ctx.heap[variable_section[variable_index]];
 
                variable.unique_id_in_scope = variable_counter;
 
                variable_counter += 1;
 
                variable_index += 1;
 
            } else {
 
                // Boy oh boy
 
                let block_id = ctx.heap[scope_section[scope_idx]].as_block().this;
 
                self.visit_block_and_assign_local_ids(ctx, block_id, var_counter);
 
                scope_idx += 1;
 
                let child_scope_id = child_scope_section[child_scope_index];
 
                self.visit_scope_and_assign_local_ids(ctx, child_scope_id, variable_counter);
 
                child_scope_index += 1;
 
            }
 
        }
 

	
 
        var_section.forget();
 
        scope_section.forget();
 
        variable_section.forget();
 
        child_scope_section.forget();
 

	
 
        // Done assigning all IDs, assign the last ID to the block statement scope
 
        let block_stmt = &mut ctx.heap[block_id];
 
        block_stmt.next_unique_id_in_scope = var_counter;
 
        let scope = &mut ctx.heap[scope_id];
 
        scope.next_unique_id_in_scope = variable_counter;
 
    }
 

	
 
    fn resolve_pending_control_flow_targets(&mut self, ctx: &mut Ctx) -> Result<(), ParseError> {
 
        for entry in &self.control_flow_stmts {
 
            let stmt = &ctx.heap[entry.statement];
 

	
 
            match stmt {
 
                Statement::Break(stmt) => {
 
                    let stmt_id = stmt.this;
 
                    let target_while_id = Self::resolve_break_or_continue_target(ctx, entry, stmt.span, &stmt.label)?;
 
                    let target_while_stmt = &ctx.heap[target_while_id];
 
                    let target_end_while_id = target_while_stmt.end_while;
 
                    debug_assert!(!target_end_while_id.is_invalid());
 

	
 
                    let break_stmt = &mut ctx.heap[stmt_id];
 
                    break_stmt.target = target_end_while_id;
 
                },
 
                Statement::Continue(stmt) => {
 
                    let stmt_id = stmt.this;
 
                    let target_while_id = Self::resolve_break_or_continue_target(ctx, entry, stmt.span, &stmt.label)?;
 

	
 
                    let continue_stmt = &mut ctx.heap[stmt_id];
 
                    continue_stmt.target = target_while_id;
 
                },
 
@@ -1593,323 +1623,275 @@ impl PassValidationLinking {
 
                        let sync_stmt = &ctx.heap[entry.in_sync];
 
                        return Err(
 
                            ParseError::new_error_str_at_span(&ctx.module().source, goto_stmt.span, "goto may not escape the surrounding synchronous block")
 
                            .with_info_str_at_span(&ctx.module().source, target_stmt.label.span, "this is the target of the goto statement")
 
                            .with_info_str_at_span(&ctx.module().source, sync_stmt.span, "which will jump past this statement")
 
                        );
 
                    }
 

	
 
                    let goto_stmt = &mut ctx.heap[stmt_id];
 
                    goto_stmt.target = target_id;
 
                },
 
                _ => unreachable!("cannot resolve control flow target for {:?}", stmt),
 
            }
 
        }
 

	
 
        return Ok(())
 
    }
 

	
 
    //--------------------------------------------------------------------------
 
    // Utilities
 
    //--------------------------------------------------------------------------
 

	
 
    /// Adds a local variable to the current scope. It will also annotate the
 
    /// `Local` in the AST with its relative position in the block.
 
    fn checked_add_local(&mut self, ctx: &mut Ctx, target_scope: Scope, target_relative_pos: i32, id: VariableId) -> Result<(), ParseError> {
 
        debug_assert!(target_scope.is_block());
 
    fn checked_add_local(&mut self, ctx: &mut Ctx, target_scope_id: ScopeId, target_relative_pos: i32, id: VariableId) -> Result<(), ParseError> {
 
        let local = &ctx.heap[id];
 

	
 
        // We immediately go to the parent scope. We check the target scope
 
        // in the call at the end. That is also where we check for collisions
 
        // with symbols.
 
        let block = &ctx.heap[target_scope.to_block()];
 
        let mut scope = block.scope_node.parent;
 
        let mut cur_relative_pos = block.scope_node.relative_pos_in_parent;
 
        loop {
 
            if let Scope::Definition(definition_id) = scope {
 
                // At outer scope, check parameters of function/component
 
                for parameter_id in ctx.heap[definition_id].parameters() {
 
                    let parameter = &ctx.heap[*parameter_id];
 
                    if local.identifier == parameter.identifier {
 
                        return Err(
 
                            ParseError::new_error_str_at_span(
 
                                &ctx.module().source, local.identifier.span, "Local variable name conflicts with parameter"
 
                            ).with_info_str_at_span(
 
                                &ctx.module().source, parameter.identifier.span, "Parameter definition is found here"
 
                            )
 
                        );
 
                    }
 
                }
 

	
 
                // No collisions
 
                break;
 
            }
 

	
 
            // If here then the parent scope is a block scope
 
            let block = &ctx.heap[scope.to_block()];
 

	
 
            for other_local_id in &block.locals {
 
                let other_local = &ctx.heap[*other_local_id];
 
                // Position check in case another variable with the same name
 
                // is defined in a higher-level scope, but later than the scope
 
                // in which the current variable resides.
 
                if local.this != *other_local_id &&
 
                    cur_relative_pos >= other_local.relative_pos_in_block &&
 
                    local.identifier == other_local.identifier {
 
                    // Collision within this scope
 
        let mut scope = &ctx.heap[target_scope_id];
 
        let mut cur_relative_pos = scope.relative_pos_in_parent;
 
        while let Some(scope_parent_id) = scope.parent {
 
            scope = &ctx.heap[scope_parent_id];
 

	
 
            // Check for collisions
 
            for variable_id in scope.variables.iter().copied() {
 
                let variable = &ctx.heap[variable_id];
 
                if variable.identifier == variable.identifier &&
 
                    variable.this != id &&
 
                    cur_relative_pos >= variable.relative_pos_in_parent {
 
                    return Err(
 
                        ParseError::new_error_str_at_span(
 
                            &ctx.module().source, local.identifier.span, "Local variable name conflicts with another variable"
 
                        ).with_info_str_at_span(
 
                            &ctx.module().source, other_local.identifier.span, "Previous variable is found here"
 
                            &ctx.module().source, variable.identifier.span, "Previous variable is found here"
 
                        )
 
                    );
 
                }
 
            }
 

	
 
            scope = block.scope_node.parent;
 
            cur_relative_pos = block.scope_node.relative_pos_in_parent;
 
            cur_relative_pos = scope.relative_pos_in_parent;
 
        }
 

	
 
        // No collisions in any of the parent scope, attempt to add to scope
 
        self.checked_at_single_scope_add_local(ctx, target_scope, target_relative_pos, id)
 
        self.checked_at_single_scope_add_local(ctx, target_scope_id, target_relative_pos, id)
 
    }
 

	
 
    /// Adds a local variable to the specified scope. Will check the specified
 
    /// scope for variable conflicts and the symbol table for global conflicts.
 
    /// Will NOT check parent scopes of the specified scope.
 
    fn checked_at_single_scope_add_local(
 
        &mut self, ctx: &mut Ctx, scope: Scope, relative_pos: i32, id: VariableId
 
        &mut self, ctx: &mut Ctx, scope_id: ScopeId, relative_pos: i32, new_variable_id: VariableId
 
    ) -> Result<(), ParseError> {
 
        // Check the symbol table for conflicts
 
        {
 
            let cur_scope = SymbolScope::Definition(self.def_type.definition_id());
 
            let ident = &ctx.heap[id].identifier;
 
            let ident = &ctx.heap[new_variable_id].identifier;
 
            if let Some(symbol) = ctx.symbols.get_symbol_by_name(cur_scope, &ident.value.as_bytes()) {
 
                return Err(ParseError::new_error_str_at_span(
 
                    &ctx.module().source, ident.span,
 
                    "local variable declaration conflicts with symbol"
 
                ).with_info_str_at_span(
 
                    &ctx.module().source, symbol.variant.span_of_introduction(&ctx.heap), "the conflicting symbol is introduced here"
 
                ));
 
            }
 
        }
 

	
 
        // Check the specified scope for conflicts
 
        let local = &ctx.heap[id];
 
        let new_variable = &ctx.heap[new_variable_id];
 
        let scope = &ctx.heap[scope_id];
 

	
 
        debug_assert!(scope.is_block());
 
        let block = &ctx.heap[scope.to_block()];
 
        for other_local_id in &block.locals {
 
            let other_local = &ctx.heap[*other_local_id];
 
            if local.this != other_local.this &&
 
        for variable_id in scope.variables.iter().copied() {
 
            let old_variable = &ctx.heap[variable_id];
 
            if new_variable.this != old_variable.this &&
 
                // relative_pos >= other_local.relative_pos_in_block &&
 
                local.identifier == other_local.identifier {
 
                new_variable.identifier == old_variable.identifier {
 
                // Collision
 
                return Err(
 
                    ParseError::new_error_str_at_span(
 
                        &ctx.module().source, local.identifier.span, "Local variable name conflicts with another variable"
 
                        &ctx.module().source, new_variable.identifier.span, "Local variable name conflicts with another variable"
 
                    ).with_info_str_at_span(
 
                        &ctx.module().source, other_local.identifier.span, "Previous variable is found here"
 
                        &ctx.module().source, old_variable.identifier.span, "Previous variable is found here"
 
                    )
 
                );
 
            }
 
        }
 

	
 
        // No collisions
 
        let block = &mut ctx.heap[scope.to_block()];
 
        block.locals.push(id);
 
        let scope = &mut ctx.heap[scope_id];
 
        scope.variables.push(new_variable_id);
 

	
 
        let local = &mut ctx.heap[id];
 
        local.relative_pos_in_block = relative_pos;
 
        let variable = &mut ctx.heap[new_variable_id];
 
        variable.relative_pos_in_parent = relative_pos;
 

	
 
        Ok(())
 
    }
 

	
 
    /// Finds a variable in the visitor's scope that must appear before the
 
    /// specified relative position within that block.
 
    fn find_variable(&self, ctx: &Ctx, mut relative_pos: i32, identifier: &Identifier) -> Option<VariableId> {
 
        debug_assert!(self.cur_scope.is_block());
 
        let mut scope_id = self.cur_scope;
 

	
 
        // No need to use iterator over namespaces if here
 
        let mut scope = &self.cur_scope;
 
        
 
        loop {
 
            debug_assert!(scope.is_block());
 
            let block = &ctx.heap[scope.to_block()];
 
            // Check if we can find the variable in the current scope
 
            let scope = &ctx.heap[scope_id];
 
            
 
            for local_id in &block.locals {
 
                let local = &ctx.heap[*local_id];
 
            for variable_id in scope.variables.iter().copied() {
 
                let variable = &ctx.heap[variable_id];
 
                
 
                if local.relative_pos_in_block < relative_pos && identifier == &local.identifier {
 
                    return Some(*local_id);
 
                if variable.relative_pos_in_parent < relative_pos && identifier == &variable.identifier {
 
                    return Some(variable_id);
 
                }
 
            }
 

	
 
            scope = &block.scope_node.parent;
 
            if !scope.is_block() {
 
                // Definition scope, need to check arguments to definition
 
                match scope {
 
                    Scope::Definition(definition_id) => {
 
                        let definition = &ctx.heap[*definition_id];
 
                        for parameter_id in definition.parameters() {
 
                            let parameter = &ctx.heap[*parameter_id];
 
                            if identifier == &parameter.identifier {
 
                                return Some(*parameter_id);
 
                            }
 
                        }
 
                    },
 
                    _ => unreachable!(),
 
                }
 

	
 
                // Variable could not be found
 
                return None
 
            } else {
 
                relative_pos = block.scope_node.relative_pos_in_parent;
 
            // Could not find variable, move to parent scope and try again
 
            if scope.parent.is_none() {
 
                return None;
 
            }
 

	
 
            scope_id = scope.parent.unwrap();
 
            relative_pos = scope.relative_pos_in_parent;
 
        }
 
    }
 

	
 
    /// Adds a particular label to the current scope. Will return an error if
 
    /// there is another label with the same name visible in the current scope.
 
    fn checked_add_label(&mut self, ctx: &mut Ctx, relative_pos: i32, in_sync: SynchronousStatementId, id: LabeledStatementId) -> Result<(), ParseError> {
 
        debug_assert!(self.cur_scope.is_block());
 

	
 
    fn checked_add_label(&mut self, ctx: &mut Ctx, relative_pos: i32, in_sync: SynchronousStatementId, new_label_id: LabeledStatementId) -> Result<(), ParseError> {
 
        // Make sure label is not defined within the current scope or any of the
 
        // parent scope.
 
        let label = &mut ctx.heap[id];
 
        label.relative_pos_in_block = relative_pos;
 
        label.in_sync = in_sync;
 
        let new_label = &mut ctx.heap[new_label_id];
 
        new_label.relative_pos_in_parent = relative_pos;
 
        new_label.in_sync = in_sync;
 

	
 
        let label = &ctx.heap[id];
 
        let mut scope = &self.cur_scope;
 
        let new_label = &ctx.heap[new_label_id];
 
        let mut scope_id = self.cur_scope;
 

	
 
        loop {
 
            debug_assert!(scope.is_block(), "scope is not a block");
 
            let block = &ctx.heap[scope.to_block()];
 
            for other_label_id in &block.labels {
 
                let other_label = &ctx.heap[*other_label_id];
 
                if other_label.label == label.label {
 
            let scope = &ctx.heap[scope_id];
 
            for existing_label_id in scope.labels.iter().copied() {
 
                let existing_label = &ctx.heap[existing_label_id];
 
                if existing_label.label == new_label.label {
 
                    // Collision
 
                    return Err(ParseError::new_error_str_at_span(
 
                        &ctx.module().source, label.label.span, "label name is used more than once"
 
                        &ctx.module().source, new_label.label.span, "label name is used more than once"
 
                    ).with_info_str_at_span(
 
                        &ctx.module().source, other_label.label.span, "the other label is found here"
 
                        &ctx.module().source, existing_label.label.span, "the other label is found here"
 
                    ));
 
                }
 
            }
 

	
 
            scope = &block.scope_node.parent;
 
            if !scope.is_block() {
 
            if scope.parent.is_none() {
 
                break;
 
            }
 

	
 
            scope_id = scope.parent.unwrap();
 
        }
 

	
 
        // No collisions
 
        let block = &mut ctx.heap[self.cur_scope.to_block()];
 
        block.labels.push(id);
 
        let scope = &mut ctx.heap[self.cur_scope];
 
        scope.labels.push(new_label_id);
 

	
 
        Ok(())
 
    }
 

	
 
    /// Finds a particular labeled statement by its identifier. Once found it
 
    /// will make sure that the target label does not skip over any variable
 
    /// declarations within the scope in which the label was found.
 
    fn find_label(mut scope: Scope, ctx: &Ctx, identifier: &Identifier) -> Result<LabeledStatementId, ParseError> {
 
        debug_assert!(scope.is_block());
 

	
 
    fn find_label(mut scope_id: ScopeId, ctx: &Ctx, identifier: &Identifier) -> Result<LabeledStatementId, ParseError> {
 
        loop {
 
            debug_assert!(scope.is_block(), "scope is not a block");
 
            let relative_scope_pos = ctx.heap[scope.to_block()].scope_node.relative_pos_in_parent;
 
            let scope = &ctx.heap[scope_id];
 
            let relative_scope_pos = scope.relative_pos_in_parent;
 

	
 
            let block = &ctx.heap[scope.to_block()];
 
            for label_id in &block.labels {
 
                let label = &ctx.heap[*label_id];
 
            for label_id in scope.labels.iter().copied() {
 
                let label = &ctx.heap[label_id];
 
                if label.label == *identifier {
 
                    for local_id in &block.locals {
 
                    // Found the target label, now make sure that the jump to
 
                    // the label doesn't imply a skipped variable declaration
 
                    for variable_id in scope.variables.iter().copied() {
 
                        // TODO: Better to do this in control flow analysis, it
 
                        //  is legal to skip over a variable declaration if it
 
                        //  is not actually being used. I might be missing
 
                        //  something here when laying out the bytecode...
 
                        let local = &ctx.heap[*local_id];
 
                        if local.relative_pos_in_block > relative_scope_pos && local.relative_pos_in_block < label.relative_pos_in_block {
 
                        //  is not actually being used.
 
                        let variable = &ctx.heap[variable_id];
 
                        if variable.relative_pos_in_parent > relative_scope_pos && variable.relative_pos_in_parent < label.relative_pos_in_parent {
 
                            return Err(
 
                                ParseError::new_error_str_at_span(&ctx.module().source, identifier.span, "this target label skips over a variable declaration")
 
                                .with_info_str_at_span(&ctx.module().source, label.label.span, "because it jumps to this label")
 
                                .with_info_str_at_span(&ctx.module().source, local.identifier.span, "which skips over this variable")
 
                                .with_info_str_at_span(&ctx.module().source, variable.identifier.span, "which skips over this variable")
 
                            );
 
                        }
 
                    }
 
                    return Ok(*label_id);
 
                    return Ok(label_id);
 
                }
 
            }
 

	
 
            scope = block.scope_node.parent;
 
            if !scope.is_block() {
 
            if scope.parent.is_none() {
 
                return Err(ParseError::new_error_str_at_span(
 
                    &ctx.module().source, identifier.span, "could not find this label"
 
                ));
 
            }
 

	
 
            scope_id = scope.parent.unwrap();
 
        }
 
    }
 

	
 
    /// This function will check if the provided while statement ID has a block
 
    /// statement that is one of our current parents.
 
    fn has_parent_while_scope(mut scope: Scope, ctx: &Ctx, id: WhileStatementId) -> bool {
 
        let while_stmt = &ctx.heap[id];
 
    /// This function will check if the provided scope has a parent that belongs
 
    /// to a while statement.
 
    fn scope_is_nested_in_while_statement(mut scope_id: ScopeId, ctx: &Ctx, expected_while_id: WhileStatementId) -> bool {
 
        let while_stmt = &ctx.heap[expected_while_id];
 

	
 
        loop {
 
            debug_assert!(scope.is_block());
 
            let block = scope.to_block();
 
            if while_stmt.body == block {
 
            let scope = &ctx.heap[scope_id];
 
            if scope.this == while_stmt.scope {
 
                return true;
 
            }
 

	
 
            let block = &ctx.heap[block];
 
            scope = block.scope_node.parent;
 
            if !scope.is_block() {
 
                return false;
 
            match scope.parent {
 
                Some(new_scope_id) => scope_id = new_scope_id,
 
                None => return false, // walked all the way up, not encountering the while statement
 
            }
 
        }
 
    }
 

	
 
    /// This function should be called while dealing with break/continue
 
    /// statements. It will try to find the targeted while statement, using the
 
    /// target label if provided. If a valid target is found then the loop's
 
    /// ID will be returned, otherwise a parsing error is constructed.
 
    /// The provided input position should be the position of the break/continue
 
    /// statement.
 
    fn resolve_break_or_continue_target(ctx: &Ctx, control_flow: &ControlFlowStatement, span: InputSpan, label: &Option<Identifier>) -> Result<WhileStatementId, ParseError> {
 
        let target = match label {
 
            Some(label) => {
 
                let target_id = Self::find_label(control_flow.in_scope, ctx, label)?;
 

	
 
                // Make sure break target is a while statement
 
                let target = &ctx.heap[target_id];
 
                if let Statement::While(target_stmt) = &ctx.heap[target.body] {
 
                    // Even though we have a target while statement, the break might not be
 
                    // present underneath this particular labeled while statement
 
                    if !Self::has_parent_while_scope(control_flow.in_scope, ctx, target_stmt.this) {
 
                    // Even though we have a target while statement, the control
 
                    // flow statement might not be present underneath this
 
                    // particular labeled while statement.
 
                    if !Self::scope_is_nested_in_while_statement(control_flow.in_scope, ctx, target_stmt.this) {
 
                        return Err(ParseError::new_error_str_at_span(
 
                            &ctx.module().source, label.span, "break statement is not nested under the target label's while statement"
 
                        ).with_info_str_at_span(
 
                            &ctx.module().source, target.label.span, "the targeted label is found here"
 
                        ));
 
                    }
 

	
 
                    target_stmt.this
 
                } else {
 
                    return Err(ParseError::new_error_str_at_span(
 
                        &ctx.module().source, label.span, "incorrect break target label, it must target a while loop"
 
                    ).with_info_str_at_span(
 
                        &ctx.module().source, target.label.span, "The targeted label is found here"
 
                    ));
 
                }
 
            },
 
            None => {
 
                // Use the enclosing while statement, the break must be
 
                // nested within that while statement
 
                if control_flow.in_while.is_invalid() {
 
                    return Err(ParseError::new_error_str_at_span(
 
                        &ctx.module().source, span, "Break statement is not nested under a while loop"
 
                    ));
 
                }
src/protocol/parser/type_table.rs
Show inline comments
 
@@ -178,49 +178,49 @@ pub struct UnionType {
 
    pub tag_type: ConcreteType,
 
    pub tag_size: usize,
 
}
 

	
 
pub struct UnionVariant {
 
    pub identifier: Identifier,
 
    pub embedded: Vec<ParserType>, // zero-length does not have embedded values
 
    pub tag_value: i64,
 
}
 

	
 
/// `StructType` is a generic C-like struct type (or record type, or product
 
/// type) type.
 
pub struct StructType {
 
    pub fields: Vec<StructField>,
 
}
 

	
 
pub struct StructField {
 
    pub identifier: Identifier,
 
    pub parser_type: ParserType,
 
}
 

	
 
/// `FunctionType` is what you expect it to be: a particular function's
 
/// signature.
 
pub struct FunctionType {
 
    pub return_types: Vec<ParserType>,
 
    pub return_type: ParserType,
 
    pub arguments: Vec<FunctionArgument>,
 
}
 

	
 
pub struct ComponentType {
 
    pub variant: ComponentVariant,
 
    pub arguments: Vec<FunctionArgument>,
 
}
 

	
 
pub struct FunctionArgument {
 
    identifier: Identifier,
 
    parser_type: ParserType,
 
}
 

	
 
/// Represents the data associated with a single expression after type inference
 
/// for a monomorph (or just the normal expression types, if dealing with a
 
/// non-polymorphic function/component).
 
pub struct MonomorphExpression {
 
    // The output type of the expression. Note that for a function it is not the
 
    // function's signature but its return type
 
    pub(crate) expr_type: ConcreteType,
 
    // Has multiple meanings: the field index for select expressions, the
 
    // monomorph index for polymorphic function calls or literals. Negative
 
    // values are never used, but used to catch programming errors.
 
    pub(crate) field_or_monomorph_idx: i32,
 
@@ -914,89 +914,85 @@ impl TypeTable {
 
        for field in &fields {
 
            Self::mark_used_polymorphic_variables(&mut poly_vars, &field.parser_type);
 
        }
 

	
 
        let is_polymorph = poly_vars.iter().any(|arg| arg.is_in_use);
 

	
 
        self.type_lookup.insert(definition_id, DefinedType{
 
            ast_root: root_id,
 
            ast_definition: definition_id,
 
            definition: DefinedTypeVariant::Struct(StructType{ fields }),
 
            poly_vars,
 
            is_polymorph
 
        });
 

	
 
        return Ok(())
 
    }
 

	
 
    /// Builds base function type.
 
    fn build_base_function_definition(&mut self, modules: &[Module], ctx: &mut PassCtx, definition_id: DefinitionId) -> Result<(), ParseError> {
 
        debug_assert!(!self.type_lookup.contains_key(&definition_id), "base function already built");
 
        let definition = ctx.heap[definition_id].as_function();
 
        let root_id = definition.defined_in;
 

	
 
        // Check and construct return types and argument types.
 
        debug_assert_eq!(definition.return_types.len(), 1, "not one return type");
 
        for return_type in &definition.return_types {
 
            Self::check_member_parser_type(
 
                modules, ctx, root_id, return_type, definition.builtin
 
            )?;
 
        }
 
        Self::check_member_parser_type(
 
            modules, ctx, root_id, &definition.return_type, definition.builtin
 
        )?;
 

	
 
        let mut arguments = Vec::with_capacity(definition.parameters.len());
 
        for parameter_id in &definition.parameters {
 
            let parameter = &ctx.heap[*parameter_id];
 
            Self::check_member_parser_type(
 
                modules, ctx, root_id, &parameter.parser_type, definition.builtin
 
            )?;
 

	
 
            arguments.push(FunctionArgument{
 
                identifier: parameter.identifier.clone(),
 
                parser_type: parameter.parser_type.clone(),
 
            });
 
        }
 

	
 
        // Check conflict of identifiers
 
        Self::check_identifier_collision(
 
            modules, root_id, &arguments, |arg| &arg.identifier, "function argument"
 
        )?;
 
        Self::check_poly_args_collision(modules, ctx, root_id, &definition.poly_vars)?;
 

	
 
        // Construct internal representation of function type
 
        let mut poly_vars = Self::create_polymorphic_variables(&definition.poly_vars);
 
        for return_type in &definition.return_types {
 
            Self::mark_used_polymorphic_variables(&mut poly_vars, return_type);
 
        }
 

	
 
        Self::mark_used_polymorphic_variables(&mut poly_vars, &definition.return_type);
 
        for argument in &arguments {
 
            Self::mark_used_polymorphic_variables(&mut poly_vars, &argument.parser_type);
 
        }
 

	
 
        let is_polymorph = poly_vars.iter().any(|arg| arg.is_in_use);
 

	
 
        self.type_lookup.insert(definition_id, DefinedType{
 
            ast_root: root_id,
 
            ast_definition: definition_id,
 
            definition: DefinedTypeVariant::Function(FunctionType{ return_types: definition.return_types.clone(), arguments }),
 
            definition: DefinedTypeVariant::Function(FunctionType{ return_type: definition.return_type.clone(), arguments }),
 
            poly_vars,
 
            is_polymorph
 
        });
 

	
 
        return Ok(());
 
    }
 

	
 
    /// Builds base component type.
 
    fn build_base_component_definition(&mut self, modules: &[Module], ctx: &mut PassCtx, definition_id: DefinitionId) -> Result<(), ParseError> {
 
        debug_assert!(!self.type_lookup.contains_key(&definition_id), "base component already built");
 

	
 
        let definition = &ctx.heap[definition_id].as_component();
 
        let root_id = definition.defined_in;
 

	
 
        // Check the argument types
 
        let mut arguments = Vec::with_capacity(definition.parameters.len());
 
        for parameter_id in &definition.parameters {
 
            let parameter = &ctx.heap[*parameter_id];
 
            Self::check_member_parser_type(
 
                modules, ctx, root_id, &parameter.parser_type, false
 
            )?;
 

	
 
            arguments.push(FunctionArgument{
 
                identifier: parameter.identifier.clone(),
src/protocol/parser/visitor.rs
Show inline comments
 
use crate::protocol::ast::*;
 
use crate::protocol::input_source::ParseError;
 
use crate::protocol::parser::{type_table::*, Module};
 
use crate::protocol::symbol_table::{SymbolTable};
 

	
 
type Unit = ();
 
pub(crate) type VisitorResult = Result<Unit, ParseError>;
 

	
 
/// Globally configured vector capacity for buffers in visitor implementations
 
pub(crate) const BUFFER_INIT_CAPACITY: usize = 256;
 
/// Globally configured capacity for large-ish buffers in visitor impls
 
pub(crate) const BUFFER_INIT_CAP_LARGE: usize = 256;
 
/// Globally configured capacity for small-ish buffers in visitor impls
 
pub(crate) const BUFFER_INIT_CAP_SMALL: usize = 64;
 

	
 
/// General context structure that is used while traversing the AST.
 
pub(crate) struct Ctx<'p> {
 
    pub heap: &'p mut Heap,
 
    pub modules: &'p mut [Module],
 
    pub module_idx: usize, // currently considered module
 
    pub symbols: &'p mut SymbolTable,
 
    pub types: &'p mut TypeTable,
 
    pub arch: &'p crate::protocol::TargetArch,
 
}
 

	
 
impl<'p> Ctx<'p> {
 
    /// Returns module `modules[module_idx]`
 
    pub(crate) fn module(&self) -> &Module {
 
        &self.modules[self.module_idx]
 
    }
 

	
 
    pub(crate) fn module_mut(&mut self) -> &mut Module {
 
        &mut self.modules[self.module_idx]
 
    }
 
}
 

	
 
/// Visitor is a generic trait that will fully walk the AST. The default
 
/// implementation of the visitors is to not recurse. The exception is the
src/protocol/tests/utils.rs
Show inline comments
 
@@ -496,72 +496,70 @@ impl<'a> UnionTester<'a> {
 
        v.push_str("Union{ name: ");
 
        v.push_str(self.ast_def.identifier.value.as_str());
 
        v.push_str(", variants: [");
 
        for (variant_idx, variant) in self.ast_def.variants.iter().enumerate() {
 
            if variant_idx != 0 { v.push_str(", "); }
 
            v.push_str(variant.identifier.value.as_str());
 
        }
 
        v.push_str("] }");
 
        v
 
    }
 
}
 

	
 
pub(crate) struct FunctionTester<'a> {
 
    ctx: TestCtx<'a>,
 
    def: &'a FunctionDefinition,
 
}
 

	
 
impl<'a> FunctionTester<'a> {
 
    fn new(ctx: TestCtx<'a>, def: &'a FunctionDefinition) -> Self {
 
        Self{ ctx, def }
 
    }
 

	
 
    pub(crate) fn for_variable<F: Fn(VariableTester)>(self, name: &str, f: F) -> Self {
 
        // Seek through the blocks in order to find the variable
 
        let wrapping_block_id = seek_stmt(
 
            self.ctx.heap, self.def.body.upcast(),
 
            &|stmt| {
 
                if let Statement::Block(block) = stmt {
 
                    for local_id in &block.locals {
 
                        let var = &self.ctx.heap[*local_id];
 
                        if var.identifier.value.as_str() == name {
 
                            return true;
 
                        }
 
        let wrapping_scope = seek_scope(
 
            self.ctx.heap, self.def.scope,
 
            &|scope| {
 
                for variable_id in scope.variables.iter().copied() {
 
                    let var = &self.ctx.heap[variable_id];
 
                    if var.identifier.value.as_str() == name {
 
                        return true;
 
                    }
 
                }
 

	
 
                false
 
            }
 
        );
 

	
 
        let mut found_local_id = None;
 
        if let Some(block_id) = wrapping_block_id {
 
            // Found the right block, find the variable inside the block again
 
            let block_stmt = self.ctx.heap[block_id].as_block();
 
            for local_id in &block_stmt.locals {
 
                let var = &self.ctx.heap[*local_id];
 
                if var.identifier.value.as_str() == name {
 
                    found_local_id = Some(*local_id);
 
        if let Some(scope_id) = wrapping_scope {
 
            // Found the right scope, find the variable inside the block again
 
            let scope = &self.ctx.heap[scope_id];
 
            for variable_id in scope.variables.iter().copied() {
 
                let variable = &self.ctx.heap[variable_id];
 
                if variable.identifier.value.as_str() == name {
 
                    found_local_id = Some(variable_id);
 
                }
 
            }
 
        }
 

	
 
        assert!(
 
            found_local_id.is_some(), "[{}] Failed to find variable '{}' in {}",
 
            self.ctx.test_name, name, self.assert_postfix()
 
        );
 

	
 
        let local = &self.ctx.heap[found_local_id.unwrap()];
 

	
 
        // Find an instance of the variable expression so we can determine its
 
        // type.
 
        let var_expr = seek_expr_in_stmt(
 
            self.ctx.heap, self.def.body.upcast(),
 
            &|expr| {
 
                if let Expression::Variable(variable_expr) = expr {
 
                    if variable_expr.identifier.value.as_str() == name {
 
                        return true;
 
                    }
 
                }
 

	
 
                false
 
            }
 
@@ -1077,65 +1075,78 @@ fn seek_def_in_modules<'a>(heap: &Heap, modules: &'a [Module], def_id: Definitio
 
            }
 
        }
 
    }
 

	
 
    None
 
}
 

	
 
fn seek_stmt<F: Fn(&Statement) -> bool>(heap: &Heap, start: StatementId, f: &F) -> Option<StatementId> {
 
    let stmt = &heap[start];
 
    if f(stmt) { return Some(start); }
 

	
 
    // This statement wasn't it, try to recurse
 
    let matched = match stmt {
 
        Statement::Block(block) => {
 
            for sub_id in &block.statements {
 
                if let Some(id) = seek_stmt(heap, *sub_id, f) {
 
                    return Some(id);
 
                }
 
            }
 

	
 
            None
 
        },
 
        Statement::Labeled(stmt) => seek_stmt(heap, stmt.body, f),
 
        Statement::If(stmt) => {
 
            if let Some(id) = seek_stmt(heap, stmt.true_body.upcast(), f) {
 
            if let Some(id) = seek_stmt(heap, stmt.true_case.body, f) {
 
                return Some(id);
 
            } else if let Some(false_body) = stmt.false_body {
 
                if let Some(id) = seek_stmt(heap, false_body.upcast(), f) {
 
            } else if let Some(false_body) = stmt.false_case {
 
                if let Some(id) = seek_stmt(heap, false_body.body, f) {
 
                    return Some(id);
 
                }
 
            }
 
            None
 
        },
 
        Statement::While(stmt) => seek_stmt(heap, stmt.body.upcast(), f),
 
        Statement::Synchronous(stmt) => seek_stmt(heap, stmt.body.upcast(), f),
 
        Statement::While(stmt) => seek_stmt(heap, stmt.body, f),
 
        Statement::Synchronous(stmt) => seek_stmt(heap, stmt.body, f),
 
        _ => None
 
    };
 

	
 
    matched
 
}
 

	
 
fn seek_scope<F: Fn(&Scope) -> bool>(heap: &Heap, start: ScopeId, f: &F) -> Option<ScopeId> {
 
    let scope = &heap[start];
 
    if f(scope) { return Some(start); }
 

	
 
    for child_scope_id in scope.nested.iter().copied() {
 
        if let Some(result) = seek_scope(heap, child_scope_id, f) {
 
            return Some(result);
 
        }
 
    }
 

	
 
    return None;
 
}
 

	
 
fn seek_expr_in_expr<F: Fn(&Expression) -> bool>(heap: &Heap, start: ExpressionId, f: &F) -> Option<ExpressionId> {
 
    let expr = &heap[start];
 
    if f(expr) { return Some(start); }
 

	
 
    match expr {
 
        Expression::Assignment(expr) => {
 
            None
 
            .or_else(|| seek_expr_in_expr(heap, expr.left, f))
 
            .or_else(|| seek_expr_in_expr(heap, expr.right, f))
 
        },
 
        Expression::Binding(expr) => {
 
            None
 
            .or_else(|| seek_expr_in_expr(heap, expr.bound_to, f))
 
            .or_else(|| seek_expr_in_expr(heap, expr.bound_from, f))
 
        }
 
        Expression::Conditional(expr) => {
 
            None
 
            .or_else(|| seek_expr_in_expr(heap, expr.test, f))
 
            .or_else(|| seek_expr_in_expr(heap, expr.true_expression, f))
 
            .or_else(|| seek_expr_in_expr(heap, expr.false_expression, f))
 
        },
 
        Expression::Binary(expr) => {
 
            None
 
            .or_else(|| seek_expr_in_expr(heap, expr.left, f))
 
@@ -1194,62 +1205,62 @@ fn seek_expr_in_expr<F: Fn(&Expression) -> bool>(heap: &Heap, start: ExpressionI
 
fn seek_expr_in_stmt<F: Fn(&Expression) -> bool>(heap: &Heap, start: StatementId, f: &F) -> Option<ExpressionId> {
 
    let stmt = &heap[start];
 

	
 
    match stmt {
 
        Statement::Local(stmt) => {
 
            match stmt {
 
                LocalStatement::Memory(stmt) => seek_expr_in_expr(heap, stmt.initial_expr.upcast(), f),
 
                LocalStatement::Channel(_) => None
 
            }
 
        }
 
        Statement::Block(stmt) => {
 
            for stmt_id in &stmt.statements {
 
                if let Some(id) = seek_expr_in_stmt(heap, *stmt_id, f) {
 
                    return Some(id)
 
                }
 
            }
 
            None
 
        },
 
        Statement::Labeled(stmt) => {
 
            seek_expr_in_stmt(heap, stmt.body, f)
 
        },
 
        Statement::If(stmt) => {
 
            None
 
            .or_else(|| seek_expr_in_expr(heap, stmt.test, f))
 
            .or_else(|| seek_expr_in_stmt(heap, stmt.true_body.upcast(), f))
 
            .or_else(|| if let Some(false_body) = stmt.false_body {
 
                seek_expr_in_stmt(heap, false_body.upcast(), f)
 
            .or_else(|| seek_expr_in_stmt(heap, stmt.true_case.body, f))
 
            .or_else(|| if let Some(false_body) = stmt.false_case {
 
                seek_expr_in_stmt(heap, false_body.body, f)
 
            } else {
 
                None
 
            })
 
        },
 
        Statement::While(stmt) => {
 
            None
 
            .or_else(|| seek_expr_in_expr(heap, stmt.test, f))
 
            .or_else(|| seek_expr_in_stmt(heap, stmt.body.upcast(), f))
 
            .or_else(|| seek_expr_in_stmt(heap, stmt.body, f))
 
        },
 
        Statement::Synchronous(stmt) => {
 
            seek_expr_in_stmt(heap, stmt.body.upcast(), f)
 
            seek_expr_in_stmt(heap, stmt.body, f)
 
        },
 
        Statement::Return(stmt) => {
 
            for expr_id in &stmt.expressions {
 
                if let Some(id) = seek_expr_in_expr(heap, *expr_id, f) {
 
                    return Some(id);
 
                }
 
            }
 
            None
 
        },
 
        Statement::New(stmt) => {
 
            seek_expr_in_expr(heap, stmt.expression.upcast(), f)
 
        },
 
        Statement::Expression(stmt) => {
 
            seek_expr_in_expr(heap, stmt.expression, f)
 
        },
 
        _ => None
 
    }
 
}
 

	
 
struct FakeRunContext{}
 
impl RunContext for FakeRunContext {
 
    fn performed_put(&mut self, _port: PortId) -> bool {
 
        unreachable!("'put' called in compiler testing code")
 
    }
0 comments (0 inline, 0 general)