Changeset - 644bbf1ed134
[Not reviewed]
0 9 0
MH - 3 years ago 2022-04-07 17:42:38
contact@maxhenger.nl
WIP on TCP component implementation, changes to interface
6 files changed:
0 comments (0 inline, 0 general)
src/protocol/ast.rs
Show inline comments
 
use std::fmt;
 
use std::fmt::{Debug, Display, Formatter};
 
use std::ops::{Index, IndexMut};
 

	
 
use super::arena::{Arena, Id};
 
use crate::collections::StringRef;
 
use crate::protocol::input_source::InputSpan;
 
use crate::protocol::TypeId;
 

	
 
/// Helper macro that defines a type alias for a AST element ID. In this case 
 
/// only used to alias the `Id<T>` types.
 
macro_rules! define_aliased_ast_id {
 
    // Variant where we just defined the alias, without any indexing
 
    ($name:ident, $parent:ty) => {
 
        pub type $name = $parent;
 
    };
 
    // Variant where we define the type, and the Index and IndexMut traits
 
    (
 
        $name:ident, $parent:ty, 
 
        index($indexed_type:ty, $indexed_arena:ident)
 
    ) => {
 
        define_aliased_ast_id!($name, $parent);
 
        impl Index<$name> for Heap {
 
            type Output = $indexed_type;
 
            fn index(&self, index: $name) -> &Self::Output {
 
                &self.$indexed_arena[index]
 
            }
 
        }
 

	
 
        impl IndexMut<$name> for Heap {
 
            fn index_mut(&mut self, index: $name) -> &mut Self::Output {
 
                &mut self.$indexed_arena[index]
 
            }
 
        }
 
    };
 
    // Variant where we define type, Index(Mut) traits and an allocation function
 
    (
 
        $name:ident, $parent:ty,
 
        index($indexed_type:ty, $indexed_arena:ident),
 
        alloc($fn_name:ident)
 
    ) => {
 
        define_aliased_ast_id!($name, $parent, index($indexed_type, $indexed_arena));
 
        impl Heap {
 
            pub fn $fn_name(&mut self, f: impl FnOnce($name) -> $indexed_type) -> $name {
 
                self.$indexed_arena.alloc_with_id(|id| f(id))
 
            }
 
        }
 
    };
 
}
 

	
 
/// Helper macro that defines a wrapper type for a particular variant of an AST
 
/// element ID. Only used to define single-wrapping IDs.
 
macro_rules! define_new_ast_id {
 
    // Variant where we just defined the new type, without any indexing
 
    ($name:ident, $parent:ty) => {
 
        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 
        pub struct $name (pub(crate) $parent);
 

	
 
        #[allow(dead_code)]
 
        impl $name {
 
            pub(crate) fn new_invalid() -> Self     { Self(<$parent>::new_invalid()) }
 
            pub(crate) fn is_invalid(&self) -> bool { self.0.is_invalid() }
 
            pub fn upcast(self) -> $parent          { self.0 }
 
        }
 
    };
 
    // Variant where we define the type, and the Index and IndexMut traits
 
    (
 
        $name:ident, $parent:ty, 
 
        index($indexed_type:ty, $wrapper_type:path, $indexed_arena:ident)
 
    ) => {
 
        define_new_ast_id!($name, $parent);
 
        impl Index<$name> for Heap {
 
            type Output = $indexed_type;
 
            fn index(&self, index: $name) -> &Self::Output {
 
                if let $wrapper_type(v) = &self.$indexed_arena[index.0] {
 
                    v
 
                } else {
 
                    unreachable!()
 
                }
 
            }
 
        }
 

	
 
        impl IndexMut<$name> for Heap {
 
            fn index_mut(&mut self, index: $name) -> &mut Self::Output {
 
                if let $wrapper_type(v) = &mut self.$indexed_arena[index.0] {
 
                    v
 
                } else {
 
                    unreachable!()
 
                }
 
            }
 
        }
 
    };
 
    // Variant where we define the type, the Index and IndexMut traits, and an allocation function
 
    (
 
        $name:ident, $parent:ty, 
 
        index($indexed_type:ty, $wrapper_type:path, $indexed_arena:ident),
 
        alloc($fn_name:ident)
 
    ) => {
 
        define_new_ast_id!($name, $parent, index($indexed_type, $wrapper_type, $indexed_arena));
 
        impl Heap {
 
            pub fn $fn_name(&mut self, f: impl FnOnce($name) -> $indexed_type) -> $name {
 
                $name(
 
                    self.$indexed_arena.alloc_with_id(|id| {
 
                        $wrapper_type(f($name(id)))
 
                    })
 
                )
 
            }
 
        }
 
    }
 
}
 

	
 
define_aliased_ast_id!(RootId, Id<Root>, index(Root, protocol_descriptions), alloc(alloc_protocol_description));
 
define_aliased_ast_id!(PragmaId, Id<Pragma>, index(Pragma, pragmas), alloc(alloc_pragma));
 
define_aliased_ast_id!(ImportId, Id<Import>, index(Import, imports), alloc(alloc_import));
 
define_aliased_ast_id!(VariableId, Id<Variable>, index(Variable, variables), alloc(alloc_variable));
 

	
 
define_aliased_ast_id!(DefinitionId, Id<Definition>, index(Definition, definitions));
 
define_new_ast_id!(StructDefinitionId, DefinitionId, index(StructDefinition, Definition::Struct, definitions), alloc(alloc_struct_definition));
 
define_new_ast_id!(EnumDefinitionId, DefinitionId, index(EnumDefinition, Definition::Enum, definitions), alloc(alloc_enum_definition));
 
define_new_ast_id!(UnionDefinitionId, DefinitionId, index(UnionDefinition, Definition::Union, definitions), alloc(alloc_union_definition));
 
define_new_ast_id!(ProcedureDefinitionId, DefinitionId, index(ProcedureDefinition, Definition::Procedure, definitions), alloc(alloc_procedure_definition));
 

	
 
define_aliased_ast_id!(StatementId, Id<Statement>, index(Statement, statements));
 
define_new_ast_id!(BlockStatementId, StatementId, index(BlockStatement, Statement::Block, statements), alloc(alloc_block_statement));
 
define_new_ast_id!(EndBlockStatementId, StatementId, index(EndBlockStatement, Statement::EndBlock, statements), alloc(alloc_end_block_statement));
 
define_new_ast_id!(LocalStatementId, StatementId, index(LocalStatement, Statement::Local, statements));
 
define_new_ast_id!(MemoryStatementId, LocalStatementId);
 
define_new_ast_id!(ChannelStatementId, LocalStatementId);
 
define_new_ast_id!(LabeledStatementId, StatementId, index(LabeledStatement, Statement::Labeled, statements), alloc(alloc_labeled_statement));
 
define_new_ast_id!(IfStatementId, StatementId, index(IfStatement, Statement::If, statements), alloc(alloc_if_statement));
 
define_new_ast_id!(EndIfStatementId, StatementId, index(EndIfStatement, Statement::EndIf, statements), alloc(alloc_end_if_statement));
 
define_new_ast_id!(WhileStatementId, StatementId, index(WhileStatement, Statement::While, statements), alloc(alloc_while_statement));
 
define_new_ast_id!(EndWhileStatementId, StatementId, index(EndWhileStatement, Statement::EndWhile, statements), alloc(alloc_end_while_statement));
 
define_new_ast_id!(BreakStatementId, StatementId, index(BreakStatement, Statement::Break, statements), alloc(alloc_break_statement));
 
define_new_ast_id!(ContinueStatementId, StatementId, index(ContinueStatement, Statement::Continue, statements), alloc(alloc_continue_statement));
 
define_new_ast_id!(SynchronousStatementId, StatementId, index(SynchronousStatement, Statement::Synchronous, statements), alloc(alloc_synchronous_statement));
 
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)))
 
            ))
 
        })))
 
    }
 
}
 

	
 
impl Index<MemoryStatementId> for Heap {
 
    type Output = MemoryStatement;
 
    fn index(&self, index: MemoryStatementId) -> &Self::Output {
 
        match &self.statements[index.0.0] {
 
            Statement::Local(LocalStatement::Memory(v)) => v,
 
            _ => unreachable!(),
 
        }
 
    }
 
}
 

	
 
impl Index<ChannelStatementId> for Heap {
 
    type Output = ChannelStatement;
 
    fn index(&self, index: ChannelStatementId) -> &Self::Output {
 
        match &self.statements[index.0.0] {
 
            Statement::Local(LocalStatement::Channel(v)) => v,
 
            _ => unreachable!(),
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct Root {
 
    pub this: RootId,
 
    // Phase 1: parser
 
    // pub position: InputPosition,
 
    pub pragmas: Vec<PragmaId>,
 
    pub imports: Vec<ImportId>,
 
    pub definitions: Vec<DefinitionId>,
 
}
 

	
 
impl Root {
 
    pub fn get_definition_ident(&self, h: &Heap, id: &[u8]) -> Option<DefinitionId> {
 
    pub fn get_definition_by_ident(&self, h: &Heap, id: &[u8]) -> Option<DefinitionId> {
 
        for &def in self.definitions.iter() {
 
            if h[def].identifier().value.as_bytes() == id {
 
                return Some(def);
 
            }
 
        }
 
        None
 
    }
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub enum Pragma {
 
    Version(PragmaVersion),
 
    Module(PragmaModule),
 
}
 

	
 
impl Pragma {
 
    pub(crate) fn as_module(&self) -> &PragmaModule {
 
        match self {
 
            Pragma::Module(pragma) => pragma,
 
            _ => unreachable!("Tried to obtain {:?} as PragmaModule", self),
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct PragmaVersion {
 
    pub this: PragmaId,
 
    // Phase 1: parser
 
    pub span: InputSpan, // of full pragma
 
    pub version: u64,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct PragmaModule {
 
    pub this: PragmaId,
 
    // Phase 1: parser
 
    pub span: InputSpan, // of full pragma
 
    pub value: Identifier,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub enum Import {
 
    Module(ImportModule),
 
    Symbols(ImportSymbols)
 
}
 

	
 
impl Import {
 
    pub(crate) fn span(&self) -> InputSpan {
 
        match self {
 
            Import::Module(v) => v.span,
 
            Import::Symbols(v) => v.span,
 
        }
 
    }
 

	
 
    pub(crate) fn as_module(&self) -> &ImportModule {
 
        match self {
 
            Import::Module(m) => m,
 
            _ => unreachable!("Unable to cast 'Import' to 'ImportModule'")
 
        }
 
    }
 
    pub(crate) fn as_symbols(&self) -> &ImportSymbols {
 
        match self {
 
            Import::Symbols(m) => m,
 
            _ => unreachable!("Unable to cast 'Import' to 'ImportSymbols'")
 
        }
 
    }
 
    pub(crate) fn as_symbols_mut(&mut self) -> &mut ImportSymbols {
 
        match self {
 
            Import::Symbols(m) => m,
 
            _ => unreachable!("Unable to cast 'Import' to 'ImportSymbols'")
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct ImportModule {
 
    pub this: ImportId,
 
    // Phase 1: parser
 
    pub span: InputSpan,
 
    pub module: Identifier,
 
    pub alias: Identifier,
 
    pub module_id: RootId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct AliasedSymbol {
 
    pub name: Identifier,
 
    pub alias: Option<Identifier>,
 
    pub definition_id: DefinitionId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct ImportSymbols {
 
    pub this: ImportId,
 
    // Phase 1: parser
 
    pub span: InputSpan,
 
    pub module: Identifier,
 
    pub module_id: RootId,
 
    pub symbols: Vec<AliasedSymbol>,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct Identifier {
 
    pub span: InputSpan,
 
    pub value: StringRef<'static>,
 
}
 

	
 
impl Identifier {
 
    pub(crate) const fn new_empty(span: InputSpan) -> Identifier {
 
        return Identifier{
 
            span,
 
            value: StringRef::new_empty(),
 
        };
 
    }
 
}
 

	
 
impl PartialEq for Identifier {
 
    fn eq(&self, other: &Self) -> bool {
 
        return self.value == other.value
 
    }
 
}
 

	
 
impl Display for Identifier {
 
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
 
        write!(f, "{}", self.value.as_str())
 
    }
 
}
 

	
 
#[derive(Debug, Clone, PartialEq, Eq)]
 
pub enum ParserTypeVariant {
 
    // Special builtin, only usable by the compiler and not constructable by the
 
    // programmer
 
    Void,
 
    InputOrOutput,
 
    ArrayLike,
 
    IntegerLike,
 
    // Basic builtin
 
    Message,
 
    Bool,
 
    UInt8, UInt16, UInt32, UInt64,
 
    SInt8, SInt16, SInt32, SInt64,
 
    Character, String,
 
    // Literals (need to get concrete builtin type during typechecking)
 
    IntegerLiteral,
 
    // Marker for inference
 
    Inferred,
 
    // Builtins expecting one subsequent type
 
    Array,
 
    Input,
 
    Output,
 
    // Tuple: expecting any number of elements. Note that the parser type can
 
    // have one-valued tuples, these will be filtered out later during type
 
    // checking.
 
    Tuple(u32), // u32 = number of subsequent types
 
    // User-defined types
 
    PolymorphicArgument(DefinitionId, u32), // u32 = index into polymorphic variables
 
    Definition(DefinitionId, u32), // u32 = number of subsequent types in the type tree.
 
}
 

	
 
impl ParserTypeVariant {
 
    pub(crate) fn num_embedded(&self) -> usize {
 
        use ParserTypeVariant::*;
 

	
 
        match self {
 
            Void | IntegerLike |
 
            Message | Bool |
 
            UInt8 | UInt16 | UInt32 | UInt64 |
 
            SInt8 | SInt16 | SInt32 | SInt64 |
 
            Character | String | IntegerLiteral |
 
            Inferred | PolymorphicArgument(_, _) =>
 
                0,
 
            ArrayLike | InputOrOutput | Array | Input | Output =>
 
                1,
 
            Definition(_, num) | Tuple(num) => *num as usize,
 
        }
 
    }
 
}
 

	
 
/// ParserTypeElement is an element of the type tree. An element may be
 
/// implicit, meaning that the user didn't specify the type, but it was set by
 
/// the compiler.
 
#[derive(Debug, Clone)]
 
pub struct ParserTypeElement {
 
    pub element_span: InputSpan, // span of this element, not including the child types
 
    pub variant: ParserTypeVariant,
 
}
 

	
 
/// ParserType is a specification of a type during the parsing phase and initial
 
/// linker/validator phase of the compilation process. These types may be
 
/// (partially) inferred or represent literals (e.g. a integer whose bytesize is
 
/// not yet determined).
 
///
 
/// Its contents are the depth-first serialization of the type tree. Each node
 
/// is a type that may accept polymorphic arguments. The polymorphic arguments
 
/// are then the children of the node.
 
#[derive(Debug, Clone)]
 
pub struct ParserType {
 
    pub elements: Vec<ParserTypeElement>,
 
    pub full_span: InputSpan,
 
}
 

	
 
impl ParserType {
 
    pub(crate) fn iter_embedded(&self, parent_idx: usize) -> ParserTypeIter {
 
        ParserTypeIter::new(&self.elements, parent_idx)
 
    }
 
}
 

	
 
/// Iterator over the embedded elements of a specific element.
 
pub struct ParserTypeIter<'a> {
 
    pub elements: &'a [ParserTypeElement],
 
    pub cur_embedded_idx: usize,
 
}
 

	
 
impl<'a> ParserTypeIter<'a> {
 
    fn new(elements: &'a [ParserTypeElement], parent_idx: usize) -> Self {
 
        debug_assert!(parent_idx < elements.len(), "parent index exceeds number of elements in ParserType");
 
        if elements[0].variant.num_embedded() == 0 {
 
            // Parent element does not have any embedded types, place
 
            // `cur_embedded_idx` at end so we will always return `None`
 
            Self{ elements, cur_embedded_idx: elements.len() }
 
        } else {
 
            // Parent element has an embedded type
 
            Self{ elements, cur_embedded_idx: parent_idx + 1 }
 
        }
 
    }
 
}
 

	
 
impl<'a> Iterator for ParserTypeIter<'a> {
 
    type Item = &'a [ParserTypeElement];
 

	
 
    fn next(&mut self) -> Option<Self::Item> {
 
        let elements_len = self.elements.len();
 
        if self.cur_embedded_idx >= elements_len {
 
            return None;
 
        }
 

	
 
        // Seek to the end of the subtree
 
        let mut depth = 1;
 
        let start_element = self.cur_embedded_idx;
 
        while self.cur_embedded_idx < elements_len {
 
            let cur_element = &self.elements[self.cur_embedded_idx];
 
            let depth_change = cur_element.variant.num_embedded() as i32 - 1;
 
            depth += depth_change;
 
            debug_assert!(depth >= 0, "illegally constructed ParserType: {:?}", self.elements);
 

	
 
            self.cur_embedded_idx += 1;
 
            if depth == 0 {
 
                break;
 
            }
 
        }
 

	
 
        debug_assert!(depth == 0, "illegally constructed ParserType: {:?}", self.elements);
 
        return Some(&self.elements[start_element..self.cur_embedded_idx]);
 
    }
 
}
 

	
 
/// ConcreteType is the representation of a type after the type inference and
 
/// checker is finished. These are fully typed.
 
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
 
pub enum ConcreteTypePart {
 
    // Special types (cannot be explicitly constructed by the programmer)
 
    Void,
 
    // Builtin types without nested types
 
    Message,
 
    Bool,
 
    UInt8, UInt16, UInt32, UInt64,
 
    SInt8, SInt16, SInt32, SInt64,
 
    Character, String,
 
    // Builtin types with one nested type
 
    Array,
 
    Slice,
 
    Input,
 
    Output,
 
    Pointer,
 
    // Tuple: variable number of nested types, will never be 1
 
    Tuple(u32),
 
    // User defined type with any number of nested types
 
    Instance(DefinitionId, u32),    // instance of data type
 
    Function(ProcedureDefinitionId, u32),    // instance of function
 
    Component(ProcedureDefinitionId, u32),   // instance of a connector
 
}
 

	
 
impl ConcreteTypePart {
 
    pub(crate) fn num_embedded(&self) -> u32 {
 
        use ConcreteTypePart::*;
 

	
 
        match self {
 
            Void | Message | Bool |
 
            UInt8 | UInt16 | UInt32 | UInt64 |
 
            SInt8 | SInt16 | SInt32 | SInt64 |
 
            Character | String =>
 
                0,
 
            Array | Slice | Input | Output | Pointer =>
 
                1,
 
            Tuple(num_embedded) => *num_embedded,
 
            Instance(_, num_embedded) => *num_embedded,
 
            Function(_, num_embedded) => *num_embedded,
 
            Component(_, num_embedded) => *num_embedded,
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone, Eq, PartialEq)]
 
pub struct ConcreteType {
 
    pub(crate) parts: Vec<ConcreteTypePart>
 
}
 

	
 
impl Default for ConcreteType {
 
    fn default() -> Self {
 
        Self{ parts: Vec::new() }
 
    }
 
}
 

	
 
impl ConcreteType {
 
    /// Returns an iterator over the subtrees that are type arguments (e.g. an
 
    /// array element's type, or a polymorphic type's arguments) to the
 
    /// provided parent type (specified by its index in the `parts` array).
 
    pub(crate) fn embedded_iter(&self, parent_part_idx: usize) -> ConcreteTypeIter {
 
        return ConcreteTypeIter::new(&self.parts, parent_part_idx);
 
    }
 

	
 
    /// Construct a human-readable name for the type. Because this performs
 
    /// a string allocation don't use it for anything else then displaying the
 
    /// type to the user.
 
    pub(crate) fn display_name(&self, heap: &Heap) -> String {
 
        return Self::type_parts_display_name(self.parts.as_slice(), heap);
 
    }
 

	
 
    // --- Utilities that operate on slice of parts
 

	
 
    /// Given the starting position of a type tree, determine the exclusive
 
    /// ending index.
 
    pub(crate) fn type_parts_subtree_end_idx(parts: &[ConcreteTypePart], start_idx: usize) -> usize {
 
        let mut depth = 1;
 
        let num_parts = parts.len();
 
        debug_assert!(start_idx < num_parts);
 

	
 
        for part_idx in start_idx..parts.len() {
 
            let depth_change = parts[part_idx].num_embedded() as i32 - 1;
 
            depth += depth_change;
 
            debug_assert!(depth >= 0);
 

	
 
            if depth == 0 {
 
                return part_idx + 1;
 
            }
 
        }
 

	
 
        debug_assert!(false, "incorrectly constructed ConcreteType instance");
 
        return 0;
 
    }
 

	
 
    /// Produces a human-readable representation of the concrete type parts
 
    fn type_parts_display_name(parts: &[ConcreteTypePart], heap: &Heap) -> String {
 
        let mut name = String::with_capacity(128);
 
        let _final_idx = Self::render_type_part_at(parts, heap, 0, &mut name);
 
        debug_assert_eq!(_final_idx, parts.len());
 

	
 
        return name;
 
    }
 

	
 
    /// Produces a human-readable representation of a single type part. Lower
 
    /// level utility for `type_parts_display_name`.
 
    fn render_type_part_at(parts: &[ConcreteTypePart], heap: &Heap, mut idx: usize, target: &mut String) -> usize {
 
        use ConcreteTypePart as CTP;
 
        use crate::protocol::parser::token_parsing::*;
 

	
 
        let cur_idx = idx;
 
        idx += 1; // increment by 1, because it always happens
 

	
 
        match parts[cur_idx] {
 
            CTP::Void => { target.push_str("void"); },
 
            CTP::Message => { target.push_str(KW_TYPE_MESSAGE_STR); },
 
            CTP::Bool => { target.push_str(KW_TYPE_BOOL_STR); },
 
            CTP::UInt8 => { target.push_str(KW_TYPE_UINT8_STR); },
 
            CTP::UInt16 => { target.push_str(KW_TYPE_UINT16_STR); },
 
            CTP::UInt32 => { target.push_str(KW_TYPE_UINT32_STR); },
 
            CTP::UInt64 => { target.push_str(KW_TYPE_UINT64_STR); },
 
            CTP::SInt8 => { target.push_str(KW_TYPE_SINT8_STR); },
 
            CTP::SInt16 => { target.push_str(KW_TYPE_SINT16_STR); },
 
            CTP::SInt32 => { target.push_str(KW_TYPE_SINT32_STR); },
 
            CTP::SInt64 => { target.push_str(KW_TYPE_SINT64_STR); },
 
            CTP::Character => { target.push_str(KW_TYPE_CHAR_STR); },
 
            CTP::String => { target.push_str(KW_TYPE_STRING_STR); },
 
            CTP::Array | CTP::Slice => {
 
                idx = Self::render_type_part_at(parts, heap, idx, target);
 
                target.push_str("[]");
 
            },
 
            CTP::Input => {
 
                target.push_str(KW_TYPE_IN_PORT_STR);
 
                target.push('<');
 
                idx = Self::render_type_part_at(parts, heap, idx, target);
 
                target.push('>');
 
            },
 
            CTP::Output => {
 
                target.push_str(KW_TYPE_OUT_PORT_STR);
 
                target.push('<');
 
                idx = Self::render_type_part_at(parts, heap, idx, target);
 
                target.push('>');
 
            },
 
            CTP::Pointer => {
 
                target.push('*');
 
                idx = Self::render_type_part_at(parts, heap, idx, target);
 
            }
 
            CTP::Tuple(num_parts) => {
 
                target.push('(');
 
                if num_parts != 0 {
 
                    idx = Self::render_type_part_at(parts, heap, idx, target);
 
                    for _ in 1..num_parts {
 
                        target.push(',');
 
                        idx = Self::render_type_part_at(parts, heap, idx, target);
 
                    }
 
                }
 
                target.push(')');
 
            },
 
            CTP::Instance(definition_id, num_poly_args) => {
 
                idx = Self::render_definition_type_parts_at(parts, heap, definition_id, num_poly_args, idx, target);
 
            }
 
            CTP::Function(definition_id, num_poly_args) |
 
            CTP::Component(definition_id, num_poly_args) => {
 
                idx = Self::render_definition_type_parts_at(parts, heap, definition_id.upcast(), num_poly_args, idx, target);
 
            }
 
        }
 

	
 
        idx
 
    }
 

	
 
    fn render_definition_type_parts_at(parts: &[ConcreteTypePart], heap: &Heap, definition_id: DefinitionId, num_poly_args: u32, mut idx: usize, target: &mut String) -> usize {
 
        let definition = &heap[definition_id];
 
        target.push_str(definition.identifier().value.as_str());
 

	
 
        if num_poly_args != 0 {
 
            target.push('<');
 
            for poly_arg_idx in 0..num_poly_args {
 
                if poly_arg_idx != 0 {
 
                    target.push(',');
 
                }
 
                idx = Self::render_type_part_at(parts, heap, idx, target);
 
            }
 
            target.push('>');
 
        }
 

	
 
        return idx;
 
    }
 
}
 

	
 
#[derive(Debug)]
 
pub struct ConcreteTypeIter<'a> {
 
    parts: &'a [ConcreteTypePart],
 
    idx_embedded: u32,
 
    num_embedded: u32,
 
    part_idx: usize,
 
}
 

	
 
impl<'a> ConcreteTypeIter<'a> {
 
    pub(crate) fn new(parts: &'a[ConcreteTypePart], parent_idx: usize) -> Self {
 
        let num_embedded = parts[parent_idx].num_embedded();
 
        return ConcreteTypeIter{
 
            parts,
 
            idx_embedded: 0,
 
            num_embedded,
 
            part_idx: parent_idx + 1,
 
        }
 
    }
 
}
 

	
 
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 ScopeAssociation {
 
    Definition(DefinitionId),
 
    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 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 Scope {
 
    pub(crate) fn new(id: ScopeId, association: ScopeAssociation) -> Self {
 
        return Self{
 
            this: id,
 
            parent: None,
 
            nested: Vec::new(),
 
            association,
 
            variables: Vec::new(),
 
            labels: Vec::new(),
 
            relative_pos_in_parent: -1,
 
            first_unique_id_in_scope: -1,
 
            next_unique_id_in_scope: -1,
 
        }
 
    }
 
}
 

	
 
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_parent: i32,
 
    pub unique_id_in_scope: i32, // Temporary fix until proper bytecode/asm is generated
 
}
 

	
 
#[derive(Debug)]
 
pub enum Definition {
 
    Struct(StructDefinition),
 
    Enum(EnumDefinition),
 
    Union(UnionDefinition),
 
    Procedure(ProcedureDefinition),
 
}
 

	
 
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'"),
 
        }
 
    }
 
    pub(crate) fn as_struct_mut(&mut self) -> &mut StructDefinition {
 
        match self {
 
            Definition::Struct(result) => result,
 
            _ => panic!("Unable to cast 'Definition' to 'StructDefinition'"),
 
        }
 
    }
 
    pub fn is_enum(&self) -> bool {
 
        match self {
 
            Definition::Enum(_) => true,
 
            _ => false,
 
        }
 
    }
 
    pub(crate) fn as_enum(&self) -> &EnumDefinition {
 
        match self {
 
            Definition::Enum(result) => result,
 
            _ => panic!("Unable to cast 'Definition' to 'EnumDefinition'"),
 
        }
 
    }
 
    pub(crate) fn as_enum_mut(&mut self) -> &mut EnumDefinition {
 
        match self {
 
            Definition::Enum(result) => result,
 
            _ => panic!("Unable to cast 'Definition' to 'EnumDefinition'"),
 
        }
 
    }
 
    pub fn is_union(&self) -> bool {
 
        match self {
 
            Definition::Union(_) => true,
 
            _ => false,
 
        }
 
    }
 
    pub(crate) fn as_union(&self) -> &UnionDefinition {
 
        match self {
 
            Definition::Union(result) => result, 
 
            _ => panic!("Unable to cast 'Definition' to 'UnionDefinition'"),
 
        }
 
    }
 

	
 
    pub(crate) fn as_union_mut(&mut self) -> &mut UnionDefinition {
 
        match self {
 
            Definition::Union(result) => result,
 
            _ => panic!("Unable to cast 'Definition' to 'UnionDefinition'"),
 
        }
 
    }
 

	
 
    pub fn is_procedure(&self) -> bool {
 
        match self {
 
            Definition::Procedure(_) => true,
 
            _ => false,
 
        }
 
    }
 

	
 
    pub(crate) fn as_procedure(&self) -> &ProcedureDefinition {
 
        match self {
 
            Definition::Procedure(result) => result,
 
            _ => panic!("Unable to cast `Definition` to `Function`"),
 
        }
 
    }
 

	
 
    pub(crate) fn as_procedure_mut(&mut self) -> &mut ProcedureDefinition {
 
        match self {
 
            Definition::Procedure(result) => result,
 
            _ => panic!("Unable to cast `Definition` to `Function`"),
 
        }
 
    }
 

	
 
    pub fn defined_in(&self) -> RootId {
 
        match self {
 
            Definition::Struct(def) => def.defined_in,
 
            Definition::Enum(def) => def.defined_in,
 
            Definition::Union(def) => def.defined_in,
 
            Definition::Procedure(def) => def.defined_in,
 
        }
 
    }
 

	
 
    pub fn identifier(&self) -> &Identifier {
 
        match self {
 
            Definition::Struct(def) => &def.identifier,
 
            Definition::Enum(def) => &def.identifier,
 
            Definition::Union(def) => &def.identifier,
 
            Definition::Procedure(def) => &def.identifier,
 
        }
 
    }
 
    pub fn poly_vars(&self) -> &Vec<Identifier> {
 
        match self {
 
            Definition::Struct(def) => &def.poly_vars,
 
            Definition::Enum(def) => &def.poly_vars,
 
            Definition::Union(def) => &def.poly_vars,
 
            Definition::Procedure(def) => &def.poly_vars,
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct StructFieldDefinition {
 
    pub span: InputSpan,
 
    pub field: Identifier,
 
    pub parser_type: ParserType,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct StructDefinition {
 
    pub this: StructDefinitionId,
 
    pub defined_in: RootId,
 
    // Symbol scanning
 
    pub identifier: Identifier,
 
    pub poly_vars: Vec<Identifier>,
 
    // Parsing
 
    pub fields: Vec<StructFieldDefinition>
 
}
 

	
 
impl StructDefinition {
 
    pub(crate) fn new_empty(
 
        this: StructDefinitionId, defined_in: RootId,
 
        identifier: Identifier, poly_vars: Vec<Identifier>
 
    ) -> Self {
 
        Self{ this, defined_in, identifier, poly_vars, fields: Vec::new() }
 
    }
 
}
 

	
 
#[derive(Debug, Clone, Copy)]
 
pub enum EnumVariantValue {
 
    None,
 
    Integer(i64),
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct EnumVariantDefinition {
 
    pub identifier: Identifier,
 
    pub value: EnumVariantValue,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct EnumDefinition {
 
    pub this: EnumDefinitionId,
 
    pub defined_in: RootId,
 
    // Symbol scanning
 
    pub identifier: Identifier,
 
    pub poly_vars: Vec<Identifier>,
 
    // Parsing
 
    pub variants: Vec<EnumVariantDefinition>,
 
}
 

	
 
impl EnumDefinition {
 
    pub(crate) fn new_empty(
 
        this: EnumDefinitionId, defined_in: RootId,
 
        identifier: Identifier, poly_vars: Vec<Identifier>
 
    ) -> Self {
 
        Self{ this, defined_in, identifier, poly_vars, variants: Vec::new() }
 
    }
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct UnionVariantDefinition {
 
    pub span: InputSpan,
 
    pub identifier: Identifier,
 
    pub value: Vec<ParserType>, // if empty, then union variant does not contain any embedded types
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct UnionDefinition {
 
    pub this: UnionDefinitionId,
 
    pub defined_in: RootId,
 
    // Phase 1: symbol scanning
 
    pub identifier: Identifier,
 
    pub poly_vars: Vec<Identifier>,
 
    // Phase 2: parsing
 
    pub variants: Vec<UnionVariantDefinition>,
 
}
 

	
 
impl UnionDefinition {
 
    pub(crate) fn new_empty(
 
        this: UnionDefinitionId, defined_in: RootId,
 
        identifier: Identifier, poly_vars: Vec<Identifier>
 
    ) -> Self {
 
        Self{ this, defined_in, identifier, poly_vars, variants: Vec::new() }
 
    }
 
}
 

	
 
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 
pub enum ProcedureKind {
 
    Function, // with return type
 
    Primitive, // without return type
 
    Composite,
src/protocol/mod.rs
Show inline comments
 
mod arena;
 
pub(crate) mod eval;
 
pub(crate) mod input_source;
 
mod parser;
 
#[cfg(test)] mod tests;
 

	
 
pub(crate) mod ast;
 
pub(crate) mod ast_writer;
 
mod token_writer;
 

	
 
use std::sync::Mutex;
 

	
 
use crate::collections::{StringPool, StringRef};
 
pub use crate::protocol::ast::*;
 
use crate::protocol::eval::*;
 
use crate::protocol::input_source::*;
 
use crate::protocol::parser::*;
 
use crate::protocol::type_table::*;
 

	
 
pub use parser::type_table::TypeId;
 

	
 
/// A protocol description module
 
pub struct Module {
 
    pub(crate) source: InputSource,
 
    pub(crate) root_id: RootId,
 
    pub(crate) name: Option<StringRef<'static>>,
 
}
 
/// Description of a protocol object, used to configure new connectors.
 
#[repr(C)]
 
pub struct ProtocolDescription {
 
    pub(crate) modules: Vec<Module>,
 
    pub(crate) heap: Heap,
 
    pub(crate) types: TypeTable,
 
    pub(crate) pool: Mutex<StringPool>,
 
}
 
#[derive(Debug, Clone)]
 
pub(crate) struct ComponentState {
 
    pub(crate) prompt: Prompt,
 
}
 

	
 
#[derive(Debug)]
 
pub enum ComponentCreationError {
 
    ModuleDoesntExist,
 
    DefinitionDoesntExist,
 
    DefinitionNotComponent,
 
    InvalidNumArguments,
 
    InvalidArgumentType(usize),
 
    UnownedPort,
 
    InSync,
 
}
 

	
 
impl ProtocolDescription {
 
    pub fn parse(buffer: &[u8]) -> Result<Self, String> {
 
        let source = InputSource::new(String::new(), Vec::from(buffer));
 
        let mut parser = Parser::new()?;
 
        parser.feed(source).expect("failed to feed source");
 
        
 
        if let Err(err) = parser.parse() {
 
            println!("ERROR:\n{}", err);
 
            return Err(format!("{}", err))
 
        }
 

	
 
        let modules: Vec<Module> = parser.modules.into_iter()
 
            .map(|module| Module{
 
                source: module.source,
 
                root_id: module.root_id,
 
                name: module.name.map(|(_, name)| name)
 
            })
 
            .collect();
 

	
 
        return Ok(ProtocolDescription {
 
            modules,
 
            heap: parser.heap,
 
            types: parser.type_table,
 
            pool: Mutex::new(parser.string_pool),
 
        });
 
    }
 

	
 
    pub(crate) fn new_component(
 
        &self, module_name: &[u8], identifier: &[u8], arguments: ValueGroup
 
    ) -> Result<Prompt, ComponentCreationError> {
 
        // Find the module in which the definition can be found
 
        let module_root = self.lookup_module_root(module_name);
 
        if module_root.is_none() {
 
            return Err(ComponentCreationError::ModuleDoesntExist);
 
        }
 
        let module_root = module_root.unwrap();
 

	
 
        let root = &self.heap[module_root];
 
        let definition_id = root.get_definition_ident(&self.heap, identifier);
 
        let definition_id = root.get_definition_by_ident(&self.heap, identifier);
 
        if definition_id.is_none() {
 
            return Err(ComponentCreationError::DefinitionDoesntExist);
 
        }
 
        let definition_id = definition_id.unwrap();
 

	
 
        let ast_definition = &self.heap[definition_id];
 
        if !ast_definition.is_procedure() {
 
            return Err(ComponentCreationError::DefinitionNotComponent);
 
        }
 

	
 
        // Make sure that the types of the provided value group matches that of
 
        // the expected types.
 
        let ast_definition = ast_definition.as_procedure();
 
        if !ast_definition.poly_vars.is_empty() || ast_definition.kind == ProcedureKind::Function {
 
            return Err(ComponentCreationError::DefinitionNotComponent);
 
        }
 

	
 
        // - check number of arguments by retrieving the one instantiated
 
        //   monomorph
 
        let concrete_type = ConcreteType{ parts: vec![ConcreteTypePart::Component(ast_definition.this, 0)] };
 
        let procedure_type_id = self.types.get_procedure_monomorph_type_id(&definition_id, &concrete_type.parts).unwrap();
 
        let procedure_type_id = self.types.get_monomorph_type_id(&definition_id, &concrete_type.parts).unwrap();
 
        let procedure_monomorph_index = self.types.get_monomorph(procedure_type_id).variant.as_procedure().monomorph_index;
 
        let monomorph_info = &ast_definition.monomorphs[procedure_monomorph_index as usize];
 
        if monomorph_info.argument_types.len() != arguments.values.len() {
 
            return Err(ComponentCreationError::InvalidNumArguments);
 
        }
 

	
 
        // - for each argument try to make sure the types match
 
        for arg_idx in 0..arguments.values.len() {
 
            let expected_type_id = monomorph_info.argument_types[arg_idx];
 
            let expected_type = &self.types.get_monomorph(expected_type_id).concrete_type;
 
            let provided_value = &arguments.values[arg_idx];
 
            if !self.verify_same_type(expected_type, 0, &arguments, provided_value) {
 
                return Err(ComponentCreationError::InvalidArgumentType(arg_idx));
 
            }
 
        }
 

	
 
        // By now we're sure that all of the arguments are correct. So create
 
        // the connector.
 
        return Ok(Prompt::new(&self.types, &self.heap, ast_definition.this, procedure_type_id, arguments));
 
    }
 

	
 
    /// A somewhat temporary method. Can be used by components to lookup type
 
    /// definitions by their name (to have their implementation somewhat
 
    /// resistant to changes in the standard library)
 
    pub(crate) fn find_type(&self, module_name: &[u8], type_name: &[u8]) -> Option<TypeInspector> {
 
        // Lookup type definition in module
 
        let root_id = self.lookup_module_root(module_name)?;
 
        let module = &self.heap[root_id];
 
        let definition_id = module.get_definition_by_ident(&self.heap, type_name)?;
 
        let definition = &self.heap[definition_id];
 

	
 
        // Make sure type is not polymorphic and is not a procedure
 
        if !definition.poly_vars().is_empty() {
 
            return None;
 
        }
 
        if definition.is_procedure() {
 
            return None;
 
        }
 

	
 
        // Lookup type in type table
 
        let type_parts = [ConcreteTypePart::Instance(definition_id, 0)];
 
        let type_id = self.types.get_monomorph_type_id(&definition_id, &type_parts)
 
            .expect("type ID for non-polymorphic type");
 
        let type_monomorph = self.types.get_monomorph(type_id);
 

	
 
        return Some(TypeInspector{
 
            heap: definition,
 
            type_table: type_monomorph
 
        });
 
    }
 

	
 
    fn lookup_module_root(&self, module_name: &[u8]) -> Option<RootId> {
 
        for module in self.modules.iter() {
 
            match &module.name {
 
                Some(name) => if name.as_bytes() == module_name {
 
                    return Some(module.root_id);
 
                },
 
                None => if module_name.is_empty() {
 
                    return Some(module.root_id);
 
                }
 
            }
 
        }
 

	
 
        return None;
 
    }
 

	
 
    fn verify_same_type(&self, expected: &ConcreteType, expected_idx: usize, arguments: &ValueGroup, argument: &Value) -> bool {
 
        use ConcreteTypePart as CTP;
 

	
 
        match &expected.parts[expected_idx] {
 
            CTP::Void | CTP::Message | CTP::Slice | CTP::Pointer | CTP::Function(_, _) | CTP::Component(_, _) => unreachable!(),
 
            CTP::Bool => if let Value::Bool(_) = argument { true } else { false },
 
            CTP::UInt8 => if let Value::UInt8(_) = argument { true } else { false },
 
            CTP::UInt16 => if let Value::UInt16(_) = argument { true } else { false },
 
            CTP::UInt32 => if let Value::UInt32(_) = argument { true } else { false },
 
            CTP::UInt64 => if let Value::UInt64(_) = argument { true } else { false },
 
            CTP::SInt8 => if let Value::SInt8(_) = argument { true } else { false },
 
            CTP::SInt16 => if let Value::SInt16(_) = argument { true } else { false },
 
            CTP::SInt32 => if let Value::SInt32(_) = argument { true } else { false },
 
            CTP::SInt64 => if let Value::SInt64(_) = argument { true } else { false },
 
            CTP::Character => if let Value::Char(_) = argument { true } else { false },
 
            CTP::String => {
 
                // Match outer string type and embedded character types
 
                if let Value::String(heap_pos) = argument {
 
                    for element in &arguments.regions[*heap_pos as usize] {
 
                        if let Value::Char(_) = element {} else {
 
                            return false;
 
                        }
 
                    }
 
                } else {
 
                    return false;
 
                }
 

	
 
                return true;
 
            },
 
            CTP::Array => {
 
                if let Value::Array(heap_pos) = argument {
 
                    let heap_pos = *heap_pos;
 
                    for element in &arguments.regions[heap_pos as usize] {
 
                        if !self.verify_same_type(expected, expected_idx + 1, arguments, element) {
 
                            return false;
 
                        }
 
                    }
 
                    return true;
 
                } else {
 
                    return false;
 
                }
 
            },
 
            CTP::Input => if let Value::Input(_) = argument { true } else { false },
 
            CTP::Output => if let Value::Output(_) = argument { true } else { false },
 
            CTP::Tuple(_) => todo!("implement full type checking on user-supplied arguments"),
 
            CTP::Instance(definition_id, _num_embedded) => {
 
                let definition = self.types.get_base_definition(definition_id).unwrap();
 
                match &definition.definition {
 
                    DefinedTypeVariant::Enum(definition) => {
 
                        if let Value::Enum(variant_value) = argument {
 
                            let is_valid = definition.variants.iter()
 
                                .any(|v| v.value == *variant_value);
 
                            return is_valid;
 
                        }
 
                    },
 
                    _ => todo!("implement full type checking on user-supplied arguments"),
 
                }
 

	
 
                return false;
 
            },
 
        }
 
    }
 
}
 

	
 
pub trait RunContext {
 
    fn performed_put(&mut self, port: PortId) -> bool;
 
    fn performed_get(&mut self, port: PortId) -> Option<ValueGroup>; // None if still waiting on message
 
    fn fires(&mut self, port: PortId) -> Option<Value>; // None if not yet branched
 
    fn performed_fork(&mut self) -> Option<bool>; // None if not yet forked
 
    fn created_channel(&mut self) -> Option<(Value, Value)>; // None if not yet prepared
 
    fn performed_select_wait(&mut self) -> Option<u32>; // None if not yet notified runtime of select blocker
 
}
 

	
 
pub struct ProtocolDescriptionBuilder {
 
    parser: Parser,
 
}
 

	
 
impl ProtocolDescriptionBuilder {
 
    pub fn new() -> Result<Self, String> {
 
        return Ok(Self{
 
            parser: Parser::new()?,
 
        })
 
    }
 

	
 
    pub fn add(&mut self, filename: String, buffer: Vec<u8>) -> Result<(), ParseError> {
 
        let input = InputSource::new(filename, buffer);
 
        self.parser.feed(input)?;
 

	
 
        return Ok(())
 
    }
 

	
 
    pub fn compile(mut self) -> Result<ProtocolDescription, ParseError> {
 
        self.parser.parse()?;
 

	
 
        let modules: Vec<Module> = self.parser.modules.into_iter()
 
            .map(|module| Module{
 
                source: module.source,
 
                root_id: module.root_id,
 
                name: module.name.map(|(_, name)| name)
 
            })
 
            .collect();
 

	
 
        return Ok(ProtocolDescription {
 
            modules,
 
            heap: self.parser.heap,
 
            types: self.parser.type_table,
 
            pool: Mutex::new(self.parser.string_pool),
 
        });
 
    }
 
}
 

	
 
pub struct TypeInspector<'a> {
 
    heap: &'a Definition,
 
    type_table: &'a MonoType,
 
}
 

	
 
impl TypeInspector {
 
    pub fn as_union(&self) -> UnionTypeInspector {
 
        let heap = self.heap.as_union();
 
        let type_table = self.type_table.variant.as_union();
 
        return UnionTypeInspector{ heap, type_table };
 
    }
 
}
 

	
 
pub struct UnionTypeInspector<'a> {
 
    heap: &'a UnionDefinition,
 
    type_table: &'a UnionMonomorph,
 
}
 

	
 
impl UnionTypeInspector {
 
    /// Retrieves union variant tag value.
 
    pub fn get_variant_tag_value(&self, variant_name: &[u8]) -> Option<i64> {
 
        let variant_index = self.heap.variants.iter()
 
            .position(|v| v.identifier.value.as_bytes() == variant_name)?;
 
        return Some(variant_index as i64);
 
    }
 
}
 
\ No newline at end of file
src/protocol/parser/pass_typing.rs
Show inline comments
 
@@ -1303,1537 +1303,1537 @@ impl PassTyping {
 

	
 
        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_data.push(VarData{
 
            var_id: channel_stmt.from,
 
            var_type: from_var_type,
 
            used_at: Vec::new(),
 
            linked_var: Some(to_var_index),
 
        });
 

	
 
        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_data.push(VarData{
 
            var_id: channel_stmt.to,
 
            var_type: to_var_type,
 
            used_at: Vec::new(),
 
            linked_var: Some(from_var_index),
 
        });
 

	
 
        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_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_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_stmt(ctx, body_id)?;
 

	
 
        Ok(())
 
    }
 

	
 
    fn visit_break_stmt(&mut self, _: &mut Ctx, _: BreakStatementId) -> VisitorResult { return Ok(()) }
 
    fn visit_continue_stmt(&mut self, _: &mut Ctx, _: ContinueStatementId) -> VisitorResult { return 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_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_stmt(ctx, left_body_id)?;
 
        if let Some(right_body_id) = 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.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)?;
 
        return Ok(());
 
    }
 

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

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

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

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

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

	
 
    // Expressions
 

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

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

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

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

	
 
        let old_parent = self.parent_index.replace(self_index);
 
        let left_index = self.visit_expr(ctx, left_expr_id)?;
 
        let right_index = self.visit_expr(ctx, right_expr_id)?;
 

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

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

	
 
        self.parent_index = old_parent;
 
        self.progress_inference_rule_tri_equal_args(ctx, self_index)?;
 
        return Ok(self_index);
 
    }
 

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

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

	
 
        let old_parent = self.parent_index.replace(self_index);
 
        let arg_to_index = self.visit_expr(ctx, bound_to_id)?;
 
        let arg_from_index = self.visit_expr(ctx, bound_from_id)?;
 

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

	
 
        self.parent_index = old_parent;
 
        self.progress_inference_rule_tri_equal_args(ctx, self_index)?;
 
        return Ok(self_index);
 
    }
 

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

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

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

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

	
 
        self.parent_index = old_parent;
 
        self.progress_inference_rule_tri_equal_all(ctx, self_index)?;
 
        return Ok(self_index);
 
    }
 

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

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

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

	
 
        let old_parent = self.parent_index.replace(self_index);
 
        let left_index = self.visit_expr(ctx, lhs_expr_id)?;
 
        let right_index = self.visit_expr(ctx, rhs_expr_id)?;
 

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

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

	
 
        self.parent_index = old_parent;
 
        self.progress_inference_rule(ctx, self_index)?;
 
        return Ok(self_index);
 
    }
 

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

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

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

	
 
        let old_parent = self.parent_index.replace(self_index);
 
        let argument_index = self.visit_expr(ctx, arg_expr_id)?;
 

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

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

	
 
        self.parent_index = old_parent;
 
        self.progress_inference_rule_bi_equal(ctx, self_index)?;
 
        return Ok(self_index);
 
    }
 

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

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

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

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

	
 
        self.parent_index = old_parent;
 
        self.progress_inference_rule_indexing_expr(ctx, self_index)?;
 
        return Ok(self_index);
 
    }
 

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

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

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

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

	
 
        self.parent_index = old_parent;
 
        self.progress_inference_rule_slicing_expr(ctx, self_index)?;
 
        return Ok(self_index);
 
    }
 

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

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

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

	
 
        let node = &mut self.infer_nodes[self_index];
 
        let inference_rule = match &ctx.heap[id].kind {
 
            SelectKind::StructField(field_identifier) =>
 
                InferenceRule::SelectStructField(InferenceRuleSelectStructField{
 
                    subject_index,
 
                    selected_field: field_identifier.clone(),
 
                }),
 
            SelectKind::TupleMember(member_index) =>
 
                InferenceRule::SelectTupleMember(InferenceRuleSelectTupleMember{
 
                    subject_index,
 
                    selected_index: *member_index,
 
                }),
 
        };
 
        node.inference_rule = inference_rule;
 

	
 
        self.parent_index = old_parent;
 
        self.progress_inference_rule(ctx, self_index)?;
 
        return Ok(self_index);
 
    }
 

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

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

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

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

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

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

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

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

	
 
                let node = &mut self.infer_nodes[self_index];
 
                node.inference_rule = InferenceRule::LiteralArray(InferenceRuleLiteralArray{
 
                    element_indices,
 
                });
 
            },
 
            Literal::Tuple(expressions) => {
 
                let expr_ids = self.expr_buffer.start_section_initialized(expressions.as_slice());
 

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

	
 
                let node = &mut self.infer_nodes[self_index];
 
                node.inference_rule = InferenceRule::LiteralTuple(InferenceRuleLiteralTuple{
 
                    element_indices,
 
                })
 
            }
 
        }
 

	
 
        self.parent_index = old_parent;
 
        self.progress_inference_rule(ctx, self_index)?;
 
        return Ok(self_index);
 
    }
 

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

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

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

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

	
 
        self.parent_index = old_parent;
 

	
 
        // The cast expression is a bit special at this point: the progression
 
        // function simply makes sure input/output types are compatible. But if
 
        // the programmer explicitly specified the output type, then we can
 
        // already perform that inference rule here.
 
        {
 
            let cast_expr = &ctx.heap[id];
 
            let specified_type = self.determine_inference_type_from_parser_type_elements(&cast_expr.to_type.elements, true);
 
            let _progress = self.apply_template_constraint(ctx, self_index, &specified_type.parts)?;
 
        }
 

	
 
        self.progress_inference_rule_cast_expr(ctx, self_index)?;
 
        return Ok(self_index);
 
    }
 

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

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

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

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

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

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

	
 
        self.parent_index = old_parent;
 
        self.progress_inference_rule_call_expr(ctx, self_index)?;
 
        return Ok(self_index);
 
    }
 

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

	
 
        let var_expr = &ctx.heap[id];
 
        debug_assert!(var_expr.declaration.is_some());
 
        let old_parent = self.parent_index.replace(self_index);
 

	
 
        let declaration = &ctx.heap[var_expr.declaration.unwrap()];
 
        let mut var_data_index = None;
 
        for (index, var_data) in self.var_data.iter().enumerate() {
 
            if var_data.var_id == declaration.this {
 
                var_data_index = Some(index);
 
                break;
 
            }
 
        }
 

	
 
        let var_data_index = if let Some(var_data_index) = var_data_index {
 
            let var_data = &mut self.var_data[var_data_index];
 
            var_data.used_at.push(self_index);
 

	
 
            var_data_index
 
        } else {
 
            // If we're in a binding expression then it might the first time we
 
            // encounter the variable, so add a `VarData` entry.
 
            debug_assert_eq!(declaration.kind, VariableKind::Binding);
 
            let var_type = self.determine_inference_type_from_parser_type_elements(
 
                &declaration.parser_type.elements, true
 
            );
 
            let var_data_index = self.var_data.len();
 
            self.var_data.push(VarData{
 
                var_id: declaration.this,
 
                var_type,
 
                used_at: vec![self_index],
 
                linked_var: None,
 
            });
 

	
 
            var_data_index
 
        };
 

	
 
        let node = &mut self.infer_nodes[self_index];
 
        node.inference_rule = InferenceRule::VariableExpr(InferenceRuleVariableExpr{
 
            var_data_index,
 
        });
 

	
 
        self.parent_index = old_parent;
 
        self.progress_inference_rule_variable_expr(ctx, self_index)?;
 
        return Ok(self_index);
 
    }
 
}
 

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

	
 
impl PassTyping {
 
    #[allow(dead_code)] // used when debug flag at the top of this file is true.
 
    fn debug_get_display_name(&self, ctx: &Ctx, node_index: InferNodeIndex) -> String {
 
        let expr_type = &self.infer_nodes[node_index].expr_type;
 
        expr_type.display_name(&ctx.heap)
 
    }
 

	
 
    fn resolve_types(&mut self, ctx: &mut Ctx, queue: &mut ResolveQueue) -> Result<(), ParseError> {
 
        // Keep inferring until we can no longer make any progress
 
        while !self.node_queued.is_empty() {
 
            while !self.node_queued.is_empty() {
 
                let node_index = self.node_queued.pop_front().unwrap();
 
                self.progress_inference_rule(ctx, node_index)?;
 
            }
 

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

	
 
                    // Requeue expression (and its parent, if it exists)
 
                    self.node_queued.push_back(infer_node_index);
 
                    if let Some(node_parent_index) = infer_node.parent_index {
 
                        self.node_queued.push_back(node_parent_index);
 
                    }
 
                }
 
            }
 
        }
 

	
 
        // Helper for transferring polymorphic variables to concrete types and
 
        // checking if they're completely specified
 
        fn poly_data_type_to_concrete_type(
 
            ctx: &Ctx, expr_id: ExpressionId, inference_poly_args: &Vec<InferenceType>,
 
            first_concrete_part: ConcreteTypePart,
 
        ) -> Result<ConcreteType, ParseError> {
 
            // Prepare storage vector
 
            let mut num_inference_parts = 0;
 
            for inference_type in inference_poly_args {
 
                num_inference_parts += inference_type.parts.len();
 
            }
 

	
 
            let mut concrete_type = ConcreteType{
 
                parts: Vec::with_capacity(1 + num_inference_parts),
 
            };
 
            concrete_type.parts.push(first_concrete_part);
 

	
 
            // Go through all polymorphic arguments and add them to the concrete
 
            // types.
 
            for (poly_idx, poly_type) in inference_poly_args.iter().enumerate() {
 
                if !poly_type.is_done {
 
                    let expr = &ctx.heap[expr_id];
 
                    let definition = match expr {
 
                        Expression::Call(expr) => expr.procedure.upcast(),
 
                        Expression::Literal(expr) => match &expr.value {
 
                            Literal::Enum(lit) => lit.definition,
 
                            Literal::Union(lit) => lit.definition,
 
                            Literal::Struct(lit) => lit.definition,
 
                            _ => unreachable!()
 
                        },
 
                        _ => unreachable!(),
 
                    };
 
                    let poly_vars = ctx.heap[definition].poly_vars();
 
                    return Err(ParseError::new_error_at_span(
 
                        &ctx.module().source, expr.operation_span(), format!(
 
                            "could not fully infer the type of polymorphic variable '{}' of this expression (got '{}')",
 
                            poly_vars[poly_idx].value.as_str(), poly_type.display_name(&ctx.heap)
 
                        )
 
                    ));
 
                }
 

	
 
                poly_type.write_concrete_type(&mut concrete_type);
 
            }
 

	
 
            Ok(concrete_type)
 
        }
 

	
 
        // Every expression checked, and new monomorphs are queued. Transfer the
 
        // expression information to the AST. If this is the first time we're
 
        // visiting this procedure then we assign expression indices as well.
 
        let procedure = &ctx.heap[self.procedure_id];
 
        let num_infer_nodes = self.infer_nodes.len();
 
        let mut monomorph = ProcedureDefinitionMonomorph{
 
            argument_types: Vec::with_capacity(procedure.parameters.len()),
 
            expr_info: Vec::with_capacity(num_infer_nodes),
 
        };
 

	
 
        // For all of the expressions look up the TypeId (or create a new one).
 
        // For function calls and component instantiations figure out if they
 
        // need to be typechecked
 
        for infer_node in self.infer_nodes.iter_mut() {
 
            // Determine type ID
 
            let expr = &ctx.heap[infer_node.expr_id];
 

	
 
            // TODO: Maybe optimize? Split insertion up into lookup, then clone
 
            //  if needed?
 
            let mut concrete_type = ConcreteType::default();
 
            infer_node.expr_type.write_concrete_type(&mut concrete_type);
 
            let info_type_id = ctx.types.add_monomorphed_type(ctx.modules, ctx.heap, ctx.arch, concrete_type)?;
 

	
 
            // Determine procedure type ID, i.e. a called/instantiated
 
            // procedure's signature.
 
            let info_variant = if let Expression::Call(expr) = expr {
 
                // Construct full function type. If not yet typechecked then
 
                // queue it for typechecking.
 
                let poly_data = &self.poly_data[infer_node.poly_data_index as usize];
 
                debug_assert!(expr.method.is_user_defined() || expr.method.is_public_builtin());
 
                let procedure_id = expr.procedure;
 
                let num_poly_vars = poly_data.poly_vars.len() as u32;
 

	
 
                let first_part = match expr.method {
 
                    Method::UserFunction => ConcreteTypePart::Function(procedure_id, num_poly_vars),
 
                    Method::UserComponent => ConcreteTypePart::Component(procedure_id, num_poly_vars),
 
                    _ => ConcreteTypePart::Function(procedure_id, num_poly_vars),
 
                };
 

	
 

	
 
                let definition_id = procedure_id.upcast();
 
                let signature_type = poly_data_type_to_concrete_type(
 
                    ctx, infer_node.expr_id, &poly_data.poly_vars, first_part
 
                )?;
 

	
 
                let (type_id, monomorph_index) = if let Some(type_id) = ctx.types.get_procedure_monomorph_type_id(&definition_id, &signature_type.parts) {
 
                let (type_id, monomorph_index) = if let Some(type_id) = ctx.types.get_monomorph_type_id(&definition_id, &signature_type.parts) {
 
                    // Procedure is already typechecked
 
                    let monomorph_index = ctx.types.get_monomorph(type_id).variant.as_procedure().monomorph_index;
 
                    (type_id, monomorph_index)
 
                } else {
 
                    // Procedure is not yet typechecked, reserve a TypeID and a monomorph index
 
                    let procedure_to_check = &mut ctx.heap[procedure_id];
 
                    let monomorph_index = procedure_to_check.monomorphs.len() as u32;
 
                    procedure_to_check.monomorphs.push(ProcedureDefinitionMonomorph::new_invalid());
 
                    let type_id = ctx.types.reserve_procedure_monomorph_type_id(&definition_id, signature_type, monomorph_index);
 

	
 
                    if !procedure_to_check.source.is_builtin() {
 
                        // Only perform typechecking on the user-defined
 
                        // procedures
 
                        queue.push_back(ResolveQueueElement{
 
                            root_id: ctx.heap[definition_id].defined_in(),
 
                            definition_id,
 
                            reserved_type_id: type_id,
 
                            reserved_monomorph_index: monomorph_index,
 
                        });
 
                    }
 

	
 
                    (type_id, monomorph_index)
 
                };
 

	
 
                ExpressionInfoVariant::Procedure(type_id, monomorph_index)
 
            } else if let Expression::Select(_expr) = expr {
 
                ExpressionInfoVariant::Select(infer_node.field_index)
 
            } else {
 
                ExpressionInfoVariant::Generic
 
            };
 

	
 
            infer_node.info_type_id = info_type_id;
 
            infer_node.info_variant = info_variant;
 
        }
 

	
 
        // Write the types of the arguments
 
        let procedure = &ctx.heap[self.procedure_id];
 
        for parameter_id in procedure.parameters.iter().copied() {
 
            let mut concrete = ConcreteType::default();
 
            let var_data = self.var_data.iter().find(|v| v.var_id == parameter_id).unwrap();
 
            var_data.var_type.write_concrete_type(&mut concrete);
 
            let type_id = ctx.types.add_monomorphed_type(ctx.modules, ctx.heap, ctx.arch, concrete)?;
 
            monomorph.argument_types.push(type_id)
 
        }
 

	
 
        // Determine if we have already assigned type indices to the expressions
 
        // before (the indices that, for a monomorph, can retrieve the type of
 
        // the expression).
 
        let has_type_indices = self.reserved_monomorph_index > 0;
 
        if has_type_indices {
 
            // already have indices, so resize and then index into it
 
            debug_assert!(monomorph.expr_info.is_empty());
 
            monomorph.expr_info.resize(num_infer_nodes, ExpressionInfo::new_invalid());
 
            for infer_node in self.infer_nodes.iter() {
 
                let type_index = ctx.heap[infer_node.expr_id].type_index();
 
                monomorph.expr_info[type_index as usize] = infer_node.as_expression_info();
 
            }
 
        } else {
 
            // no indices yet, need to be assigned in AST
 
            for infer_node in self.infer_nodes.iter() {
 
                let type_index = monomorph.expr_info.len();
 
                monomorph.expr_info.push(infer_node.as_expression_info());
 
                *ctx.heap[infer_node.expr_id].type_index_mut() = type_index as i32;
 
            }
 
        }
 

	
 
        // Push the information into the AST
 
        let procedure = &mut ctx.heap[self.procedure_id];
 
        procedure.monomorphs[self.reserved_monomorph_index as usize] = monomorph;
 

	
 
        Ok(())
 
    }
 

	
 
    fn progress_inference_rule(&mut self, ctx: &Ctx, node_index: InferNodeIndex) -> Result<(), ParseError> {
 
        use InferenceRule as IR;
 

	
 
        let node = &self.infer_nodes[node_index];
 
        match &node.inference_rule {
 
            IR::Noop =>
 
                unreachable!(),
 
            IR::MonoTemplate(_) =>
 
                self.progress_inference_rule_mono_template(ctx, node_index),
 
            IR::BiEqual(_) =>
 
                self.progress_inference_rule_bi_equal(ctx, node_index),
 
            IR::TriEqualArgs(_) =>
 
                self.progress_inference_rule_tri_equal_args(ctx, node_index),
 
            IR::TriEqualAll(_) =>
 
                self.progress_inference_rule_tri_equal_all(ctx, node_index),
 
            IR::Concatenate(_) =>
 
                self.progress_inference_rule_concatenate(ctx, node_index),
 
            IR::IndexingExpr(_) =>
 
                self.progress_inference_rule_indexing_expr(ctx, node_index),
 
            IR::SlicingExpr(_) =>
 
                self.progress_inference_rule_slicing_expr(ctx, node_index),
 
            IR::SelectStructField(_) =>
 
                self.progress_inference_rule_select_struct_field(ctx, node_index),
 
            IR::SelectTupleMember(_) =>
 
                self.progress_inference_rule_select_tuple_member(ctx, node_index),
 
            IR::LiteralStruct(_) =>
 
                self.progress_inference_rule_literal_struct(ctx, node_index),
 
            IR::LiteralEnum =>
 
                self.progress_inference_rule_literal_enum(ctx, node_index),
 
            IR::LiteralUnion(_) =>
 
                self.progress_inference_rule_literal_union(ctx, node_index),
 
            IR::LiteralArray(_) =>
 
                self.progress_inference_rule_literal_array(ctx, node_index),
 
            IR::LiteralTuple(_) =>
 
                self.progress_inference_rule_literal_tuple(ctx, node_index),
 
            IR::CastExpr(_) =>
 
                self.progress_inference_rule_cast_expr(ctx, node_index),
 
            IR::CallExpr(_) =>
 
                self.progress_inference_rule_call_expr(ctx, node_index),
 
            IR::VariableExpr(_) =>
 
                self.progress_inference_rule_variable_expr(ctx, node_index),
 
        }
 
    }
 

	
 
    fn progress_inference_rule_mono_template(&mut self, ctx: &Ctx, node_index: InferNodeIndex) -> Result<(), ParseError> {
 
        let node = &self.infer_nodes[node_index];
 
        let rule = *node.inference_rule.as_mono_template();
 

	
 
        let progress = self.progress_template(ctx, node_index, rule.application, rule.template)?;
 
        if progress { self.queue_node_parent(node_index); }
 

	
 
        return Ok(());
 
    }
 

	
 
    fn progress_inference_rule_bi_equal(&mut self, ctx: &Ctx, node_index: InferNodeIndex) -> Result<(), ParseError> {
 
        let node = &self.infer_nodes[node_index];
 
        let rule = node.inference_rule.as_bi_equal();
 
        let template = rule.template;
 
        let arg_index = rule.argument_index;
 

	
 
        let base_progress = self.progress_template(ctx, node_index, template.application, template.template)?;
 
        let (node_progress, arg_progress) = self.apply_equal2_constraint(ctx, node_index, node_index, 0, arg_index, 0)?;
 

	
 
        if base_progress || node_progress { self.queue_node_parent(node_index); }
 
        if arg_progress { self.queue_node(arg_index); }
 

	
 
        return Ok(())
 
    }
 

	
 
    fn progress_inference_rule_tri_equal_args(&mut self, ctx: &Ctx, node_index: InferNodeIndex) -> Result<(), ParseError> {
 
        let node = &self.infer_nodes[node_index];
 
        let rule = node.inference_rule.as_tri_equal_args();
 

	
 
        let result_template = rule.result_template;
 
        let argument_template = rule.argument_template;
 
        let arg1_index = rule.argument1_index;
 
        let arg2_index = rule.argument2_index;
 

	
 
        let self_template_progress = self.progress_template(ctx, node_index, result_template.application, result_template.template)?;
 
        let arg1_template_progress = self.progress_template(ctx, arg1_index, argument_template.application, argument_template.template)?;
 
        let (arg1_progress, arg2_progress) = self.apply_equal2_constraint(ctx, node_index, arg1_index, 0, arg2_index, 0)?;
 

	
 
        if self_template_progress { self.queue_node_parent(node_index); }
 
        if arg1_template_progress || arg1_progress { self.queue_node(arg1_index); }
 
        if arg2_progress { self.queue_node(arg2_index); }
 

	
 
        return Ok(());
 
    }
 

	
 
    fn progress_inference_rule_tri_equal_all(&mut self, ctx: &Ctx, node_index: InferNodeIndex) -> Result<(), ParseError> {
 
        let node = &self.infer_nodes[node_index];
 
        let rule = node.inference_rule.as_tri_equal_all();
 

	
 
        let template = rule.template;
 
        let arg1_index = rule.argument1_index;
 
        let arg2_index = rule.argument2_index;
 

	
 
        let template_progress = self.progress_template(ctx, node_index, template.application, template.template)?;
 
        let (node_progress, arg1_progress, arg2_progress) =
 
            self.apply_equal3_constraint(ctx, node_index, arg1_index, arg2_index, 0)?;
 

	
 
        if template_progress || node_progress { self.queue_node_parent(node_index); }
 
        if arg1_progress { self.queue_node(arg1_index); }
 
        if arg2_progress { self.queue_node(arg2_index); }
 

	
 
        return Ok(());
 
    }
 

	
 
    fn progress_inference_rule_concatenate(&mut self, ctx: &Ctx, node_index: InferNodeIndex) -> Result<(), ParseError> {
 
        let node = &self.infer_nodes[node_index];
 
        let rule = node.inference_rule.as_concatenate();
 
        let arg1_index = rule.argument1_index;
 
        let arg2_index = rule.argument2_index;
 

	
 
        // Two cases: one of the arguments is a string (then all must be), or
 
        // one of the arguments is an array (and all must be arrays).
 
        let (expr_is_str, expr_is_not_str) = self.type_is_certainly_or_certainly_not_string(node_index);
 
        let (arg1_is_str, arg1_is_not_str) = self.type_is_certainly_or_certainly_not_string(arg1_index);
 
        let (arg2_is_str, arg2_is_not_str) = self.type_is_certainly_or_certainly_not_string(arg2_index);
 

	
 
        let someone_is_str = expr_is_str || arg1_is_str || arg2_is_str;
 
        let someone_is_not_str = expr_is_not_str || arg1_is_not_str || arg2_is_not_str;
 
        // Note: this statement is an expression returning the progression bools
 
        let (node_progress, arg1_progress, arg2_progress) = if someone_is_str {
 
            // One of the arguments is a string, then all must be strings
 
            self.apply_equal3_constraint(ctx, node_index, arg1_index, arg2_index, 0)?
 
        } else {
 
            let progress_expr = if someone_is_not_str {
 
                // Output must be a normal array
 
                self.apply_template_constraint(ctx, node_index, &ARRAY_TEMPLATE)?
 
            } else {
 
                // Output may still be anything
 
                self.apply_template_constraint(ctx, node_index, &ARRAYLIKE_TEMPLATE)?
 
            };
 

	
 
            let progress_arg1 = self.apply_template_constraint(ctx, arg1_index, &ARRAYLIKE_TEMPLATE)?;
 
            let progress_arg2 = self.apply_template_constraint(ctx, arg2_index, &ARRAYLIKE_TEMPLATE)?;
 

	
 
            // If they're all arraylike, then we want the subtype to match
 
            let (subtype_expr, subtype_arg1, subtype_arg2) =
 
                self.apply_equal3_constraint(ctx, node_index, arg1_index, arg2_index, 1)?;
 

	
 
            (progress_expr || subtype_expr, progress_arg1 || subtype_arg1, progress_arg2 || subtype_arg2)
 
        };
 

	
 
        if node_progress { self.queue_node_parent(node_index); }
 
        if arg1_progress { self.queue_node(arg1_index); }
 
        if arg2_progress { self.queue_node(arg2_index); }
 

	
 
        return Ok(())
 
    }
 

	
 
    fn progress_inference_rule_indexing_expr(&mut self, ctx: &Ctx, node_index: InferNodeIndex) -> Result<(), ParseError> {
 
        let node = &self.infer_nodes[node_index];
 
        let rule = node.inference_rule.as_indexing_expr();
 
        let subject_index = rule.subject_index;
 
        let index_index = rule.index_index; // which one?
 

	
 
        // Subject is arraylike, index in integerlike
 
        let subject_template_progress = self.apply_template_constraint(ctx, subject_index, &ARRAYLIKE_TEMPLATE)?;
 
        let index_template_progress = self.apply_template_constraint(ctx, index_index, &INTEGERLIKE_TEMPLATE)?;
 

	
 
        // If subject is type `Array<T>`, then expr type is `T`
 
        let (node_progress, subject_progress) =
 
            self.apply_equal2_constraint(ctx, node_index, node_index, 0, subject_index, 1)?;
 

	
 
        if node_progress { self.queue_node_parent(node_index); }
 
        if subject_template_progress || subject_progress { self.queue_node(subject_index); }
 
        if index_template_progress { self.queue_node(index_index); }
 

	
 
        return Ok(());
 
    }
 

	
 
    fn progress_inference_rule_slicing_expr(&mut self, ctx: &Ctx, node_index: InferNodeIndex) -> Result<(), ParseError> {
 
        let node = &self.infer_nodes[node_index];
 
        let rule = node.inference_rule.as_slicing_expr();
 
        let subject_index = rule.subject_index;
 
        let from_index_index = rule.from_index;
 
        let to_index_index = rule.to_index;
 

	
 
        debug_log!("Rule slicing [node: {}, expr: {}]", node_index, node.expr_id.index);
 

	
 
        // Subject is arraylike, indices are integerlike
 
        let subject_template_progress = self.apply_template_constraint(ctx, subject_index, &ARRAYLIKE_TEMPLATE)?;
 
        let from_template_progress = self.apply_template_constraint(ctx, from_index_index, &INTEGERLIKE_TEMPLATE)?;
 
        let to_template_progress = self.apply_template_constraint(ctx, to_index_index, &INTEGERLIKE_TEMPLATE)?;
 
        let (from_index_progress, to_index_progress) =
 
            self.apply_equal2_constraint(ctx, node_index, from_index_index, 0, to_index_index, 0)?;
 

	
 
        // Same as array indexing: result depends on whether subject is string
 
        // or array
 
        let (is_string, is_not_string) = self.type_is_certainly_or_certainly_not_string(node_index);
 
        let (node_progress, subject_progress) = if is_string {
 
            // Certainly a string
 
            (
 
                self.apply_forced_constraint(ctx, node_index, &STRING_TEMPLATE)?,
 
                false
 
            )
 
        } else if is_not_string {
 
            // Certainly not a string, apply template constraint. Then make sure
 
            // that if we have an `Array<T>`, that the slice produces `Slice<T>`
 
            let node_template_progress = self.apply_template_constraint(ctx, node_index, &SLICE_TEMPLATE)?;
 
            let (node_progress, subject_progress) =
 
                self.apply_equal2_constraint(ctx, node_index, node_index, 1, subject_index, 1)?;
 

	
 
            (
 
                node_template_progress || node_progress,
 
                subject_progress
 
            )
 
        } else {
 
            // Not sure yet
 
            let node_template_progress = self.apply_template_constraint(ctx, node_index, &ARRAYLIKE_TEMPLATE)?;
 
            let (node_progress, subject_progress) =
 
                self.apply_equal2_constraint(ctx, node_index, node_index, 1, subject_index, 1)?;
 

	
 
            (
 
                node_template_progress || node_progress,
 
                subject_progress
 
            )
 
        };
 

	
 
        if node_progress { self.queue_node_parent(node_index); }
 
        if subject_template_progress || subject_progress { self.queue_node(subject_index); }
 
        if from_template_progress || from_index_progress { self.queue_node(from_index_index); }
 
        if to_template_progress || to_index_progress { self.queue_node(to_index_index); }
 

	
 
        return Ok(());
 
    }
 

	
 
    fn progress_inference_rule_select_struct_field(&mut self, ctx: &Ctx, node_index: InferNodeIndex) -> Result<(), ParseError> {
 
        let node = &self.infer_nodes[node_index];
 
        let rule = node.inference_rule.as_select_struct_field();
 

	
 
        let subject_index = rule.subject_index;
 
        let selected_field = rule.selected_field.clone();
 

	
 
        fn get_definition_id_from_inference_type(inference_type: &InferenceType) -> Result<Option<DefinitionId>, ()> {
 
            for part in inference_type.parts.iter() {
 
                if part.is_marker() { continue; }
 
                if !part.is_concrete() { break; }
 

	
 
                if let InferenceTypePart::Instance(definition_id, _) = part {
 
                    return Ok(Some(*definition_id));
 
                } else {
 
                    return Err(())
 
                }
 
            }
 

	
 
            // Nothing is known yet
 
            return Ok(None);
 
        }
 

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

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

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

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

	
 
        // If here then the field index is known, hence we can start inferring
 
        // the type of the selected field
 
        let field_expr_id = self.infer_nodes[node_index].expr_id;
 
        let subject_expr_id = self.infer_nodes[subject_index].expr_id;
 
        let mut poly_progress_section = self.poly_progress_buffer.start_section();
 

	
 
        let (_, progress_subject_1) = self.apply_polydata_equal2_constraint(
 
            ctx, node_index, subject_expr_id, "selected struct's",
 
            PolyDataTypeIndex::Associated(0), 0, subject_index, 0, &mut poly_progress_section
 
        )?;
 
        let (_, progress_field_1) = self.apply_polydata_equal2_constraint(
 
            ctx, node_index, field_expr_id, "selected field's",
 
            PolyDataTypeIndex::Returned, 0, node_index, 0, &mut poly_progress_section
 
        )?;
 

	
 
        // Maybe make progress on types due to inferred polymorphic variables
 
        let progress_subject_2 = self.apply_polydata_polyvar_constraint(
 
            ctx, node_index, PolyDataTypeIndex::Associated(0), subject_index, &poly_progress_section
 
        );
 
        let progress_field_2 = self.apply_polydata_polyvar_constraint(
 
            ctx, node_index, PolyDataTypeIndex::Returned, node_index, &poly_progress_section
 
        );
 

	
 
        if progress_subject_1 || progress_subject_2 { self.queue_node(subject_index); }
 
        if progress_field_1 || progress_field_2 { self.queue_node_parent(node_index); }
 

	
 
        poly_progress_section.forget();
 
        self.finish_polydata_constraint(node_index);
 
        return Ok(())
 
    }
 

	
 
    fn progress_inference_rule_select_tuple_member(&mut self, ctx: &Ctx, node_index: InferNodeIndex) -> Result<(), ParseError> {
 
        let node = &self.infer_nodes[node_index];
 
        let rule = node.inference_rule.as_select_tuple_member();
 
        let subject_index = rule.subject_index;
 
        let tuple_member_index = rule.selected_index;
 

	
 
        if node.field_index < 0 {
 
            let subject_type = &self.infer_nodes[subject_index].expr_type;
 
            let tuple_size = get_tuple_size_from_inference_type(subject_type);
 
            let tuple_size = match tuple_size {
 
                Ok(Some(tuple_size)) => {
 
                    tuple_size
 
                },
 
                Ok(None) => {
 
                    // We can't infer anything yet
 
                    return Ok(())
 
                },
 
                Err(()) => {
 
                    let select_expr_span = ctx.heap[node.expr_id].full_span();
 
                    return Err(ParseError::new_error_at_span(
 
                        &ctx.module().source, select_expr_span, format!(
 
                            "tuple element select cannot be applied to a subject of type '{}'",
 
                            subject_type.display_name(&ctx.heap)
 
                        )
 
                    ));
 
                }
 
            };
 

	
 
            // If here then we at least have the tuple size. Now check if the
 
            // index doesn't exceed that size.
 
            if tuple_member_index >= tuple_size as u64 {
 
                let select_expr_span = ctx.heap[node.expr_id].full_span();
 
                return Err(ParseError::new_error_at_span(
 
                    &ctx.module().source, select_expr_span, format!(
 
                        "element index {} is out of bounds, tuple has {} elements",
 
                        tuple_member_index, tuple_size
 
                    )
 
                ));
 
            }
 

	
 
            // Within bounds, set index on the type inference node
 
            let node = &mut self.infer_nodes[node_index];
 
            node.field_index = tuple_member_index as i32;
 
        }
 

	
 
        // If here then we know we can use `tuple_member_index`. We need to keep
 
        // computing the offset to the subtype, as its value changes during
 
        // inference
 
        let subject_type = &self.infer_nodes[subject_index].expr_type;
 
        let mut selected_member_start_index = 1; // start just after the InferenceTypeElement::Tuple
 
        for _ in 0..tuple_member_index {
 
            selected_member_start_index = InferenceType::find_subtree_end_idx(&subject_type.parts, selected_member_start_index);
 
        }
 

	
 
        let (progress_member, progress_subject) = self.apply_equal2_constraint(
 
            ctx, node_index, node_index, 0, subject_index, selected_member_start_index
 
        )?;
 

	
 
        if progress_member { self.queue_node_parent(node_index); }
 
        if progress_subject { self.queue_node(subject_index); }
 

	
 
        return Ok(());
 
    }
 

	
 
    fn progress_inference_rule_literal_struct(&mut self, ctx: &Ctx, node_index: InferNodeIndex) -> Result<(), ParseError> {
 
        let node = &self.infer_nodes[node_index];
 
        let node_expr_id = node.expr_id;
 
        let rule = node.inference_rule.as_literal_struct();
 

	
 
        // For each of the fields in the literal struct, apply the type equality
 
        // constraint. If the literal is polymorphic, then we try to progress
 
        // their types during this process
 
        let element_indices_section = self.index_buffer.start_section_initialized(&rule.element_indices);
 
        let mut poly_progress_section = self.poly_progress_buffer.start_section();
 
        for (field_index, field_node_index) in element_indices_section.iter_copied().enumerate() {
 
            let field_expr_id = self.infer_nodes[field_node_index].expr_id;
 
            let (_, progress_field) = self.apply_polydata_equal2_constraint(
 
                ctx, node_index, field_expr_id, "struct field's",
 
                PolyDataTypeIndex::Associated(field_index), 0,
 
                field_node_index, 0, &mut poly_progress_section
 
            )?;
 

	
 
            if progress_field { self.queue_node(field_node_index); }
 
        }
 

	
 
        // Now we do the same thing for the struct literal expression (the type
 
        // of the struct itself).
 
        let (_, progress_literal_1) = self.apply_polydata_equal2_constraint(
 
            ctx, node_index, node_expr_id, "struct literal's",
 
            PolyDataTypeIndex::Returned, 0, node_index, 0, &mut poly_progress_section
 
        )?;
 

	
 
        // And the other way around: if any of our polymorphic variables are
 
        // more specific then they were before, then we forward that information
 
        // back to our struct/fields.
 
        for (field_index, field_node_index) in element_indices_section.iter_copied().enumerate() {
 
            let progress_field = self.apply_polydata_polyvar_constraint(
 
                ctx, node_index, PolyDataTypeIndex::Associated(field_index),
 
                field_node_index, &poly_progress_section
 
            );
 

	
 
            if progress_field { self.queue_node(field_node_index); }
 
        }
 

	
 
        let progress_literal_2 = self.apply_polydata_polyvar_constraint(
 
            ctx, node_index, PolyDataTypeIndex::Returned,
 
            node_index, &poly_progress_section
 
        );
 

	
 
        if progress_literal_1 || progress_literal_2 { self.queue_node_parent(node_index); }
 

	
 
        poly_progress_section.forget();
 
        element_indices_section.forget();
 

	
 
        self.finish_polydata_constraint(node_index);
 
        return Ok(())
 
    }
 

	
 
    fn progress_inference_rule_literal_enum(&mut self, ctx: &Ctx, node_index: InferNodeIndex) -> Result<(), ParseError> {
 
        let node = &self.infer_nodes[node_index];
 
        let node_expr_id = node.expr_id;
 
        let mut poly_progress_section = self.poly_progress_buffer.start_section();
 

	
 
        // An enum literal type is simply, well, the enum's type. However, it
 
        // might still have polymorphic variables, hence the use of `PolyData`.
 
        let (_, progress_literal_1) = self.apply_polydata_equal2_constraint(
 
            ctx, node_index, node_expr_id, "enum literal's",
 
            PolyDataTypeIndex::Returned, 0, node_index, 0, &mut poly_progress_section
 
        )?;
 

	
 
        let progress_literal_2 = self.apply_polydata_polyvar_constraint(
 
            ctx, node_index, PolyDataTypeIndex::Returned, node_index, &poly_progress_section
 
        );
 

	
 
        if progress_literal_1 || progress_literal_2 { self.queue_node_parent(node_index); }
 

	
 
        poly_progress_section.forget();
 
        self.finish_polydata_constraint(node_index);
 
        return Ok(());
 
    }
 

	
 
    fn progress_inference_rule_literal_union(&mut self, ctx: &Ctx, node_index: InferNodeIndex) -> Result<(), ParseError> {
 
        let node = &self.infer_nodes[node_index];
 
        let node_expr_id = node.expr_id;
 
        let rule = node.inference_rule.as_literal_union();
 

	
 
        // Infer type of any embedded values in the union variant. At the same
 
        // time progress the polymorphic variables associated with the union.
 
        let element_indices_section = self.index_buffer.start_section_initialized(&rule.element_indices);
 
        let mut poly_progress_section = self.poly_progress_buffer.start_section();
 

	
 
        for (embedded_index, embedded_node_index) in element_indices_section.iter_copied().enumerate() {
 
            let embedded_node_expr_id = self.infer_nodes[embedded_node_index].expr_id;
 
            let (_, progress_embedded) = self.apply_polydata_equal2_constraint(
 
                ctx, node_index, embedded_node_expr_id, "embedded value's",
 
                PolyDataTypeIndex::Associated(embedded_index), 0,
 
                embedded_node_index, 0, &mut poly_progress_section
 
            )?;
 

	
 
            if progress_embedded { self.queue_node(embedded_node_index); }
 
        }
 

	
 
        let (_, progress_literal_1) = self.apply_polydata_equal2_constraint(
 
            ctx, node_index, node_expr_id, "union's",
 
            PolyDataTypeIndex::Returned, 0, node_index, 0, &mut poly_progress_section
 
        )?;
 

	
 
        // Propagate progress in the polymorphic variables to the expressions
 
        // that constitute the union literal.
 
        for (embedded_index, embedded_node_index) in element_indices_section.iter_copied().enumerate() {
 
            let progress_embedded = self.apply_polydata_polyvar_constraint(
 
                ctx, node_index, PolyDataTypeIndex::Associated(embedded_index),
 
                embedded_node_index, &poly_progress_section
 
            );
 

	
 
            if progress_embedded { self.queue_node(embedded_node_index); }
 
        }
 

	
 
        let progress_literal_2 = self.apply_polydata_polyvar_constraint(
 
            ctx, node_index, PolyDataTypeIndex::Returned, node_index, &poly_progress_section
 
        );
 

	
 
        if progress_literal_1 || progress_literal_2 { self.queue_node_parent(node_index); }
 

	
 
        poly_progress_section.forget();
 
        self.finish_polydata_constraint(node_index);
 
        return Ok(());
 
    }
 

	
 
    fn progress_inference_rule_literal_array(&mut self, ctx: &Ctx, node_index: InferNodeIndex) -> Result<(), ParseError> {
 
        let node = &self.infer_nodes[node_index];
 
        let rule = node.inference_rule.as_literal_array();
 

	
 
        // Apply equality rule to all of the elements that form the array
 
        let argument_node_indices = self.index_buffer.start_section_initialized(&rule.element_indices);
 
        let mut argument_progress_section = self.bool_buffer.start_section();
 
        self.apply_equal_n_constraint(ctx, node_index, &argument_node_indices, &mut argument_progress_section)?;
 

	
 
        debug_assert_eq!(argument_node_indices.len(), argument_progress_section.len());
 
        for argument_index in 0..argument_node_indices.len() {
 
            let argument_node_index = argument_node_indices[argument_index];
 
            let progress = argument_progress_section[argument_index];
 

	
 
            if progress { self.queue_node(argument_node_index); }
 
        }
 

	
 
        // If elements are of type `T`, then the array is of type `Array<T>`, so:
 
        let mut progress_literal = self.apply_template_constraint(ctx, node_index, &ARRAY_TEMPLATE)?;
 
        if argument_node_indices.len() != 0 {
 
            let argument_node_index = argument_node_indices[0];
 
            let (progress_literal_inner, progress_argument) = self.apply_equal2_constraint(
 
                ctx, node_index, node_index, 1, argument_node_index, 0
 
            )?;
 

	
 
            progress_literal = progress_literal || progress_literal_inner;
 

	
 
            // It is possible that the `Array<T>` has a more progress `T` then
 
            // the arguments. So in the case we progress our argument type we
 
            // simply queue this rule again
 
            if progress_argument { self.queue_node(node_index); }
 
        }
 

	
 
        argument_node_indices.forget();
 
        argument_progress_section.forget();
 

	
 
        if progress_literal { self.queue_node_parent(node_index); }
 
        return Ok(());
 
    }
 

	
 
    fn progress_inference_rule_literal_tuple(&mut self, ctx: &Ctx, node_index: InferNodeIndex) -> Result<(), ParseError> {
 
        let node = &self.infer_nodes[node_index];
 
        let rule = node.inference_rule.as_literal_tuple();
 

	
 
        let element_indices = self.index_buffer.start_section_initialized(&rule.element_indices);
 

	
 
        // Check if we need to apply the initial tuple template type. Note that
 
        // this is a hacky check.
 
        let num_tuple_elements = rule.element_indices.len();
 
        let mut template_type = Vec::with_capacity(num_tuple_elements + 1); // TODO: @performance
 
        template_type.push(InferenceTypePart::Tuple(num_tuple_elements as u32));
 
        for _ in 0..num_tuple_elements {
 
            template_type.push(InferenceTypePart::Unknown);
 
        }
 

	
 
        let mut progress_literal = self.apply_template_constraint(ctx, node_index, &template_type)?;
 

	
 
        // Because of the (early returning error) check above, we're certain
 
        // that the tuple has the correct number of elements. Now match each
 
        // element expression type to the tuple subtype.
 
        let mut element_subtree_start_index = 1; // first element is InferenceTypePart::Tuple
 
        for element_node_index in element_indices.iter_copied() {
 
            let (progress_literal_element, progress_element) = self.apply_equal2_constraint(
 
                ctx, node_index, node_index, element_subtree_start_index, element_node_index, 0
 
            )?;
 

	
 
            progress_literal = progress_literal || progress_literal_element;
 
            if progress_element {
 
                self.queue_node(element_node_index);
 
            }
 

	
 
            // Prepare for next element
 
            let node = &self.infer_nodes[node_index];
 
            let subtree_end_index = InferenceType::find_subtree_end_idx(&node.expr_type.parts, element_subtree_start_index);
 
            element_subtree_start_index = subtree_end_index;
 
        }
 
        debug_assert_eq!(element_subtree_start_index, self.infer_nodes[node_index].expr_type.parts.len());
 

	
 
        if progress_literal { self.queue_node_parent(node_index); }
 

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

	
 
    fn progress_inference_rule_cast_expr(&mut self, ctx: &Ctx, node_index: InferNodeIndex) -> Result<(), ParseError> {
 
        let node = &self.infer_nodes[node_index];
 
        let rule = node.inference_rule.as_cast_expr();
 
        let subject_index = rule.subject_index;
 
        let subject = &self.infer_nodes[subject_index];
 

	
 
        // Make sure that both types are completely done. Note: a cast
 
        // expression cannot really infer anything between the subject and the
 
        // output type, we can only make sure that, at the end, the cast is
 
        // correct.
 
        if !node.expr_type.is_done || !subject.expr_type.is_done {
 
            return Ok(());
 
        }
 

	
 
        // Both types are known, currently the only valid casts are bool,
 
        // integer and character casts.
 
        fn is_bool_int_or_char(parts: &[InferenceTypePart]) -> bool {
 
            let mut index = 0;
 
            while index < parts.len() {
 
                let part = &parts[index];
 
                if !part.is_marker() { break; }
 
                index += 1;
 
            }
 

	
 
            debug_assert!(index != parts.len());
 
            let part = &parts[index];
 
            if *part == InferenceTypePart::Bool || *part == InferenceTypePart::Character || part.is_concrete_integer() {
 
                debug_assert!(index + 1 == parts.len()); // type is done, first part does not have children -> must be at end
 
                return true;
 
            } else {
 
                return false;
 
            }
 
        }
 

	
 
        let is_valid = if is_bool_int_or_char(&node.expr_type.parts) && is_bool_int_or_char(&subject.expr_type.parts) {
 
            true
 
        } else if InferenceType::check_subtrees(&node.expr_type.parts, 0, &subject.expr_type.parts, 0) {
 
            // again: check_subtrees is sufficient since both types are done
 
            true
 
        } else {
 
            false
 
        };
 

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

	
 
        return Ok(())
 
    }
 

	
 
    fn progress_inference_rule_call_expr(&mut self, ctx: &Ctx, node_index: InferNodeIndex) -> Result<(), ParseError> {
 
        let node = &self.infer_nodes[node_index];
 
        let node_expr_id = node.expr_id;
 
        let rule = node.inference_rule.as_call_expr();
 

	
 
        let mut poly_progress_section = self.poly_progress_buffer.start_section();
 
        let argument_node_indices = self.index_buffer.start_section_initialized(&rule.argument_indices);
 

	
 
        // Perform inference on arguments to function, while trying to figure
 
        // out the polymorphic variables
 
        for (argument_index, argument_node_index) in argument_node_indices.iter_copied().enumerate() {
 
            let argument_expr_id = self.infer_nodes[argument_node_index].expr_id;
 
            let (_, progress_argument) = self.apply_polydata_equal2_constraint(
 
                ctx, node_index, argument_expr_id, "argument's",
 
                PolyDataTypeIndex::Associated(argument_index), 0,
src/protocol/parser/type_table.rs
Show inline comments
 
/**
 
 * type_table.rs
 
 *
 
 * The type table is a lookup from AST definition (which contains just what the
 
 * programmer typed) to a type with additional information computed (e.g. the
 
 * byte size and offsets of struct members). The type table should be considered
 
 * the authoritative source of information on types by the compiler (not the
 
 * AST itself!).
 
 *
 
 * The type table operates in two modes: one is where we just look up the type,
 
 * check its fields for correctness and mark whether it is polymorphic or not.
 
 * The second one is where we compute byte sizes, alignment and offsets.
 
 *
 
 * The basic algorithm for type resolving and computing byte sizes is to
 
 * recursively try to lay out each member type of a particular type. This is
 
 * done in a stack-like fashion, where each embedded type pushes a breadcrumb
 
 * unto the stack. We may discover a cycle in embedded types (we call this a
 
 * "type loop"). After which the type table attempts to break the type loop by
 
 * making specific types heap-allocated. Upon doing so we know their size
 
 * because their stack-size is now based on pointers. Hence breaking the type
 
 * loop required for computing the byte size of types.
 
 *
 
 * The reason for these type shenanigans is because PDL is a value-based
 
 * language, but we would still like to be able to express recursively defined
 
 * types like trees or linked lists. Hence we need to insert pointers somewhere
 
 * to break these cycles.
 
 *
 
 * We will insert these pointers into the variants of unions. However note that
 
 * we can only compute the stack size of a union until we've looked at *all*
 
 * variants. Hence we perform an initial pass where we detect type loops, a
 
 * second pass where we compute the stack sizes of everything, and a third pass
 
 * where we actually compute the size of the heap allocations for unions.
 
 *
 
 * As a final bit of global documentation: non-polymorphic types will always
 
 * have one "monomorph" entry. This contains the non-polymorphic type's memory
 
 * layout.
 
 */
 

	
 
// Programmer note: deduplication of types is currently disabled, see the
 
// @Deduplication key. Tests might fail when it is re-enabled.
 
use std::collections::HashMap;
 
use std::hash::{Hash, Hasher};
 

	
 
use crate::protocol::ast::*;
 
use crate::protocol::parser::symbol_table::SymbolScope;
 
use crate::protocol::input_source::ParseError;
 
use crate::protocol::parser::*;
 

	
 
//------------------------------------------------------------------------------
 
// Defined Types
 
//------------------------------------------------------------------------------
 

	
 
/// Struct wrapping around a potentially polymorphic type. If the type does not
 
/// have any polymorphic arguments then it will not have any monomorphs and
 
/// `is_polymorph` will be set to `false`. A type with polymorphic arguments
 
/// only has `is_polymorph` set to `true` if the polymorphic arguments actually
 
/// appear in the types associated types (function return argument, struct
 
/// field, enum variant, etc.). Otherwise the polymorphic argument is just a
 
/// marker and does not influence the bytesize of the type.
 
#[allow(unused)]
 
pub struct DefinedType {
 
    pub(crate) ast_root: RootId,
 
    pub(crate) ast_definition: DefinitionId,
 
    pub(crate) definition: DefinedTypeVariant,
 
    pub(crate) poly_vars: Vec<PolymorphicVariable>,
 
    pub(crate) is_polymorph: bool,
 
}
 

	
 
pub enum DefinedTypeVariant {
 
    Enum(EnumType),
 
    Union(UnionType),
 
    Struct(StructType),
 
    Procedure(ProcedureType),
 
}
 

	
 
impl DefinedTypeVariant {
 
    pub(crate) fn is_data_type(&self) -> bool {
 
        use DefinedTypeVariant as DTV;
 

	
 
        match self {
 
            DTV::Struct(_) | DTV::Enum(_) | DTV::Union(_) => return true,
 
            DTV::Procedure(_) => return false,
 
        }
 
    }
 

	
 
    pub(crate) fn as_struct(&self) -> &StructType {
 
        match self {
 
            DefinedTypeVariant::Struct(v) => v,
 
            _ => unreachable!()
 
        }
 
    }
 

	
 
    pub(crate) fn as_enum(&self) -> &EnumType {
 
        match self {
 
            DefinedTypeVariant::Enum(v) => v,
 
            _ => unreachable!()
 
        }
 
    }
 

	
 
    pub(crate) fn as_union(&self) -> &UnionType {
 
        match self {
 
            DefinedTypeVariant::Union(v) => v,
 
            _ => unreachable!()
 
        }
 
    }
 
}
 

	
 
pub struct PolymorphicVariable {
 
    pub(crate) identifier: Identifier,
 
    pub(crate) is_in_use: bool, // a polymorphic argument may be defined, but not used by the type definition
 
}
 

	
 
/// `EnumType` is the classical C/C++ enum type. It has various variants with
 
/// an assigned integer value. The integer values may be user-defined,
 
/// compiler-defined, or a mix of the two. If a user assigns the same enum
 
/// value multiple times, we assume the user is an expert and we consider both
 
/// variants to be equal to one another.
 
pub struct EnumType {
 
    pub variants: Vec<EnumVariant>,
 
    pub minimum_tag_value: i64,
 
    pub maximum_tag_value: i64,
 
    pub tag_type: ConcreteType,
 
    pub size: usize,
 
    pub alignment: usize,
 
}
 

	
 
// TODO: Also support maximum u64 value
 
pub struct EnumVariant {
 
    pub identifier: Identifier,
 
    pub value: i64,
 
}
 

	
 
/// `UnionType` is the algebraic datatype (or sum type, or discriminated union).
 
/// A value is an element of the union, identified by its tag, and may contain
 
/// a single subtype.
 
/// For potentially infinite types (i.e. a tree, or a linked list) only unions
 
/// can break the infinite cycle. So when we lay out these unions in memory we
 
/// will reserve enough space on the stack for all union variants that do not
 
/// cause "type loops" (i.e. a union `A` with a variant containing a struct
 
/// `B`). And we will reserve enough space on the heap (and store a pointer in
 
/// the union) for all variants which do cause type loops (i.e. a union `A`
 
/// with a variant to a struct `B` that contains the union `A` again).
 
pub struct UnionType {
 
    pub variants: Vec<UnionVariant>,
 
    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,
 
}
 

	
 
/// `ProcedureType` is the signature of a procedure/component
 
pub struct ProcedureType {
 
    pub kind: ProcedureKind,
 
    pub return_type: Option<ParserType>,
 
    pub arguments: Vec<ProcedureArgument>,
 
}
 

	
 
pub struct ProcedureArgument {
 
    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,
 
    pub(crate) type_id: TypeId,
 
}
 

	
 
//------------------------------------------------------------------------------
 
// Type monomorph storage
 
//------------------------------------------------------------------------------
 

	
 
pub(crate) enum MonoTypeVariant {
 
    Builtin, // no extra data, added manually in compiler initialization code
 
    Enum, // no extra data
 
    Struct(StructMonomorph),
 
    Union(UnionMonomorph),
 
    Procedure(ProcedureMonomorph), // functions, components
 
    Tuple(TupleMonomorph),
 
}
 

	
 
impl MonoTypeVariant {
 
    fn as_struct_mut(&mut self) -> &mut StructMonomorph {
 
        match self {
 
            MonoTypeVariant::Struct(v) => v,
 
            _ => unreachable!(),
 
        }
 
    }
 

	
 
    pub(crate) fn as_union(&self) -> &UnionMonomorph {
 
        match self {
 
            MonoTypeVariant::Union(v) => v,
 
            _ => unreachable!(),
 
        }
 
    }
 

	
 
    fn as_union_mut(&mut self) -> &mut UnionMonomorph {
 
        match self {
 
            MonoTypeVariant::Union(v) => v,
 
            _ => unreachable!(),
 
        }
 
    }
 

	
 
    fn as_tuple_mut(&mut self) -> &mut TupleMonomorph {
 
        match self {
 
            MonoTypeVariant::Tuple(v) => v,
 
            _ => unreachable!(),
 
        }
 
    }
 

	
 
    pub(crate) fn as_procedure(&self) -> &ProcedureMonomorph {
 
        match self {
 
            MonoTypeVariant::Procedure(v) => v,
 
            _ => unreachable!(),
 
        }
 
    }
 

	
 
    fn as_procedure_mut(&mut self) -> &mut ProcedureMonomorph {
 
        match self {
 
            MonoTypeVariant::Procedure(v) => v,
 
            _ => unreachable!(),
 
        }
 
    }
 
}
 

	
 
/// Struct monomorph
 
pub struct StructMonomorph {
 
    pub fields: Vec<StructMonomorphField>,
 
}
 

	
 
pub struct StructMonomorphField {
 
    pub type_id: TypeId,
 
    concrete_type: ConcreteType,
 
    pub size: usize,
 
    pub alignment: usize,
 
    pub offset: usize,
 
}
 

	
 
/// Union monomorph
 
pub struct UnionMonomorph {
 
    pub variants: Vec<UnionMonomorphVariant>,
 
    pub tag_size: usize, // copied from `UnionType` upon monomorph construction.
 
    // note that the stack size is in the `TypeMonomorph` struct. This size and
 
    // alignment will include the size of the union tag.
 
    //
 
    // heap_size contains the allocated size of the union in the case it
 
    // is used to break a type loop. If it is 0, then it doesn't require
 
    // allocation and lives entirely on the stack.
 
    pub heap_size: usize,
 
    pub heap_alignment: usize,
 
}
 

	
 
pub struct UnionMonomorphVariant {
 
    pub lives_on_heap: bool,
 
    pub embedded: Vec<UnionMonomorphEmbedded>,
 
}
 

	
 
pub struct UnionMonomorphEmbedded {
 
    pub type_id: TypeId,
 
    concrete_type: ConcreteType,
 
    // Note that the meaning of the offset (and alignment) depend on whether or
 
    // not the variant lives on the stack/heap. If it lives on the stack then
 
    // they refer to the offset from the start of the union value (so the first
 
    // embedded type lives at a non-zero offset, because the union tag sits in
 
    // the front). If it lives on the heap then it refers to the offset from the
 
    // allocated memory region (so the first embedded type lives at a 0 offset).
 
    pub size: usize,
 
    pub alignment: usize,
 
    pub offset: usize,
 
}
 

	
 
/// Procedure (functions and components of all possible types) monomorph. Also
 
/// stores the expression type data from the typechecking/inferencing pass.
 
pub struct ProcedureMonomorph {
 
    pub monomorph_index: u32,
 
    pub builtin: bool,
 
}
 

	
 
/// Tuple monomorph. Again a kind of exception because one cannot define a named
 
/// tuple type containing explicit polymorphic variables. But again: we need to
 
/// store size/offset/alignment information, so we do it here.
 
pub struct TupleMonomorph {
 
    pub members: Vec<TupleMonomorphMember>
 
}
 

	
 
pub struct TupleMonomorphMember {
 
    pub type_id: TypeId,
 
    concrete_type: ConcreteType,
 
    pub size: usize,
 
    pub alignment: usize,
 
    pub offset: usize,
 
}
 

	
 
/// Generic unique type ID. Every monomorphed type and every non-polymorphic
 
/// type will have one of these associated with it.
 
#[derive(Debug, Clone, Copy, PartialEq)]
 
pub struct TypeId(i64);
 

	
 
impl TypeId {
 
    pub(crate) fn new_invalid() -> Self {
 
        return Self(-1);
 
    }
 
}
 

	
 
/// A monomorphed type (or non-polymorphic type's) memory layout and information
 
/// regarding associated types (like a struct's field type).
 
pub struct MonoType {
 
    pub type_id: TypeId,
 
    pub concrete_type: ConcreteType,
 
    pub size: usize,
 
    pub alignment: usize,
 
    pub(crate) variant: MonoTypeVariant
 
}
 

	
 
impl MonoType {
 
    #[inline]
 
    fn new_empty(type_id: TypeId, concrete_type: ConcreteType, variant: MonoTypeVariant) -> Self {
 
        return Self {
 
            type_id, concrete_type,
 
            size: 0,
 
            alignment: 0,
 
            variant,
 
        }
 
    }
 

	
 
    /// Little internal helper function as a reminder: if alignment is 0, then
 
    /// the size/alignment are not actually computed yet!
 
    #[inline]
 
    fn get_size_alignment(&self) -> Option<(usize, usize)> {
 
        if self.alignment == 0 {
 
            return None
 
        } else {
 
            return Some((self.size, self.alignment));
 
        }
 
    }
 
}
 

	
 
/// Special structure that acts like the lookup key for `ConcreteType` instances
 
/// that have already been added to the type table before.
 
#[derive(Clone)]
 
struct MonoSearchKey {
 
    // Uses bitflags to denote when parts between search keys should match and
 
    // whether they should be checked. Needs to have a system like this to
 
    // accommodate tuples.
 
    parts: Vec<(u8, ConcreteTypePart)>,
 
    change_bit: u8,
 
}
 

	
 
impl MonoSearchKey {
 
    const KEY_IN_USE: u8 = 0x01;
 
    const KEY_CHANGE_BIT: u8 = 0x02;
 

	
 
    fn with_capacity(capacity: usize) -> Self {
 
        return MonoSearchKey{
 
            parts: Vec::with_capacity(capacity),
 
            change_bit: 0,
 
        };
 
    }
 

	
 
    /// Sets the search key based on a single concrete type and its polymorphic
 
    /// variables.
 
    fn set(&mut self, concrete_type_parts: &[ConcreteTypePart], poly_var_in_use: &[PolymorphicVariable]) {
 
        self.set_top_type(concrete_type_parts[0]);
 

	
 
        let mut poly_var_index = 0;
 
        for subtype in ConcreteTypeIter::new(concrete_type_parts, 0) {
 
            let in_use = poly_var_in_use[poly_var_index].is_in_use;
 
            poly_var_index += 1;
 
            self.push_subtype(subtype, in_use);
 
        }
 

	
 
        debug_assert_eq!(poly_var_index, poly_var_in_use.len());
 
    }
 

	
 
    /// Starts setting the search key based on an initial top-level type,
 
    /// programmer must call `push_subtype` the appropriate number of times
 
    /// after calling this function
 
    fn set_top_type(&mut self, type_part: ConcreteTypePart) {
 
        self.parts.clear();
 
        self.parts.push((Self::KEY_IN_USE, type_part));
 
        self.change_bit = Self::KEY_CHANGE_BIT;
 
    }
 

	
 
    fn push_subtype(&mut self, concrete_type: &[ConcreteTypePart], in_use: bool) {
 
        let flag = self.change_bit | (if in_use { Self::KEY_IN_USE } else { 0 });
 

	
 
        for part in concrete_type {
 
            self.parts.push((flag, *part));
 
        }
 
        self.change_bit ^= Self::KEY_CHANGE_BIT;
 
    }
 

	
 
    fn push_subtree(&mut self, concrete_type: &[ConcreteTypePart], poly_var_in_use: &[PolymorphicVariable]) {
 
        self.parts.push((self.change_bit | Self::KEY_IN_USE, concrete_type[0]));
 
        self.change_bit ^= Self::KEY_CHANGE_BIT;
 

	
 
        let mut poly_var_index = 0;
 
        for subtype in ConcreteTypeIter::new(concrete_type, 0) {
 
            let in_use = poly_var_in_use[poly_var_index].is_in_use;
 
            poly_var_index += 1;
 
            self.push_subtype(subtype, in_use);
 
        }
 

	
 
        debug_assert_eq!(poly_var_index, poly_var_in_use.len());
 
    }
 

	
 
    // Utilities for hashing and comparison
 
    fn find_end_index(&self, start_index: usize) -> usize {
 
        // Check if we're already at the end
 
        let mut index = start_index;
 
        if index >= self.parts.len() {
 
            return index;
 
        }
 

	
 
        // Iterate until bit flips, or until at end
 
        let expected_bit = self.parts[index].0 & Self::KEY_CHANGE_BIT;
 

	
 
        index += 1;
 
        while index < self.parts.len() {
 
            let current_bit = self.parts[index].0 & Self::KEY_CHANGE_BIT;
 
            if current_bit != expected_bit {
 
                return index;
 
            }
 

	
 
            index += 1;
 
        }
 

	
 
        return index;
 
    }
 
}
 

	
 
impl Hash for MonoSearchKey {
 
    fn hash<H: Hasher>(&self, state: &mut H) {
 
        for index in 0..self.parts.len() {
 
            let (_flags, part) = self.parts[index];
 
            // if flags & Self::KEY_IN_USE != 0 { @Deduplication
 
            part.hash(state);
 
            // }
 
        }
 
    }
 
}
 

	
 
impl PartialEq for MonoSearchKey {
 
    fn eq(&self, other: &Self) -> bool {
 
        let mut self_index = 0;
 
        let mut other_index = 0;
 

	
 
        while self_index < self.parts.len() && other_index < other.parts.len() {
 
            // Retrieve part and flags
 
            let (_self_bits, _) = self.parts[self_index];
 
            let (_other_bits, _) = other.parts[other_index];
 
            let self_in_use = true; // (self_bits & Self::KEY_IN_USE) != 0; @Deduplication
 
            let other_in_use = true; // (other_bits & Self::KEY_IN_USE) != 0; @Deduplication
 

	
 
            // Determine ending indices
 
            let self_end_index = self.find_end_index(self_index);
 
            let other_end_index = other.find_end_index(other_index);
 

	
 
            if self_in_use == other_in_use {
 
                if self_in_use {
 
                    // Both are in use, so both parts should be equal
 
                    let delta_self = self_end_index - self_index;
 
                    let delta_other = other_end_index - other_index;
 
                    if delta_self != delta_other {
 
                        // Both in use, but not of equal length, so the types
 
                        // cannot match
 
                        return false;
 
                    }
 

	
 
                    for _ in 0..delta_self {
 
                        let (_, self_part) = self.parts[self_index];
 
                        let (_, other_part) = other.parts[other_index];
 

	
 
                        if self_part != other_part {
 
                            return false;
 
                        }
 

	
 
                        self_index += 1;
 
                        other_index += 1;
 
                    }
 
                } else {
 
                    // Both not in use, so skip associated parts
 
                    self_index = self_end_index;
 
                    other_index = other_end_index;
 
                }
 
            } else {
 
                // No agreement on importance of parts. This is practically
 
                // impossible
 
                unreachable!();
 
            }
 
        }
 

	
 
        // Everything matched, so if we're at the end of both arrays then we're
 
        // certain that the two keys are equal.
 
        return self_index == self.parts.len() && other_index == other.parts.len();
 
    }
 
}
 

	
 
impl Eq for MonoSearchKey{}
 

	
 
//------------------------------------------------------------------------------
 
// Type table
 
//------------------------------------------------------------------------------
 

	
 
const POLY_VARS_IN_USE: [PolymorphicVariable; 1] = [PolymorphicVariable{ identifier: Identifier::new_empty(InputSpan::new()), is_in_use: true }];
 

	
 
// Programmer note: keep this struct free of dynamically allocated memory
 
#[derive(Clone)]
 
struct TypeLoopBreadcrumb {
 
    type_id: TypeId,
 
    next_member: u32,
 
    next_embedded: u32, // for unions, the index into the variant's embedded types
 
}
 

	
 
// Programmer note: keep this struct free of dynamically allocated memory
 
#[derive(Clone)]
 
struct MemoryBreadcrumb {
 
    type_id: TypeId,
 
    next_member: u32,
 
    next_embedded: u32,
 
    first_size_alignment_idx: u32,
 
}
 

	
 
#[derive(Debug, PartialEq, Eq)]
 
enum TypeLoopResult {
 
    TypeExists,
 
    PushBreadcrumb(DefinitionId, ConcreteType),
 
    TypeLoop(usize), // index into vec of breadcrumbs at which the type matched
 
}
 

	
 
enum MemoryLayoutResult {
 
    TypeExists(usize, usize), // (size, alignment)
 
    PushBreadcrumb(MemoryBreadcrumb),
 
}
 

	
 
// TODO: @Optimize, initial memory-unoptimized implementation
 
struct TypeLoopEntry {
 
    type_id: TypeId,
 
    is_union: bool,
 
}
 

	
 
struct TypeLoop {
 
    members: Vec<TypeLoopEntry>,
 
}
 

	
 
type DefinitionMap = HashMap<DefinitionId, DefinedType>;
 
type MonoTypeMap = HashMap<MonoSearchKey, TypeId>;
 
type MonoTypeArray = Vec<MonoType>;
 

	
 
pub struct TypeTable {
 
    // Lookup from AST DefinitionId to a defined type. Also lookups for
 
    // concrete type to monomorphs
 
    pub(crate) definition_lookup: DefinitionMap,
 
    mono_type_lookup: MonoTypeMap,
 
    pub(crate) mono_types: MonoTypeArray,
 
    mono_search_key: MonoSearchKey,
 
    // Breadcrumbs left behind while trying to find type loops. Also used to
 
    // determine sizes of types when all type loops are detected.
 
    type_loop_breadcrumbs: Vec<TypeLoopBreadcrumb>,
 
    type_loops: Vec<TypeLoop>,
 
    // Stores all encountered types during type loop detection. Used afterwards
 
    // to iterate over all types in order to compute size/alignment.
 
    encountered_types: Vec<TypeLoopEntry>,
 
    // Breadcrumbs and temporary storage during memory layout computation.
 
    memory_layout_breadcrumbs: Vec<MemoryBreadcrumb>,
 
    size_alignment_stack: Vec<(usize, usize)>,
 
}
 

	
 
impl TypeTable {
 
    /// Construct a new type table without any resolved types.
 
    pub(crate) fn new() -> Self {
 
        Self{ 
 
            definition_lookup: HashMap::with_capacity(128),
 
            mono_type_lookup: HashMap::with_capacity(128),
 
            mono_types: Vec::with_capacity(128),
 
            mono_search_key: MonoSearchKey::with_capacity(32),
 
            type_loop_breadcrumbs: Vec::with_capacity(32),
 
            type_loops: Vec::with_capacity(8),
 
            encountered_types: Vec::with_capacity(32),
 
            memory_layout_breadcrumbs: Vec::with_capacity(32),
 
            size_alignment_stack: Vec::with_capacity(64),
 
        }
 
    }
 

	
 
    /// Iterates over all defined types (polymorphic and non-polymorphic) and
 
    /// add their types in two passes. In the first pass we will just add the
 
    /// base types (we will not consider monomorphs, and we will not compute
 
    /// byte sizes). In the second pass we will compute byte sizes of
 
    /// non-polymorphic types, and potentially the monomorphs that are embedded
 
    /// in those types.
 
    pub(crate) fn build_base_types(&mut self, modules: &mut [Module], ctx: &mut PassCtx) -> Result<(), ParseError> {
 
        // Make sure we're allowed to cast root_id to index into ctx.modules
 
        debug_assert!(modules.iter().all(|m| m.phase >= ModuleCompilationPhase::DefinitionsParsed));
 
        debug_assert!(self.definition_lookup.is_empty());
 

	
 
        dbg_code!({
 
            for (index, module) in modules.iter().enumerate() {
 
                debug_assert_eq!(index, module.root_id.index as usize);
 
            }
 
        });
 

	
 
        // Use context to guess hashmap size of the base types
 
        let reserve_size = ctx.heap.definitions.len();
 
        self.definition_lookup.reserve(reserve_size);
 

	
 
        // Resolve all base types
 
        for definition_idx in 0..ctx.heap.definitions.len() {
 
            let definition_id = ctx.heap.definitions.get_id(definition_idx);
 
            let definition = &ctx.heap[definition_id];
 

	
 
            match definition {
 
                Definition::Enum(_) => self.build_base_enum_definition(modules, ctx, definition_id)?,
 
                Definition::Union(_) => self.build_base_union_definition(modules, ctx, definition_id)?,
 
                Definition::Struct(_) => self.build_base_struct_definition(modules, ctx, definition_id)?,
 
                Definition::Procedure(_) => self.build_base_procedure_definition(modules, ctx, definition_id)?,
 
            }
 
        }
 

	
 
        debug_assert_eq!(self.definition_lookup.len(), reserve_size, "mismatch in reserved size of type table");
 
        for module in modules.iter_mut() {
 
            module.phase = ModuleCompilationPhase::TypesAddedToTable;
 
        }
 

	
 
        // Go through all types again, lay out all types that are not
 
        // polymorphic. This might cause us to lay out monomorphized polymorphs
 
        // if these were member types of non-polymorphic types.
 
        for definition_idx in 0..ctx.heap.definitions.len() {
 
            let definition_id = ctx.heap.definitions.get_id(definition_idx);
 
            let poly_type = self.definition_lookup.get(&definition_id).unwrap();
 

	
 
            if !poly_type.definition.is_data_type() || !poly_type.poly_vars.is_empty() {
 
                continue;
 
            }
 

	
 
            // If here then the type is a data type without polymorphic
 
            // variables, but we might have instantiated it already, so:
 
            let concrete_parts = [ConcreteTypePart::Instance(definition_id, 0)];
 
            self.mono_search_key.set(&concrete_parts, &[]);
 
            let type_id = self.mono_type_lookup.get(&self.mono_search_key);
 
            if type_id.is_none() {
 
                self.detect_and_resolve_type_loops_for(
 
                    modules, ctx.heap, ctx.arch,
 
                    ConcreteType{
 
                        parts: vec![ConcreteTypePart::Instance(definition_id, 0)]
 
                    },
 
                )?;
 
                self.lay_out_memory_for_encountered_types(ctx.arch);
 
            }
 
        }
 

	
 
        Ok(())
 
    }
 

	
 
    /// Retrieves base definition from type table. We must be able to retrieve
 
    /// it as we resolve all base types upon type table construction (for now).
 
    /// However, in the future we might do on-demand type resolving, so return
 
    /// an option anyway
 
    #[inline]
 
    pub(crate) fn get_base_definition(&self, definition_id: &DefinitionId) -> Option<&DefinedType> {
 
        self.definition_lookup.get(&definition_id)
 
    }
 

	
 
    /// Returns the index into the monomorph type array if the procedure type
 
    /// Returns the index into the monomorph type array if the provided type
 
    /// already has a (reserved) monomorph.
 
    #[inline]
 
    pub(crate) fn get_procedure_monomorph_type_id(&self, definition_id: &DefinitionId, type_parts: &[ConcreteTypePart]) -> Option<TypeId> {
 
    pub(crate) fn get_monomorph_type_id(&self, definition_id: &DefinitionId, type_parts: &[ConcreteTypePart]) -> Option<TypeId> {
 
        // Cannot use internal search key due to mutability issues. But this
 
        // method should end up being deprecated at some point anyway.
 
        debug_assert_eq!(get_concrete_type_definition(type_parts).unwrap(), *definition_id);
 
        let base_type = self.definition_lookup.get(definition_id).unwrap();
 
        let mut search_key = MonoSearchKey::with_capacity(type_parts.len());
 
        search_key.set(type_parts, &base_type.poly_vars);
 

	
 
        return self.mono_type_lookup.get(&search_key).copied();
 
    }
 

	
 
    #[inline]
 
    pub(crate) fn get_monomorph(&self, type_id: TypeId) -> &MonoType {
 
        return &self.mono_types[type_id.0 as usize];
 
    }
 

	
 
    /// Reserves space for a monomorph of a polymorphic procedure. The index
 
    /// will point into a (reserved) slot of the array of expression types. The
 
    /// monomorph may NOT exist yet (because the reservation implies that we're
 
    /// going to be performing typechecking on it, and we don't want to
 
    /// check the same monomorph twice)
 
    pub(crate) fn reserve_procedure_monomorph_type_id(&mut self, definition_id: &DefinitionId, concrete_type: ConcreteType, monomorph_index: u32) -> TypeId {
 
        debug_assert_eq!(get_concrete_type_definition(&concrete_type.parts).unwrap(), *definition_id);
 
        let type_id = TypeId(self.mono_types.len() as i64);
 
        let base_type = self.definition_lookup.get_mut(definition_id).unwrap();
 
        self.mono_search_key.set(&concrete_type.parts, &base_type.poly_vars);
 

	
 
        debug_assert!(!self.mono_type_lookup.contains_key(&self.mono_search_key));
 
        self.mono_type_lookup.insert(self.mono_search_key.clone(), type_id);
 
        self.mono_types.push(MonoType::new_empty(type_id, concrete_type, MonoTypeVariant::Procedure(ProcedureMonomorph{
 
            monomorph_index,
 
            builtin: false,
 
        })));
 

	
 
        return type_id;
 
    }
 

	
 
    /// Adds a builtin type to the type table. As this is only called by the
 
    /// compiler during setup we assume it cannot fail.
 
    pub(crate) fn add_builtin_data_type(&mut self, concrete_type: ConcreteType, poly_vars: &[PolymorphicVariable], size: usize, alignment: usize) -> TypeId {
 
        self.mono_search_key.set(&concrete_type.parts, poly_vars);
 
        debug_assert!(!self.mono_type_lookup.contains_key(&self.mono_search_key));
 
        debug_assert_ne!(alignment, 0);
 
        let type_id = TypeId(self.mono_types.len() as i64);
 
        self.mono_type_lookup.insert(self.mono_search_key.clone(), type_id);
 
        self.mono_types.push(MonoType{
 
            type_id,
 
            concrete_type,
 
            size,
 
            alignment,
 
            variant: MonoTypeVariant::Builtin,
 
        });
 

	
 
        return type_id;
 
    }
 

	
 
    /// Adds a builtin procedure to the type table.
 
    pub(crate) fn add_builtin_procedure_type(&mut self, concrete_type: ConcreteType, poly_vars: &[PolymorphicVariable]) -> TypeId {
 
        self.mono_search_key.set(&concrete_type.parts, poly_vars);
 
        debug_assert!(!self.mono_type_lookup.contains_key(&self.mono_search_key));
 
        let type_id = TypeId(self.mono_types.len() as i64);
 
        self.mono_type_lookup.insert(self.mono_search_key.clone(), type_id);
 
        self.mono_types.push(MonoType{
 
            type_id,
 
            concrete_type,
 
            size: 0,
 
            alignment: 0,
 
            variant: MonoTypeVariant::Procedure(ProcedureMonomorph{
 
                monomorph_index: u32::MAX,
 
                builtin: true,
 
            })
 
        });
 

	
 
        return type_id;
 
    }
 

	
 
    /// Adds a monomorphed type to the type table. If it already exists then the
 
    /// previous entry will be used.
 
    pub(crate) fn add_monomorphed_type(
 
        &mut self, modules: &[Module], heap: &Heap, arch: &TargetArch, concrete_type: ConcreteType
 
    ) -> Result<TypeId, ParseError> {
 
        // Check if the concrete type was already added
 
        Self::set_search_key_to_type(&mut self.mono_search_key, &self.definition_lookup, &concrete_type.parts);
 
        if let Some(type_id) = self.mono_type_lookup.get(&self.mono_search_key) {
 
            return Ok(*type_id);
 
        }
 

	
 
        // Concrete type needs to be added
 
        self.detect_and_resolve_type_loops_for(modules, heap, arch, concrete_type)?;
 
        let type_id = self.encountered_types[0].type_id;
 
        self.lay_out_memory_for_encountered_types(arch);
 

	
 
        return Ok(type_id);
 
    }
 

	
 
    //--------------------------------------------------------------------------
 
    // Building base types
 
    //--------------------------------------------------------------------------
 

	
 
    /// Builds the base type for an enum. Will not compute byte sizes
 
    fn build_base_enum_definition(&mut self, modules: &[Module], ctx: &mut PassCtx, definition_id: DefinitionId) -> Result<(), ParseError> {
 
        debug_assert!(!self.definition_lookup.contains_key(&definition_id), "base enum already built");
 
        let definition = ctx.heap[definition_id].as_enum();
 
        let root_id = definition.defined_in;
 

	
 
        // Determine enum variants
 
        let mut enum_value = -1;
 
        let mut variants = Vec::with_capacity(definition.variants.len());
 

	
 
        for variant in &definition.variants {
 
            if enum_value == i64::MAX {
 
                let source = &modules[definition.defined_in.index as usize].source;
 
                return Err(ParseError::new_error_str_at_span(
 
                    source, variant.identifier.span,
 
                    "this enum variant has an integer value that is too large"
 
                ));
 
            }
 

	
 
            enum_value += 1;
 
            if let EnumVariantValue::Integer(explicit_value) = variant.value {
 
                enum_value = explicit_value;
 
            }
 

	
 
            variants.push(EnumVariant{
 
                identifier: variant.identifier.clone(),
 
                value: enum_value,
 
            });
 
        }
 

	
 
        // Determine tag size
 
        let mut min_enum_value = 0;
 
        let mut max_enum_value = 0;
 
        if !variants.is_empty() {
 
            min_enum_value = variants[0].value;
 
            max_enum_value = variants[0].value;
 
            for variant in variants.iter().skip(1) {
 
                min_enum_value = min_enum_value.min(variant.value);
 
                max_enum_value = max_enum_value.max(variant.value);
 
            }
 
        }
 

	
 
        let (tag_type, size_and_alignment) = Self::variant_tag_type_from_values(min_enum_value, max_enum_value);
 

	
 
        // Enum names and polymorphic args do not conflict
 
        Self::check_identifier_collision(
 
            modules, root_id, &variants, |variant| &variant.identifier, "enum variant"
 
        )?;
 

	
 
        // Polymorphic arguments cannot appear as embedded types, because
 
        // they can only consist of integer variants.
 
        Self::check_poly_args_collision(modules, ctx, root_id, &definition.poly_vars)?;
 
        let poly_vars = Self::create_polymorphic_variables(&definition.poly_vars);
 

	
 
        self.definition_lookup.insert(definition_id, DefinedType {
 
            ast_root: root_id,
 
            ast_definition: definition_id,
 
            definition: DefinedTypeVariant::Enum(EnumType{
 
                variants,
 
                minimum_tag_value: min_enum_value,
 
                maximum_tag_value: max_enum_value,
 
                tag_type,
 
                size: size_and_alignment,
 
                alignment: size_and_alignment
 
            }),
 
            poly_vars,
 
            is_polymorph: false,
 
        });
 

	
 
        return Ok(());
 
    }
 

	
 
    /// Builds the base type for a union. Will compute byte sizes.
 
    fn build_base_union_definition(&mut self, modules: &[Module], ctx: &mut PassCtx, definition_id: DefinitionId) -> Result<(), ParseError> {
 
        debug_assert!(!self.definition_lookup.contains_key(&definition_id), "base union already built");
 
        let definition = ctx.heap[definition_id].as_union();
 
        let root_id = definition.defined_in;
 

	
 
        // Check all variants and their embedded types
 
        let mut variants = Vec::with_capacity(definition.variants.len());
 
        let mut tag_counter = 0;
 
        for variant in &definition.variants {
 
            for embedded in &variant.value {
 
                Self::check_member_parser_type(
 
                    modules, ctx, root_id, embedded, false
 
                )?;
 
            }
 

	
 
            variants.push(UnionVariant{
 
                identifier: variant.identifier.clone(),
 
                embedded: variant.value.clone(),
 
                tag_value: tag_counter,
 
            });
 
            tag_counter += 1;
 
        }
 

	
 
        let mut max_tag_value = 0;
 
        if tag_counter != 0 {
 
            max_tag_value = tag_counter - 1
 
        }
 

	
 
        let (tag_type, tag_size) = Self::variant_tag_type_from_values(0, max_tag_value);
 

	
 
        // Make sure there are no conflicts in identifiers
 
        Self::check_identifier_collision(
 
            modules, root_id, &variants, |variant| &variant.identifier, "union variant"
 
        )?;
 
        Self::check_poly_args_collision(modules, ctx, root_id, &definition.poly_vars)?;
 

	
 
        // Construct internal representation of union
 
        let mut poly_vars = Self::create_polymorphic_variables(&definition.poly_vars);
 
        for variant in &definition.variants {
 
            for embedded in &variant.value {
 
                Self::mark_used_polymorphic_variables(&mut poly_vars, embedded);
 
            }
 
        }
 

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

	
 
        self.definition_lookup.insert(definition_id, DefinedType{
 
            ast_root: root_id,
 
            ast_definition: definition_id,
 
            definition: DefinedTypeVariant::Union(UnionType{ variants, tag_type, tag_size }),
 
            poly_vars,
 
            is_polymorph
 
        });
 

	
 
        return Ok(());
 
    }
 

	
 
    /// Builds base struct type. Will not compute byte sizes.
 
    fn build_base_struct_definition(&mut self, modules: &[Module], ctx: &mut PassCtx, definition_id: DefinitionId) -> Result<(), ParseError> {
 
        debug_assert!(!self.definition_lookup.contains_key(&definition_id), "base struct already built");
 
        let definition = ctx.heap[definition_id].as_struct();
 
        let root_id = definition.defined_in;
 

	
 
        // Check all struct fields and construct internal representation
 
        let mut fields = Vec::with_capacity(definition.fields.len());
 

	
 
        for field in &definition.fields {
 
            Self::check_member_parser_type(
 
                modules, ctx, root_id, &field.parser_type, false
 
            )?;
 

	
 
            fields.push(StructField{
 
                identifier: field.field.clone(),
 
                parser_type: field.parser_type.clone(),
 
            });
 
        }
 

	
 
        // Make sure there are no conflicting variables
 
        Self::check_identifier_collision(
 
            modules, root_id, &fields, |field| &field.identifier, "struct field"
 
        )?;
 
        Self::check_poly_args_collision(modules, ctx, root_id, &definition.poly_vars)?;
 

	
 
        // Construct base type in table
 
        let mut poly_vars = Self::create_polymorphic_variables(&definition.poly_vars);
 
        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.definition_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 procedure type.
 
    fn build_base_procedure_definition(&mut self, modules: &[Module], ctx: &mut PassCtx, definition_id: DefinitionId) -> Result<(), ParseError> {
 
        debug_assert!(!self.definition_lookup.contains_key(&definition_id), "base function already built");
 
        let definition = ctx.heap[definition_id].as_procedure();
 
        let root_id = definition.defined_in;
 

	
 
        // Check and construct return types and argument types.
 
        if let Some(return_type) = &definition.return_type {
 
            Self::check_member_parser_type(
 
                modules, ctx, root_id, return_type, definition.source.is_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.source.is_builtin()
 
            )?;
 

	
 
            arguments.push(ProcedureArgument{
 
                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, "procedure argument"
 
        )?;
 
        Self::check_poly_args_collision(modules, ctx, root_id, &definition.poly_vars)?;
 

	
 
        // Construct internal representation of function type
 
        // TODO: Marking used polymorphic variables should take statements in
 
        //  the body into account. But currently we don't. Hence mark them all
 
        //  as being in-use. Note to self: true condition should be that the
 
        //  polymorphic variables are used in places where the resulting types
 
        //  are themselves truly polymorphic types (e.g. not a phantom type).
 
        let mut poly_vars = Self::create_polymorphic_variables(&definition.poly_vars);
 
        for poly_var in &mut poly_vars {
 
            poly_var.is_in_use = true;
 
        }
 

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

	
 
        self.definition_lookup.insert(definition_id, DefinedType{
 
            ast_root: root_id,
 
            ast_definition: definition_id,
 
            definition: DefinedTypeVariant::Procedure(ProcedureType{
 
                kind: definition.kind,
 
                return_type: definition.return_type.clone(),
 
                arguments
 
            }),
 
            poly_vars,
 
            is_polymorph
 
        });
 

	
 
        return Ok(());
 
    }
 

	
 
    /// Will check if the member type (field of a struct, embedded type in a
 
    /// union variant) is valid.
 
    fn check_member_parser_type(
 
        modules: &[Module], ctx: &PassCtx, base_definition_root_id: RootId,
 
        member_parser_type: &ParserType, allow_special_compiler_types: bool
 
    ) -> Result<(), ParseError> {
 
        use ParserTypeVariant as PTV;
 

	
 
        for element in &member_parser_type.elements {
 
            match element.variant {
 
                // Special cases
 
                PTV::Void | PTV::InputOrOutput | PTV::ArrayLike | PTV::IntegerLike => {
 
                    if !allow_special_compiler_types {
 
                        unreachable!("compiler-only ParserTypeVariant in member type");
 
                    }
 
                },
 
                // Builtin types, always valid
 
                PTV::Message | PTV::Bool |
 
                PTV::UInt8 | PTV::UInt16 | PTV::UInt32 | PTV::UInt64 |
 
                PTV::SInt8 | PTV::SInt16 | PTV::SInt32 | PTV::SInt64 |
 
                PTV::Character | PTV::String |
 
                PTV::Array | PTV::Input | PTV::Output | PTV::Tuple(_) |
 
                // Likewise, polymorphic variables are always valid
 
                PTV::PolymorphicArgument(_, _) => {},
 
                // Types that are not constructable, or types that are not
 
                // allowed (and checked earlier)
 
                PTV::IntegerLiteral | PTV::Inferred => {
 
                    unreachable!("illegal ParserTypeVariant within type definition");
 
                },
 
                // Finally, user-defined types
 
                PTV::Definition(definition_id, _) => {
 
                    let definition = &ctx.heap[definition_id];
 
                    if !(definition.is_struct() || definition.is_enum() || definition.is_union()) {
 
                        let source = &modules[base_definition_root_id.index as usize].source;
 
                        return Err(ParseError::new_error_str_at_span(
 
                            source, element.element_span, "expected a datatype (a struct, enum or union)"
 
                        ));
 
                    }
 

	
 
                    // Otherwise, we're fine
 
                }
 
            }
 
        }
 

	
 
        // If here, then all elements check out
 
        return Ok(());
 
    }
 

	
 
    /// Go through a list of identifiers and ensure that all identifiers have
 
    /// unique names
 
    fn check_identifier_collision<T: Sized, F: Fn(&T) -> &Identifier>(
 
        modules: &[Module], root_id: RootId, items: &[T], getter: F, item_name: &'static str
 
    ) -> Result<(), ParseError> {
 
        for (item_idx, item) in items.iter().enumerate() {
 
            let item_ident = getter(item);
 
            for other_item in &items[0..item_idx] {
 
                let other_item_ident = getter(other_item);
 
                if item_ident == other_item_ident {
 
                    let module_source = &modules[root_id.index as usize].source;
 
                    return Err(ParseError::new_error_at_span(
 
                        module_source, item_ident.span, format!("This {} is defined more than once", item_name)
 
                    ).with_info_at_span(
 
                        module_source, other_item_ident.span, format!("The other {} is defined here", item_name)
 
                    ));
 
                }
 
            }
 
        }
 

	
 
        Ok(())
 
    }
 

	
 
    /// Go through a list of polymorphic arguments and make sure that the
 
    /// arguments all have unique names, and the arguments do not conflict with
 
    /// any symbols defined at the module scope.
 
    fn check_poly_args_collision(
 
        modules: &[Module], ctx: &PassCtx, root_id: RootId, poly_args: &[Identifier]
 
    ) -> Result<(), ParseError> {
 
        // Make sure polymorphic arguments are unique and none of the
 
        // identifiers conflict with any imported scopes
 
        for (arg_idx, poly_arg) in poly_args.iter().enumerate() {
 
            for other_poly_arg in &poly_args[..arg_idx] {
 
                if poly_arg == other_poly_arg {
 
                    let module_source = &modules[root_id.index as usize].source;
 
                    return Err(ParseError::new_error_str_at_span(
 
                        module_source, poly_arg.span,
 
                        "This polymorphic argument is defined more than once"
 
                    ).with_info_str_at_span(
 
                        module_source, other_poly_arg.span,
 
                        "It conflicts with this polymorphic argument"
 
                    ));
 
                }
 
            }
 

	
 
            // Check if identifier conflicts with a symbol defined or imported
 
            // in the current module
 
            if let Some(symbol) = ctx.symbols.get_symbol_by_name(SymbolScope::Module(root_id), poly_arg.value.as_bytes()) {
 
                // We have a conflict
 
                let module_source = &modules[root_id.index as usize].source;
 
                let introduction_span = symbol.variant.span_of_introduction(ctx.heap);
 
                return Err(ParseError::new_error_str_at_span(
 
                    module_source, poly_arg.span,
 
                    "This polymorphic argument conflicts with another symbol"
 
                ).with_info_str_at_span(
 
                    module_source, introduction_span,
 
                    "It conflicts due to this symbol"
 
                ));
 
            }
 
        }
 

	
 
        // All arguments are fine
 
        Ok(())
 
    }
 

	
 
    //--------------------------------------------------------------------------
 
    // Detecting type loops
 
    //--------------------------------------------------------------------------
 

	
 
    /// Internal function that will detect type loops and check if they're
 
    /// resolvable. If so then the appropriate union variants will be marked as
 
    /// "living on heap". If not then a `ParseError` will be returned
 
    fn detect_and_resolve_type_loops_for(&mut self, modules: &[Module], heap: &Heap, arch: &TargetArch, concrete_type: ConcreteType) -> Result<(), ParseError> {
 
        // Programmer notes: what happens here is the we call
 
        // `check_member_for_type_loops` for a particular type's member, and
 
        // then take action using the return value:
 
        // 1. It might already be resolved: in this case it implies we don't
 
        //  have type loops, or they have been resolved.
 
        // 2. A new type is encountered. If so then it is added to the type loop
 
        //  breadcrumbs.
 
        // 3. A type loop is detected (implying the type is already resolved, or
 
        //  already exists in the type loop breadcrumbs).
 
        //
 
        // Using the breadcrumbs we incrementally check every member type of a
 
        // particular considered type (e.g. a struct field, tuple member), and
 
        // do the same as above. Note that when a breadcrumb is added we reserve
 
        // space in the monomorph storage, initialized to zero-values (i.e.
 
        // wrong values). The breadcrumbs keep track of how far and along we are
 
        // with resolving the member types.
 
        //
 
        // At the end we may have some type loops. If they're unresolvable then
 
        // we throw an error). If there are no type loops or they are all
 
        // resolvable then we end up with a list of `encountered_types`. These
 
        // are then used by `lay_out_memory_for_encountered_types`.
 
        debug_assert!(self.type_loop_breadcrumbs.is_empty());
 
        debug_assert!(self.type_loops.is_empty());
 
        debug_assert!(self.encountered_types.is_empty());
 

	
 
        // Push the initial breadcrumb
 
        let initial_breadcrumb = Self::check_member_for_type_loops(
 
            &self.type_loop_breadcrumbs, &self.definition_lookup, &self.mono_type_lookup,
 
            &mut self.mono_search_key, &concrete_type
 
        );
 

	
 
        if let TypeLoopResult::PushBreadcrumb(definition_id, concrete_type) = initial_breadcrumb {
 
            self.handle_new_breadcrumb_for_type_loops(arch, definition_id, concrete_type);
 
        } else {
 
            unreachable!()
 
        };
 

	
 
        // Enter into the main resolving loop
 
        while !self.type_loop_breadcrumbs.is_empty() {
 
            // Because we might be modifying the breadcrumb array we need to
 
            let breadcrumb_idx = self.type_loop_breadcrumbs.len() - 1;
 
            let mut breadcrumb = self.type_loop_breadcrumbs[breadcrumb_idx].clone();
 

	
 
            let mono_type = &self.mono_types[breadcrumb.type_id.0 as usize];
 
            let resolve_result = match &mono_type.variant {
 
                MonoTypeVariant::Builtin => {
 
                    TypeLoopResult::TypeExists
 
                }
 
                MonoTypeVariant::Enum => {
 
                    TypeLoopResult::TypeExists
 
                },
 
                MonoTypeVariant::Union(monomorph) => {
 
                    let num_variants = monomorph.variants.len() as u32;
 
                    let mut union_result = TypeLoopResult::TypeExists;
 

	
 
                    'member_loop: while breadcrumb.next_member < num_variants {
 
                        let mono_variant = &monomorph.variants[breadcrumb.next_member as usize];
 
                        let num_embedded = mono_variant.embedded.len() as u32;
 

	
 
                        while breadcrumb.next_embedded < num_embedded {
 
                            let mono_embedded = &mono_variant.embedded[breadcrumb.next_embedded as usize];
 
                            union_result = Self::check_member_for_type_loops(
 
                                &self.type_loop_breadcrumbs, &self.definition_lookup, &self.mono_type_lookup,
 
                                &mut self.mono_search_key, &mono_embedded.concrete_type
 
                            );
 

	
 
                            if union_result != TypeLoopResult::TypeExists {
 
                                // In type loop or new breadcrumb pushed, so
 
                                // break out of the resolving loop
 
                                break 'member_loop;
 
                            }
 

	
 
                            breadcrumb.next_embedded += 1;
 
                        }
 

	
 
                        breadcrumb.next_embedded = 0;
 
                        breadcrumb.next_member += 1
 
                    }
 

	
 
                    union_result
 
                },
 
                MonoTypeVariant::Struct(monomorph) => {
 
                    let num_fields = monomorph.fields.len() as u32;
 

	
 
                    let mut struct_result = TypeLoopResult::TypeExists;
 
                    while breadcrumb.next_member < num_fields {
 
                        let mono_field = &monomorph.fields[breadcrumb.next_member as usize];
 
                        struct_result = Self::check_member_for_type_loops(
 
                            &self.type_loop_breadcrumbs, &self.definition_lookup, &self.mono_type_lookup,
 
                            &mut self.mono_search_key, &mono_field.concrete_type
 
                        );
 

	
 
                        if struct_result != TypeLoopResult::TypeExists {
 
                            // Type loop or breadcrumb pushed, so break out of
 
                            // the resolving loop
 
                            break;
 
                        }
 

	
 
                        breadcrumb.next_member += 1;
 
                    }
 

	
 
                    struct_result
 
                },
 
                MonoTypeVariant::Procedure(_) => unreachable!(),
 
                MonoTypeVariant::Tuple(monomorph) => {
 
                    let num_members = monomorph.members.len() as u32;
 
                    let mut tuple_result = TypeLoopResult::TypeExists;
 

	
 
                    while breadcrumb.next_member < num_members {
 
                        let tuple_member = &monomorph.members[breadcrumb.next_member as usize];
 
                        tuple_result = Self::check_member_for_type_loops(
 
                            &self.type_loop_breadcrumbs, &self.definition_lookup, &self.mono_type_lookup,
 
                            &mut self.mono_search_key, &tuple_member.concrete_type
 
                        );
 

	
 
                        if tuple_result != TypeLoopResult::TypeExists {
 
                            break;
 
                        }
 

	
 
                        breadcrumb.next_member += 1;
 
                    }
 

	
 
                    tuple_result
 
                }
 
            };
 

	
 
            // Handle the result of attempting to resolve the current breadcrumb
 
            match resolve_result {
 
                TypeLoopResult::TypeExists => {
 
                    // We finished parsing the type
 
                    self.type_loop_breadcrumbs.pop();
 
                },
 
                TypeLoopResult::PushBreadcrumb(definition_id, concrete_type) => {
 
                    // We recurse into the member type.
 
                    self.type_loop_breadcrumbs[breadcrumb_idx] = breadcrumb;
 
                    self.handle_new_breadcrumb_for_type_loops(arch, definition_id, concrete_type);
 
                },
 
                TypeLoopResult::TypeLoop(first_idx) => {
 
                    // Because we will be modifying breadcrumbs within the
 
                    // type-loop handling code, put back the modified breadcrumb
 
                    self.type_loop_breadcrumbs[breadcrumb_idx] = breadcrumb;
 

	
 
                    // We're in a type loop. Add the type loop
 
                    let mut loop_members = Vec::with_capacity(self.type_loop_breadcrumbs.len() - first_idx);
 
                    let mut contains_union = false;
 

	
 
                    for breadcrumb_idx in first_idx..self.type_loop_breadcrumbs.len() {
 
                        let breadcrumb = &mut self.type_loop_breadcrumbs[breadcrumb_idx];
 
                        let mut is_union = false;
 

	
 
                        // Check if type loop member is a union that may be
 
                        // broken up by moving some of its members to the heap.
 
                        let mono_type = &mut self.mono_types[breadcrumb.type_id.0 as usize];
 
                        if let MonoTypeVariant::Union(union_type) = &mut mono_type.variant {
 
                            // Mark the variant that caused the loop as heap
 
                            // allocated to break the type loop.
 
                            let variant = &mut union_type.variants[breadcrumb.next_member as usize];
 
                            variant.lives_on_heap = true;
 
                            breadcrumb.next_embedded += 1;
 

	
 
                            is_union = true;
 
                            contains_union = true;
 
                        } // else: we don't care about the type for now
 

	
 
                        loop_members.push(TypeLoopEntry{
 
                            type_id: breadcrumb.type_id,
 
                            is_union
 
                        });
 
                    }
 

	
 
                    let new_type_loop = TypeLoop{ members: loop_members };
 
                    if !contains_union {
 
                        // No way to (potentially) break the union. So return a
 
                        // type loop error. This is because otherwise our
 
                        // breadcrumb resolver ends up in an infinite loop.
 
                        return Err(construct_type_loop_error(
 
                            &self.mono_types, &new_type_loop, modules, heap
 
                        ));
 
                    }
 

	
 
                    self.type_loops.push(new_type_loop);
 
                }
 
            }
 
        }
 

	
 
        // All breadcrumbs have been cleared. So now `type_loops` contains all
 
        // of the encountered type loops, and `encountered_types` contains a
 
        // list of all unique monomorphs we encountered.
 

	
 
        // The next step is to figure out if all of the type loops can be
 
        // broken. A type loop can be broken if at least one union exists in the
 
        // loop and that union ended up having variants that are not part of
 
        // a type loop.
 
        fn type_loop_source_span_and_message<'a>(
 
            modules: &'a [Module], heap: &Heap, mono_types: &MonoTypeArray,
 
            definition_id: DefinitionId, mono_type_id: TypeId, index_in_loop: usize
 
        ) -> (&'a InputSource, InputSpan, String) {
 
            // Note: because we will discover the type loop the *first* time we
 
            // instantiate a monomorph with the provided polymorphic arguments
 
            // (not all arguments are actually used in the type). We don't have
 
            // to care about a second instantiation where certain unused
 
            // polymorphic arguments are different.
 
            let mono_type = &mono_types[mono_type_id.0 as usize];
 
            let type_name = mono_type.concrete_type.display_name(heap);
 

	
 
            let message = if index_in_loop == 0 {
 
                format!(
 
                    "encountered an infinitely large type for '{}' (which can be fixed by \
 
                    introducing a union type that has a variant whose embedded types are \
 
                    not part of a type loop, or do not have embedded types)",
 
                    type_name
 
                )
 
            } else if index_in_loop == 1 {
 
                format!("because it depends on the type '{}'", type_name)
 
            } else {
 
                format!("which depends on the type '{}'", type_name)
 
            };
 

	
 
            let ast_definition = &heap[definition_id];
 
            let ast_root_id = ast_definition.defined_in();
 

	
 
            return (
 
                &modules[ast_root_id.index as usize].source,
 
                ast_definition.identifier().span,
 
                message
 
            );
 
        }
 

	
 
        fn construct_type_loop_error(mono_types: &MonoTypeArray, type_loop: &TypeLoop, modules: &[Module], heap: &Heap) -> ParseError {
 
            // Seek first entry to produce parse error. Then continue builder
 
            // pattern. This is the error case so efficiency can go home.
 
            let mut parse_error = None;
 
            let mut next_member_index = 0;
 
            while next_member_index < type_loop.members.len() {
 
                let first_entry = &type_loop.members[next_member_index];
 
                next_member_index += 1;
 

	
 
                // Retrieve definition of first type in loop
 
                let first_mono_type = &mono_types[first_entry.type_id.0 as usize];
 
                let first_definition_id = get_concrete_type_definition(&first_mono_type.concrete_type.parts);
 
                if first_definition_id.is_none() {
 
                    continue;
 
                }
 
                let first_definition_id = first_definition_id.unwrap();
 

	
 
                // Produce error message for first type in loop
 
                let (first_module, first_span, first_message) = type_loop_source_span_and_message(
 
                    modules, heap, mono_types, first_definition_id, first_entry.type_id, 0
 
                );
 
                parse_error = Some(ParseError::new_error_at_span(first_module, first_span, first_message));
 
                break;
 
            }
 

	
 
            let mut parse_error = parse_error.unwrap(); // Loop above cannot have failed, because we must have a type loop, type loops cannot contain only unnamed types
 

	
 
            let mut error_counter = 1;
 
            for member_idx in next_member_index..type_loop.members.len() {
 
                let entry = &type_loop.members[member_idx];
 
                let mono_type = &mono_types[entry.type_id.0 as usize];
 
                let definition_id = get_concrete_type_definition(&mono_type.concrete_type.parts);
 
                if definition_id.is_none() {
 
                    continue;
 
                }
 
                let definition_id = definition_id.unwrap();
 

	
 
                let (module, span, message) = type_loop_source_span_and_message(
 
                    modules, heap, mono_types, definition_id, entry.type_id, error_counter
 
                );
 
                parse_error = parse_error.with_info_at_span(module, span, message);
 
                error_counter += 1;
 
            }
 

	
 
            parse_error
 
        }
 

	
 
        for type_loop in &self.type_loops {
 
            let mut can_be_broken = false;
 
            debug_assert!(!type_loop.members.is_empty());
 

	
 
            for entry in &type_loop.members {
 
                if entry.is_union {
 
                    let mono_type = self.mono_types[entry.type_id.0 as usize].variant.as_union();
 
                    debug_assert!(!mono_type.variants.is_empty()); // otherwise it couldn't be part of the type loop
 
                    let has_stack_variant = mono_type.variants.iter().any(|variant| !variant.lives_on_heap);
 
                    if has_stack_variant {
 
                        can_be_broken = true;
 
                        break;
 
                    }
 
                }
 
            }
 

	
 
            if !can_be_broken {
 
                // Construct a type loop error
 
                return Err(construct_type_loop_error(&self.mono_types, type_loop, modules, heap));
 
            }
 
        }
 

	
 
        // If here, then all type loops have been resolved and we can lay out
 
        // all of the members
 
        self.type_loops.clear();
 

	
 
        return Ok(());
 
    }
 

	
 
    /// Checks if the specified type needs to be resolved (i.e. we need to push
 
    /// a breadcrumb), is already resolved (i.e. we can continue with the next
 
    /// member of the currently considered type) or is in the process of being
 
    /// resolved (i.e. we're in a type loop). Because of borrowing rules we
 
    /// don't do any modifications of internal types here. Hence: if we
 
    /// return `PushBreadcrumb` then call `handle_new_breadcrumb_for_type_loops`
 
    /// to take care of storing the appropriate types.
 
    fn check_member_for_type_loops(
 
        breadcrumbs: &[TypeLoopBreadcrumb], definition_map: &DefinitionMap, mono_type_map: &MonoTypeMap,
 
        mono_key: &mut MonoSearchKey, concrete_type: &ConcreteType
src/protocol/tests/utils.rs
Show inline comments
 
use crate::collections::StringPool;
 
use crate::protocol::{Module, ast::*, input_source::*, parser::{
 
    Parser,
 
    type_table::*,
 
    symbol_table::SymbolTable,
 
    token_parsing::*,
 
}, eval::*, RunContext};
 

	
 
// Carries information about the test into utility structures for builder-like
 
// assertions
 
#[derive(Clone, Copy)]
 
struct TestCtx<'a> {
 
    test_name: &'a str,
 
    heap: &'a Heap,
 
    modules: &'a Vec<Module>,
 
    types: &'a TypeTable,
 
    symbols: &'a SymbolTable,
 
}
 

	
 
//------------------------------------------------------------------------------
 
// Interface for parsing and compiling
 
//------------------------------------------------------------------------------
 

	
 
pub(crate) struct Tester {
 
    test_name: String,
 
    sources: Vec<String>
 
}
 

	
 
impl Tester {
 
    /// Constructs a new tester, allows adding multiple sources before compiling
 
    pub(crate) fn new<S: ToString>(test_name: S) -> Self {
 
        Self{
 
            test_name: test_name.to_string(),
 
            sources: Vec::new()
 
        }
 
    }
 

	
 
    /// Utility for quick tests that use a single source file and expect the
 
    /// compilation to succeed.
 
    pub(crate) fn new_single_source_expect_ok<T: ToString, S: ToString>(test_name: T, source: S) -> AstOkTester {
 
        Self::new(test_name)
 
            .with_source(source)
 
            .compile()
 
            .expect_ok()
 
    }
 

	
 
    /// Utility for quick tests that use a single source file and expect the
 
    /// compilation to fail.
 
    pub(crate) fn new_single_source_expect_err<T: ToString, S: ToString>(test_name: T, source: S) -> AstErrTester {
 
        Self::new(test_name)
 
            .with_source(source)
 
            .compile()
 
            .expect_err()
 
    }
 

	
 
    pub(crate) fn with_source<S: ToString>(mut self, source: S) -> Self {
 
        self.sources.push(source.to_string());
 
        self
 
    }
 

	
 
    pub(crate) fn compile(self) -> AstTesterResult {
 
        let mut parser = Parser::new().unwrap();
 
        for source in self.sources.into_iter() {
 
            let source = source.into_bytes();
 
            let input_source = InputSource::new(String::from(""), source);
 

	
 
            if let Err(err) = parser.feed(input_source) {
 
                return AstTesterResult::Err(AstErrTester::new(self.test_name, err))
 
            }
 
        }
 

	
 
        if let Err(err) = parser.parse() {
 
            return AstTesterResult::Err(AstErrTester::new(self.test_name, err))
 
        }
 

	
 
        AstTesterResult::Ok(AstOkTester::new(self.test_name, parser))
 
    }
 
}
 

	
 
pub(crate) enum AstTesterResult {
 
    Ok(AstOkTester),
 
    Err(AstErrTester)
 
}
 

	
 
impl AstTesterResult {
 
    pub(crate) fn expect_ok(self) -> AstOkTester {
 
        match self {
 
            AstTesterResult::Ok(v) => v,
 
            AstTesterResult::Err(err) => {
 
                let wrapped = ErrorTester{ test_name: &err.test_name, error: &err.error };
 
                println!("DEBUG: Full error:\n{}", &err.error);
 
                assert!(
 
                    false,
 
                    "[{}] Expected compilation to succeed, but it failed with {}",
 
                    err.test_name, wrapped.assert_postfix()
 
                );
 
                unreachable!();
 
            }
 
        }
 
    }
 

	
 
    pub(crate) fn expect_err(self) -> AstErrTester {
 
        match self {
 
            AstTesterResult::Ok(ok) => {
 
                assert!(false, "[{}] Expected compilation to fail, but it succeeded", ok.test_name);
 
                unreachable!();
 
            },
 
            AstTesterResult::Err(err) => err,
 
        }
 
    }
 
}
 

	
 
//------------------------------------------------------------------------------
 
// Interface for successful compilation
 
//------------------------------------------------------------------------------
 

	
 
#[allow(dead_code)]
 
pub(crate) struct AstOkTester {
 
    test_name: String,
 
    modules: Vec<Module>,
 
    heap: Heap,
 
    symbols: SymbolTable,
 
    types: TypeTable,
 
    pool: StringPool, // This is stored because if we drop it on the floor, we lose all our `StringRef<'static>`s
 
}
 

	
 
impl AstOkTester {
 
    fn new(test_name: String, parser: Parser) -> Self {
 
        Self {
 
            test_name,
 
            modules: parser.modules.into_iter().map(|module| Module{
 
                source: module.source,
 
                root_id: module.root_id,
 
                name: module.name.map(|(_, name)| name)
 
            }).collect(),
 
            heap: parser.heap,
 
            symbols: parser.symbol_table,
 
            types: parser.type_table,
 
            pool: parser.string_pool,
 
        }
 
    }
 

	
 
    pub(crate) fn for_struct<F: Fn(StructTester)>(self, name: &str, f: F) -> Self {
 
        let mut found = false;
 
        for definition in self.heap.definitions.iter() {
 
            if let Definition::Struct(ast_definition) = definition {
 
                if ast_definition.identifier.value.as_str() != name {
 
                    continue;
 
                }
 

	
 
                // Found struct with the same name
 
                let definition_id = ast_definition.this.upcast();
 
                let type_entry = self.types.get_base_definition(&definition_id).unwrap();
 
                let type_definition = type_entry.definition.as_struct();
 

	
 
                let tester = StructTester::new(self.ctx(), ast_definition, type_definition);
 
                f(tester);
 
                found = true;
 
                break
 
            }
 
        }
 

	
 
        assert!(
 
            found, "[{}] Failed to find definition for struct '{}'",
 
            self.test_name, name
 
        );
 
        self
 
    }
 

	
 
    pub(crate) fn for_enum<F: Fn(EnumTester)>(self, name: &str, f: F) -> Self {
 
        let mut found = false;
 
        for definition in self.heap.definitions.iter() {
 
            if let Definition::Enum(definition) = definition {
 
                if definition.identifier.value.as_str() != name {
 
                    continue;
 
                }
 

	
 
                // Found enum with the same name
 
                let tester = EnumTester::new(self.ctx(), definition);
 
                f(tester);
 
                found = true;
 
                break;
 
            }
 
        }
 

	
 
        assert!(
 
            found, "[{}] Failed to find definition for enum '{}'",
 
            self.test_name, name
 
        );
 
        self
 
    }
 

	
 
    pub(crate) fn for_union<F: Fn(UnionTester)>(self, name: &str, f: F) -> Self {
 
        let mut found = false;
 
        for definition in self.heap.definitions.iter() {
 
            if let Definition::Union(definition) = definition {
 
                if definition.identifier.value.as_str() != name {
 
                    continue;
 
                }
 

	
 
                // Found union with the same name
 
                let definition_id = definition.this.upcast();
 
                let base_type = self.types.get_base_definition(&definition_id).unwrap();
 
                let tester = UnionTester::new(self.ctx(), definition, &base_type.definition.as_union());
 
                f(tester);
 
                found = true;
 
                break;
 
            }
 
        }
 

	
 
        assert!(
 
            found, "[{}] Failed to find definition for union '{}'",
 
            self.test_name, name
 
        );
 
        self
 
    }
 

	
 
    pub(crate) fn for_function<F: FnOnce(FunctionTester)>(self, name: &str, f: F) -> Self {
 
        let mut found = false;
 
        for definition in self.heap.definitions.iter() {
 
            if let Definition::Procedure(definition) = definition {
 
                if definition.identifier.value.as_str() != name {
 
                    continue;
 
                }
 

	
 
                // Found function
 
                let tester = FunctionTester::new(self.ctx(), definition);
 
                f(tester);
 
                found = true;
 
                break;
 
            }
 
        }
 

	
 
        if found { return self }
 

	
 
        assert!(
 
            false, "[{}] failed to find definition for function '{}'",
 
            self.test_name, name
 
        );
 
        unreachable!();
 
    }
 

	
 
    fn ctx(&self) -> TestCtx {
 
        TestCtx{
 
            test_name: &self.test_name,
 
            modules: &self.modules,
 
            heap: &self.heap,
 
            types: &self.types,
 
            symbols: &self.symbols,
 
        }
 
    }
 
}
 

	
 
//------------------------------------------------------------------------------
 
// Utilities for successful compilation
 
//------------------------------------------------------------------------------
 

	
 
pub(crate) struct StructTester<'a> {
 
    ctx: TestCtx<'a>,
 
    ast_def: &'a StructDefinition,
 
    type_def: &'a StructType,
 
}
 

	
 
impl<'a> StructTester<'a> {
 
    fn new(ctx: TestCtx<'a>, ast_def: &'a StructDefinition, type_def: &'a StructType) -> Self {
 
        Self{ ctx, ast_def, type_def }
 
    }
 

	
 
    pub(crate) fn assert_num_fields(self, num: usize) -> Self {
 
        assert_eq!(
 
            num, self.ast_def.fields.len(),
 
            "[{}] Expected {} struct fields, but found {} for {}",
 
            self.ctx.test_name, num, self.ast_def.fields.len(), self.assert_postfix()
 
        );
 
        self
 
    }
 

	
 
    pub(crate) fn assert_num_monomorphs(self, num: usize) -> Self {
 
        let (is_equal, num_encountered) = has_equal_num_monomorphs(self.ctx, num, self.ast_def.this.upcast());
 
        assert!(
 
            is_equal, "[{}] Expected {} monomorphs, but got {} for {}",
 
            self.ctx.test_name, num, num_encountered, self.assert_postfix()
 
        );
 
        self
 
    }
 

	
 
    pub(crate) fn assert_has_monomorph(self, serialized_monomorph: &str) -> Self {
 
        let (has_monomorph, serialized) = has_monomorph(self.ctx, self.ast_def.this.upcast(), serialized_monomorph);
 
        assert!(
 
            has_monomorph.is_some(), "[{}] Expected to find monomorph {}, but got {} for {}",
 
            self.ctx.test_name, serialized_monomorph, &serialized, self.assert_postfix()
 
        );
 
        self
 
    }
 

	
 
    pub(crate) fn assert_size_alignment(mut self, monomorph: &str, size: usize, alignment: usize) -> Self {
 
        self = self.assert_has_monomorph(monomorph);
 
        let (mono_idx, _) = has_monomorph(self.ctx, self.ast_def.this.upcast(), monomorph);
 
        let type_id = mono_idx.unwrap();
 
        let mono = self.ctx.types.get_monomorph(type_id);
 

	
 
        assert!(
 
            mono.size == size && mono.alignment == alignment,
 
            "[{}] Expected (size,alignment) of ({}, {}), but got ({}, {}) for {}",
 
            self.ctx.test_name, size, alignment, mono.size, mono.alignment, self.assert_postfix()
 
        );
 
        self
 
    }
 

	
 
    pub(crate) fn for_field<F: Fn(StructFieldTester)>(self, name: &str, f: F) -> Self {
 
        // Find field with specified name
 
        for field in &self.ast_def.fields {
 
            if field.field.value.as_str() == name {
 
                let tester = StructFieldTester::new(self.ctx, field);
 
                f(tester);
 
                return self;
 
            }
 
        }
 

	
 
        assert!(
 
            false, "[{}] Could not find struct field '{}' for {}",
 
            self.ctx.test_name, name, self.assert_postfix()
 
        );
 
        unreachable!();
 
    }
 

	
 
    fn assert_postfix(&self) -> String {
 
        let mut v = String::new();
 
        v.push_str("Struct{ name: ");
 
        v.push_str(self.ast_def.identifier.value.as_str());
 
        v.push_str(", fields: [");
 
        for (field_idx, field) in self.ast_def.fields.iter().enumerate() {
 
            if field_idx != 0 { v.push_str(", "); }
 
            v.push_str(field.field.value.as_str());
 
        }
 
        v.push_str("] }");
 
        v
 
    }
 
}
 

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

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

	
 
    pub(crate) fn assert_parser_type(self, expected: &str) -> Self {
 
        let mut serialized_type = String::new();
 
        serialize_parser_type(&mut serialized_type, &self.ctx.heap, &self.def.parser_type);
 
        assert_eq!(
 
            expected, &serialized_type,
 
            "[{}] Expected type '{}', but got '{}' for {}",
 
            self.ctx.test_name, expected, &serialized_type, self.assert_postfix()
 
        );
 
        self
 
    }
 

	
 
    fn assert_postfix(&self) -> String {
 
        let mut serialized_type = String::new();
 
        serialize_parser_type(&mut serialized_type, &self.ctx.heap, &self.def.parser_type);
 
        format!("StructField{{ name: {}, parser_type: {} }}", self.def.field.value.as_str(), serialized_type)
 
    }
 
}
 

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

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

	
 
    pub(crate) fn assert_num_variants(self, num: usize) -> Self {
 
        assert_eq!(
 
            num, self.def.variants.len(),
 
            "[{}] Expected {} enum variants, but found {} for {}",
 
            self.ctx.test_name, num, self.def.variants.len(), self.assert_postfix()
 
        );
 
        self
 
    }
 

	
 
    pub(crate) fn assert_num_monomorphs(self, num: usize) -> Self {
 
        let (is_equal, num_encountered) = has_equal_num_monomorphs(self.ctx, num, self.def.this.upcast());
 
        assert!(
 
            is_equal, "[{}] Expected {} monomorphs, but got {} for {}",
 
            self.ctx.test_name, num, num_encountered, self.assert_postfix()
 
        );
 
        self
 
    }
 

	
 
    pub(crate) fn assert_has_monomorph(self, serialized_monomorph: &str) -> Self {
 
        let (has_monomorph, serialized) = has_monomorph(self.ctx, self.def.this.upcast(), serialized_monomorph);
 
        assert!(
 
            has_monomorph.is_some(), "[{}] Expected to find monomorph {}, but got {} for {}",
 
            self.ctx.test_name, serialized_monomorph, serialized, self.assert_postfix()
 
        );
 
        self
 
    }
 

	
 
    pub(crate) fn assert_size_alignment(mut self, serialized_monomorph: &str, size: usize, alignment: usize) -> Self {
 
        self = self.assert_has_monomorph(serialized_monomorph);
 
        let (has_monomorph, _) = has_monomorph(self.ctx, self.def.this.upcast(), serialized_monomorph);
 
        let mono_index = has_monomorph.unwrap();
 
        let mono = self.ctx.types.get_monomorph(mono_index);
 

	
 
        assert!(
 
            mono.size == size && mono.alignment == alignment,
 
            "[{}] Expected (size,alignment) of ({}, {}), but got ({}, {}) for {}",
 
            self.ctx.test_name, size, alignment, mono.size, mono.alignment, self.assert_postfix()
 
        );
 
        self
 
    }
 

	
 
    pub(crate) fn assert_postfix(&self) -> String {
 
        let mut v = String::new();
 
        v.push_str("Enum{ name: ");
 
        v.push_str(self.def.identifier.value.as_str());
 
        v.push_str(", variants: [");
 
        for (variant_idx, variant) in self.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 UnionTester<'a> {
 
    ctx: TestCtx<'a>,
 
    ast_def: &'a UnionDefinition,
 
    type_def: &'a UnionType,
 
}
 

	
 
impl<'a> UnionTester<'a> {
 
    fn new(ctx: TestCtx<'a>, ast_def: &'a UnionDefinition, type_def: &'a UnionType) -> Self {
 
        Self{ ctx, ast_def, type_def }
 
    }
 

	
 
    pub(crate) fn assert_num_variants(self, num: usize) -> Self {
 
        assert_eq!(
 
            num, self.ast_def.variants.len(),
 
            "[{}] Expected {} union variants, but found {} for {}",
 
            self.ctx.test_name, num, self.ast_def.variants.len(), self.assert_postfix()
 
        );
 
        self
 
    }
 

	
 
    pub(crate) fn assert_num_monomorphs(self, num: usize) -> Self {
 
        let (is_equal, num_encountered) = has_equal_num_monomorphs(self.ctx, num, self.ast_def.this.upcast());
 
        assert!(
 
            is_equal, "[{}] Expected {} monomorphs, but got {} for {}",
 
            self.ctx.test_name, num, num_encountered, self.assert_postfix()
 
        );
 
        self
 
    }
 

	
 
    pub(crate) fn assert_has_monomorph(self, serialized_monomorph: &str) -> Self {
 
        let (has_monomorph, serialized) = has_monomorph(self.ctx, self.ast_def.this.upcast(), serialized_monomorph);
 
        assert!(
 
            has_monomorph.is_some(), "[{}] Expected to find monomorph {}, but got {} for {}",
 
            self.ctx.test_name, serialized_monomorph, serialized, self.assert_postfix()
 
        );
 
        self
 
    }
 

	
 
    pub(crate) fn assert_size_alignment(
 
        mut self, serialized_monomorph: &str,
 
        stack_size: usize, stack_alignment: usize, heap_size: usize, heap_alignment: usize
 
    ) -> Self {
 
        self = self.assert_has_monomorph(serialized_monomorph);
 
        let (mono_idx, _) = has_monomorph(self.ctx, self.ast_def.this.upcast(), serialized_monomorph);
 
        let mono_idx = mono_idx.unwrap();
 
        let mono_base = self.ctx.types.get_monomorph(mono_idx);
 
        let mono_union = mono_base.variant.as_union();
 

	
 
        assert!(
 
            stack_size == mono_base.size && stack_alignment == mono_base.alignment &&
 
                heap_size == mono_union.heap_size && heap_alignment == mono_union.heap_alignment,
 
            "[{}] Expected (stack | heap) (size, alignment) of ({}, {} | {}, {}), but got ({}, {} | {}, {}) for {}",
 
            self.ctx.test_name,
 
            stack_size, stack_alignment, heap_size, heap_alignment,
 
            mono_base.size, mono_base.alignment, mono_union.heap_size, mono_union.heap_alignment,
 
            self.assert_postfix()
 
        );
 
        self
 
    }
 

	
 
    fn assert_postfix(&self) -> String {
 
        let mut v = String::new();
 
        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 ProcedureDefinition,
 
}
 

	
 
impl<'a> FunctionTester<'a> {
 
    fn new(ctx: TestCtx<'a>, def: &'a ProcedureDefinition) -> 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_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(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
 
            }
 
        );
 

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

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

	
 
        // Construct tester and pass to tester function
 
        let tester = VariableTester::new(
 
            self.ctx, self.def.this.upcast(), local,
 
            var_expr.as_variable()
 
        );
 

	
 
        f(tester);
 

	
 
        self
 
    }
 

	
 
    /// Finds a specific expression within a function. There are two matchers:
 
    /// one outer matcher (to find a rough indication of the expression) and an
 
    /// inner matcher to find the exact expression. 
 
    ///
 
    /// The reason being that, for example, a function's body might be littered
 
    /// with addition symbols, so we first match on "some_var + some_other_var",
 
    /// and then match exactly on "+".
 
    pub(crate) fn for_expression_by_source<F: Fn(ExpressionTester)>(self, outer_match: &str, inner_match: &str, f: F) -> Self {
 
        // Seek the expression in the source code
 
        assert!(outer_match.contains(inner_match), "improper testing code");
 

	
 
        let module = seek_def_in_modules(
 
            &self.ctx.heap, &self.ctx.modules, self.def.this.upcast()
 
        ).unwrap();
 

	
 
        // Find the first occurrence of the expression after the definition of
 
        // the function, we'll check that it is included in the body later.
 
        let body = &self.ctx.heap[self.def.body];
 
        let mut outer_match_idx = body.span.begin.offset as usize;
 
        while outer_match_idx < module.source.input.len() {
 
            if module.source.input[outer_match_idx..].starts_with(outer_match.as_bytes()) {
 
                break;
 
            }
 
            outer_match_idx += 1
 
        }
 

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

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

	
 
        // We have the expression, call the testing function
 
        let tester = ExpressionTester::new(
 
            self.ctx, self.def.this.upcast(), &self.ctx.heap[expr_id]
 
        );
 
        f(tester);
 

	
 
        self
 
    }
 

	
 
    pub(crate) fn call_ok(self, expected_result: Option<Value>) -> Self {
 
        use crate::protocol::*;
 

	
 
        let (prompt, result) = self.eval_until_end();
 
        match result {
 
            Ok(_) => {
 
                assert!(
 
                    prompt.store.stack.len() > 0, // note: stack never shrinks
 
                    "[{}] No value on stack after calling function for {}",
 
                    self.ctx.test_name, self.assert_postfix()
 
                );
 
            },
 
            Err(err) => {
 
                println!("DEBUG: Formatted evaluation error:\n{}", err);
 
                assert!(
 
                    false,
 
                    "[{}] Expected call to succeed, but got {:?} for {}",
 
                    self.ctx.test_name, err, self.assert_postfix()
 
                )
 
            }
 
        }
 

	
 
        if let Some(expected_result) = expected_result {
 
            debug_assert!(expected_result.get_heap_pos().is_none(), "comparing against heap thingamajigs is not yet implemented");
 
            assert!(
 
                value::apply_equality_operator(&prompt.store, &prompt.store.stack[0], &expected_result),
 
                "[{}] Result from call was {:?}, but expected {:?} for {}",
 
                self.ctx.test_name, &prompt.store.stack[0], &expected_result, self.assert_postfix()
 
            )
 
        }
 

	
 
        self
 
    }
 

	
 
    // Keeping this simple for now, will likely change
 
    pub(crate) fn call_err(self, expected_result: &str) -> Self {
 
        let (_, result) = self.eval_until_end();
 
        match result {
 
            Ok(_) => {
 
                assert!(
 
                    false,
 
                    "[{}] Expected an error, but evaluation finished successfully for {}",
 
                    self.ctx.test_name, self.assert_postfix()
 
                );
 
            },
 
            Err(err) => {
 
                println!("DEBUG: Formatted evaluation error:\n{}", err);
 
                debug_assert_eq!(err.statements.len(), 1);
 
                assert!(
 
                    err.statements[0].message.contains(&expected_result),
 
                    "[{}] Expected error message to contain '{}', but it was '{}' for {}",
 
                    self.ctx.test_name, expected_result, err.statements[0].message, self.assert_postfix()
 
                );
 
            }
 
        }
 

	
 
        self
 
    }
 

	
 
    fn eval_until_end(&self) -> (Prompt, Result<EvalContinuation, EvalError>) {
 
        use crate::protocol::*;
 

	
 
        // Assuming the function is not polymorphic
 
        let definition_id = self.def.this;
 
        let func_type = [ConcreteTypePart::Function(definition_id, 0)];
 
        let mono_index = self.ctx.types.get_procedure_monomorph_type_id(&definition_id.upcast(), &func_type).unwrap();
 
        let mono_index = self.ctx.types.get_monomorph_type_id(&definition_id.upcast(), &func_type).unwrap();
 

	
 
        let mut prompt = Prompt::new(&self.ctx.types, &self.ctx.heap, definition_id, mono_index, ValueGroup::new_stack(Vec::new()));
 
        let mut call_context = FakeRunContext{};
 
        loop {
 
            let result = prompt.step(&self.ctx.types, &self.ctx.heap, &self.ctx.modules, &mut call_context);
 
            match result {
 
                Ok(EvalContinuation::Stepping) => {},
 
                _ => return (prompt, result),
 
            }
 
        }
 
    }
 

	
 
    fn assert_postfix(&self) -> String {
 
        format!("Function{{ name: {} }}", self.def.identifier.value.as_str())
 
    }
 
}
 

	
 
pub(crate) struct VariableTester<'a> {
 
    ctx: TestCtx<'a>,
 
    definition_id: DefinitionId,
 
    variable: &'a Variable,
 
    var_expr: &'a VariableExpression,
 
}
 

	
 
impl<'a> VariableTester<'a> {
 
    fn new(
 
        ctx: TestCtx<'a>, definition_id: DefinitionId, variable: &'a Variable, var_expr: &'a VariableExpression
 
    ) -> Self {
 
        Self{ ctx, definition_id, variable, var_expr }
 
    }
 

	
 
    pub(crate) fn assert_parser_type(self, expected: &str) -> Self {
 
        let mut serialized = String::new();
 
        serialize_parser_type(&mut serialized, self.ctx.heap, &self.variable.parser_type);
 

	
 
        assert_eq!(
 
            expected, &serialized,
 
            "[{}] Expected parser type '{}', but got '{}' for {}",
 
            self.ctx.test_name, expected, &serialized, self.assert_postfix()
 
        );
 
        self
 
    }
 

	
 
    pub(crate) fn assert_concrete_type(self, expected: &str) -> Self {
 
        // Lookup concrete type in type table
 
        let mono_proc = get_procedure_monomorph(&self.ctx.heap, &self.ctx.types, self.definition_id);
 
        let mono_index = mono_proc.monomorph_index;
 
        let mono_data = &self.ctx.heap[self.definition_id].as_procedure().monomorphs[mono_index as usize];
 
        let expr_info = &mono_data.expr_info[self.var_expr.type_index as usize];
 
        let concrete_type = &self.ctx.types.get_monomorph(expr_info.type_id).concrete_type;
 

	
 
        // Serialize and check
 
        let serialized = concrete_type.display_name(self.ctx.heap);
 

	
 
        assert_eq!(
 
            expected, &serialized,
 
            "[{}] Expected concrete type '{}', but got '{}' for {}",
 
            self.ctx.test_name, expected, &serialized, self.assert_postfix()
 
        );
 
        self
 
    }
 

	
 
    fn assert_postfix(&self) -> String {
 
        format!("Variable{{ name: {} }}", self.variable.identifier.value.as_str())
 
    }
 
}
 

	
 
pub(crate) struct ExpressionTester<'a> {
 
    ctx: TestCtx<'a>,
 
    definition_id: DefinitionId, // of the enclosing function/component
 
    expr: &'a Expression
 
}
 

	
 
impl<'a> ExpressionTester<'a> {
 
    fn new(
 
        ctx: TestCtx<'a>, definition_id: DefinitionId, expr: &'a Expression
 
    ) -> Self {
 
        Self{ ctx, definition_id, expr }
 
    }
 

	
 
    pub(crate) fn assert_concrete_type(self, expected: &str) -> Self {
 
        // Lookup concrete type
 
        let mono_proc = get_procedure_monomorph(&self.ctx.heap, &self.ctx.types, self.definition_id);
 
        let mono_index = mono_proc.monomorph_index;
 
        let mono_data = &self.ctx.heap[self.definition_id].as_procedure().monomorphs[mono_index as usize];
 
        let expr_info = &mono_data.expr_info[self.expr.type_index() as usize];
 
        let concrete_type = &self.ctx.types.get_monomorph(expr_info.type_id).concrete_type;
 

	
 
        // Serialize and check type
 
        let serialized = concrete_type.display_name(self.ctx.heap);
 

	
 
        assert_eq!(
 
            expected, &serialized,
 
            "[{}] Expected concrete type '{}', but got '{}' for {}",
 
            self.ctx.test_name, expected, &serialized, self.assert_postfix()
 
        );
 
        self
 
    }
 

	
 
    fn assert_postfix(&self) -> String {
 
        format!(
 
            "Expression{{ debug: {:?} }}",
 
            self.expr
 
        )
 
    }
 
}
 

	
 
fn get_procedure_monomorph<'a>(heap: &Heap, types: &'a TypeTable, definition_id: DefinitionId) -> &'a ProcedureMonomorph {
 
    let ast_definition = heap[definition_id].as_procedure();
 
    let func_type = if ast_definition.kind == ProcedureKind::Function {
 
        [ConcreteTypePart::Function(ast_definition.this, 0)]
 
    } else {
 
        [ConcreteTypePart::Component(ast_definition.this, 0)]
 
    };
 

	
 
    let mono_index = types.get_procedure_monomorph_type_id(&definition_id, &func_type).unwrap();
 
    let mono_index = types.get_monomorph_type_id(&definition_id, &func_type).unwrap();
 
    let mono_data = types.get_monomorph(mono_index).variant.as_procedure();
 

	
 
    mono_data
 
}
 

	
 
//------------------------------------------------------------------------------
 
// Interface for failed compilation
 
//------------------------------------------------------------------------------
 

	
 
pub(crate) struct AstErrTester {
 
    test_name: String,
 
    error: ParseError,
 
}
 

	
 
impl AstErrTester {
 
    fn new(test_name: String, error: ParseError) -> Self {
 
        Self{ test_name, error }
 
    }
 

	
 
    pub(crate) fn error<F: Fn(ErrorTester)>(&self, f: F) {
 
        // Maybe multiple errors will be supported in the future
 
        let tester = ErrorTester{ test_name: &self.test_name, error: &self.error };
 
        f(tester)
 
    }
 
}
 

	
 
//------------------------------------------------------------------------------
 
// Utilities for failed compilation
 
//------------------------------------------------------------------------------
 

	
 
pub(crate) struct ErrorTester<'a> {
 
    test_name: &'a str,
 
    error: &'a ParseError,
 
}
 

	
 
impl<'a> ErrorTester<'a> {
 
    pub(crate) fn assert_num(self, num: usize) -> Self {
 
        assert_eq!(
 
            num, self.error.statements.len(),
 
            "[{}] expected error to consist of '{}' parts, but encountered '{}' for {}",
 
            self.test_name, num, self.error.statements.len(), self.assert_postfix()
 
        );
 

	
 
        self
 
    }
 

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

	
 
        self
 
    }
 

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

	
 
        self
 
    }
 

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

	
 
        self
 
    }
 

	
 
    fn assert_postfix(&self) -> String {
 
        let mut v = String::new();
 
        v.push_str("error: [");
 
        for (idx, stmt) in self.error.statements.iter().enumerate() {
 
            if idx != 0 {
 
                v.push_str(", ");
 
            }
 

	
 
            v.push_str(&format!("{{ context: {}, message: {} }}", &stmt.context, stmt.message));
 
        }
 
        v.push(']');
 
        v
 
    }
 
}
 

	
 
//------------------------------------------------------------------------------
 
// Generic utilities
 
//------------------------------------------------------------------------------
 

	
 
fn has_equal_num_monomorphs(ctx: TestCtx, num: usize, definition_id: DefinitionId) -> (bool, usize) {
 
    // Again: inefficient, but its testing code
 
    let mut num_on_type = 0;
 

	
 
    for mono in &ctx.types.mono_types {
 
        match &mono.concrete_type.parts[0] {
 
            ConcreteTypePart::Instance(def_id, _) => {
 
                if *def_id == definition_id {
 
                    num_on_type += 1;
 
                }
 
            }
 
            ConcreteTypePart::Function(def_id, _) |
 
            ConcreteTypePart::Component(def_id, _) => {
 
                if def_id.upcast() == definition_id {
 
                    num_on_type += 1;
 
                }
 
            },
 
            _ => {},
 
        };
 
    }
 

	
 
    (num_on_type == num, num_on_type)
 
}
 

	
 
fn has_monomorph(ctx: TestCtx, definition_id: DefinitionId, serialized_monomorph: &str) -> (Option<TypeId>, String) {
 
    // Note: full_buffer is just for error reporting
 
    let mut full_buffer = String::new();
 
    let mut has_match = None;
 

	
 
    full_buffer.push('[');
 
    let mut append_to_full_buffer = |concrete_type: &ConcreteType, type_id: TypeId| {
 
        if full_buffer.len() != 1 {
 
            full_buffer.push_str(", ");
 
        }
 
        full_buffer.push('"');
 

	
 
        let first_idx = full_buffer.len();
 
        full_buffer.push_str(concrete_type.display_name(ctx.heap).as_str());
 
        if &full_buffer[first_idx..] == serialized_monomorph {
 
            has_match = Some(type_id);
 
        }
 

	
 
        full_buffer.push('"');
 
    };
 

	
 
    // Bit wasteful, but this is (temporary?) testing code:
 
    for (_mono_idx, mono) in ctx.types.mono_types.iter().enumerate() {
 
        let got_definition_id = match &mono.concrete_type.parts[0] {
 
            ConcreteTypePart::Instance(v, _) => *v,
 
            ConcreteTypePart::Function(v, _) |
 
            ConcreteTypePart::Component(v, _) => v.upcast(),
 
            _ => DefinitionId::new_invalid(),
 
        };
 
        if got_definition_id == definition_id {
 
            append_to_full_buffer(&mono.concrete_type, mono.type_id);
 
        }
 
    }
 

	
 
    full_buffer.push(']');
 

	
 
    (has_match, full_buffer)
 
}
 

	
 
fn serialize_parser_type(buffer: &mut String, heap: &Heap, parser_type: &ParserType) {
 
    use ParserTypeVariant as PTV;
 

	
 
    fn serialize_variant(buffer: &mut String, heap: &Heap, parser_type: &ParserType, mut idx: usize) -> usize {
 
        match &parser_type.elements[idx].variant {
 
            PTV::Void => buffer.push_str("void"),
 
            PTV::InputOrOutput => {
 
                buffer.push_str("portlike<");
 
                idx = serialize_variant(buffer, heap, parser_type, idx + 1);
 
                buffer.push('>');
 
            },
 
            PTV::ArrayLike => {
 
                idx = serialize_variant(buffer, heap, parser_type, idx + 1);
 
                buffer.push_str("[???]");
 
            },
 
            PTV::IntegerLike => buffer.push_str("integerlike"),
 
            PTV::Message => buffer.push_str(KW_TYPE_MESSAGE_STR),
 
            PTV::Bool => buffer.push_str(KW_TYPE_BOOL_STR),
 
            PTV::UInt8 => buffer.push_str(KW_TYPE_UINT8_STR),
 
            PTV::UInt16 => buffer.push_str(KW_TYPE_UINT16_STR),
 
            PTV::UInt32 => buffer.push_str(KW_TYPE_UINT32_STR),
 
            PTV::UInt64 => buffer.push_str(KW_TYPE_UINT64_STR),
 
            PTV::SInt8 => buffer.push_str(KW_TYPE_SINT8_STR),
 
            PTV::SInt16 => buffer.push_str(KW_TYPE_SINT16_STR),
 
            PTV::SInt32 => buffer.push_str(KW_TYPE_SINT32_STR),
 
            PTV::SInt64 => buffer.push_str(KW_TYPE_SINT64_STR),
 
            PTV::Character => buffer.push_str(KW_TYPE_CHAR_STR),
 
            PTV::String => buffer.push_str(KW_TYPE_STRING_STR),
 
            PTV::IntegerLiteral => buffer.push_str("int_literal"),
 
            PTV::Inferred => buffer.push_str(KW_TYPE_INFERRED_STR),
 
            PTV::Array => {
 
                idx = serialize_variant(buffer, heap, parser_type, idx + 1);
 
                buffer.push_str("[]");
 
            },
 
            PTV::Input => {
 
                buffer.push_str(KW_TYPE_IN_PORT_STR);
 
                buffer.push('<');
 
                idx = serialize_variant(buffer, heap, parser_type, idx + 1);
 
                buffer.push('>');
 
            },
 
            PTV::Output => {
 
                buffer.push_str(KW_TYPE_OUT_PORT_STR);
 
                buffer.push('<');
 
                idx = serialize_variant(buffer, heap, parser_type, idx + 1);
 
                buffer.push('>');
 
            },
 
            PTV::Tuple(num_embedded) => {
 
                buffer.push('(');
 
                for embedded_idx in 0..*num_embedded {
 
                    if embedded_idx != 0 {
 
                        buffer.push(',');
 
                    }
 
                    idx = serialize_variant(buffer, heap, parser_type, idx + 1);
 
                }
 
                buffer.push(')');
 
            },
 
            PTV::PolymorphicArgument(definition_id, poly_idx) => {
 
                let definition = &heap[*definition_id];
 
                let poly_arg = &definition.poly_vars()[*poly_idx as usize];
 
                buffer.push_str(poly_arg.value.as_str());
 
            },
 
            PTV::Definition(definition_id, num_embedded) => {
 
                let definition = &heap[*definition_id];
 
                buffer.push_str(definition.identifier().value.as_str());
 

	
 
                let num_embedded = *num_embedded;
 
                if num_embedded != 0 {
 
                    buffer.push('<');
 
                    for embedded_idx in 0..num_embedded {
 
                        if embedded_idx != 0 {
 
                            buffer.push(',');
 
                        }
 
                        idx = serialize_variant(buffer, heap, parser_type, idx + 1);
 
                    }
 
                    buffer.push('>');
 
                }
 
            }
 
        }
 

	
 
        idx
 
    }
 

	
 
    serialize_variant(buffer, heap, parser_type, 0);
 
}
 

	
 
fn seek_def_in_modules<'a>(heap: &Heap, modules: &'a [Module], def_id: DefinitionId) -> Option<&'a Module> {
 
    for module in modules {
 
        let root = &heap.protocol_descriptions[module.root_id];
 
        for definition in &root.definitions {
 
            if *definition == def_id {
 
                return Some(module)
 
            }
 
        }
 
    }
 

	
 
    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_case.body, f) {
 
                return Some(id);
 
            } 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, 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))
 
            .or_else(|| seek_expr_in_expr(heap, expr.right, f))
 
        },
 
        Expression::Unary(expr) => {
 
            seek_expr_in_expr(heap, expr.expression, f)
 
        },
 
        Expression::Indexing(expr) => {
 
            None
 
            .or_else(|| seek_expr_in_expr(heap, expr.subject, f))
 
            .or_else(|| seek_expr_in_expr(heap, expr.index, f))
 
        },
 
        Expression::Slicing(expr) => {
 
            None
 
            .or_else(|| seek_expr_in_expr(heap, expr.subject, f))
 
            .or_else(|| seek_expr_in_expr(heap, expr.from_index, f))
 
            .or_else(|| seek_expr_in_expr(heap, expr.to_index, f))
 
        },
 
        Expression::Select(expr) => {
 
            seek_expr_in_expr(heap, expr.subject, f)
 
        },
 
        Expression::Literal(expr) => {
 
            if let Literal::Struct(lit) = &expr.value {
 
                for field in &lit.fields {
 
                    if let Some(id) = seek_expr_in_expr(heap, field.value, f) {
 
                        return Some(id)
 
                    }
 
                }
 
            } else if let Literal::Array(elements) = &expr.value {
 
                for element in elements {
 
                    if let Some(id) = seek_expr_in_expr(heap, *element, f) {
 
                        return Some(id)
 
                    }
 
                }
 
            }
 
            None
 
        },
 
        Expression::Cast(expr) => {
 
            seek_expr_in_expr(heap, expr.subject, f)
 
        }
 
        Expression::Call(expr) => {
 
            for arg in &expr.arguments {
 
                if let Some(id) = seek_expr_in_expr(heap, *arg, f) {
 
                    return Some(id)
 
                }
 
            }
 
            None
 
        },
 
        Expression::Variable(_expr) => {
 
            None
 
        }
 
    }
 
}
 

	
 
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_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, f))
 
        },
 
        Statement::Synchronous(stmt) => {
 
            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!() }
 
    fn performed_get(&mut self, _port: PortId) -> Option<ValueGroup> { unreachable!() }
 
    fn fires(&mut self, _port: PortId) -> Option<Value> { unreachable!() }
 
    fn performed_fork(&mut self) -> Option<bool> { unreachable!() }
 
    fn created_channel(&mut self) -> Option<(Value, Value)> { unreachable!() }
 
    fn performed_select_wait(&mut self) -> Option<u32> { unreachable!() }
 
}
 
\ No newline at end of file
src/runtime2/component/component.rs
Show inline comments
 
use crate::protocol::eval::{Prompt, EvalError, ValueGroup, PortId as EvalPortId};
 
use crate::protocol::*;
 
use crate::runtime2::*;
 
use crate::runtime2::communication::*;
 

	
 
use super::{CompCtx, CompPDL};
 
use super::component_context::*;
 
use super::component_random::*;
 
use super::control_layer::*;
 
use super::consensus::*;
 

	
 
pub enum CompScheduling {
 
    Immediate,
 
    Requeue,
 
    Sleep,
 
    Exit,
 
}
 

	
 
/// Generic representation of a component (as viewed by a scheduler).
 
pub(crate) trait Component {
 
    /// Called upon the creation of the component.
 
    fn on_creation(&mut self, sched_ctx: &SchedulerCtx);
 

	
 
    /// Called if the component is created by another component and the messages
 
    /// are being transferred between the two.
 
    fn adopt_message(&mut self, comp_ctx: &mut CompCtx, message: DataMessage);
 

	
 
    /// Called if the component receives a new message. The component is
 
    /// responsible for deciding where that messages goes.
 
    fn handle_message(&mut self, sched_ctx: &mut SchedulerCtx, comp_ctx: &mut CompCtx, message: Message);
 

	
 
    /// Called if the component's routine should be executed. The return value
 
    /// can be used to indicate when the routine should be run again.
 
    fn run(&mut self, sched_ctx: &mut SchedulerCtx, comp_ctx: &mut CompCtx) -> Result<CompScheduling, EvalError>;
 
}
 

	
 
/// Representation of the generic operating mode of a component.
 
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
 
pub(crate) enum CompMode {
 
    NonSync, // not in sync mode
 
    Sync, // in sync mode, can interact with other components
 
    SyncEnd, // awaiting a solution, i.e. encountered the end of the sync block
 
    BlockedGet, // blocked because we need to receive a message on a particular port
 
    BlockedPut, // component is blocked because the port is blocked
 
    BlockedSelect, // waiting on message to complete the select statement
 
    StartExit, // temporary state: if encountered then we start the shutdown process
 
    BusyExit, // temporary state: waiting for Acks for all the closed ports
 
    Exit, // exiting: shutdown process started, now waiting until the reference count drops to 0
 
}
 

	
 
impl CompMode {
 
    pub(crate) fn is_in_sync_block(&self) -> bool {
 
        use CompMode::*;
 

	
 
        match self {
 
            Sync | SyncEnd | BlockedGet | BlockedPut | BlockedSelect => true,
 
            NonSync | StartExit | BusyExit | Exit => false,
 
        }
 
    }
 
}
 

	
 
/// Component execution state: the execution mode along with some descriptive
 
/// fields. Fields are public for ergonomic reasons, use member functions when
 
/// appropriate.
 
pub(crate) struct CompExecState {
 
    pub mode: CompMode,
 
    pub mode_port: PortId, // valid if blocked on a port (put/get)
 
    pub mode_value: ValueGroup, // valid if blocked on a put
 
}
 

	
 
impl CompExecState {
 
    pub(crate) fn new() -> Self {
 
        return Self{
 
            mode: CompMode::NonSync,
 
            mode_port: PortId::new_invalid(),
 
            mode_value: ValueGroup::default(),
 
        }
 
    }
 

	
 
    pub(crate) fn set_as_blocked_get(&mut self, port: PortId) {
 
        self.mode = CompMode::BlockedGet;
 
        self.mode_port = port;
 
        debug_assert!(self.mode_value.values.is_empty());
 
    }
 

	
 
    pub(crate) fn is_blocked_on_get(&self, port: PortId) -> bool {
 
        return
 
            self.mode == CompMode::BlockedGet &&
 
            self.mode_port == port;
 
    }
 

	
 
    pub(crate) fn set_as_blocked_put(&mut self, port: PortId, value: ValueGroup) {
 
        self.mode = CompMode::BlockedPut;
 
        self.mode_port = port;
 
        self.mode_value = value;
 
    }
 

	
 
    pub(crate) fn is_blocked_on_put(&self, port: PortId) -> bool {
 
        return
 
            self.mode == CompMode::BlockedPut &&
 
            self.mode_port == port;
 
    }
 
}
 

	
 
/// Creates a new component based on its definition. Meaning that if it is a
 
/// user-defined component then we set up the PDL code state. Otherwise we
 
/// construct a custom component. This does NOT take care of port and message
 
/// management.
 
pub(crate) fn create_component(
 
    protocol: &ProtocolDescription,
 
    definition_id: ProcedureDefinitionId, type_id: TypeId,
 
    arguments: ValueGroup, num_ports: usize
 
) -> Box<dyn Component> {
 
    let definition = &protocol.heap[definition_id];
 
    debug_assert!(definition.kind == ProcedureKind::Primitive || definition.kind == ProcedureKind::Composite);
 

	
 
    if definition.source.is_builtin() {
 
        // Builtin component
 
        let component = match definition.source {
 
            ProcedureSource::CompRandomU32 => Box::new(ComponentRandomU32::new(arguments)),
 
            _ => unreachable!(),
 
        };
 

	
 
        return component;
 
    } else {
 
        // User-defined component
 
        let prompt = Prompt::new(
 
            &protocol.types, &protocol.heap,
 
            definition_id, type_id, arguments
 
        );
 
        let component = CompPDL::new(prompt, num_ports);
 
        return Box::new(component);
 
    }
 
}
 

	
 
// -----------------------------------------------------------------------------
 
// Generic component messaging utilities (for sending and receiving)
 
// -----------------------------------------------------------------------------
 

	
 
/// Handles control messages in the default way. Note that this function may
 
/// take a lot of actions in the name of the caller: pending messages may be
 
/// sent, ports may become blocked/unblocked, etc. So the execution
 
/// (`CompExecState`), control (`ControlLayer`) and consensus (`Consensus`)
 
/// state may all change.
 
pub(crate) fn default_handle_control_message(
 
    exec_state: &mut CompExecState, control: &mut ControlLayer, consensus: &mut Consensus,
 
    message: ControlMessage, sched_ctx: &SchedulerCtx, comp_ctx: &mut CompCtx
 
) {
 
    match message.content {
 
        ControlMessageContent::Ack => {
 
            default_handle_ack(control, message.id, sched_ctx, comp_ctx);
 
        },
 
        ControlMessageContent::BlockPort(port_id) => {
 
            // One of our messages was accepted, but the port should be
 
            // blocked.
 
            let port_handle = comp_ctx.get_port_handle(port_id);
 
            let port_info = comp_ctx.get_port(port_handle);
 
            debug_assert_eq!(port_info.kind, PortKind::Putter);
 
            if port_info.state == PortState::Open {
 
                // only when open: we don't do this when closed, and we we don't do this if we're blocked due to peer changes
 
                comp_ctx.set_port_state(port_handle, PortState::BlockedDueToFullBuffers);
 
            }
 
        },
 
        ControlMessageContent::ClosePort(port_id) => {
 
            // Request to close the port. We immediately comply and remove
 
            // the component handle as well
 
            let port_handle = comp_ctx.get_port_handle(port_id);
 
            let peer_comp_id = comp_ctx.get_port(port_handle).peer_comp_id;
 
            let peer_handle = comp_ctx.get_peer_handle(peer_comp_id);
 

	
 
            // One exception to sending an `Ack` is if we just closed the
 
            // port ourselves, meaning that the `ClosePort` messages got
 
            // sent to one another.
 
            if let Some(control_id) = control.has_close_port_entry(port_handle, comp_ctx) {
 
                default_handle_ack(control, control_id, sched_ctx, comp_ctx);
 
            } else {
 
                default_send_ack(message.id, peer_handle, sched_ctx, comp_ctx);
 
                comp_ctx.remove_peer(sched_ctx, port_handle, peer_comp_id, false); // do not remove if closed
 
                comp_ctx.set_port_state(port_handle, PortState::Closed); // now set to closed
 
            }
 
        },
 
        ControlMessageContent::UnblockPort(port_id) => {
 
            // We were previously blocked (or already closed)
 
            let port_handle = comp_ctx.get_port_handle(port_id);
 
            let port_info = comp_ctx.get_port(port_handle);
 
            debug_assert_eq!(port_info.kind, PortKind::Putter);
 
            if port_info.state == PortState::BlockedDueToFullBuffers {
 
                default_handle_unblock_put(exec_state, consensus, port_handle, sched_ctx, comp_ctx);
 
            }
 
        },
 
        ControlMessageContent::PortPeerChangedBlock(port_id) => {
 
            // The peer of our port has just changed. So we are asked to
 
            // temporarily block the port (while our original recipient is
 
            // potentially rerouting some of the in-flight messages) and
 
            // Ack. Then we wait for the `unblock` call.
 
            debug_assert_eq!(message.target_port_id, Some(port_id));
 
            let port_handle = comp_ctx.get_port_handle(port_id);
 
            comp_ctx.set_port_state(port_handle, PortState::BlockedDueToPeerChange);
 

	
 
            let port_info = comp_ctx.get_port(port_handle);
 
            let peer_handle = comp_ctx.get_peer_handle(port_info.peer_comp_id);
 

	
 
            default_send_ack(message.id, peer_handle, sched_ctx, comp_ctx);
 
        },
 
        ControlMessageContent::PortPeerChangedUnblock(new_port_id, new_comp_id) => {
 
            let port_handle = comp_ctx.get_port_handle(message.target_port_id.unwrap());
 
            let port_info = comp_ctx.get_port(port_handle);
 
            debug_assert!(port_info.state == PortState::BlockedDueToPeerChange);
 
            let old_peer_id = port_info.peer_comp_id;
 

	
 
            comp_ctx.remove_peer(sched_ctx, port_handle, old_peer_id, false);
 

	
 
            let port_info = comp_ctx.get_port_mut(port_handle);
 
            port_info.peer_comp_id = new_comp_id;
 
            port_info.peer_port_id = new_port_id;
 
            comp_ctx.add_peer(port_handle, sched_ctx, new_comp_id, None);
 
            default_handle_unblock_put(exec_state, consensus, port_handle, sched_ctx, comp_ctx);
 
        }
 
    }
 
}
 

	
 
/// Handles a component initiating the exiting procedure, and closing all of its
 
/// ports. Should only be called once per component (which is ensured by
 
/// checking and modifying the mode in the execution state).
 
pub(crate) fn default_handle_start_exit(
 
    exec_state: &mut CompExecState, control: &mut ControlLayer,
 
    sched_ctx: &SchedulerCtx, comp_ctx: &mut CompCtx
 
) -> CompScheduling {
 
    debug_assert_eq!(exec_state.mode, CompMode::StartExit);
 
    sched_ctx.log("Component starting exit");
 
    exec_state.mode = CompMode::BusyExit;
 

	
 
    // Iterating by index to work around borrowing rules
 
    for port_index in 0..comp_ctx.num_ports() {
 
        let port = comp_ctx.get_port_by_index_mut(port_index);
 
        if port.state == PortState::Closed {
 
            // Already closed, or in the process of being closed
 
            continue;
 
        }
 

	
 
        // Mark as closed
 
        let port_id = port.self_id;
 
        port.state = PortState::Closed;
 

	
 
        // Notify peer of closing
 
        let port_handle = comp_ctx.get_port_handle(port_id);
 
        let (peer, message) = control.initiate_port_closing(port_handle, comp_ctx);
 
        let peer_info = comp_ctx.get_peer(peer);
 
        peer_info.handle.send_message(&sched_ctx.runtime, Message::Control(message), true);
 
    }
 

	
 
    return CompScheduling::Immediate; // to check if we can shut down immediately
 
}
 

	
 
/// Handles a component waiting until all peers are notified that it is quitting
 
/// (i.e. after calling `default_handle_start_exit`).
 
pub(crate) fn default_handle_busy_exit(
 
    exec_state: &mut CompExecState, control: &ControlLayer,
 
    sched_ctx: &SchedulerCtx
 
) -> CompScheduling {
 
    debug_assert_eq!(exec_state.mode, CompMode::BusyExit);
 
    if control.has_acks_remaining() {
 
        sched_ctx.log("Component busy exiting, still has `Ack`s remaining");
 
        return CompScheduling::Sleep;
 
    } else {
 
        sched_ctx.log("Component busy exiting, now shutting down");
 
        exec_state.mode = CompMode::Exit;
 
        return CompScheduling::Exit;
 
    }
 
}
 

	
 
/// Handles a potential synchronous round decision. If there was a decision then
 
/// the `Some(success)` value indicates whether the round succeeded or not.
 
pub(crate) fn default_handle_sync_decision(
 
    exec_state: &mut CompExecState, decision: SyncRoundDecision,
 
    consensus: &mut Consensus
 
) -> Option<bool> {
 
    debug_assert_eq!(exec_state.mode, CompMode::SyncEnd);
 
    let success = match decision {
 
        SyncRoundDecision::None => return None,
 
        SyncRoundDecision::Solution => true,
 
        SyncRoundDecision::Failure => false,
 
    };
 

	
 
    debug_assert_eq!(exec_state.mode, CompMode::SyncEnd);
 
    if success {
 
        exec_state.mode = CompMode::NonSync;
 
        consensus.notify_sync_decision(decision);
 
        return Some(true);
 
    } else {
 
        exec_state.mode = CompMode::StartExit;
 
        return Some(false);
 
    }
 
}
 

	
 

	
 
#[inline]
 
pub(crate) fn default_handle_exit(_exec_state: &CompExecState) -> CompScheduling {
 
    debug_assert_eq!(_exec_state.mode, CompMode::Exit);
 
    return CompScheduling::Exit;
 
}
 

	
 
// -----------------------------------------------------------------------------
 
// Internal messaging/state utilities
 
// -----------------------------------------------------------------------------
 

	
 
/// Handles an `Ack` for the control layer.
 
fn default_handle_ack(
 
    control: &mut ControlLayer, control_id: ControlId,
 
    sched_ctx: &SchedulerCtx, comp_ctx: &mut CompCtx
 
) {
 
    // Since an `Ack` may cause another one, handle them in a loop
 
    let mut to_ack = control_id;
 
    loop {
 
        let (action, new_to_ack) = control.handle_ack(to_ack, sched_ctx, comp_ctx);
 
        match action {
 
            AckAction::SendMessage(target_comp, message) => {
 
                // FIX @NoDirectHandle
 
                let mut handle = sched_ctx.runtime.get_component_public(target_comp);
 
                handle.send_message(&sched_ctx.runtime, Message::Control(message), true);
 
                let _should_remove = handle.decrement_users();
 
                debug_assert!(_should_remove.is_none());
 
            },
 
            AckAction::ScheduleComponent(to_schedule) => {
 
                // FIX @NoDirectHandle
 
                let mut handle = sched_ctx.runtime.get_component_public(to_schedule);
 

	
 
                // Note that the component is intentionally not
 
                // sleeping, so we just wake it up
 
                debug_assert!(!handle.sleeping.load(std::sync::atomic::Ordering::Acquire));
 
                let key = unsafe { to_schedule.upgrade() };
 
                sched_ctx.runtime.enqueue_work(key);
 
                let _should_remove = handle.decrement_users();
 
                debug_assert!(_should_remove.is_none());
 
            },
 
            AckAction::None => {}
 
        }
 

	
 
        match new_to_ack {
 
            Some(new_to_ack) => to_ack = new_to_ack,
 
            None => break,
 
        }
 
    }
 
}
 

	
 
/// Little helper for sending the most common kind of `Ack`
 
fn default_send_ack(
 
    causer_of_ack_id: ControlId, peer_handle: LocalPeerHandle,
 
    sched_ctx: &SchedulerCtx, comp_ctx: &CompCtx
 
) {
 
    let peer_info = comp_ctx.get_peer(peer_handle);
 
    peer_info.handle.send_message(&sched_ctx.runtime, Message::Control(ControlMessage{
 
        id: causer_of_ack_id,
 
        sender_comp_id: comp_ctx.id,
 
        target_port_id: None,
 
        content: ControlMessageContent::Ack
 
    }), true);
 
}
 

	
 
/// Handles the unblocking of a putter port. In case there is a pending message
 
/// on that port then it will be sent.
 
fn default_handle_unblock_put(
 
    exec_state: &mut CompExecState, consensus: &mut Consensus,
 
    port_handle: LocalPortHandle, sched_ctx: &SchedulerCtx, comp_ctx: &mut CompCtx,
 
) {
 
    let port_info = comp_ctx.get_port_mut(port_handle);
 
    let port_id = port_info.self_id;
 
    debug_assert!(port_info.state.is_blocked());
 
    port_info.state = PortState::Open;
 

	
 
    if exec_state.is_blocked_on_put(port_id) {
 
        // Annotate the message that we're going to send
 
        let port_info = comp_ctx.get_port(port_handle); // for immutable access
 
        debug_assert_eq!(port_info.kind, PortKind::Putter);
 
        let to_send = exec_state.mode_value.take();
 
        let to_send = consensus.annotate_data_message(comp_ctx, port_info, to_send);
 

	
 
        // Retrieve peer to send the message
 
        let peer_handle = comp_ctx.get_peer_handle(port_info.peer_comp_id);
 
        let peer_info = comp_ctx.get_peer(peer_handle);
 
        peer_info.handle.send_message(&sched_ctx.runtime, Message::Data(to_send), true);
 

	
 
        exec_state.mode = CompMode::Sync; // because we're blocked on a `put`, we must've started in the sync state.
 
        exec_state.mode_port = PortId::new_invalid();
 
    }
 
}
 

	
 
#[inline]
 
pub(crate) fn port_id_from_eval(port_id: EvalPortId) -> PortId {
 
    return PortId(port_id.id);
 
}
 

	
 
#[inline]
 
pub(crate) fn port_id_to_eval(port_id: PortId) -> EvalPortId {
 
    return EvalPortId{ id: port_id.0 };
 
}

Changeset was too big and was cut off... Show full diff anyway

0 comments (0 inline, 0 general)