Changeset - fe0efc81bd4e
[Not reviewed]
0 17 1
MH - 4 years ago 2021-05-26 16:01:28
contact@maxhenger.nl
Implement binding evaluation and tests
11 files changed:
0 comments (0 inline, 0 general)
src/collections/scoped_buffer.rs
Show inline comments
 
/// scoped_buffer.rs
 
///
 
/// Solves the common pattern where we are performing some kind of recursive
 
/// pattern while using a temporary buffer. At the start, or during the
 
/// procedure, we push stuff into the buffer. At the end we take out what we
 
/// have put in.
 
///
 
/// It is unsafe because we're using pointers to circumvent borrowing rules in
 
/// the name of code cleanliness. The correctness of use is checked in debug
 
/// mode.
 

	
 
use std::iter::FromIterator;
 

	
 
pub(crate) struct ScopedBuffer<T: Sized> {
 
    pub inner: Vec<T>,
 
}
 

	
 
/// A section of the buffer. Keeps track of where we started the section. When
 
/// done with the section one must call `into_vec` or `forget` to remove the
 
/// section from the underlying buffer. This will also be done upon dropping the
 
/// ScopedSection in case errors are being handled.
 
pub(crate) struct ScopedSection<T: Sized> {
 
    inner: *mut Vec<T>,
 
    start_size: u32,
 
    #[cfg(debug_assertions)] cur_size: u32,
 
}
 

	
 
impl<T: Sized> ScopedBuffer<T> {
 
    pub(crate) fn new_reserved(capacity: usize) -> Self {
 
        Self { inner: Vec::with_capacity(capacity) }
 
    }
 

	
 
    pub(crate) fn start_section(&mut self) -> ScopedSection<T> {
 
        let start_size = self.inner.len() as u32;
 
        ScopedSection {
 
            inner: &mut self.inner,
 
            start_size,
 
            #[cfg(debug_assertions)] cur_size: start_size
 
        }
 
    }
 
}
 

	
 
impl<T: Clone> ScopedBuffer<T> {
 
    pub(crate) fn start_section_initialized(&mut self, initialize_with: &[T]) -> ScopedSection<T> {
 
        let start_size = self.inner.len() as u32;
 
        let data_size = initialize_with.len() as u32;
 
        self.inner.extend_from_slice(initialize_with);
 
        ScopedSection{
 
            inner: &mut self.inner,
 
            start_size,
 
            #[cfg(debug_assertions)] cur_size: start_size + data_size,
 
        }
 
    }
 
}
 

	
 
#[cfg(debug_assertions)]
 
impl<T: Sized> Drop for ScopedBuffer<T> {
 
    fn drop(&mut self) {
 
        // Make sure that everyone cleaned up the buffer neatly
 
        debug_assert!(self.inner.is_empty(), "dropped non-empty scoped buffer");
 
    }
 
}
 

	
 
impl<T: Sized> ScopedSection<T> {
 
    #[inline]
 
    pub(crate) fn push(&mut self, value: T) {
 
        let vec = unsafe{&mut *self.inner};
 
        #[cfg(debug_assertions)] debug_assert_eq!(vec.len(), self.cur_size as usize, "trying to push onto section, but size is larger than expected");
 
        vec.push(value);
 
        #[cfg(debug_assertions)] { self.cur_size += 1; }
 
    }
 

	
 
    pub(crate) fn len(&self) -> usize {
 
        let vec = unsafe{&mut *self.inner};
 
        #[cfg(debug_assertions)] debug_assert_eq!(vec.len(), self.cur_size as usize, "trying to get section length, but size is larger than expected");
 
        return vec.len() - self.start_size as usize;
 
    }
 

	
 
    #[inline]
 
    #[allow(unused_mut)] // used in debug mode
 
    pub(crate) fn forget(mut self) {
 
        let vec = unsafe{&mut *self.inner};
 
        #[cfg(debug_assertions)] {
 
            debug_assert_eq!(
 
                vec.len(), self.cur_size as usize,
 
                "trying to forget section, but size is larger than expected"
 
            );
 
            self.cur_size = self.start_size;
 
        }
 
        vec.truncate(self.start_size as usize);
 
    }
 

	
 
    #[inline]
 
    #[allow(unused_mut)] // used in debug mode
 
    pub(crate) fn into_vec(mut self) -> Vec<T> {
 
        let vec = unsafe{&mut *self.inner};
 
        #[cfg(debug_assertions)]  {
 
            debug_assert_eq!(
 
                vec.len(), self.cur_size as usize,
 
                "trying to turn section into vec, but size is larger than expected"
 
            );
 
            self.cur_size = self.start_size;
 
        }
 
        let section = Vec::from_iter(vec.drain(self.start_size as usize..));
 
        section
 
    }
 
}
 

	
 
impl<T: Sized> std::ops::Index<usize> for ScopedSection<T> {
 
    type Output = T;
 

	
 
    fn index(&self, idx: usize) -> &Self::Output {
 
        let vec = unsafe{&*self.inner};
 
        return &vec[self.start_size as usize + idx]
 
    }
 
}
 

	
 
#[cfg(debug_assertions)]
 
impl<T: Sized> Drop for ScopedSection<T> {
 
    fn drop(&mut self) {
 
        let vec = unsafe{&mut *self.inner};
 
        #[cfg(debug_assertions)] debug_assert_eq!(vec.len(), self.cur_size as usize);
 
        vec.truncate(self.start_size as usize);
 
    }
 
}
 
\ No newline at end of file
src/common.rs
Show inline comments
 
///////////////////// PRELUDE /////////////////////
 
pub(crate) use crate::protocol::{ComponentState, ProtocolDescription};
 
pub(crate) use crate::runtime::{error::AddComponentError, NonsyncProtoContext, SyncProtoContext};
 
pub(crate) use core::{
 
    cmp::Ordering,
 
    fmt::{Debug, Formatter},
 
    hash::Hash,
 
    ops::Range,
 
    time::Duration,
 
};
 
pub(crate) use maplit::hashmap;
 
pub(crate) use mio::{
 
    net::{TcpListener, TcpStream},
 
    Events, Interest, Poll, Token,
 
};
 
pub(crate) use std::{
 
    collections::{BTreeMap, HashMap, HashSet},
 
    convert::TryInto,
 
    io::{Read, Write},
 
    net::SocketAddr,
 
    sync::Arc,
 
    time::Instant,
 
};
 
pub(crate) use Polarity::*;
 

	
 
pub(crate) trait IdParts {
 
    fn id_parts(self) -> (ConnectorId, U32Suffix);
 
}
 

	
 
/// Used by various distributed algorithms to identify connectors.
 
pub type ConnectorId = u32;
 

	
 
/// Used in conjunction with the `ConnectorId` type to create identifiers for ports and components
 
pub type U32Suffix = u32;
 
#[derive(Copy, Clone, Eq, PartialEq, Ord, Hash, PartialOrd)]
 

	
 
/// Generalization of a port/component identifier
 
#[derive(serde::Serialize, serde::Deserialize)]
 
#[repr(C)]
 
pub struct Id {
 
    pub(crate) connector_id: ConnectorId,
 
    pub(crate) u32_suffix: U32Suffix,
 
}
 
#[derive(Clone, Debug, Default)]
 
pub struct U32Stream {
 
    next: u32,
 
}
 

	
 
/// Identifier of a component in a session
 
#[derive(Copy, Clone, Eq, PartialEq, Ord, Hash, PartialOrd, serde::Serialize, serde::Deserialize)]
 
pub struct ComponentId(Id); // PUB because it can be returned by errors
 

	
 
/// Identifier of a port in a session
 
#[derive(Copy, Clone, Eq, PartialEq, Ord, Hash, PartialOrd, serde::Serialize, serde::Deserialize)]
 
#[repr(transparent)]
 
pub struct PortId(Id);
 

	
 
/// A safely aliasable heap-allocated payload of message bytes
 
#[derive(Default, Eq, PartialEq, Clone, Ord, PartialOrd)]
 
pub struct Payload(pub Arc<Vec<u8>>);
 
#[derive(Debug, Eq, PartialEq, Clone, Hash, Copy, Ord, PartialOrd)]
 

	
 
/// "Orientation" of a port, determining whether they can send or receive messages with `put` and `get` respectively.
 
#[repr(C)]
 
#[derive(serde::Serialize, serde::Deserialize)]
 
pub enum Polarity {
 
    Putter, // output port (from the perspective of the component)
 
    Getter, // input port (from the perspective of the component)
 
}
 
#[derive(Debug, Eq, PartialEq, Clone, Hash, Copy, Ord, PartialOrd)]
 

	
 
/// "Orientation" of a transport-layer network endpoint, dictating how it's connection procedure should
 
/// be conducted. Corresponds with connect() / accept() familiar to TCP socket programming.
 
#[repr(C)]
 
pub enum EndpointPolarity {
 
    Active,  // calls connect()
 
    Passive, // calls bind() listen() accept()
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub(crate) enum NonsyncBlocker {
 
    Inconsistent,
 
    ComponentExit,
 
    SyncBlockStart,
 
}
 
#[derive(Debug, Clone)]
 
pub(crate) enum SyncBlocker {
 
    Inconsistent,
 
    SyncBlockEnd,
 
    CouldntReadMsg(PortId),
 
    CouldntCheckFiring(PortId),
 
    PutMsg(PortId, Payload),
 
}
 
pub(crate) struct DenseDebugHex<'a>(pub &'a [u8]);
 
pub(crate) struct DebuggableIter<I: Iterator<Item = T> + Clone, T: Debug>(pub(crate) I);
 
///////////////////// IMPL /////////////////////
 
impl IdParts for Id {
 
    fn id_parts(self) -> (ConnectorId, U32Suffix) {
 
        (self.connector_id, self.u32_suffix)
 
    }
 
}
 
impl IdParts for PortId {
 
    fn id_parts(self) -> (ConnectorId, U32Suffix) {
 
        self.0.id_parts()
 
    }
 
}
 
impl IdParts for ComponentId {
 
    fn id_parts(self) -> (ConnectorId, U32Suffix) {
 
        self.0.id_parts()
 
    }
 
}
 
impl U32Stream {
 
    pub(crate) fn next(&mut self) -> u32 {
 
        if self.next == u32::MAX {
src/protocol/arena.rs
Show inline comments
 
use crate::common::*;
 
use core::hash::Hash;
 
use core::marker::PhantomData;
 

	
 
pub struct Id<T> {
 
    // Not actually a signed index into the heap. But the index is set to -1 if
 
    // we don't know an ID yet. This is checked during debug mode.
 
    pub(crate) index: i32,
 
    _phantom: PhantomData<T>,
 
}
 

	
 
impl<T> Id<T> {
 
    pub(crate) fn new_invalid() -> Self     { Self{ index: -1, _phantom: Default::default() } }
 
    pub(crate) fn new(index: i32) -> Self   { Self{ index, _phantom: Default::default() } }
 
    pub(crate) fn is_invalid(&self) -> bool { self.index < 0 }
 
}
 

	
 
#[derive(Debug)]
 
pub(crate) struct Arena<T> {
 
    store: Vec<T>,
 
}
 
//////////////////////////////////
 

	
 
impl<T> Debug for Id<T> {
 
    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
 
        f.debug_struct("Id").field("index", &self.index).finish()
 
    }
 
}
 
impl<T> Clone for Id<T> {
 
    fn clone(&self) -> Self {
 
        *self
 
    }
 
}
 
impl<T> Copy for Id<T> {}
 
impl<T> PartialEq for Id<T> {
 
    fn eq(&self, other: &Self) -> bool {
 
        self.index.eq(&other.index)
 
    }
 
}
 
impl<T> Eq for Id<T> {}
 
impl<T> Hash for Id<T> {
 
    fn hash<H: std::hash::Hasher>(&self, h: &mut H) {
 
        self.index.hash(h);
 
    }
 
}
 

	
 
impl<T> Arena<T> {
 
    pub fn new() -> Self {
 
        Self { store: vec![] }
 
    }
 

	
 
    pub fn alloc_with_id(&mut self, f: impl FnOnce(Id<T>) -> T) -> Id<T> {
 
        // Lets keep this a runtime assert.
 
        assert!(self.store.len() < i32::max_value() as usize, "Arena out of capacity");
 
        let id = Id::new(self.store.len() as i32);
 
        self.store.push(f(id));
 
        id
 
    }
 

	
 
    // Compiler-internal direct retrieval
 
    pub(crate) fn get(&self, idx: usize) -> &T {
 
        return &self.store[idx]
 
    }
 
    pub(crate) fn get_id(&self, idx: usize) -> Id<T> {
 
        debug_assert!(idx < self.store.len());
 
        return Id::new(idx as i32);
 
    }
 

	
 
    pub fn iter(&self) -> impl Iterator<Item = &T> {
 
        self.store.iter()
 
    }
 

	
 
    pub fn len(&self) -> usize {
 
        self.store.len()
 
    }
 
}
 
impl<T> core::ops::Index<Id<T>> for Arena<T> {
 
    type Output = T;
 
    fn index(&self, id: Id<T>) -> &Self::Output {
 
        debug_assert!(!id.is_invalid(), "attempted to index into Arena with an invalid id (index < 0)");
 
        self.store.index(id.index as usize)
 
    }
 
}
 
impl<T> core::ops::IndexMut<Id<T>> for Arena<T> {
 
    fn index_mut(&mut self, id: Id<T>) -> &mut Self::Output {
 
        debug_assert!(!id.is_invalid(), "attempted to index_mut into Arena with an invalid id (index < 0)");
 
        self.store.index_mut(id.index as usize)
 
    }
 
}
 
\ No newline at end of file
src/protocol/ast.rs
Show inline comments
 
@@ -1784,99 +1784,100 @@ pub enum Literal {
 
    True,
 
    False,
 
    Character(char),
 
    String(StringRef<'static>),
 
    Integer(LiteralInteger),
 
    Struct(LiteralStruct),
 
    Enum(LiteralEnum),
 
    Union(LiteralUnion),
 
    Array(Vec<ExpressionId>),
 
}
 

	
 
impl Literal {
 
    pub(crate) fn as_struct(&self) -> &LiteralStruct {
 
        if let Literal::Struct(literal) = self{
 
            literal
 
        } else {
 
            unreachable!("Attempted to obtain {:?} as Literal::Struct", self)
 
        }
 
    }
 

	
 
    pub(crate) fn as_struct_mut(&mut self) -> &mut LiteralStruct {
 
        if let Literal::Struct(literal) = self{
 
            literal
 
        } else {
 
            unreachable!("Attempted to obtain {:?} as Literal::Struct", self)
 
        }
 
    }
 

	
 
    pub(crate) fn as_enum(&self) -> &LiteralEnum {
 
        if let Literal::Enum(literal) = self {
 
            literal
 
        } else {
 
            unreachable!("Attempted to obtain {:?} as Literal::Enum", self)
 
        }
 
    }
 

	
 
    pub(crate) fn as_union(&self) -> &LiteralUnion {
 
        if let Literal::Union(literal) = self {
 
            literal
 
        } else {
 
            unreachable!("Attempted to obtain {:?} as Literal::Union", self)
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct LiteralInteger {
 
    pub(crate) unsigned_value: u64,
 
    pub(crate) negated: bool, // for constant expression evaluation, TODO: @Int
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct LiteralStructField {
 
    // Phase 1: parser
 
    pub(crate) identifier: Identifier,
 
    pub(crate) value: ExpressionId,
 
    // Phase 2: linker
 
    pub(crate) field_idx: usize, // in struct definition
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct LiteralStruct {
 
    // Phase 1: parser
 
    pub(crate) parser_type: ParserType,
 
    pub(crate) fields: Vec<LiteralStructField>,
 
    pub(crate) definition: DefinitionId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct LiteralEnum {
 
    // Phase 1: parser
 
    pub(crate) parser_type: ParserType,
 
    pub(crate) variant: Identifier,
 
    pub(crate) definition: DefinitionId,
 
    // Phase 2: linker
 
    pub(crate) variant_idx: usize, // as present in the type table
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct LiteralUnion {
 
    // Phase 1: parser
 
    pub(crate) parser_type: ParserType,
 
    pub(crate) variant: Identifier,
 
    pub(crate) values: Vec<ExpressionId>,
 
    pub(crate) definition: DefinitionId,
 
    // Phase 2: linker
 
    pub(crate) variant_idx: usize, // as present in type table
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct VariableExpression {
 
    pub this: VariableExpressionId,
 
    // Parsing
 
    pub identifier: Identifier,
 
    // Validator/Linker
 
    pub declaration: Option<VariableId>,
 
    pub used_as_binding_target: bool,
 
    pub parent: ExpressionParent,
 
    pub unique_id_in_definition: i32,
 
}
 
\ No newline at end of file
src/protocol/ast_printer.rs
Show inline comments
 
#![allow(dead_code)]
 

	
 
use std::fmt::{Debug, Display};
 
use std::io::Write as IOWrite;
 

	
 
use super::ast::*;
 
use super::token_parsing::*;
 

	
 
const INDENT: usize = 2;
 

	
 
const PREFIX_EMPTY: &'static str = "    ";
 
const PREFIX_ROOT_ID: &'static str = "Root";
 
const PREFIX_PRAGMA_ID: &'static str = "Prag";
 
const PREFIX_IMPORT_ID: &'static str = "Imp ";
 
const PREFIX_TYPE_ANNOT_ID: &'static str = "TyAn";
 
const PREFIX_VARIABLE_ID: &'static str = "Var ";
 
const PREFIX_DEFINITION_ID: &'static str = "Def ";
 
const PREFIX_STRUCT_ID: &'static str = "DefS";
 
const PREFIX_ENUM_ID: &'static str = "DefE";
 
const PREFIX_UNION_ID: &'static str = "DefU";
 
const PREFIX_COMPONENT_ID: &'static str = "DefC";
 
const PREFIX_FUNCTION_ID: &'static str = "DefF";
 
const PREFIX_STMT_ID: &'static str = "Stmt";
 
const PREFIX_BLOCK_STMT_ID: &'static str = "SBl ";
 
const PREFIX_ENDBLOCK_STMT_ID: &'static str = "SEBl";
 
const PREFIX_LOCAL_STMT_ID: &'static str = "SLoc";
 
const PREFIX_MEM_STMT_ID: &'static str = "SMem";
 
const PREFIX_CHANNEL_STMT_ID: &'static str = "SCha";
 
const PREFIX_SKIP_STMT_ID: &'static str = "SSki";
 
const PREFIX_LABELED_STMT_ID: &'static str = "SLab";
 
const PREFIX_IF_STMT_ID: &'static str = "SIf ";
 
const PREFIX_ENDIF_STMT_ID: &'static str = "SEIf";
 
const PREFIX_WHILE_STMT_ID: &'static str = "SWhi";
 
const PREFIX_ENDWHILE_STMT_ID: &'static str = "SEWh";
 
const PREFIX_BREAK_STMT_ID: &'static str = "SBre";
 
const PREFIX_CONTINUE_STMT_ID: &'static str = "SCon";
 
const PREFIX_SYNC_STMT_ID: &'static str = "SSyn";
 
const PREFIX_ENDSYNC_STMT_ID: &'static str = "SESy";
 
const PREFIX_RETURN_STMT_ID: &'static str = "SRet";
 
const PREFIX_ASSERT_STMT_ID: &'static str = "SAsr";
 
const PREFIX_GOTO_STMT_ID: &'static str = "SGot";
 
const PREFIX_NEW_STMT_ID: &'static str = "SNew";
 
const PREFIX_PUT_STMT_ID: &'static str = "SPut";
 
const PREFIX_EXPR_STMT_ID: &'static str = "SExp";
 
const PREFIX_ASSIGNMENT_EXPR_ID: &'static str = "EAsi";
 
const PREFIX_BINDING_EXPR_ID: &'static str = "EBnd";
 
const PREFIX_CONDITIONAL_EXPR_ID: &'static str = "ECnd";
 
const PREFIX_BINARY_EXPR_ID: &'static str = "EBin";
 
const PREFIX_UNARY_EXPR_ID: &'static str = "EUna";
 
const PREFIX_INDEXING_EXPR_ID: &'static str = "EIdx";
 
const PREFIX_SLICING_EXPR_ID: &'static str = "ESli";
 
const PREFIX_SELECT_EXPR_ID: &'static str = "ESel";
 
const PREFIX_LITERAL_EXPR_ID: &'static str = "ELit";
 
const PREFIX_CAST_EXPR_ID: &'static str = "ECas";
 
const PREFIX_CALL_EXPR_ID: &'static str = "ECll";
 
const PREFIX_VARIABLE_EXPR_ID: &'static str = "EVar";
 

	
 
struct KV<'a> {
 
    buffer: &'a mut String,
 
    prefix: Option<(&'static str, i32)>,
 
    indent: usize,
 
    temp_key: &'a mut String,
 
    temp_val: &'a mut String,
 
}
 

	
 
impl<'a> KV<'a> {
 
    fn new(buffer: &'a mut String, temp_key: &'a mut String, temp_val: &'a mut String, indent: usize) -> Self {
 
        temp_key.clear();
 
        temp_val.clear();
 
        KV{
 
            buffer,
 
            prefix: None,
 
            indent,
 
            temp_key,
 
            temp_val
 
        }
 
    }
 

	
 
    fn with_id(mut self, prefix: &'static str, id: i32) -> Self {
 
        self.prefix = Some((prefix, id));
 
        self
 
    }
 

	
 
    fn with_s_key(self, key: &str) -> Self {
 
        self.temp_key.push_str(key);
 
        self
 
    }
 

	
 
    fn with_d_key<D: Display>(self, key: &D) -> Self {
 
        self.temp_key.push_str(&key.to_string());
 
        self
 
    }
 

	
 
    fn with_s_val(self, val: &str) -> Self {
 
        self.temp_val.push_str(val);
 
        self
 
    }
 

	
 
    fn with_disp_val<D: Display>(self, val: &D) -> Self {
 
        self.temp_val.push_str(&format!("{}", val));
 
        self
 
    }
 

	
 
    fn with_debug_val<D: Debug>(self, val: &D) -> Self {
 
        self.temp_val.push_str(&format!("{:?}", val));
 
        self
 
    }
 

	
 
    fn with_identifier_val(self, val: &Identifier) -> Self {
 
        self.temp_val.push_str(val.value.as_str());
 
        self
 
    }
 

	
 
    fn with_opt_disp_val<D: Display>(self, val: Option<&D>) -> Self {
 
        match val {
 
            Some(v) => { self.temp_val.push_str(&format!("Some({})", v)); },
 
            None => { self.temp_val.push_str("None"); }
 
        }
 
        self
 
    }
 

	
 
    fn with_opt_identifier_val(self, val: Option<&Identifier>) -> Self {
 
        match val {
 
            Some(v) => {
 
                self.temp_val.push_str("Some(");
 
                self.temp_val.push_str(v.value.as_str());
 
                self.temp_val.push(')');
 
            },
 
            None => {
 
                self.temp_val.push_str("None");
 
            }
 
        }
 
        self
 
    }
 

	
 
    fn with_custom_val<F: Fn(&mut String)>(mut self, val_fn: F) -> Self {
 
        val_fn(&mut self.temp_val);
 
        self
 
    }
 
}
 

	
 
impl<'a> Drop for KV<'a> {
 
    fn drop(&mut self) {
 
        // Prefix and indent
 
        if let Some((prefix, id)) = &self.prefix {
 
            self.buffer.push_str(&format!("{}[{:04}]", prefix, id));
 
        } else {
 
            self.buffer.push_str("           ");
 
        }
 

	
 
@@ -612,193 +613,200 @@ impl ASTWriter {
 
                self.kv(indent2).with_s_key("Index");
 
                self.write_expr(heap, expr.index, indent3);
 
                self.kv(indent2).with_s_key("Parent")
 
                    .with_custom_val(|v| write_expression_parent(v, &expr.parent));
 
            },
 
            Expression::Slicing(expr) => {
 
                self.kv(indent).with_id(PREFIX_SLICING_EXPR_ID, expr.this.0.index)
 
                    .with_s_key("SlicingExpr");
 
                self.kv(indent2).with_s_key("Subject");
 
                self.write_expr(heap, expr.subject, indent3);
 
                self.kv(indent2).with_s_key("FromIndex");
 
                self.write_expr(heap, expr.from_index, indent3);
 
                self.kv(indent2).with_s_key("ToIndex");
 
                self.write_expr(heap, expr.to_index, indent3);
 
                self.kv(indent2).with_s_key("Parent")
 
                    .with_custom_val(|v| write_expression_parent(v, &expr.parent));
 
            },
 
            Expression::Select(expr) => {
 
                self.kv(indent).with_id(PREFIX_SELECT_EXPR_ID, expr.this.0.index)
 
                    .with_s_key("SelectExpr");
 
                self.kv(indent2).with_s_key("Subject");
 
                self.write_expr(heap, expr.subject, indent3);
 

	
 
                self.kv(indent2).with_s_key("Field").with_identifier_val(&expr.field_name);
 
                self.kv(indent2).with_s_key("Parent")
 
                    .with_custom_val(|v| write_expression_parent(v, &expr.parent));
 
            },
 
            Expression::Literal(expr) => {
 
                self.kv(indent).with_id(PREFIX_LITERAL_EXPR_ID, expr.this.0.index)
 
                    .with_s_key("LiteralExpr");
 

	
 
                let val = self.kv(indent2).with_s_key("Value");
 
                match &expr.value {
 
                    Literal::Null => { val.with_s_val("null"); },
 
                    Literal::True => { val.with_s_val("true"); },
 
                    Literal::False => { val.with_s_val("false"); },
 
                    Literal::Character(data) => { val.with_disp_val(data); },
 
                    Literal::String(data) => {
 
                        // Stupid hack
 
                        let string = String::from(data.as_str());
 
                        val.with_disp_val(&string);
 
                    },
 
                    Literal::Integer(data) => { val.with_debug_val(data); },
 
                    Literal::Struct(data) => {
 
                        val.with_s_val("Struct");
 
                        let indent4 = indent3 + 1;
 

	
 
                        self.kv(indent3).with_s_key("ParserType")
 
                            .with_custom_val(|t| write_parser_type(t, heap, &data.parser_type));
 
                        self.kv(indent3).with_s_key("Definition").with_disp_val(&data.definition.index);
 

	
 
                        for field in &data.fields {
 
                            self.kv(indent3).with_s_key("Field");
 
                            self.kv(indent4).with_s_key("Name").with_identifier_val(&field.identifier);
 
                            self.kv(indent4).with_s_key("Index").with_disp_val(&field.field_idx);
 
                            self.kv(indent4).with_s_key("ParserType");
 
                            self.write_expr(heap, field.value, indent4 + 1);
 
                        }
 
                    },
 
                    Literal::Enum(data) => {
 
                        val.with_s_val("Enum");
 

	
 
                        self.kv(indent3).with_s_key("ParserType")
 
                            .with_custom_val(|t| write_parser_type(t, heap, &data.parser_type));
 
                        self.kv(indent3).with_s_key("Definition").with_disp_val(&data.definition.index);
 
                        self.kv(indent3).with_s_key("VariantIdx").with_disp_val(&data.variant_idx);
 
                    },
 
                    Literal::Union(data) => {
 
                        val.with_s_val("Union");
 
                        let indent4 = indent3 + 1;
 

	
 
                        self.kv(indent3).with_s_key("ParserType")
 
                            .with_custom_val(|t| write_parser_type(t, heap, &data.parser_type));
 
                        self.kv(indent3).with_s_key("Definition").with_disp_val(&data.definition.index);
 
                        self.kv(indent3).with_s_key("VariantIdx").with_disp_val(&data.variant_idx);
 

	
 
                        for value in &data.values {
 
                            self.kv(indent3).with_s_key("Value");
 
                            self.write_expr(heap, *value, indent4);
 
                        }
 
                    }
 
                    Literal::Array(data) => {
 
                        val.with_s_val("Array");
 
                        let indent4 = indent3 + 1;
 

	
 
                        self.kv(indent3).with_s_key("Elements");
 
                        for expr_id in data {
 
                            self.write_expr(heap, *expr_id, indent4);
 
                        }
 
                    }
 
                }
 

	
 
                self.kv(indent2).with_s_key("Parent")
 
                    .with_custom_val(|v| write_expression_parent(v, &expr.parent));
 
            },
 
            Expression::Cast(expr) => {
 
                todo!("print casting expression")
 
                self.kv(indent).with_id(PREFIX_CAST_EXPR_ID, expr.this.0.index)
 
                    .with_s_key("CallExpr");
 
                self.kv(indent2).with_s_key("ToType")
 
                    .with_custom_val(|t| write_parser_type(t, heap, &expr.to_type));
 
                self.kv(indent2).with_s_key("Subject");
 
                self.write_expr(heap, expr.subject, indent3);
 
                self.kv(indent2).with_s_key("Parent")
 
                    .with_custom_val(|v| write_expression_parent(v, &expr.parent));
 
            }
 
            Expression::Call(expr) => {
 
                self.kv(indent).with_id(PREFIX_CALL_EXPR_ID, expr.this.0.index)
 
                    .with_s_key("CallExpr");
 

	
 
                let definition = &heap[expr.definition];
 
                match definition {
 
                    Definition::Component(definition) => {
 
                        self.kv(indent2).with_s_key("BuiltIn").with_disp_val(&false);
 
                        self.kv(indent2).with_s_key("Variant").with_debug_val(&definition.variant);
 
                    },
 
                    Definition::Function(definition) => {
 
                        self.kv(indent2).with_s_key("BuiltIn").with_disp_val(&definition.builtin);
 
                        self.kv(indent2).with_s_key("Variant").with_s_val("Function");
 
                    },
 
                    _ => unreachable!()
 
                }
 
                self.kv(indent2).with_s_key("MethodName").with_identifier_val(definition.identifier());
 
                self.kv(indent2).with_s_key("ParserType")
 
                    .with_custom_val(|t| write_parser_type(t, heap, &expr.parser_type));
 

	
 
                // Arguments
 
                self.kv(indent2).with_s_key("Arguments");
 
                for arg_id in &expr.arguments {
 
                    self.write_expr(heap, *arg_id, indent3);
 
                }
 

	
 
                // Parent
 
                self.kv(indent2).with_s_key("Parent")
 
                    .with_custom_val(|v| write_expression_parent(v, &expr.parent));
 
            },
 
            Expression::Variable(expr) => {
 
                self.kv(indent).with_id(PREFIX_VARIABLE_EXPR_ID, expr.this.0.index)
 
                    .with_s_key("VariableExpr");
 
                self.kv(indent2).with_s_key("Name").with_identifier_val(&expr.identifier);
 
                self.kv(indent2).with_s_key("Definition")
 
                    .with_opt_disp_val(expr.declaration.as_ref().map(|v| &v.index));
 
                self.kv(indent2).with_s_key("Parent")
 
                    .with_custom_val(|v| write_expression_parent(v, &expr.parent));
 
            }
 
        }
 
    }
 

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

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

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

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

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

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

	
 
fn write_option<V: Display>(target: &mut String, value: Option<V>) {
 
    target.clear();
 
    match &value {
 
        Some(v) => target.push_str(&format!("Some({})", v)),
 
        None => target.push_str("None")
 
    };
 
}
 

	
 
fn write_parser_type(target: &mut String, heap: &Heap, t: &ParserType) {
 
    use ParserTypeVariant as PTV;
 

	
 
    fn write_element(target: &mut String, heap: &Heap, t: &ParserType, mut element_idx: usize) -> usize {
 
        let element = &t.elements[element_idx];
 
        match &element.variant {
 
            PTV::Void => target.push_str("void"),
 
            PTV::InputOrOutput => {
 
                target.push_str("portlike<");
 
                element_idx = write_element(target, heap, t, element_idx + 1);
 
                target.push('>');
 
            },
 
            PTV::ArrayLike => {
 
                element_idx = write_element(target, heap, t, element_idx + 1);
 
                target.push_str("[???]");
 
            },
src/protocol/eval/executor.rs
Show inline comments
 

	
 
use std::collections::VecDeque;
 

	
 
use super::value::*;
 
use super::store::*;
 
use super::error::*;
 
use crate::protocol::*;
 
use crate::protocol::ast::*;
 
use crate::protocol::type_table::*;
 

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

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

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

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

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

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

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

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

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

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

	
 
        self.serialize_expression(heap, expr_id);
 
    }
 

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

	
 
        for expr_id in expr_ids {
 
            self.expr_stack.push_back(ExprInstruction::PushValToFront);
 
            self.serialize_expression(heap, *expr_id);
 
        }
 
    }
 

	
 
    /// Performs depth-first serialization of expression tree. Let's not care
 
    /// about performance for a temporary runtime implementation
 
    fn serialize_expression(&mut self, heap: &Heap, id: ExpressionId) {
 
        self.expr_stack.push_back(ExprInstruction::EvalExpr(id));
 

	
 
        match &heap[id] {
 
            Expression::Assignment(expr) => {
 
                self.serialize_expression(heap, expr.left);
 
                self.serialize_expression(heap, expr.right);
 
            },
 
            Expression::Binding(expr) => {
 
                todo!("implement binding expression");
 
                self.serialize_expression(heap, expr.bound_to);
 
                self.serialize_expression(heap, expr.bound_from);
 
            },
 
            Expression::Conditional(expr) => {
 
                self.serialize_expression(heap, expr.test);
 
            },
 
            Expression::Binary(expr) => {
 
                self.serialize_expression(heap, expr.left);
 
                self.serialize_expression(heap, expr.right);
 
            },
 
            Expression::Unary(expr) => {
 
                self.serialize_expression(heap, expr.expression);
 
            },
 
            Expression::Indexing(expr) => {
 
                self.serialize_expression(heap, expr.index);
 
                self.serialize_expression(heap, expr.subject);
 
            },
 
            Expression::Slicing(expr) => {
 
                self.serialize_expression(heap, expr.from_index);
 
                self.serialize_expression(heap, expr.to_index);
 
                self.serialize_expression(heap, expr.subject);
 
            },
 
            Expression::Select(expr) => {
 
                self.serialize_expression(heap, expr.subject);
 
            },
 
            Expression::Literal(expr) => {
 
                // Here we only care about literals that have subexpressions
 
                match &expr.value {
 
                    Literal::Null | Literal::True | Literal::False |
 
                    Literal::Character(_) | Literal::String(_) |
 
                    Literal::Integer(_) | Literal::Enum(_) => {
 
                        // No subexpressions
 
                    },
 
                    Literal::Struct(literal) => {
 
                        // Note: fields expressions are evaluated in programmer-
 
                        // specified order. But struct construction expects them
 
                        // in type-defined order. I might want to come back to
 
                        // this.
 
                        let mut _num_pushed = 0;
 
                        for want_field_idx in 0..literal.fields.len() {
 
                            for field in &literal.fields {
 
                                if field.field_idx == want_field_idx {
 
                                    _num_pushed += 1;
 
                                    self.expr_stack.push_back(ExprInstruction::PushValToFront);
 
                                    self.serialize_expression(heap, field.value);
 
                                }
 
                            }
 
                        }
 
                        debug_assert_eq!(_num_pushed, literal.fields.len())
 
                    },
 
                    Literal::Union(literal) => {
 
                        for value_expr_id in &literal.values {
 
                            self.expr_stack.push_back(ExprInstruction::PushValToFront);
 
                            self.serialize_expression(heap, *value_expr_id);
 
                        }
 
                    },
 
                    Literal::Array(value_expr_ids) => {
 
                        for value_expr_id in value_expr_ids {
 
                            self.expr_stack.push_back(ExprInstruction::PushValToFront);
 
                            self.serialize_expression(heap, *value_expr_id);
 
                        }
 
                    }
 
                }
 
            },
 
            Expression::Cast(expr) => {
 
                self.serialize_expression(heap, expr.subject);
 
            }
 
            Expression::Call(expr) => {
 
                for arg_expr_id in &expr.arguments {
 
                    self.expr_stack.push_back(ExprInstruction::PushValToFront);
 
                    self.serialize_expression(heap, *arg_expr_id);
 
                }
 
            },
 
            Expression::Variable(expr) => {
 
                // No subexpressions
 
            }
 
        }
 
    }
 
}
 

	
 
type EvalResult = Result<EvalContinuation, EvalError>;
 

	
 
pub enum EvalContinuation {
 
    Stepping,
 
    Inconsistent,
 
    Terminal,
 
    SyncBlockStart,
 
    SyncBlockEnd,
 
    NewComponent(DefinitionId, i32, ValueGroup),
 
    BlockFires(Value),
 
    BlockGet(Value),
 
    Put(Value, Value),
 
}
 

	
 
// Note: cloning is fine, methinks. cloning all values and the heap regions then
 
// we end up with valid "pointers" to heap regions.
 
#[derive(Debug, Clone)]
 
pub struct Prompt {
 
    pub(crate) frames: Vec<Frame>,
 
    pub(crate) store: Store,
 
}
 

	
 
impl Prompt {
 
    pub fn new(_types: &TypeTable, heap: &Heap, def: DefinitionId, monomorph_idx: i32, args: ValueGroup) -> Self {
 
        let mut prompt = Self{
 
            frames: Vec::new(),
 
            store: Store::new(),
 
        };
 

	
 
        // Maybe do typechecking in the future?
 
        debug_assert!((monomorph_idx as usize) < _types.get_base_definition(&def).unwrap().definition.procedure_monomorphs().len());
 
        prompt.frames.push(Frame::new(heap, def, monomorph_idx));
 
        let new_frame = Frame::new(heap, def, monomorph_idx);
 
        let max_stack_size = new_frame.max_stack_size;
 
        prompt.frames.push(new_frame);
 
        args.into_store(&mut prompt.store);
 
        prompt.store.reserve_stack(max_stack_size);
 

	
 
        prompt
 
    }
 

	
 
    pub(crate) fn step(&mut self, types: &TypeTable, heap: &Heap, modules: &[Module], ctx: &mut EvalContext) -> EvalResult {
 
        // Helper function to transfer multiple values from the expression value
 
        // array into a heap region (e.g. constructing arrays or structs).
 
        fn transfer_expression_values_front_into_heap(cur_frame: &mut Frame, store: &mut Store, num_values: usize) -> HeapPos {
 
            let heap_pos = store.alloc_heap();
 

	
 
            // Do the transformation first (because Rust...)
 
            for val_idx in 0..num_values {
 
                cur_frame.expr_values[val_idx] = store.read_take_ownership(cur_frame.expr_values[val_idx].clone());
 
            }
 

	
 
            // And now transfer to the heap region
 
            let values = &mut store.heap_regions[heap_pos as usize].values;
 
            debug_assert!(values.is_empty());
 
            values.reserve(num_values);
 
            for _ in 0..num_values {
 
                values.push(cur_frame.expr_values.pop_front().unwrap());
 
            }
 

	
 
            heap_pos
 
        }
 

	
 
        // Helper function to make sure that an index into an aray is valid.
 
        fn array_inclusive_index_is_invalid(store: &Store, array_heap_pos: u32, idx: i64) -> bool {
 
            let array_len = store.heap_regions[array_heap_pos as usize].values.len();
 
            return idx < 0 || idx >= array_len as i64;
 
        }
 

	
 
        fn array_exclusive_index_is_invalid(store: &Store, array_heap_pos: u32, idx: i64) -> bool {
 
            let array_len = store.heap_regions[array_heap_pos as usize].values.len();
 
            return idx < 0 || idx > array_len as i64;
 
        }
 

	
 
        fn construct_array_error(prompt: &Prompt, modules: &[Module], heap: &Heap, expr_id: ExpressionId, heap_pos: u32, idx: i64) -> EvalError {
 
            let array_len = prompt.store.heap_regions[heap_pos as usize].values.len();
 
            return EvalError::new_error_at_expr(
 
                prompt, modules, heap, expr_id,
 
                format!("index {} is out of bounds: array length is {}", idx, array_len)
 
            )
 
        }
 

	
 
        // Checking if we're at the end of execution
 
        let cur_frame = self.frames.last_mut().unwrap();
 
        if cur_frame.position.is_invalid() {
 
            if heap[cur_frame.definition].is_function() {
 
                todo!("End of function without return, return an evaluation error");
 
            }
 
            return Ok(EvalContinuation::Terminal);
 
        }
 

	
 
        debug_log!("Taking step in '{}'", heap[cur_frame.definition].identifier().value.as_str());
 

	
 
        // Execute all pending expressions
 
        while !cur_frame.expr_stack.is_empty() {
 
            let next = cur_frame.expr_stack.pop_back().unwrap();
 
            debug_log!("Expr stack: {:?}", next);
 
            match next {
 
                ExprInstruction::PushValToFront => {
 
                    cur_frame.expr_values.rotate_right(1);
 
                },
 
                ExprInstruction::EvalExpr(expr_id) => {
 
                    let expr = &heap[expr_id];
 
                    match expr {
 
                        Expression::Assignment(expr) => {
 
                            let to = cur_frame.expr_values.pop_back().unwrap().as_ref();
 
                            let rhs = cur_frame.expr_values.pop_back().unwrap();
 

	
 
                            // Note: although not pretty, the assignment operator takes ownership
 
                            // of the right-hand side value when possible. So we do not drop the
 
                            // rhs's optionally owned heap data.
 
                            let rhs = self.store.read_take_ownership(rhs);
 
                            apply_assignment_operator(&mut self.store, to, expr.operation, rhs);
 
                        },
 
                        Expression::Binding(_expr) => {
 
                            todo!("Binding expression");
 
                            let bind_to = cur_frame.expr_values.pop_back().unwrap();
 
                            let bind_from = cur_frame.expr_values.pop_back().unwrap();
 
                            let bind_to_heap_pos = bind_to.get_heap_pos();
 
                            let bind_from_heap_pos = bind_from.get_heap_pos();
 

	
 
                            let result = apply_binding_operator(&mut self.store, bind_to, bind_from);
 
                            self.store.drop_value(bind_to_heap_pos);
 
                            self.store.drop_value(bind_from_heap_pos);
 
                            cur_frame.expr_values.push_back(Value::Bool(result));
 
                        },
 
                        Expression::Conditional(expr) => {
 
                            // Evaluate testing expression, then extend the
 
                            // expression stack with the appropriate expression
 
                            let test_result = cur_frame.expr_values.pop_back().unwrap().as_bool();
 
                            if test_result {
 
                                cur_frame.serialize_expression(heap, expr.true_expression);
 
                            } else {
 
                                cur_frame.serialize_expression(heap, expr.false_expression);
 
                            }
 
                        },
 
                        Expression::Binary(expr) => {
 
                            let lhs = cur_frame.expr_values.pop_back().unwrap();
 
                            let rhs = cur_frame.expr_values.pop_back().unwrap();
 
                            let result = apply_binary_operator(&mut self.store, &lhs, expr.operation, &rhs);
 
                            cur_frame.expr_values.push_back(result);
 
                            self.store.drop_value(lhs.get_heap_pos());
 
                            self.store.drop_value(rhs.get_heap_pos());
 
                        },
 
                        Expression::Unary(expr) => {
 
                            let val = cur_frame.expr_values.pop_back().unwrap();
 
                            let result = apply_unary_operator(&mut self.store, expr.operation, &val);
 
                            cur_frame.expr_values.push_back(result);
 
                            self.store.drop_value(val.get_heap_pos());
 
                        },
 
                        Expression::Indexing(expr) => {
 
                            // Evaluate index. Never heap allocated so we do
 
                            // not have to drop it.
 
                            let index = cur_frame.expr_values.pop_back().unwrap();
 
                            let index = self.store.maybe_read_ref(&index);
 

	
 
                            debug_assert!(index.is_integer());
 
                            let index = if index.is_signed_integer() {
 
                                index.as_signed_integer() as i64
 
                            } else {
 
                                index.as_unsigned_integer() as i64
 
                            };
 

	
 
                            let subject = cur_frame.expr_values.pop_back().unwrap();
 

	
 
                            let (deallocate_heap_pos, value_to_push, subject_heap_pos) = match subject {
 
                                Value::Ref(value_ref) => {
 
                                    // Our expression stack value is a reference to something that
 
                                    // exists in the normal stack/heap. We don't want to deallocate
 
                                    // this thing. Rather we want to return a reference to it.
 
                                    let subject = self.store.read_ref(value_ref);
 
                                    let subject_heap_pos = subject.as_array();
 

	
 
                                    if array_inclusive_index_is_invalid(&self.store, subject_heap_pos, index) {
 
                                        return Err(construct_array_error(self, modules, heap, expr_id, subject_heap_pos, index));
 
                                    }
 

	
 
                                    (None, Value::Ref(ValueId::Heap(subject_heap_pos, index as u32)), subject_heap_pos)
 
                                },
 
                                _ => {
 
                                    // Our value lives on the expression stack, hence we need to
 
                                    // clone whatever we're referring to. Then drop the subject.
 
                                    let subject_heap_pos = subject.as_array();
 

	
 
                                    if array_inclusive_index_is_invalid(&self.store, subject_heap_pos, index) {
 
                                        return Err(construct_array_error(self, modules, heap, expr_id, subject_heap_pos, index));
 
                                    }
 

	
 
                                    let subject_indexed = Value::Ref(ValueId::Heap(subject_heap_pos, index as u32));
 
                                    (Some(subject_heap_pos), self.store.clone_value(subject_indexed), subject_heap_pos)
 
                                },
 
                            };
 

	
 
                            cur_frame.expr_values.push_back(value_to_push);
 
                            self.store.drop_value(deallocate_heap_pos);
 
                        },
 
                        Expression::Slicing(expr) => {
 
                            // Evaluate indices
 
                            let from_index = cur_frame.expr_values.pop_back().unwrap();
 
                            let from_index = self.store.maybe_read_ref(&from_index);
 
                            let to_index = cur_frame.expr_values.pop_back().unwrap();
 
                            let to_index = self.store.maybe_read_ref(&to_index);
 

	
 
                            debug_assert!(from_index.is_integer() && to_index.is_integer());
 
                            let from_index = if from_index.is_signed_integer() {
 
                                from_index.as_signed_integer()
 
                            } else {
 
                                from_index.as_unsigned_integer() as i64
 
                            };
 
                            let to_index = if to_index.is_signed_integer() {
 
                                to_index.as_signed_integer()
 
                            } else {
 
                                to_index.as_unsigned_integer() as i64
 
                            };
 

	
 
                            // Dereference subject if needed
 
                            let subject = cur_frame.expr_values.pop_back().unwrap();
 
                            let deref_subject = self.store.maybe_read_ref(&subject);
 

	
 
                            // Slicing needs to produce a copy anyway (with the
 
                            // current evaluator implementation)
 
@@ -416,359 +449,393 @@ impl Prompt {
 
                            let (deallocate_heap_pos, value_to_push) = match subject {
 
                                Value::Ref(value_ref) => {
 
                                    let subject = self.store.read_ref(value_ref);
 
                                    let subject_heap_pos = subject.as_struct();
 

	
 
                                    (None, Value::Ref(ValueId::Heap(subject_heap_pos, field_idx)))
 
                                },
 
                                _ => {
 
                                    let subject_heap_pos = subject.as_struct();
 
                                    let subject_indexed = Value::Ref(ValueId::Heap(subject_heap_pos, field_idx));
 
                                    (Some(subject_heap_pos), self.store.clone_value(subject_indexed))
 
                                },
 
                            };
 

	
 
                            cur_frame.expr_values.push_back(value_to_push);
 
                            self.store.drop_value(deallocate_heap_pos);
 
                        },
 
                        Expression::Literal(expr) => {
 
                            let value = match &expr.value {
 
                                Literal::Null => Value::Null,
 
                                Literal::True => Value::Bool(true),
 
                                Literal::False => Value::Bool(false),
 
                                Literal::Character(lit_value) => Value::Char(*lit_value),
 
                                Literal::String(lit_value) => {
 
                                    let heap_pos = self.store.alloc_heap();
 
                                    let values = &mut self.store.heap_regions[heap_pos as usize].values;
 
                                    let value = lit_value.as_str();
 
                                    debug_assert!(values.is_empty());
 
                                    values.reserve(value.len());
 
                                    for character in value.as_bytes() {
 
                                        debug_assert!(character.is_ascii());
 
                                        values.push(Value::Char(*character as char));
 
                                    }
 
                                    Value::String(heap_pos)
 
                                }
 
                                Literal::Integer(lit_value) => {
 
                                    use ConcreteTypePart as CTP;
 
                                    let def_types = types.get_procedure_expression_data(&cur_frame.definition, cur_frame.monomorph_idx);
 
                                    let concrete_type = &def_types.expr_data[expr.unique_id_in_definition as usize].expr_type;
 

	
 
                                    debug_assert_eq!(concrete_type.parts.len(), 1);
 
                                    match concrete_type.parts[0] {
 
                                        CTP::UInt8  => Value::UInt8(lit_value.unsigned_value as u8),
 
                                        CTP::UInt16 => Value::UInt16(lit_value.unsigned_value as u16),
 
                                        CTP::UInt32 => Value::UInt32(lit_value.unsigned_value as u32),
 
                                        CTP::UInt64 => Value::UInt64(lit_value.unsigned_value as u64),
 
                                        CTP::SInt8  => Value::SInt8(lit_value.unsigned_value as i8),
 
                                        CTP::SInt16 => Value::SInt16(lit_value.unsigned_value as i16),
 
                                        CTP::SInt32 => Value::SInt32(lit_value.unsigned_value as i32),
 
                                        CTP::SInt64 => Value::SInt64(lit_value.unsigned_value as i64),
 
                                        _ => unreachable!("got concrete type {:?} for integer literal at expr {:?}", concrete_type, expr_id),
 
                                    }
 
                                }
 
                                Literal::Struct(lit_value) => {
 
                                    let heap_pos = transfer_expression_values_front_into_heap(
 
                                        cur_frame, &mut self.store, lit_value.fields.len()
 
                                    );
 
                                    Value::Struct(heap_pos)
 
                                }
 
                                Literal::Enum(lit_value) => {
 
                                    Value::Enum(lit_value.variant_idx as i64)
 
                                }
 
                                Literal::Union(lit_value) => {
 
                                    let heap_pos = transfer_expression_values_front_into_heap(
 
                                        cur_frame, &mut self.store, lit_value.values.len()
 
                                    );
 
                                    Value::Union(lit_value.variant_idx as i64, heap_pos)
 
                                }
 
                                Literal::Array(lit_value) => {
 
                                    let heap_pos = transfer_expression_values_front_into_heap(
 
                                        cur_frame, &mut self.store, lit_value.len()
 
                                    );
 
                                    Value::Array(heap_pos)
 
                                }
 
                            };
 

	
 
                            cur_frame.expr_values.push_back(value);
 
                        },
 
                        Expression::Cast(expr) => {
 
                            let mono_data = types.get_procedure_expression_data(&cur_frame.definition, cur_frame.monomorph_idx);
 
                            let output_type = &mono_data.expr_data[expr.unique_id_in_definition as usize].expr_type;
 

	
 
                            // Typechecking reduced this to two cases: either we
 
                            // have casting noop (same types), or we're casting
 
                            // between integer/bool/char types.
 
                            let subject = cur_frame.expr_values.pop_back().unwrap();
 
                            match apply_casting(&mut self.store, output_type, &subject) {
 
                                Ok(value) => cur_frame.expr_values.push_back(value),
 
                                Err(msg) => {
 
                                    return Err(EvalError::new_error_at_expr(self, modules, heap, expr.this.upcast(), msg));
 
                                }
 
                            }
 

	
 
                            self.store.drop_value(subject.get_heap_pos());
 
                        }
 
                        Expression::Call(expr) => {
 
                            // Push a new frame. Note that all expressions have
 
                            // been pushed to the front, so they're in the order
 
                            // of the definition.
 
                            let num_args = expr.arguments.len();
 

	
 
                            // Determine stack boundaries
 
                            let cur_stack_boundary = self.store.cur_stack_boundary;
 
                            let new_stack_boundary = self.store.stack.len();
 

	
 
                            // Push new boundary and function arguments for new frame
 
                            self.store.stack.push(Value::PrevStackBoundary(cur_stack_boundary as isize));
 
                            for _ in 0..num_args {
 
                                let argument = self.store.read_take_ownership(cur_frame.expr_values.pop_front().unwrap());
 
                                self.store.stack.push(argument);
 
                            }
 
                            // If we're dealing with a builtin we don't do any
 
                            // fancy shenanigans at all, just push the result.
 
                            match expr.method {
 
                                Method::Length => {
 
                                    let value = cur_frame.expr_values.pop_front().unwrap();
 
                                    let value_heap_pos = value.get_heap_pos();
 
                                    let value = self.store.maybe_read_ref(&value);
 

	
 
                                    let heap_pos = match value {
 
                                        Value::Array(pos) => *pos,
 
                                        Value::String(pos) => *pos,
 
                                        _ => unreachable!("length(...) on {:?}", value),
 
                                    };
 

	
 
                                    let len = self.store.heap_regions[heap_pos as usize].values.len();
 

	
 
                                    // TODO: @PtrInt
 
                                    cur_frame.expr_values.push_back(Value::UInt32(len as u32));
 
                                    self.store.drop_value(value_heap_pos);
 
                                },
 
                                Method::UserComponent => todo!("component creation"),
 
                                Method::UserFunction => {
 
                                    // Push a new frame. Note that all expressions have
 
                                    // been pushed to the front, so they're in the order
 
                                    // of the definition.
 
                                    let num_args = expr.arguments.len();
 

	
 
                                    // Determine stack boundaries
 
                                    let cur_stack_boundary = self.store.cur_stack_boundary;
 
                                    let new_stack_boundary = self.store.stack.len();
 

	
 
                                    // Push new boundary and function arguments for new frame
 
                                    self.store.stack.push(Value::PrevStackBoundary(cur_stack_boundary as isize));
 
                                    for _ in 0..num_args {
 
                                        let argument = self.store.read_take_ownership(cur_frame.expr_values.pop_front().unwrap());
 
                                        self.store.stack.push(argument);
 
                                    }
 

	
 
                            // Determine the monomorph index of the function we're calling
 
                            let mono_data = types.get_procedure_expression_data(&cur_frame.definition, cur_frame.monomorph_idx);
 
                            let call_data = &mono_data.expr_data[expr.unique_id_in_definition as usize];
 
                                    // Determine the monomorph index of the function we're calling
 
                                    let mono_data = types.get_procedure_expression_data(&cur_frame.definition, cur_frame.monomorph_idx);
 
                                    let call_data = &mono_data.expr_data[expr.unique_id_in_definition as usize];
 

	
 
                            // Push the new frame
 
                            self.frames.push(Frame::new(heap, expr.definition, call_data.field_or_monomorph_idx));
 
                            self.store.cur_stack_boundary = new_stack_boundary;
 
                                    // Push the new frame and reserve its stack size
 
                                    let new_frame = Frame::new(heap, expr.definition, call_data.field_or_monomorph_idx);
 
                                    let new_stack_size = new_frame.max_stack_size;
 
                                    self.frames.push(new_frame);
 
                                    self.store.cur_stack_boundary = new_stack_boundary;
 
                                    self.store.reserve_stack(new_stack_size);
 

	
 
                            // To simplify the logic a little bit we will now
 
                            // return and ask our caller to call us again
 
                            return Ok(EvalContinuation::Stepping);
 
                                    // To simplify the logic a little bit we will now
 
                                    // return and ask our caller to call us again
 
                                    return Ok(EvalContinuation::Stepping);
 
                                },
 
                                _ => todo!("other builtins"),
 
                            }
 
                        },
 
                        Expression::Variable(expr) => {
 
                            let variable = &heap[expr.declaration.unwrap()];
 
                            cur_frame.expr_values.push_back(Value::Ref(ValueId::Stack(variable.unique_id_in_scope as StackPos)));
 
                            let ref_value = if expr.used_as_binding_target {
 
                                Value::Binding(variable.unique_id_in_scope as StackPos)
 
                            } else {
 
                                Value::Ref(ValueId::Stack(variable.unique_id_in_scope as StackPos))
 
                            };
 
                            cur_frame.expr_values.push_back(ref_value);
 
                        }
 
                    }
 
                }
 
            }
 
        }
 

	
 
        debug_log!("Frame [{:?}] at {:?}, stack size = {}", cur_frame.definition, cur_frame.position, self.store.stack.len());
 
        debug_log!("Frame [{:?}] at {:?}", cur_frame.definition, cur_frame.position);
 
        if debug_enabled!() {
 
            debug_log!("Stack:");
 
            debug_log!("Expression value stack (size = {}):", cur_frame.expr_values.len());
 
            for (stack_idx, stack_val) in cur_frame.expr_values.iter().enumerate() {
 
                debug_log!("  [{:03}] {:?}", stack_idx, stack_val);
 
            }
 

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

	
 
            debug_log!("Heap:");
 
            for (heap_idx, heap_region) in self.store.heap_regions.iter().enumerate() {
 
                let is_free = self.store.free_regions.iter().any(|idx| *idx as usize == heap_idx);
 
                debug_log!("  [{:03}] in_use: {}, len: {}, vals: {:?}", heap_idx, !is_free, heap_region.values.len(), &heap_region.values);
 
            }
 
        }
 
        // No (more) expressions to evaluate. So evaluate statement (that may
 
        // depend on the result on the last evaluated expression(s))
 
        let stmt = &heap[cur_frame.position];
 
        let return_value = match stmt {
 
            Statement::Block(stmt) => {
 
                // Reserve space on stack, but also make sure excess stack space
 
                // is cleared
 
                self.store.clear_stack(stmt.first_unique_id_in_scope as usize);
 
                self.store.reserve_stack(stmt.next_unique_id_in_scope as usize);
 
                cur_frame.position = stmt.statements[0];
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::EndBlock(stmt) => {
 
                let block = &heap[stmt.start_block];
 
                self.store.clear_stack(block.first_unique_id_in_scope as usize);
 
                cur_frame.position = stmt.next;
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::Local(stmt) => {
 
                match stmt {
 
                    LocalStatement::Memory(stmt) => {
 
                        let variable = &heap[stmt.variable];
 
                        self.store.write(ValueId::Stack(variable.unique_id_in_scope as u32), Value::Unassigned);
 

	
 
                        cur_frame.position = stmt.next;
 
                    },
 
                    LocalStatement::Channel(stmt) => {
 
                        let [from_value, to_value] = ctx.new_channel();
 
                        self.store.write(ValueId::Stack(heap[stmt.from].unique_id_in_scope as u32), from_value);
 
                        self.store.write(ValueId::Stack(heap[stmt.to].unique_id_in_scope as u32), to_value);
 

	
 
                        cur_frame.position = stmt.next;
 
                    }
 
                }
 

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

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

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::EndIf(stmt) => {
 
                cur_frame.position = stmt.next;
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::While(stmt) => {
 
                debug_assert_eq!(cur_frame.expr_values.len(), 1, "expected one expr value for while statement");
 
                let test_value = cur_frame.expr_values.pop_back().unwrap().as_bool();
 
                if test_value {
 
                    cur_frame.position = stmt.body.upcast();
 
                } else {
 
                    cur_frame.position = stmt.end_while.upcast();
 
                }
 

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

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

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

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

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

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

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

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

	
 
                // Clean up our section of the stack
 
                self.store.clear_stack(0);
 
                self.store.stack.truncate(self.store.cur_stack_boundary + 1);
 
                let prev_stack_idx = self.store.stack.pop().unwrap().as_stack_boundary();
 

	
 
                // TODO: Temporary hack for testing, remove at some point
 
                if self.frames.is_empty() {
 
                    debug_assert!(prev_stack_idx == -1);
 
                    debug_assert!(self.store.stack.len() == 0);
 
                    self.store.stack.push(return_value);
 
                    return Ok(EvalContinuation::Terminal);
 
                }
 

	
 
                debug_assert!(prev_stack_idx >= 0);
 
                // Return to original state of stack frame
 
                self.store.cur_stack_boundary = prev_stack_idx as usize;
 
                let cur_frame = self.frames.last_mut().unwrap();
 
                cur_frame.expr_values.push_back(return_value);
 

	
 
                // We just returned to the previous frame, which might be in
 
                // the middle of evaluating expressions for a particular
 
                // statement. So we don't want to enter the code below.
 
                return Ok(EvalContinuation::Stepping);
 
            },
 
            Statement::Goto(stmt) => {
 
                cur_frame.position = stmt.target.unwrap().upcast();
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::New(stmt) => {
 
                let call_expr = &heap[stmt.expression];
 
                debug_assert!(heap[call_expr.definition].is_component());
 
                debug_assert_eq!(
 
                    cur_frame.expr_values.len(), heap[call_expr.definition].parameters().len(),
 
                    "mismatch in expr stack size and number of arguments for new statement"
 
                );
 

	
 
                let mono_data = types.get_procedure_expression_data(&cur_frame.definition, cur_frame.monomorph_idx);
 
                let expr_data = &mono_data.expr_data[call_expr.unique_id_in_definition as usize];
 

	
 
                // Note that due to expression value evaluation they exist in
 
                // reverse order on the stack.
 
                // TODO: Revise this code, keep it as is to be compatible with current runtime
 
                let mut args = Vec::new();
 
                while let Some(value) = cur_frame.expr_values.pop_front() {
 
                    args.push(value);
 
                }
 

	
 
                // Construct argument group, thereby copying heap regions
 
                let argument_group = ValueGroup::from_store(&self.store, &args);
 

	
 
                // Clear any heap regions
 
                for arg in &args {
 
                    self.store.drop_value(arg.get_heap_pos());
 
                }
 

	
 
                cur_frame.position = stmt.next;
 

	
 
                todo!("Make sure this is handled correctly, transfer 'heap' values to another Prompt");
 
                Ok(EvalContinuation::NewComponent(call_expr.definition, expr_data.field_or_monomorph_idx, argument_group))
 
            },
 
            Statement::Expression(stmt) => {
 
                // The expression has just been completely evaluated. Some
 
                // values might have remained on the expression value stack.
 
                cur_frame.expr_values.clear();
 
                cur_frame.position = stmt.next;
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
        };
 

	
 
        assert!(
 
            cur_frame.expr_values.is_empty(),
 
            "This is a debugging assertion that will fail if you perform expressions without \
 
            assigning to anything. This should be completely valid, and this assertion should be \
 
            replaced by something that clears the expression values if needed, but I'll keep this \
 
            in for now for debugging purposes."
 
        );
 

	
 
        // If the next statement requires evaluating expressions then we push
 
        // these onto the expression stack. This way we will evaluate this
 
        // stack in the next loop, then evaluate the statement using the result
 
        // from the expression evaluation.
 
        if !cur_frame.position.is_invalid() {
 
            let stmt = &heap[cur_frame.position];
 

	
 
            match stmt {
 
                Statement::If(stmt) => cur_frame.prepare_single_expression(heap, stmt.test),
 
                Statement::While(stmt) => cur_frame.prepare_single_expression(heap, stmt.test),
 
                Statement::Return(stmt) => {
 
                    debug_assert_eq!(stmt.expressions.len(), 1); // TODO: @ReturnValues
 
                    cur_frame.prepare_single_expression(heap, stmt.expressions[0]);
 
                },
 
                Statement::New(stmt) => {
 
                    // Note that we will end up not evaluating the call itself.
 
                    // Rather we will evaluate its expressions and then
 
                    // instantiate the component upon reaching the "new" stmt.
 
                    let call_expr = &heap[stmt.expression];
 
                    cur_frame.prepare_multiple_expressions(heap, &call_expr.arguments);
src/protocol/eval/store.rs
Show inline comments
 

	
 
use std::collections::VecDeque;
 

	
 
use super::value::{Value, ValueId, HeapPos};
 

	
 
#[derive(Debug, Clone)]
 
pub(crate) struct HeapAllocation {
 
    pub values: Vec<Value>,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub(crate) struct Store {
 
    // The stack where variables/parameters are stored. Note that this is a
 
    // non-shrinking stack. So it may be filled with garbage.
 
    pub(crate) stack: Vec<Value>,
 
    // Represents the place in the stack where we find the `PrevStackBoundary`
 
    // value containing the previous stack boundary. This is so we can pop from
 
    // the stack after function calls.
 
    pub(crate) cur_stack_boundary: usize,
 
    // A rather ridiculous simulated heap, but this allows us to "allocate"
 
    // things that occupy more then one stack slot.
 
    pub(crate) heap_regions: Vec<HeapAllocation>,
 
    pub(crate) free_regions: VecDeque<HeapPos>,
 
}
 

	
 
impl Store {
 
    pub(crate) fn new() -> Self {
 
        let mut store = Self{
 
            stack: Vec::with_capacity(64),
 
            cur_stack_boundary: 0,
 
            heap_regions: Vec::new(),
 
            free_regions: VecDeque::new(),
 
        };
 

	
 
        store.stack.push(Value::PrevStackBoundary(-1));
 
        store
 
    }
 

	
 
    /// Resizes(!) the stack to fit the required number of values. Any
 
    /// unallocated slots are initialized to `Unassigned`. The specified stack
 
    /// index is exclusive.
 
    pub(crate) fn reserve_stack(&mut self, unique_stack_idx: usize) {
 
        let new_size = self.cur_stack_boundary + unique_stack_idx + 1;
 
    pub(crate) fn reserve_stack(&mut self, unique_stack_idx: u32) {
 
        let new_size = self.cur_stack_boundary + unique_stack_idx as usize + 1;
 
        if new_size > self.stack.len() {
 
            self.stack.resize(new_size, Value::Unassigned);
 
        }
 
    }
 

	
 
    /// Clears values on the stack and removes their heap allocations when
 
    /// applicable. The specified index itself will also be cleared (so if you
 
    /// specify 0 all values in the frame will be destroyed)
 
    pub(crate) fn clear_stack(&mut self, unique_stack_idx: usize) {
 
        let new_size = self.cur_stack_boundary + unique_stack_idx + 1;
 
        for idx in new_size..self.stack.len() {
 
            self.drop_value(self.stack[idx].get_heap_pos());
 
            self.stack[idx] = Value::Unassigned;
 
        }
 
        self.stack.truncate(new_size);
 
    }
 

	
 
    /// Reads a value and takes ownership. This is different from a move because
 
    /// the value might indirectly reference stack/heap values. For these kinds
 
    /// values we will actually return a cloned value.
 
    pub(crate) fn read_take_ownership(&mut self, value: Value) -> Value {
 
        match value {
 
            Value::Ref(ValueId::Stack(pos)) => {
 
                let abs_pos = self.cur_stack_boundary + 1 + pos as usize;
 
                return self.clone_value(self.stack[abs_pos].clone());
 
            },
 
            Value::Ref(ValueId::Heap(heap_pos, value_idx)) => {
 
                let heap_pos = heap_pos as usize;
 
                let value_idx = value_idx as usize;
 
                return self.clone_value(self.heap_regions[heap_pos].values[value_idx].clone());
 
            },
 
            _ => value
 
        }
 
    }
 

	
 
    /// Reads a value from a specific address. The value is always copied, hence
 
    /// if the value ends up not being written, one should call `drop_value` on
 
    /// it.
 
    pub(crate) fn read_copy(&mut self, address: ValueId) -> Value {
 
        match address {
 
            ValueId::Stack(pos) => {
 
                let cur_pos = self.cur_stack_boundary + 1 + pos as usize;
 
                return self.clone_value(self.stack[cur_pos].clone());
 
            },
 
            ValueId::Heap(heap_pos, region_idx) => {
 
                return self.clone_value(self.heap_regions[heap_pos as usize].values[region_idx as usize].clone())
 
            }
 
        }
 
    }
 

	
 
    /// Potentially reads a reference value. The supplied `Value` might not
 
    /// actually live in the store's stack or heap, but live on the expression
 
    /// stack. Generally speaking you only want to call this if the value comes
 
    /// from the expression stack due to borrowing issues.
 
    pub(crate) fn maybe_read_ref<'a>(&'a self, value: &'a Value) -> &'a Value {
 
        match value {
 
            Value::Ref(value_id) => self.read_ref(*value_id),
 
            _ => value,
 
        }
 
    }
 

	
 
    /// Returns an immutable reference to the value pointed to by an address
 
    pub(crate) fn read_ref(&self, address: ValueId) -> &Value {
 
        match address {
 
            ValueId::Stack(pos) => {
 
                let cur_pos = self.cur_stack_boundary + 1 + pos as usize;
 
                return &self.stack[cur_pos];
 
            },
 
            ValueId::Heap(heap_pos, region_idx) => {
 
                return &self.heap_regions[heap_pos as usize].values[region_idx as usize];
 
            }
 
        }
 
    }
 

	
 
    /// Returns a mutable reference to the value pointed to by an address
 
    pub(crate) fn read_mut_ref(&mut self, address: ValueId) -> &mut Value {
 
        match address {
 
            ValueId::Stack(pos) => {
 
                let cur_pos = self.cur_stack_boundary + 1 + pos as usize;
 
                return &mut self.stack[cur_pos];
 
            },
 
            ValueId::Heap(heap_pos, region_idx) => {
 
                return &mut self.heap_regions[heap_pos as usize].values[region_idx as usize];
 
            }
 
        }
 
    }
 

	
 
    /// Writes a value
 
    pub(crate) fn write(&mut self, address: ValueId, value: Value) {
 
        match address {
 
            ValueId::Stack(pos) => {
 
                let cur_pos = self.cur_stack_boundary + 1 + pos as usize;
 
                self.drop_value(self.stack[cur_pos].get_heap_pos());
 
                self.stack[cur_pos] = value;
 
            },
 
            ValueId::Heap(heap_pos, region_idx) => {
 
                let heap_pos = heap_pos as usize;
 
                let region_idx = region_idx as usize;
 
                self.drop_value(self.heap_regions[heap_pos].values[region_idx].get_heap_pos());
 
                self.heap_regions[heap_pos].values[region_idx] = value
 
            }
 
        }
 
    }
 

	
 
    /// This thing takes a cloned Value, because of borrowing issues (which is
 
    /// either a direct value, or might contain an index to a heap value), but
 
    /// should be treated by the programmer as a reference (i.e. don't call
 
    /// `drop_value(thing)` after calling `clone_value(thing.clone())`.
 
    pub(crate) fn clone_value(&mut self, value: Value) -> Value {
 
        // Quickly check if the value is not on the heap
 
        let source_heap_pos = value.get_heap_pos();
src/protocol/eval/value.rs
Show inline comments
 

	
 
use super::store::*;
 
use crate::PortId;
 
use crate::protocol::ast::{
 
    AssignmentOperator,
 
    BinaryOperator,
 
    UnaryOperator,
 
    ConcreteType,
 
    ConcreteTypePart,
 
};
 
use crate::protocol::parser::token_parsing::*;
 

	
 
pub type StackPos = u32;
 
pub type HeapPos = u32;
 

	
 
#[derive(Debug, Copy, Clone)]
 
pub enum ValueId {
 
    Stack(StackPos), // place on stack
 
    Heap(HeapPos, u32), // allocated region + values within that region
 
}
 

	
 
/// Represents a value stored on the stack or on the heap. Some values contain
 
/// a `HeapPos`, implying that they're stored in the store's `Heap`. Clearing
 
/// a `Value` with a `HeapPos` from a stack must also clear the associated
 
/// region from the `Heap`.
 
#[derive(Debug, Clone)]
 
pub enum Value {
 
    // Special types, never encountered during evaluation if the compiler works correctly
 
    Unassigned,                 // Marker when variables are first declared, immediately followed by assignment
 
    PrevStackBoundary(isize),   // Marker for stack frame beginning, so we can pop stack values
 
    Ref(ValueId),               // Reference to a value, used by expressions producing references
 
    Binding(StackPos),          // Reference to a binding variable (reserved on the stack)
 
    // Builtin types
 
    Input(PortId),
 
    Output(PortId),
 
    Message(HeapPos),
 
    Null,
 
    Bool(bool),
 
    Char(char),
 
    String(HeapPos),
 
    UInt8(u8),
 
    UInt16(u16),
 
    UInt32(u32),
 
    UInt64(u64),
 
    SInt8(i8),
 
    SInt16(i16),
 
    SInt32(i32),
 
    SInt64(i64),
 
    Array(HeapPos),
 
    // Instances of user-defined types
 
    Enum(i64),
 
    Union(i64, HeapPos),
 
    Struct(HeapPos),
 
}
 

	
 
macro_rules! impl_union_unpack_as_value {
 
    ($func_name:ident, $variant_name:path, $return_type:ty) => {
 
        impl Value {
 
            pub(crate) fn $func_name(&self) -> $return_type {
 
                match self {
 
                    $variant_name(v) => *v,
 
                    _ => panic!(concat!("called ", stringify!($func_name()), " on {:?}"), self),
 
                }
 
            }
 
        }
 
    }
 
}
 

	
 
impl_union_unpack_as_value!(as_stack_boundary, Value::PrevStackBoundary, isize);
 
impl_union_unpack_as_value!(as_ref,     Value::Ref,     ValueId);
 
impl_union_unpack_as_value!(as_input,   Value::Input,   PortId);
 
impl_union_unpack_as_value!(as_output,  Value::Output,  PortId);
 
impl_union_unpack_as_value!(as_message, Value::Message, HeapPos);
 
impl_union_unpack_as_value!(as_bool,    Value::Bool,    bool);
 
impl_union_unpack_as_value!(as_char,    Value::Char,    char);
 
impl_union_unpack_as_value!(as_string,  Value::String,  HeapPos);
 
impl_union_unpack_as_value!(as_uint8,   Value::UInt8,   u8);
 
impl_union_unpack_as_value!(as_uint16,  Value::UInt16,  u16);
 
impl_union_unpack_as_value!(as_uint32,  Value::UInt32,  u32);
 
impl_union_unpack_as_value!(as_uint64,  Value::UInt64,  u64);
 
impl_union_unpack_as_value!(as_sint8,   Value::SInt8,   i8);
 
impl_union_unpack_as_value!(as_sint16,  Value::SInt16,  i16);
 
impl_union_unpack_as_value!(as_sint32,  Value::SInt32,  i32);
 
impl_union_unpack_as_value!(as_sint64,  Value::SInt64,  i64);
 
impl_union_unpack_as_value!(as_array,   Value::Array,   HeapPos);
 
impl_union_unpack_as_value!(as_enum,    Value::Enum,    i64);
 
impl_union_unpack_as_value!(as_struct,  Value::Struct,  HeapPos);
 

	
 
impl Value {
 
    pub(crate) fn as_union(&self) -> (i64, HeapPos) {
 
        match self {
 
            Value::Union(tag, v) => (*tag, *v),
 
            _ => panic!("called as_union on {:?}", self),
 
        }
 
    }
 

	
 
    pub(crate) fn is_integer(&self) -> bool {
 
        match self {
 
            Value::UInt8(_) | Value::UInt16(_) | Value::UInt32(_) | Value::UInt64(_) |
 
            Value::SInt8(_) | Value::SInt16(_) | Value::SInt32(_) | Value::SInt64(_) => true,
 
            _ => false
 
        }
 
    }
 

	
 
    pub(crate) fn is_unsigned_integer(&self) -> bool {
 
        match self {
 
            Value::UInt8(_) | Value::UInt16(_) | Value::UInt32(_) | Value::UInt64(_) => true,
 
            _ => false
 
        }
 
    }
 

	
 
    pub(crate) fn is_signed_integer(&self) -> bool {
 
        match self {
 
            Value::SInt8(_) | Value::SInt16(_) | Value::SInt32(_) | Value::SInt64(_) => true,
 
            _ => false
 
        }
 
    }
 

	
 
    pub(crate) fn as_unsigned_integer(&self) -> u64 {
 
        match self {
 
            Value::UInt8(v)  => *v as u64,
 
            Value::UInt16(v) => *v as u64,
 
            Value::UInt32(v) => *v as u64,
 
            Value::UInt64(v) => *v as u64,
 
            _ => unreachable!("called as_unsigned_integer on {:?}", self),
 
        }
 
    }
 

	
 
@@ -438,375 +439,442 @@ pub(crate) fn apply_binary_operator(store: &mut Store, lhs: &Value, op: BinaryOp
 
        BO::BitwiseAnd       => { apply_int_op_and_return_self!(lhs, &,  op, rhs); },
 
        BO::Equality         => { Value::Bool(apply_equality_operator(store, lhs, rhs)) },
 
        BO::Inequality       => { Value::Bool(apply_inequality_operator(store, lhs, rhs)) },
 
        BO::LessThan         => { apply_int_op_and_return_bool!(lhs, <,  op, rhs); },
 
        BO::GreaterThan      => { apply_int_op_and_return_bool!(lhs, >,  op, rhs); },
 
        BO::LessThanEqual    => { apply_int_op_and_return_bool!(lhs, <=, op, rhs); },
 
        BO::GreaterThanEqual => { apply_int_op_and_return_bool!(lhs, >=, op, rhs); },
 
        BO::ShiftLeft        => { apply_int_op_and_return_self!(lhs, <<, op, rhs); },
 
        BO::ShiftRight       => { apply_int_op_and_return_self!(lhs, >>, op, rhs); },
 
        BO::Add              => { apply_int_op_and_return_self!(lhs, +,  op, rhs); },
 
        BO::Subtract         => { apply_int_op_and_return_self!(lhs, -,  op, rhs); },
 
        BO::Multiply         => { apply_int_op_and_return_self!(lhs, *,  op, rhs); },
 
        BO::Divide           => { apply_int_op_and_return_self!(lhs, /,  op, rhs); },
 
        BO::Remainder        => { apply_int_op_and_return_self!(lhs, %,  op, rhs); }
 
    }
 
}
 

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

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

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

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

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

	
 
    // Note: character is not included, needs per-type checking
 
    macro_rules! unchecked_cast {
 
        ($input: expr, $output_part: expr) => {
 
            return Ok(match $output_part {
 
                CTP::UInt8 => Value::UInt8($input as u8),
 
                CTP::UInt16 => Value::UInt16($input as u16),
 
                CTP::UInt32 => Value::UInt32($input as u32),
 
                CTP::UInt64 => Value::UInt64($input as u64),
 
                CTP::SInt8 => Value::SInt8($input as i8),
 
                CTP::SInt16 => Value::SInt16($input as i16),
 
                CTP::SInt32 => Value::SInt32($input as i32),
 
                CTP::SInt64 => Value::SInt64($input as i64),
 
                _ => unreachable!()
 
            })
 
        }
 
    };
 
    }
 

	
 
    macro_rules! from_unsigned_cast {
 
        ($input:expr, $input_type:ty, $output_part:expr) => {
 
            {
 
                let target_type_name = match $output_part {
 
                    CTP::Bool => return Ok(Value::Bool($input != 0)),
 
                    CTP::Character => if $input <= u8::MAX as $input_type {
 
                        return Ok(Value::Char(($input as u8) as char))
 
                    } else {
 
                        KW_TYPE_CHAR_STR
 
                    },
 
                    CTP::UInt8 => if $input <= u8::MAX as $input_type {
 
                        return Ok(Value::UInt8($input as u8))
 
                    } else {
 
                        KW_TYPE_UINT8_STR
 
                    },
 
                    CTP::UInt16 => if $input <= u16::MAX as $input_type {
 
                        return Ok(Value::UInt16($input as u16))
 
                    } else {
 
                        KW_TYPE_UINT16_STR
 
                    },
 
                    CTP::UInt32 => if $input <= u32::MAX as $input_type {
 
                        return Ok(Value::UInt32($input as u32))
 
                    } else {
 
                        KW_TYPE_UINT32_STR
 
                    },
 
                    CTP::UInt64 => return Ok(Value::UInt64($input as u64)), // any unsigned int to u64 is fine
 
                    CTP::SInt8 => if $input <= i8::MAX as $input_type {
 
                        return Ok(Value::SInt8($input as i8))
 
                    } else {
 
                        KW_TYPE_SINT8_STR
 
                    },
 
                    CTP::SInt16 => if $input <= i16::MAX as $input_type {
 
                        return Ok(Value::SInt16($input as i16))
 
                    } else {
 
                        KW_TYPE_SINT16_STR
 
                    },
 
                    CTP::SInt32 => if $input <= i32::MAX as $input_type {
 
                        return Ok(Value::SInt32($input as i32))
 
                    } else {
 
                        KW_TYPE_SINT32_STR
 
                    },
 
                    CTP::SInt64 => if $input <= i64::MAX as $input_type {
 
                        return Ok(Value::SInt64($input as i64))
 
                    } else {
 
                        KW_TYPE_SINT64_STR
 
                    },
 
                    _ => unreachable!(),
 
                };
 

	
 
                return Err(format!("value is '{}' which doesn't fit in a type '{}'", $input, target_type_name));
 
            }
 
        }
 
    }
 

	
 
    macro_rules! from_signed_cast {
 
        // Programmer note: for signed checking we cannot do
 
        //  output_type::MAX as input_type,
 
        //
 
        // because if the output type's width is larger than the input type,
 
        // then the cast results in a negative number. So we mask with the
 
        // maximum possible value the input type can become. As in:
 
        //  (output_type::MAX as input_type) & input_type::MAX
 
        //
 
        // This way:
 
        // 1. output width is larger than input width: fine in all cases, we
 
        //  simply compare against the max input value, which is always true.
 
        // 2. output width is equal to input width: by masking we "remove the
 
        //  signed bit from the unsigned number" and again compare against the
 
        //  maximum input value.
 
        // 3. output width is smaller than the input width: masking does nothing
 
        //  because the signed bit is never set, and we simply compare against
 
        //  the maximum possible output value.
 
        //
 
        // A similar kind of mechanism for the minimum value, but here we do
 
        // a binary OR. We do a:
 
        //  (output_type::MIN as input_type) & input_type::MIN
 
        //
 
        // This way:
 
        // 1. output width is larger than input width: initial cast truncates to
 
        //  0, then we OR with the actual minimum value, so we attain the
 
        //  minimum value of the input type.
 
        // 2. output width is equal to input width: we OR the minimum value with
 
        //  itself.
 
        // 3. output width is smaller than input width: the cast produces the
 
        //  min value of the output type, the subsequent OR does nothing, as it
 
        //  essentially just sets the signed bit (which must already be set,
 
        //  since we're dealing with a signed minimum value)
 
        //
 
        // After all of this expanding, we simply hope the compiler does a best
 
        // effort constant expression evaluation, and presto!
 
        ($input:expr, $input_type:ty, $output_type:expr) => {
 
            {
 
                let target_type_name = match $output_type {
 
                    CTP::Bool => return Ok(Value::Bool($input != 0)),
 
                    CTP::Character => if $input >= 0 && $input <= (u8::max as $input_type & <$input_type>::MAX) {
 
                        return Ok(Value::Char(($input as u8) as char))
 
                    } else {
 
                        KW_TYPE_CHAR_STR
 
                    },
 
                    CTP::UInt8 => if $input >= 0 && $input <= ((u8::MAX as $input_type) & <$input_type>::MAX) {
 
                        return Ok(Value::UInt8($input as u8));
 
                    } else {
 
                        KW_TYPE_UINT8_STR
 
                    },
 
                    CTP::UInt16 => if $input >= 0 && $input <= ((u16::MAX as $input_type) & <$input_type>::MAX) {
 
                        return Ok(Value::UInt16($input as u16));
 
                    } else {
 
                        KW_TYPE_UINT16_STR
 
                    },
 
                    CTP::UInt32 => if $input >= 0 && $input <= ((u32::MAX as $input_type) & <$input_type>::MAX) {
 
                        return Ok(Value::UInt32($input as u32));
 
                    } else {
 
                        KW_TYPE_UINT32_STR
 
                    },
 
                    CTP::UInt64 => if $input >= 0 && $input <= ((u64::MAX as $input_type) & <$input_type>::MAX) {
 
                        return Ok(Value::UInt64($input as u64));
 
                    } else {
 
                        KW_TYPE_UINT64_STR
 
                    },
 
                    CTP::SInt8 => if $input >= ((i8::MIN as $input_type) | <$input_type>::MIN) && $input <= ((i8::MAX as $input_type) & <$input_type>::MAX) {
 
                        return Ok(Value::SInt8($input as i8));
 
                    } else {
 
                        KW_TYPE_SINT8_STR
 
                    },
 
                    CTP::SInt16 => if $input >= ((i16::MIN as $input_type | <$input_type>::MIN)) && $input <= ((i16::MAX as $input_type) & <$input_type>::MAX) {
 
                        return Ok(Value::SInt16($input as i16));
 
                    } else {
 
                        KW_TYPE_SINT16_STR
 
                    },
 
                    CTP::SInt32 => if $input >= ((i32::MIN as $input_type | <$input_type>::MIN)) && $input <= ((i32::MAX as $input_type) & <$input_type>::MAX) {
 
                        return Ok(Value::SInt32($input as i32));
 
                    } else {
 
                        KW_TYPE_SINT32_STR
 
                    },
 
                    CTP::SInt64 => return Ok(Value::SInt64($input as i64)),
 
                    _ => unreachable!(),
 
                };
 

	
 
                return Err(format!("value is '{}' which doesn't fit in a type '{}'", $input, target_type_name));
 
            }
 
        }
 
    }
 

	
 
    // If here, then the types might still be equal, but at least we're dealing
 
    // with a simple integer/boolean/character input and output type.
 
    let subject = store.maybe_read_ref(subject);
 
    match subject {
 
        Value::Bool(val) => {
 
            match part {
 
                CTP::Bool => return Ok(Value::Bool(*val)),
 
                CTP::Character => return Ok(Value::Char(1 as char)),
 
                _ => unchecked_cast!(*val, part),
 
            }
 
        },
 
        Value::Char(val) => {
 
            match part {
 
                CTP::Bool => return Ok(Value::Bool(*val != 0 as char)),
 
                CTP::Character => return Ok(Value::Char(*val)),
 
                _ => unchecked_cast!(*val, part),
 
            }
 
        },
 
        Value::UInt8(val) => from_unsigned_cast!(*val, u8, part),
 
        Value::UInt16(val) => from_unsigned_cast!(*val, u16, part),
 
        Value::UInt32(val) => from_unsigned_cast!(*val, u32, part),
 
        Value::UInt64(val) => from_unsigned_cast!(*val, u64, part),
 
        Value::SInt8(val) => from_signed_cast!(*val, i8, part),
 
        Value::SInt16(val) => from_signed_cast!(*val, i16, part),
 
        Value::SInt32(val) => from_signed_cast!(*val, i32, part),
 
        Value::SInt64(val) => from_signed_cast!(*val, i64, part),
 
        _ => unreachable!("mismatch between 'cast' type checking and 'cast' evaluation"),
 
    }
 
}
 

	
 
/// Recursively checks for equality.
 
pub(crate) fn apply_equality_operator(store: &Store, lhs: &Value, rhs: &Value) -> bool {
 
    let lhs = store.maybe_read_ref(lhs);
 
    let rhs = store.maybe_read_ref(rhs);
 

	
 
    fn eval_equality_heap(store: &Store, lhs_pos: HeapPos, rhs_pos: HeapPos) -> bool {
 
        let lhs_vals = &store.heap_regions[lhs_pos as usize].values;
 
        let rhs_vals = &store.heap_regions[rhs_pos as usize].values;
 
        let lhs_len = lhs_vals.len();
 
        if lhs_len != rhs_vals.len() {
 
            return false;
 
        }
 

	
 
        for idx in 0..lhs_len {
 
            let lhs_val = &lhs_vals[idx];
 
            let rhs_val = &rhs_vals[idx];
 
            if !apply_equality_operator(store, lhs_val, rhs_val) {
 
                return false;
 
            }
 
        }
 

	
 
        return true;
 
    }
 

	
 
    match lhs {
 
        Value::Input(v) => *v == rhs.as_input(),
 
        Value::Output(v) => *v == rhs.as_output(),
 
        Value::Message(lhs_pos) => eval_equality_heap(store, *lhs_pos, rhs.as_message()),
 
        Value::Null => todo!("remove null"),
 
        Value::Bool(v) => *v == rhs.as_bool(),
 
        Value::Char(v) => *v == rhs.as_char(),
 
        Value::String(lhs_pos) => eval_equality_heap(store, *lhs_pos, rhs.as_string()),
 
        Value::UInt8(v) => *v == rhs.as_uint8(),
 
        Value::UInt16(v) => *v == rhs.as_uint16(),
 
        Value::UInt32(v) => *v == rhs.as_uint32(),
 
        Value::UInt64(v) => *v == rhs.as_uint64(),
 
        Value::SInt8(v) => *v == rhs.as_sint8(),
 
        Value::SInt16(v) => *v == rhs.as_sint16(),
 
        Value::SInt32(v) => *v == rhs.as_sint32(),
 
        Value::SInt64(v) => *v == rhs.as_sint64(),
 
        Value::Array(lhs_pos) => eval_equality_heap(store, *lhs_pos, rhs.as_array()),
 
        Value::Enum(v) => *v == rhs.as_enum(),
 
        Value::Union(lhs_tag, lhs_pos) => {
 
            let (rhs_tag, rhs_pos) = rhs.as_union();
 
            if *lhs_tag != rhs_tag {
 
                return false;
 
            }
 
            eval_equality_heap(store, *lhs_pos, rhs_pos)
 
        },
 
        Value::Struct(lhs_pos) => eval_equality_heap(store, *lhs_pos, rhs.as_struct()),
 
        _ => unreachable!("apply_equality_operator to lhs {:?}", lhs),
 
    }
 
}
 

	
 
/// Recursively checks for inequality
 
pub(crate) fn apply_inequality_operator(store: &Store, lhs: &Value, rhs: &Value) -> bool {
 
    let lhs = store.maybe_read_ref(lhs);
 
    let rhs = store.maybe_read_ref(rhs);
 

	
 
    fn eval_inequality_heap(store: &Store, lhs_pos: HeapPos, rhs_pos: HeapPos) -> bool {
 
        let lhs_vals = &store.heap_regions[lhs_pos as usize].values;
 
        let rhs_vals = &store.heap_regions[rhs_pos as usize].values;
 
        let lhs_len = lhs_vals.len();
 
        if lhs_len != rhs_vals.len() {
 
            return true;
 
        }
 

	
 
        for idx in 0..lhs_len {
 
            let lhs_val = &lhs_vals[idx];
 
            let rhs_val = &rhs_vals[idx];
 
            if apply_inequality_operator(store, lhs_val, rhs_val) {
 
                return true;
 
            }
 
        }
 

	
 
        return false;
 
    }
 

	
 
    match lhs {
 
        Value::Input(v) => *v != rhs.as_input(),
 
        Value::Output(v) => *v != rhs.as_output(),
 
        Value::Message(lhs_pos) => eval_inequality_heap(store, *lhs_pos, rhs.as_message()),
 
        Value::Null => todo!("remove null"),
 
        Value::Bool(v) => *v != rhs.as_bool(),
 
        Value::Char(v) => *v != rhs.as_char(),
 
        Value::String(lhs_pos) => eval_inequality_heap(store, *lhs_pos, rhs.as_string()),
 
        Value::UInt8(v) => *v != rhs.as_uint8(),
 
        Value::UInt16(v) => *v != rhs.as_uint16(),
 
        Value::UInt32(v) => *v != rhs.as_uint32(),
 
        Value::UInt64(v) => *v != rhs.as_uint64(),
 
        Value::SInt8(v) => *v != rhs.as_sint8(),
 
        Value::SInt16(v) => *v != rhs.as_sint16(),
 
        Value::SInt32(v) => *v != rhs.as_sint32(),
 
        Value::SInt64(v) => *v != rhs.as_sint64(),
 
        Value::Enum(v) => *v != rhs.as_enum(),
 
        Value::Union(lhs_tag, lhs_pos) => {
 
            let (rhs_tag, rhs_pos) = rhs.as_union();
 
            if *lhs_tag != rhs_tag {
 
                return true;
 
            }
 
            eval_inequality_heap(store, *lhs_pos, rhs_pos)
 
        },
 
        Value::String(lhs_pos) => eval_inequality_heap(store, *lhs_pos, rhs.as_struct()),
 
        _ => unreachable!("apply_inequality_operator to lhs {:?}", lhs)
 
    }
 
}
 

	
 
/// Recursively applies binding operator. Essentially an equality operator with
 
/// special handling if the LHS contains a binding reference to a stack
 
/// stack variable.
 
// Note: that there is a lot of `Value.clone()` going on here. As always: this
 
// is potentially cloning the references to heap values, not actually cloning
 
// those heap regions into a new heap region.
 
pub(crate) fn apply_binding_operator(store: &mut Store, lhs: Value, rhs: Value) -> bool {
 
    let lhs = store.maybe_read_ref(&lhs).clone();
 
    let rhs = store.maybe_read_ref(&rhs).clone();
 

	
 
    fn eval_binding_heap(store: &mut Store, lhs_pos: HeapPos, rhs_pos: HeapPos) -> bool {
 
        let lhs_len = store.heap_regions[lhs_pos as usize].values.len();
 
        let rhs_len = store.heap_regions[rhs_pos as usize].values.len();
 
        if lhs_len != rhs_len {
 
            return false;
 
        }
 

	
 
        for idx in 0..lhs_len {
 
            // More rust shenanigans... I'm going to calm myself by saying that
 
            // this is just a temporary evaluator implementation.
 
            let lhs_val = store.heap_regions[lhs_pos as usize].values[idx].clone();
 
            let rhs_val = store.heap_regions[rhs_pos as usize].values[idx].clone();
 
            if !apply_binding_operator(store, lhs_val, rhs_val) {
 
                return false;
 
            }
 
        }
 

	
 
        return true;
 
    }
 

	
 
    match lhs {
 
        Value::Binding(var_pos) => {
 
            let to_write = store.clone_value(rhs.clone());
 
            store.write(ValueId::Stack(var_pos), to_write);
 
            return true;
 
        },
 
        Value::Input(v) => v == rhs.as_input(),
 
        Value::Output(v) => v == rhs.as_output(),
 
        Value::Message(lhs_pos) => eval_binding_heap(store, lhs_pos, rhs.as_message()),
 
        Value::Null => todo!("remove null"),
 
        Value::Bool(v) => v == rhs.as_bool(),
 
        Value::Char(v) => v == rhs.as_char(),
 
        Value::String(lhs_pos) => eval_binding_heap(store, lhs_pos, rhs.as_string()),
 
        Value::UInt8(v) => v == rhs.as_uint8(),
 
        Value::UInt16(v) => v == rhs.as_uint16(),
 
        Value::UInt32(v) => v == rhs.as_uint32(),
 
        Value::UInt64(v) => v == rhs.as_uint64(),
 
        Value::SInt8(v) => v == rhs.as_sint8(),
 
        Value::SInt16(v) => v == rhs.as_sint16(),
 
        Value::SInt32(v) => v == rhs.as_sint32(),
 
        Value::SInt64(v) => v == rhs.as_sint64(),
 
        Value::Array(lhs_pos) => eval_binding_heap(store, lhs_pos, rhs.as_array()),
 
        Value::Enum(v) => v == rhs.as_enum(),
 
        Value::Union(lhs_tag, lhs_pos) => {
 
            let (rhs_tag, rhs_pos) = rhs.as_union();
 
            if lhs_tag != rhs_tag {
 
                return false;
 
            }
 
            eval_binding_heap(store, lhs_pos, rhs_pos)
 
        },
 
        Value::Struct(lhs_pos) => eval_binding_heap(store, lhs_pos, rhs.as_struct()),
 
        _ => unreachable!("apply_binding_operator to lhs {:?}", lhs),
 
    }
 
}
 
\ No newline at end of file
src/protocol/parser/mod.rs
Show inline comments
 
@@ -36,193 +36,193 @@ pub enum ModuleCompilationPhase {
 
    SymbolsScanned,         // all definitions are linked to their type class
 
    ImportsResolved,        // all imports are added to the symbol table
 
    DefinitionsParsed,      // produced the AST for the entire module
 
    TypesAddedToTable,      // added all definitions to the type table
 
    ValidatedAndLinked,     // AST is traversed and has linked the required AST nodes
 
    Typed,                  // Type inference and checking has been performed
 
}
 

	
 
pub struct Module {
 
    // Buffers
 
    pub source: InputSource,
 
    pub tokens: TokenBuffer,
 
    // Identifiers
 
    pub root_id: RootId,
 
    pub name: Option<(PragmaId, StringRef<'static>)>,
 
    pub version: Option<(PragmaId, i64)>,
 
    pub phase: ModuleCompilationPhase,
 
}
 

	
 
pub struct PassCtx<'a> {
 
    heap: &'a mut Heap,
 
    symbols: &'a mut SymbolTable,
 
    pool: &'a mut StringPool,
 
}
 

	
 
pub struct Parser {
 
    pub(crate) heap: Heap,
 
    pub(crate) string_pool: StringPool,
 
    pub(crate) modules: Vec<Module>,
 
    pub(crate) symbol_table: SymbolTable,
 
    pub(crate) type_table: TypeTable,
 
    // Compiler passes
 
    pass_tokenizer: PassTokenizer,
 
    pass_symbols: PassSymbols,
 
    pass_import: PassImport,
 
    pass_definitions: PassDefinitions,
 
    pass_validation: PassValidationLinking,
 
    pass_typing: PassTyping,
 
}
 

	
 
impl Parser {
 
    pub fn new() -> Self {
 
        let mut parser = Parser{
 
            heap: Heap::new(),
 
            string_pool: StringPool::new(),
 
            modules: Vec::new(),
 
            symbol_table: SymbolTable::new(),
 
            type_table: TypeTable::new(),
 
            pass_tokenizer: PassTokenizer::new(),
 
            pass_symbols: PassSymbols::new(),
 
            pass_import: PassImport::new(),
 
            pass_definitions: PassDefinitions::new(),
 
            pass_validation: PassValidationLinking::new(),
 
            pass_typing: PassTyping::new(),
 
        };
 

	
 
        parser.symbol_table.insert_scope(None, SymbolScope::Global);
 

	
 
        fn quick_type(variants: &[ParserTypeVariant]) -> ParserType {
 
            let mut t = ParserType{ elements: Vec::with_capacity(variants.len()) };
 
            for variant in variants {
 
                t.elements.push(ParserTypeElement{ full_span: InputSpan::new(), variant: variant.clone() });
 
            }
 
            t
 
        }
 

	
 
        use ParserTypeVariant as PTV;
 
        insert_builtin_function(&mut parser, "get", &["T"], |id| (
 
            vec![
 
                ("input", quick_type(&[PTV::Input, PTV::PolymorphicArgument(id.upcast(), 0)]))
 
            ],
 
            quick_type(&[PTV::PolymorphicArgument(id.upcast(), 0)])
 
        ));
 
        insert_builtin_function(&mut parser, "put", &["T"], |id| (
 
            vec![
 
                ("output", quick_type(&[PTV::Output, PTV::PolymorphicArgument(id.upcast(), 0)])),
 
                ("value", quick_type(&[PTV::PolymorphicArgument(id.upcast(), 0)])),
 
            ],
 
            quick_type(&[PTV::Void])
 
        ));
 
        insert_builtin_function(&mut parser, "fires", &["T"], |id| (
 
            vec![
 
                ("port", quick_type(&[PTV::InputOrOutput, PTV::PolymorphicArgument(id.upcast(), 0)]))
 
            ],
 
            quick_type(&[PTV::Bool])
 
        ));
 
        insert_builtin_function(&mut parser, "create", &["T"], |id| (
 
            vec![
 
                ("length", quick_type(&[PTV::IntegerLike]))
 
            ],
 
            quick_type(&[PTV::ArrayLike, PTV::PolymorphicArgument(id.upcast(), 0)])
 
        ));
 
        insert_builtin_function(&mut parser, "length", &["T"], |id| (
 
            vec![
 
                ("array", quick_type(&[PTV::ArrayLike, PTV::PolymorphicArgument(id.upcast(), 0)]))
 
            ],
 
            quick_type(&[PTV::IntegerLike])
 
            quick_type(&[PTV::UInt32]) // TODO: @PtrInt
 
        ));
 
        insert_builtin_function(&mut parser, "assert", &[], |id| (
 
            vec![
 
                ("condition", quick_type(&[PTV::Bool])),
 
            ],
 
            quick_type(&[PTV::Void])
 
        ));
 

	
 
        parser
 
    }
 

	
 
    pub fn feed(&mut self, mut source: InputSource) -> Result<(), ParseError> {
 
        // TODO: @Optimize
 
        let mut token_buffer = TokenBuffer::new();
 
        self.pass_tokenizer.tokenize(&mut source, &mut token_buffer)?;
 

	
 
        let module = Module{
 
            source,
 
            tokens: token_buffer,
 
            root_id: RootId::new_invalid(),
 
            name: None,
 
            version: None,
 
            phase: ModuleCompilationPhase::Tokenized,
 
        };
 
        self.modules.push(module);
 

	
 
        Ok(())
 
    }
 

	
 
    pub fn parse(&mut self) -> Result<(), ParseError> {
 
        let mut pass_ctx = PassCtx{
 
            heap: &mut self.heap,
 
            symbols: &mut self.symbol_table,
 
            pool: &mut self.string_pool,
 
        };
 

	
 
        // Advance all modules to the phase where all symbols are scanned
 
        for module_idx in 0..self.modules.len() {
 
            self.pass_symbols.parse(&mut self.modules, module_idx, &mut pass_ctx)?;
 
        }
 

	
 
        // With all symbols scanned, perform further compilation until we can
 
        // add all base types to the type table.
 
        for module_idx in 0..self.modules.len() {
 
            self.pass_import.parse(&mut self.modules, module_idx, &mut pass_ctx)?;
 
            self.pass_definitions.parse(&mut self.modules, module_idx, &mut pass_ctx)?;
 
        }
 

	
 
        // Add every known type to the type table
 
        self.type_table.build_base_types(&mut self.modules, &mut pass_ctx)?;
 

	
 
        // Continue compilation with the remaining phases now that the types
 
        // are all in the type table
 
        for module_idx in 0..self.modules.len() {
 
            // TODO: Remove the entire Visitor abstraction. It really doesn't
 
            //  make sense considering the amount of special handling we do
 
            //  in each pass.
 
            let mut ctx = visitor::Ctx{
 
                heap: &mut self.heap,
 
                module: &mut self.modules[module_idx],
 
                symbols: &mut self.symbol_table,
 
                types: &mut self.type_table,
 
            };
 
            self.pass_validation.visit_module(&mut ctx)?;
 
        }
 

	
 
        // Perform typechecking on all modules
 
        let mut queue = ResolveQueue::new();
 
        for module in &mut self.modules {
 
            let mut ctx = visitor::Ctx{
 
                heap: &mut self.heap,
 
                module,
 
                symbols: &mut self.symbol_table,
 
                types: &mut self.type_table,
 
            };
 
            PassTyping::queue_module_definitions(&mut ctx, &mut queue);
 
        };
 
        while !queue.is_empty() {
 
            let top = queue.pop().unwrap();
 
            let mut ctx = visitor::Ctx{
 
                heap: &mut self.heap,
 
                module: &mut self.modules[top.root_id.index as usize],
 
                symbols: &mut self.symbol_table,
 
                types: &mut self.type_table,
 
            };
 
            self.pass_typing.handle_module_definition(&mut ctx, &mut queue, top)?;
 
        }
 

	
 
        // Perform remaining steps
 
        // TODO: Phase out at some point
 
        for module in &self.modules {
 
            let root_id = module.root_id;
 
            if let Err((position, message)) = Self::parse_inner(&mut self.heap, root_id) {
 
                return Err(ParseError::new_error_str_at_pos(&self.modules[0].source, position, &message))
 
            }
 
        }
src/protocol/parser/pass_definitions.rs
Show inline comments
 
@@ -256,193 +256,192 @@ impl PassDefinitions {
 
        let definition_id = ctx.symbols.get_symbol_by_name_defined_in_scope(module_scope, ident_text)
 
            .unwrap().variant.as_definition().definition_id;
 
        self.cur_definition = definition_id;
 

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

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

	
 
        // Consume return types
 
        consume_token(&module.source, iter, TokenKind::ArrowRight)?;
 
        let mut return_types = self.parser_types.start_section();
 
        let mut open_curly_pos = iter.last_valid_pos(); // bogus value
 
        consume_comma_separated_until(
 
            TokenKind::OpenCurly, &module.source, iter, ctx,
 
            |source, iter, ctx| {
 
                let poly_vars = ctx.heap[definition_id].poly_vars(); // TODO: @Cleanup, this is really ugly. But rust...
 
                consume_parser_type(source, iter, &ctx.symbols, &ctx.heap, poly_vars, module_scope, definition_id, false, 0)
 
            },
 
            &mut return_types, "a return type", Some(&mut open_curly_pos)
 
        )?;
 
        let return_types = return_types.into_vec();
 

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

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

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

	
 
        Ok(())
 
    }
 

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

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

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

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

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

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

	
 
        Ok(())
 
    }
 

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

	
 
            debug_assert_eq!(statements.len(), 1);
 
            let statements = statements.into_vec();
 

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

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

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

	
 
            Ok(id)
 
        }
 
    }
 

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

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

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

	
 
                let if_stmt = &mut ctx.heap[id];
 
                if_stmt.end_if = end_if;
 
            } else if ident == KW_STMT_WHILE {
 
                let id = self.consume_while_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 

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

	
 
                let while_stmt = &mut ctx.heap[id];
 
                while_stmt.end_while = end_while;
 
            } else if ident == KW_STMT_BREAK {
 
                let id = self.consume_break_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 
            } else if ident == KW_STMT_CONTINUE {
 
                let id = self.consume_continue_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 
            } else if ident == KW_STMT_SYNC {
 
                let id = self.consume_synchronous_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 

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

	
 
                let sync_stmt = &mut ctx.heap[id];
 
                sync_stmt.end_sync = end_sync;
 
            } else if ident == KW_STMT_RETURN {
 
                let id = self.consume_return_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 
            } else if ident == KW_STMT_GOTO {
 
                let id = self.consume_goto_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 
            } else if ident == KW_STMT_NEW {
 
                let id = self.consume_new_statement(module, iter, ctx)?;
 
                section.push(id.upcast());
 
            } else if ident == KW_STMT_CHANNEL {
 
                let id = self.consume_channel_statement(module, iter, ctx)?;
 
                section.push(id.upcast().upcast());
 
            } else if iter.peek() == Some(TokenKind::Colon) {
 
                self.consume_labeled_statement(module, iter, ctx, section)?;
 
            } else {
 
                // Two fallback possibilities: the first one is a memory
 
@@ -742,192 +741,193 @@ impl PassDefinitions {
 
            this,
 
            span: channel_span,
 
            from, to,
 
            relative_pos_in_block: 0,
 
            next: StatementId::new_invalid(),
 
        }))
 
    }
 

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

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

	
 
        let stmt_id = ctx.heap.alloc_labeled_statement(|this| LabeledStatement {
 
            this,
 
            label,
 
            body: inner_section[0],
 
            relative_pos_in_block: 0,
 
            in_sync: SynchronousStatementId::new_invalid(),
 
        });
 

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

	
 
        Ok(())
 
    }
 

	
 
    fn maybe_consume_memory_statement(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<Option<(MemoryStatementId, ExpressionStatementId)>, ParseError> {
 
        // This is a bit ugly. It would be nicer if we could somehow
 
        // consume the expression with a type hint if we do get a valid
 
        // type, but we don't get an identifier following it
 
        let iter_state = iter.save();
 
        let definition_id = self.cur_definition;
 
        let poly_vars = ctx.heap[definition_id].poly_vars();
 

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

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

	
 
                let initial_expr_begin_pos = iter.last_valid_pos();
 
                let initial_expr_id = self.consume_expression(module, iter, ctx)?;
 
                let initial_expr_end_pos = iter.last_valid_pos();
 
                consume_token(&module.source, iter, TokenKind::SemiColon)?;
 

	
 
                // Allocate the memory statement with the variable
 
                let local_id = ctx.heap.alloc_variable(|this| Variable{
 
                    this,
 
                    kind: VariableKind::Local,
 
                    identifier: identifier.clone(),
 
                    parser_type,
 
                    relative_pos_in_block: 0,
 
                    unique_id_in_scope: -1,
 
                });
 
                let memory_stmt_id = ctx.heap.alloc_memory_statement(|this| MemoryStatement{
 
                    this,
 
                    span: memory_span,
 
                    variable: local_id,
 
                    next: StatementId::new_invalid()
 
                });
 

	
 
                // Allocate the initial assignment
 
                let variable_expr_id = ctx.heap.alloc_variable_expression(|this| VariableExpression{
 
                    this,
 
                    identifier,
 
                    declaration: None,
 
                    used_as_binding_target: false,
 
                    parent: ExpressionParent::None,
 
                    unique_id_in_definition: -1,
 
                });
 
                let assignment_expr_id = ctx.heap.alloc_assignment_expression(|this| AssignmentExpression{
 
                    this,
 
                    span: assign_span,
 
                    left: variable_expr_id.upcast(),
 
                    operation: AssignmentOperator::Set,
 
                    right: initial_expr_id,
 
                    parent: ExpressionParent::None,
 
                    unique_id_in_definition: -1,
 
                });
 
                let assignment_stmt_id = ctx.heap.alloc_expression_statement(|this| ExpressionStatement{
 
                    this,
 
                    span: InputSpan::from_positions(initial_expr_begin_pos, initial_expr_end_pos),
 
                    expression: assignment_expr_id.upcast(),
 
                    next: StatementId::new_invalid(),
 
                });
 

	
 
                return Ok(Some((memory_stmt_id, assignment_stmt_id)))
 
            }
 
        }
 

	
 
        // If here then one of the preconditions for a memory statement was not
 
        // met. So recover the iterator and return
 
        iter.load(iter_state);
 
        Ok(None)
 
    }
 

	
 
    fn consume_expression_statement(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<ExpressionStatementId, ParseError> {
 
        let start_pos = iter.last_valid_pos();
 
        let expression = self.consume_expression(module, iter, ctx)?;
 
        let end_pos = iter.last_valid_pos();
 
        consume_token(&module.source, iter, TokenKind::SemiColon)?;
 

	
 
        Ok(ctx.heap.alloc_expression_statement(|this| ExpressionStatement{
 
            this,
 
            span: InputSpan::from_positions(start_pos, end_pos),
 
            expression,
 
            next: StatementId::new_invalid(),
 
        }))
 
    }
 

	
 
    //--------------------------------------------------------------------------
 
    // Expression Parsing
 
    //--------------------------------------------------------------------------
 

	
 
    // TODO: @Cleanup This is fine for now. But I prefer my stacktraces not to
 
    //  look like enterprise Java code...
 
    fn consume_expression(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<ExpressionId, ParseError> {
 
        self.consume_assignment_expression(module, iter, ctx)
 
    }
 

	
 
    fn consume_assignment_expression(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<ExpressionId, ParseError> {
 
        // Utility to convert token into assignment operator
 
        fn parse_assignment_operator(token: Option<TokenKind>) -> Option<AssignmentOperator> {
 
            use TokenKind as TK;
 
            use AssignmentOperator as AO;
 

	
 
            if token.is_none() {
 
                return None
 
            }
 

	
 
            match token.unwrap() {
 
                TK::Equal               => Some(AO::Set),
 
                TK::StarEquals          => Some(AO::Multiplied),
 
                TK::SlashEquals         => Some(AO::Divided),
 
                TK::PercentEquals       => Some(AO::Remained),
 
                TK::PlusEquals          => Some(AO::Added),
 
                TK::MinusEquals         => Some(AO::Subtracted),
 
                TK::ShiftLeftEquals     => Some(AO::ShiftedLeft),
 
                TK::ShiftRightEquals    => Some(AO::ShiftedRight),
 
                TK::AndEquals           => Some(AO::BitwiseAnded),
 
                TK::CaretEquals         => Some(AO::BitwiseXored),
 
                TK::OrEquals            => Some(AO::BitwiseOred),
 
                _                       => None
 
            }
 
        }
 

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

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

	
 
            Ok(ctx.heap.alloc_assignment_expression(|this| AssignmentExpression{
 
                this, span, left, operation, right,
 
                parent: ExpressionParent::None,
 
@@ -1443,192 +1443,193 @@ impl PassDefinitions {
 

	
 
                    // Parse builtin literal
 
                    let value = match ident_text {
 
                        KW_LIT_NULL => Literal::Null,
 
                        KW_LIT_TRUE => Literal::True,
 
                        KW_LIT_FALSE => Literal::False,
 
                        _ => unreachable!(),
 
                    };
 

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

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

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

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

	
 
                    ctx.heap.alloc_cast_expression(|this| CastExpression{
 
                        this,
 
                        span: ident_span,
 
                        to_type,
 
                        subject,
 
                        parent: ExpressionParent::None,
 
                        unique_id_in_definition: -1,
 
                    }).upcast()
 
                } else {
 
                    // Not a builtin literal, but also not a known type. So we
 
                    // assume it is a variable expression. Although if we do,
 
                    // then if a programmer mistyped a struct/function name the
 
                    // error messages will be rather cryptic. For polymorphic
 
                    // arguments we can't really do anything at all (because it
 
                    // uses the '<' token). In the other cases we try to provide
 
                    // a better error message.
 
                    iter.consume();
 
                    let next = iter.next();
 
                    if Some(TokenKind::ColonColon) == next {
 
                        return Err(ParseError::new_error_str_at_span(&module.source, ident_span, "unknown identifier"));
 
                    } else if Some(TokenKind::OpenParen) == next {
 
                        return Err(ParseError::new_error_str_at_span(
 
                            &module.source, ident_span,
 
                            "unknown identifier, did you mistype a union variant's or a function's name?"
 
                        ));
 
                    } else if Some(TokenKind::OpenCurly) == next {
 
                        return Err(ParseError::new_error_str_at_span(
 
                            &module.source, ident_span,
 
                            "unknown identifier, did you mistype a struct type's name?"
 
                        ))
 
                    }
 

	
 
                    let ident_text = ctx.pool.intern(ident_text);
 
                    let identifier = Identifier { span: ident_span, value: ident_text };
 

	
 
                    ctx.heap.alloc_variable_expression(|this| VariableExpression {
 
                        this,
 
                        identifier,
 
                        declaration: None,
 
                        used_as_binding_target: false,
 
                        parent: ExpressionParent::None,
 
                        unique_id_in_definition: -1,
 
                    }).upcast()
 
                }
 
            }
 
        } else {
 
            return Err(ParseError::new_error_str_at_pos(
 
                &module.source, iter.last_valid_pos(), "expected an expression"
 
            ));
 
        };
 

	
 
        Ok(result)
 
    }
 

	
 
    //--------------------------------------------------------------------------
 
    // Expression Utilities
 
    //--------------------------------------------------------------------------
 

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

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

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

	
 
        Ok(result)
 
    }
 

	
 
    #[inline]
 
    fn consume_expression_list(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx, end_pos: Option<&mut InputPosition>
 
    ) -> Result<Vec<ExpressionId>, ParseError> {
 
        let mut section = self.expressions.start_section();
 
        consume_comma_separated(
 
            TokenKind::OpenParen, TokenKind::CloseParen, &module.source, iter, ctx,
 
            |_source, iter, ctx| self.consume_expression(module, iter, ctx),
 
            &mut section, "an expression", "a list of expressions", end_pos
 
        )?;
 
        Ok(section.into_vec())
 
    }
 
}
 

	
 
/// Consumes a type. A type always starts with an identifier which may indicate
 
/// a builtin type or a user-defined type. The fact that it may contain
 
/// polymorphic arguments makes it a tree-like structure. Because we cannot rely
 
/// on knowing the exact number of polymorphic arguments we do not check for
 
/// these.
 
///
 
/// Note that the first depth index is used as a hack.
 
// TODO: @Optimize, @Span fix, @Cleanup
 
fn consume_parser_type(
 
    source: &InputSource, iter: &mut TokenIter, symbols: &SymbolTable, heap: &Heap, poly_vars: &[Identifier],
 
    cur_scope: SymbolScope, wrapping_definition: DefinitionId, allow_inference: bool, first_angle_depth: i32,
 
) -> Result<ParserType, ParseError> {
 
    struct Entry{
 
        element: ParserTypeElement,
 
        depth: i32,
 
    }
 

	
 
    // After parsing the array modified "[]", we need to insert an array type
 
    // before the most recently parsed type.
 
    fn insert_array_before(elements: &mut Vec<Entry>, depth: i32, span: InputSpan) {
 
        let index = elements.iter().rposition(|e| e.depth == depth).unwrap();
 
        elements.insert(index, Entry{
 
            element: ParserTypeElement{ full_span: span, variant: ParserTypeVariant::Array },
 
            depth,
 
        });
 
    }
 

	
 
    // Most common case we just have one type, perhaps with some array
 
    // annotations. This is both the hot-path, and simplifies the state machine
 
    // that follows and is responsible for parsing more complicated types.
 
    let element = consume_parser_type_ident(source, iter, symbols, heap, poly_vars, cur_scope, wrapping_definition, allow_inference)?;
 
    if iter.next() != Some(TokenKind::OpenAngle) {
 
        let num_embedded = element.variant.num_embedded();
 
        let mut num_array = 0;
 
        while iter.next() == Some(TokenKind::OpenSquare) {
 
            iter.consume();
 
            consume_token(source, iter, TokenKind::CloseSquare)?;
 
            num_array += 1;
 
        }
src/protocol/parser/pass_typing.rs
Show inline comments
 
@@ -392,231 +392,242 @@ impl InferenceType {
 
        to_check_parts: &[InferenceTypePart], to_check_idx: &mut usize,
 
        template_parts: &[InferenceTypePart], template_idx: &mut usize
 
    ) -> Option<i32> {
 
        use InferenceTypePart as ITP;
 

	
 
        let to_check_part = &to_check_parts[*to_check_idx];
 
        let template_part = &template_parts[*template_idx];
 

	
 
        // Checking programmer errors
 
        debug_assert_ne!(to_check_part, template_part);
 
        debug_assert!(!to_check_part.is_marker(), "marker encountered in 'to_check part'");
 
        debug_assert!(!template_part.is_marker(), "marker encountered in 'template part'");
 

	
 
        if to_check_part.may_be_inferred_from(template_part) {
 
            let depth_change = to_check_part.depth_change();
 
            debug_assert_eq!(depth_change, template_part.depth_change());
 
            *to_check_idx += 1;
 
            *template_idx += 1;
 
            return Some(depth_change);
 
        }
 

	
 
        if *to_check_part == ITP::Unknown {
 
            *to_check_idx += 1;
 
            *template_idx = Self::find_subtree_end_idx(template_parts, *template_idx);
 

	
 
            // By definition LHS and RHS had depth change of -1
 
            return Some(-1);
 
        }
 

	
 
        None
 
    }
 

	
 
    /// Attempts to infer types between two `InferenceType` instances. This 
 
    /// function is unsafe as it accepts pointers to work around Rust's 
 
    /// borrowing rules. The caller must ensure that the pointers are distinct.
 
    unsafe fn infer_subtrees_for_both_types(
 
        type_a: *mut InferenceType, start_idx_a: usize,
 
        type_b: *mut InferenceType, start_idx_b: usize
 
    ) -> DualInferenceResult {
 
        debug_assert!(!std::ptr::eq(type_a, type_b), "encountered pointers to the same inference type");
 
        let type_a = &mut *type_a;
 
        let type_b = &mut *type_b;
 

	
 
        let mut modified_a = false;
 
        let mut modified_b = false;
 
        let mut idx_a = start_idx_a;
 
        let mut idx_b = start_idx_b;
 
        let mut depth = 1;
 

	
 
        while depth > 0 {
 
            // Advance indices if we encounter markers or equal parts
 
            let part_a = &type_a.parts[idx_a];
 
            let part_b = &type_b.parts[idx_b];
 
            
 
            if part_a == part_b {
 
                let depth_change = part_a.depth_change();
 
                depth += depth_change;
 
                debug_assert_eq!(depth_change, part_b.depth_change());
 
                idx_a += 1;
 
                idx_b += 1;
 
                continue;
 
            }
 
            if part_a.is_marker() { idx_a += 1; continue; }
 
            if part_b.is_marker() { idx_b += 1; continue; }
 

	
 
            // Types are not equal and are both not markers
 
            if let Some(depth_change) = Self::infer_part_for_single_type(type_a, &mut idx_a, &type_b.parts, &mut idx_b) {
 
                depth += depth_change;
 
                modified_a = true;
 
                continue;
 
            }
 
            if let Some(depth_change) = Self::infer_part_for_single_type(type_b, &mut idx_b, &type_a.parts, &mut idx_a) {
 
                depth += depth_change;
 
                modified_b = true;
 
                continue;
 
            }
 

	
 
            // Types can not be inferred in any way: types must be incompatible
 
            return DualInferenceResult::Incompatible;
 
        }
 

	
 
        if modified_a { type_a.recompute_is_done(); }
 
        if modified_b { type_b.recompute_is_done(); }
 

	
 
        // If here then we completely inferred the subtrees.
 
        match (modified_a, modified_b) {
 
            (false, false) => DualInferenceResult::Neither,
 
            (false, true) => DualInferenceResult::Second,
 
            (true, false) => DualInferenceResult::First,
 
            (true, true) => DualInferenceResult::Both
 
        }
 
    }
 

	
 
    /// Attempts to infer the first subtree based on the template. Like
 
    /// `infer_subtrees_for_both_types`, but now only applying inference to
 
    /// `to_infer` based on the type information in `template`.
 
    ///
 
    /// The `forced_template` flag controls whether `to_infer` is considered
 
    /// valid if it is more specific then the template. When `forced_template`
 
    /// is false, then as long as the `to_infer` and `template` types are
 
    /// compatible the inference will succeed. If `forced_template` is true,
 
    /// then `to_infer` MUST be less specific than `template` (e.g.
 
    /// `IntegerLike` is less specific than `UInt32`. Likewise `Bool` is less
 
    /// specific than `BindingBool`)
 
    fn infer_subtree_for_single_type(
 
        to_infer: &mut InferenceType, mut to_infer_idx: usize,
 
        template: &[InferenceTypePart], mut template_idx: usize,
 
        forced_template: bool,
 
    ) -> SingleInferenceResult {
 
        let mut modified = false;
 
        let mut depth = 1;
 

	
 
        while depth > 0 {
 
            let to_infer_part = &to_infer.parts[to_infer_idx];
 
            let template_part = &template[template_idx];
 

	
 
            if to_infer_part == template_part {
 
                let depth_change = to_infer_part.depth_change();
 
                depth += depth_change;
 
                debug_assert_eq!(depth_change, template_part.depth_change());
 
                to_infer_idx += 1;
 
                template_idx += 1;
 
                continue;
 
            }
 
            if to_infer_part.is_marker() { to_infer_idx += 1; continue; }
 
            if template_part.is_marker() { template_idx += 1; continue; }
 

	
 
            // Types are not equal and not markers. So check if we can infer 
 
            // anything
 
            if let Some(depth_change) = Self::infer_part_for_single_type(
 
                to_infer, &mut to_infer_idx, template, &mut template_idx
 
            ) {
 
                depth += depth_change;
 
                modified = true;
 
                continue;
 
            }
 

	
 
            // We cannot infer anything, but the template may still be 
 
            // compatible with the type we're inferring
 
            if let Some(depth_change) = Self::check_part_for_single_type(
 
                template, &mut template_idx, &to_infer.parts, &mut to_infer_idx
 
            ) {
 
                depth += depth_change;
 
                continue;
 
            if !forced_template {
 
                // We cannot infer anything, but the template may still be
 
                // compatible with the type we're inferring
 
                if let Some(depth_change) = Self::check_part_for_single_type(
 
                    template, &mut template_idx, &to_infer.parts, &mut to_infer_idx
 
                ) {
 
                    depth += depth_change;
 
                    continue;
 
                }
 
            }
 

	
 
            return SingleInferenceResult::Incompatible
 
        }
 

	
 
        if modified {
 
            to_infer.recompute_is_done();
 
            return SingleInferenceResult::Modified;
 
        } else {
 
            return SingleInferenceResult::Unmodified;
 
        }
 
    }
 

	
 
    /// Checks if both types are compatible, doesn't perform any inference
 
    fn check_subtrees(
 
        type_parts_a: &[InferenceTypePart], start_idx_a: usize,
 
        type_parts_b: &[InferenceTypePart], start_idx_b: usize
 
    ) -> bool {
 
        let mut depth = 1;
 
        let mut idx_a = start_idx_a;
 
        let mut idx_b = start_idx_b;
 

	
 
        while depth > 0 {
 
            let part_a = &type_parts_a[idx_a];
 
            let part_b = &type_parts_b[idx_b];
 

	
 
            if part_a == part_b {
 
                let depth_change = part_a.depth_change();
 
                depth += depth_change;
 
                debug_assert_eq!(depth_change, part_b.depth_change());
 
                idx_a += 1;
 
                idx_b += 1;
 
                continue;
 
            }
 
            
 
            if part_a.is_marker() { idx_a += 1; continue; }
 
            if part_b.is_marker() { idx_b += 1; continue; }
 

	
 
            if let Some(depth_change) = Self::check_part_for_single_type(
 
                type_parts_a, &mut idx_a, type_parts_b, &mut idx_b
 
            ) {
 
                depth += depth_change;
 
                continue;
 
            }
 
            if let Some(depth_change) = Self::check_part_for_single_type(
 
                type_parts_b, &mut idx_b, type_parts_a, &mut idx_a
 
            ) {
 
                depth += depth_change;
 
                continue;
 
            }
 

	
 
            return false;
 
        }
 

	
 
        true
 
    }
 

	
 
    /// Performs the conversion of the inference type into a concrete type.
 
    /// By calling this function you must make sure that no unspecified types
 
    /// (e.g. Unknown or IntegerLike) exist in the type.
 
    fn write_concrete_type(&self, concrete_type: &mut ConcreteType) {
 
        use InferenceTypePart as ITP;
 
        use ConcreteTypePart as CTP;
 

	
 
        // Make sure inference type is specified but concrete type is not yet specified
 
        debug_assert!(!self.parts.is_empty());
 
        debug_assert!(concrete_type.parts.is_empty());
 
        concrete_type.parts.reserve(self.parts.len());
 

	
 
        let mut idx = 0;
 
        while idx < self.parts.len() {
 
            let part = &self.parts[idx];
 
            let converted_part = match part {
 
                ITP::Marker(_) => {
 
                    // Markers are removed when writing to the concrete type.
 
                    idx += 1;
 
                    continue;
 
                },
 
                ITP::Unknown | ITP::BoolLike | ITP::NumberLike |
 
                ITP::IntegerLike | ITP::ArrayLike | ITP::PortLike => {
 
                    // Should not happen if type inferencing works correctly: we
 
                    // should have returned a programmer-readable error or have
 
                    // inferred all types.
 
                    unreachable!("attempted to convert inference type part {:?} into concrete type", part);
 
                },
 
                ITP::Void => CTP::Void,
 
                ITP::Message => CTP::Message,
 
                ITP::BindingBool => CTP::Bool,
 
                ITP::Bool => CTP::Bool,
 
                ITP::UInt8 => CTP::UInt8,
 
                ITP::UInt16 => CTP::UInt16,
 
                ITP::UInt32 => CTP::UInt32,
 
                ITP::UInt64 => CTP::UInt64,
 
                ITP::SInt8 => CTP::SInt8,
 
                ITP::SInt16 => CTP::SInt16,
 
                ITP::SInt32 => CTP::SInt32,
 
@@ -1537,459 +1548,459 @@ impl PassTyping {
 
        };
 

	
 
        let target = ctx.types.get_procedure_expression_data_mut(&definition_id, self.reserved_idx);
 
        debug_assert!(target.poly_args == self.poly_vars);
 
        debug_assert!(target.expr_data.is_empty()); // makes sure we never queue something twice
 

	
 
        target.expr_data.reserve(self.expr_types.len());
 
        for infer_expr in self.expr_types.iter() {
 
            let mut concrete = ConcreteType::default();
 
            infer_expr.expr_type.write_concrete_type(&mut concrete);
 
            target.expr_data.push(MonomorphExpression{
 
                expr_type: concrete,
 
                field_or_monomorph_idx: infer_expr.field_or_monomorph_idx
 
            });
 
        }
 

	
 
        Ok(())
 
    }
 

	
 
    fn progress_expr(&mut self, ctx: &mut Ctx, idx: i32) -> Result<(), ParseError> {
 
        let id = self.expr_types[idx as usize].expr_id; // TODO: @Temp
 
        match &ctx.heap[id] {
 
            Expression::Assignment(expr) => {
 
                let id = expr.this;
 
                self.progress_assignment_expr(ctx, id)
 
            },
 
            Expression::Binding(expr) => {
 
                let id = expr.this;
 
                self.progress_binding_expr(ctx, id)
 
            },
 
            Expression::Conditional(expr) => {
 
                let id = expr.this;
 
                self.progress_conditional_expr(ctx, id)
 
            },
 
            Expression::Binary(expr) => {
 
                let id = expr.this;
 
                self.progress_binary_expr(ctx, id)
 
            },
 
            Expression::Unary(expr) => {
 
                let id = expr.this;
 
                self.progress_unary_expr(ctx, id)
 
            },
 
            Expression::Indexing(expr) => {
 
                let id = expr.this;
 
                self.progress_indexing_expr(ctx, id)
 
            },
 
            Expression::Slicing(expr) => {
 
                let id = expr.this;
 
                self.progress_slicing_expr(ctx, id)
 
            },
 
            Expression::Select(expr) => {
 
                let id = expr.this;
 
                self.progress_select_expr(ctx, id)
 
            },
 
            Expression::Literal(expr) => {
 
                let id = expr.this;
 
                self.progress_literal_expr(ctx, id)
 
            },
 
            Expression::Cast(expr) => {
 
                let id = expr.this;
 
                self.progress_cast_expr(ctx, id)
 
            },
 
            Expression::Call(expr) => {
 
                let id = expr.this;
 
                self.progress_call_expr(ctx, id)
 
            },
 
            Expression::Variable(expr) => {
 
                let id = expr.this;
 
                self.progress_variable_expr(ctx, id)
 
            }
 
        }
 
    }
 

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

	
 
        let upcast_id = id.upcast();
 

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

	
 
        debug_log!("Assignment expr '{:?}': {}", expr.operation, upcast_id.index);
 
        debug_log!(" * Before:");
 
        debug_log!("   - Arg1 type: {}", self.temp_get_display_name(ctx, arg1_expr_id));
 
        debug_log!("   - Arg2 type: {}", self.temp_get_display_name(ctx, arg2_expr_id));
 
        debug_log!("   - Expr type: {}", self.temp_get_display_name(ctx, upcast_id));
 

	
 
        // Assignment does not return anything (it operates like a statement)
 
        let progress_expr = self.apply_forced_constraint(ctx, upcast_id, &VOID_TEMPLATE)?;
 

	
 
        // Apply forced constraint to LHS value
 
        let progress_forced = match expr.operation {
 
            AO::Set =>
 
                false,
 
            AO::Multiplied | AO::Divided | AO::Added | AO::Subtracted =>
 
                self.apply_forced_constraint(ctx, arg1_expr_id, &NUMBERLIKE_TEMPLATE)?,
 
                self.apply_template_constraint(ctx, arg1_expr_id, &NUMBERLIKE_TEMPLATE)?,
 
            AO::Remained | AO::ShiftedLeft | AO::ShiftedRight |
 
            AO::BitwiseAnded | AO::BitwiseXored | AO::BitwiseOred =>
 
                self.apply_forced_constraint(ctx, arg1_expr_id, &INTEGERLIKE_TEMPLATE)?,
 
                self.apply_template_constraint(ctx, arg1_expr_id, &INTEGERLIKE_TEMPLATE)?,
 
        };
 

	
 
        let (progress_arg1, progress_arg2) = self.apply_equal2_constraint(
 
            ctx, upcast_id, arg1_expr_id, 0, arg2_expr_id, 0
 
        )?;
 
        debug_assert!(if progress_forced { progress_arg2 } else { true });
 

	
 
        debug_log!(" * After:");
 
        debug_log!("   - Arg1 type [{}]: {}", progress_forced || progress_arg1, self.temp_get_display_name(ctx, arg1_expr_id));
 
        debug_log!("   - Arg2 type [{}]: {}", progress_arg2, self.temp_get_display_name(ctx, arg2_expr_id));
 
        debug_log!("   - Expr type [{}]: {}", progress_expr, self.temp_get_display_name(ctx, upcast_id));
 

	
 

	
 
        if progress_expr { self.queue_expr_parent(ctx, upcast_id); }
 
        if progress_forced || progress_arg1 { self.queue_expr(ctx, arg1_expr_id); }
 
        if progress_arg2 { self.queue_expr(ctx, arg2_expr_id); }
 

	
 
        Ok(())
 
    }
 

	
 
    fn progress_binding_expr(&mut self, ctx: &mut Ctx, id: BindingExpressionId) -> Result<(), ParseError> {
 
        let upcast_id = id.upcast();
 
        let binding_expr = &ctx.heap[id];
 
        let bound_from_id = binding_expr.bound_from;
 
        let bound_to_id = binding_expr.bound_to;
 

	
 
        // Output of a binding expression is a special kind of boolean that can
 
        // only be used in binary-and expressions
 
        let progress_expr = self.apply_forced_constraint(ctx, upcast_id, &BINDING_BOOL_TEMPLATE)?;
 
        let (progress_from, progress_to) = self.apply_equal2_constraint(ctx, upcast_id, bound_from_id, 0, bound_to_id, 0)?;
 

	
 
        if progress_expr { self.queue_expr_parent(ctx, upcast_id); }
 
        if progress_from { self.queue_expr(ctx, bound_from_id); }
 
        if progress_to { self.queue_expr(ctx, bound_to_id); }
 

	
 
        Ok(())
 
    }
 

	
 
    fn progress_conditional_expr(&mut self, ctx: &mut Ctx, id: ConditionalExpressionId) -> Result<(), ParseError> {
 
        // Note: test expression type is already enforced
 
        let upcast_id = id.upcast();
 
        let expr = &ctx.heap[id];
 
        let arg1_expr_id = expr.true_expression;
 
        let arg2_expr_id = expr.false_expression;
 

	
 
        debug_log!("Conditional expr: {}", upcast_id.index);
 
        debug_log!(" * Before:");
 
        debug_log!("   - Arg1 type: {}", self.temp_get_display_name(ctx, arg1_expr_id));
 
        debug_log!("   - Arg2 type: {}", self.temp_get_display_name(ctx, arg2_expr_id));
 
        debug_log!("   - Expr type: {}", self.temp_get_display_name(ctx, upcast_id));
 

	
 
        let (progress_expr, progress_arg1, progress_arg2) = self.apply_equal3_constraint(
 
            ctx, upcast_id, arg1_expr_id, arg2_expr_id, 0
 
        )?;
 

	
 
        debug_log!(" * After:");
 
        debug_log!("   - Arg1 type [{}]: {}", progress_arg1, self.temp_get_display_name(ctx, arg1_expr_id));
 
        debug_log!("   - Arg2 type [{}]: {}", progress_arg2, self.temp_get_display_name(ctx, arg2_expr_id));
 
        debug_log!("   - Expr type [{}]: {}", progress_expr, self.temp_get_display_name(ctx, upcast_id));
 

	
 
        if progress_expr { self.queue_expr_parent(ctx, upcast_id); }
 
        if progress_arg1 { self.queue_expr(ctx, arg1_expr_id); }
 
        if progress_arg2 { self.queue_expr(ctx, arg2_expr_id); }
 

	
 
        Ok(())
 
    }
 

	
 
    fn progress_binary_expr(&mut self, ctx: &mut Ctx, id: BinaryExpressionId) -> Result<(), ParseError> {
 
        // Note: our expression type might be fixed by our parent, but we still
 
        // need to make sure it matches the type associated with our operation.
 
        use BinaryOperator as BO;
 

	
 
        let upcast_id = id.upcast();
 
        let expr = &ctx.heap[id];
 
        let arg1_id = expr.left;
 
        let arg2_id = expr.right;
 

	
 
        debug_log!("Binary expr '{:?}': {}", expr.operation, upcast_id.index);
 
        debug_log!(" * Before:");
 
        debug_log!("   - Arg1 type: {}", self.temp_get_display_name(ctx, arg1_id));
 
        debug_log!("   - Arg2 type: {}", self.temp_get_display_name(ctx, arg2_id));
 
        debug_log!("   - Expr type: {}", self.temp_get_display_name(ctx, upcast_id));
 

	
 
        let (progress_expr, progress_arg1, progress_arg2) = match expr.operation {
 
            BO::Concatenate => {
 
                // Arguments may be arrays/slices, output is always an array
 
                let progress_expr = self.apply_forced_constraint(ctx, upcast_id, &ARRAY_TEMPLATE)?;
 
                let progress_arg1 = self.apply_forced_constraint(ctx, arg1_id, &ARRAYLIKE_TEMPLATE)?;
 
                let progress_arg2 = self.apply_forced_constraint(ctx, arg2_id, &ARRAYLIKE_TEMPLATE)?;
 
                let progress_expr = self.apply_template_constraint(ctx, upcast_id, &ARRAY_TEMPLATE)?;
 
                let progress_arg1 = self.apply_template_constraint(ctx, arg1_id, &ARRAYLIKE_TEMPLATE)?;
 
                let progress_arg2 = self.apply_template_constraint(ctx, arg2_id, &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, upcast_id, arg1_id, arg2_id, 1)?;
 

	
 
                (progress_expr || subtype_expr, progress_arg1 || subtype_arg1, progress_arg2 || subtype_arg2)
 
            },
 
            BO::LogicalAnd => {
 
                // Logical AND may operate both on normal booleans and on
 
                // booleans that are the result of a binding expression. So we
 
                // force the expression to bool-like, then apply an equal-3
 
                // force the expression to bool-like, then apply an equal_3
 
                // constraint. Any BindingBool will promote all the other Bool
 
                // types.
 
                let base_expr = self.apply_forced_constraint(ctx, upcast_id, &BOOLLIKE_TEMPLATE)?;
 
                let base_expr = self.apply_template_constraint(ctx, upcast_id, &BOOLLIKE_TEMPLATE)?;
 
                let (progress_expr, progress_arg1, progress_arg2) =
 
                    self.apply_equal3_constraint(ctx, upcast_id, arg1_id, arg2_id, 0)?;
 

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

	
 
                (progress_expr, progress_arg1, progress_arg2)
 
            },
 
            BO::BitwiseOr | BO::BitwiseXor | BO::BitwiseAnd | BO::Remainder | BO::ShiftLeft | BO::ShiftRight => {
 
                // All equal of integer type
 
                let progress_base = self.apply_forced_constraint(ctx, upcast_id, &INTEGERLIKE_TEMPLATE)?;
 
                let progress_base = self.apply_template_constraint(ctx, upcast_id, &INTEGERLIKE_TEMPLATE)?;
 
                let (progress_expr, progress_arg1, progress_arg2) =
 
                    self.apply_equal3_constraint(ctx, upcast_id, arg1_id, arg2_id, 0)?;
 

	
 
                (progress_base || progress_expr, progress_base || progress_arg1, progress_base || progress_arg2)
 
            },
 
            BO::Equality | BO::Inequality => {
 
                // Equal2 on args, forced boolean output
 
                let progress_expr = self.apply_forced_constraint(ctx, upcast_id, &BOOL_TEMPLATE)?;
 
                let progress_expr = self.apply_template_constraint(ctx, upcast_id, &BOOLLIKE_TEMPLATE)?;
 
                let (progress_arg1, progress_arg2) =
 
                    self.apply_equal2_constraint(ctx, upcast_id, arg1_id, 0, arg2_id, 0)?;
 

	
 
                (progress_expr, progress_arg1, progress_arg2)
 
            },
 
            BO::LessThan | BO::GreaterThan | BO::LessThanEqual | BO::GreaterThanEqual => {
 
                // Equal2 on args with numberlike type, forced boolean output
 
                let progress_expr = self.apply_forced_constraint(ctx, upcast_id, &BOOL_TEMPLATE)?;
 
                let progress_arg_base = self.apply_forced_constraint(ctx, arg1_id, &NUMBERLIKE_TEMPLATE)?;
 
                let progress_expr = self.apply_template_constraint(ctx, upcast_id, &BOOLLIKE_TEMPLATE)?;
 
                let progress_arg_base = self.apply_template_constraint(ctx, arg1_id, &NUMBERLIKE_TEMPLATE)?;
 
                let (progress_arg1, progress_arg2) =
 
                    self.apply_equal2_constraint(ctx, upcast_id, arg1_id, 0, arg2_id, 0)?;
 

	
 
                (progress_expr, progress_arg_base || progress_arg1, progress_arg_base || progress_arg2)
 
            },
 
            BO::Add | BO::Subtract | BO::Multiply | BO::Divide => {
 
                // All equal of number type
 
                let progress_base = self.apply_forced_constraint(ctx, upcast_id, &NUMBERLIKE_TEMPLATE)?;
 
                let progress_base = self.apply_template_constraint(ctx, upcast_id, &NUMBERLIKE_TEMPLATE)?;
 
                let (progress_expr, progress_arg1, progress_arg2) =
 
                    self.apply_equal3_constraint(ctx, upcast_id, arg1_id, arg2_id, 0)?;
 

	
 
                (progress_base || progress_expr, progress_base || progress_arg1, progress_base || progress_arg2)
 
            },
 
        };
 

	
 
        debug_log!(" * After:");
 
        debug_log!("   - Arg1 type [{}]: {}", progress_arg1, self.temp_get_display_name(ctx, arg1_id));
 
        debug_log!("   - Arg2 type [{}]: {}", progress_arg2, self.temp_get_display_name(ctx, arg2_id));
 
        debug_log!("   - Expr type [{}]: {}", progress_expr, self.temp_get_display_name(ctx, upcast_id));
 

	
 
        if progress_expr { self.queue_expr_parent(ctx, upcast_id); }
 
        if progress_arg1 { self.queue_expr(ctx, arg1_id); }
 
        if progress_arg2 { self.queue_expr(ctx, arg2_id); }
 

	
 
        Ok(())
 
    }
 

	
 
    fn progress_unary_expr(&mut self, ctx: &mut Ctx, id: UnaryExpressionId) -> Result<(), ParseError> {
 
        use UnaryOperator as UO;
 

	
 
        let upcast_id = id.upcast();
 
        let expr = &ctx.heap[id];
 
        let arg_id = expr.expression;
 

	
 
        debug_log!("Unary expr '{:?}': {}", expr.operation, upcast_id.index);
 
        debug_log!(" * Before:");
 
        debug_log!("   - Arg  type: {}", self.temp_get_display_name(ctx, arg_id));
 
        debug_log!("   - Expr type: {}", self.temp_get_display_name(ctx, upcast_id));
 

	
 
        let (progress_expr, progress_arg) = match expr.operation {
 
            UO::Positive | UO::Negative => {
 
                // Equal types of numeric class
 
                let progress_base = self.apply_forced_constraint(ctx, upcast_id, &NUMBERLIKE_TEMPLATE)?;
 
                let progress_base = self.apply_template_constraint(ctx, upcast_id, &NUMBERLIKE_TEMPLATE)?;
 
                let (progress_expr, progress_arg) =
 
                    self.apply_equal2_constraint(ctx, upcast_id, upcast_id, 0, arg_id, 0)?;
 

	
 
                (progress_base || progress_expr, progress_base || progress_arg)
 
            },
 
            UO::BitwiseNot | UO::PreIncrement | UO::PreDecrement | UO::PostIncrement | UO::PostDecrement => {
 
                // Equal types of integer class
 
                let progress_base = self.apply_forced_constraint(ctx, upcast_id, &INTEGERLIKE_TEMPLATE)?;
 
                let progress_base = self.apply_template_constraint(ctx, upcast_id, &INTEGERLIKE_TEMPLATE)?;
 
                let (progress_expr, progress_arg) =
 
                    self.apply_equal2_constraint(ctx, upcast_id, upcast_id, 0, arg_id, 0)?;
 

	
 
                (progress_base || progress_expr, progress_base || progress_arg)
 
            },
 
            UO::LogicalNot => {
 
                // Both booleans
 
                let progress_expr = self.apply_forced_constraint(ctx, upcast_id, &BOOL_TEMPLATE)?;
 
                let progress_arg = self.apply_forced_constraint(ctx, upcast_id, &BOOL_TEMPLATE)?;
 
                (progress_expr, progress_arg)
 
            }
 
        };
 

	
 
        debug_log!(" * After:");
 
        debug_log!("   - Arg  type [{}]: {}", progress_arg, self.temp_get_display_name(ctx, arg_id));
 
        debug_log!("   - Expr type [{}]: {}", progress_expr, self.temp_get_display_name(ctx, upcast_id));
 

	
 
        if progress_expr { self.queue_expr_parent(ctx, upcast_id); }
 
        if progress_arg { self.queue_expr(ctx, arg_id); }
 

	
 
        Ok(())
 
    }
 

	
 
    fn progress_indexing_expr(&mut self, ctx: &mut Ctx, id: IndexingExpressionId) -> Result<(), ParseError> {
 
        let upcast_id = id.upcast();
 
        let expr = &ctx.heap[id];
 
        let subject_id = expr.subject;
 
        let index_id = expr.index;
 

	
 
        debug_log!("Indexing expr: {}", upcast_id.index);
 
        debug_log!(" * Before:");
 
        debug_log!("   - Subject type: {}", self.temp_get_display_name(ctx, subject_id));
 
        debug_log!("   - Index   type: {}", self.temp_get_display_name(ctx, index_id));
 
        debug_log!("   - Expr    type: {}", self.temp_get_display_name(ctx, upcast_id));
 

	
 
        // Make sure subject is arraylike and index is integerlike
 
        let progress_subject_base = self.apply_forced_constraint(ctx, subject_id, &ARRAYLIKE_TEMPLATE)?;
 
        let progress_index = self.apply_forced_constraint(ctx, index_id, &INTEGERLIKE_TEMPLATE)?;
 
        let progress_subject_base = self.apply_template_constraint(ctx, subject_id, &ARRAYLIKE_TEMPLATE)?;
 
        let progress_index = self.apply_template_constraint(ctx, index_id, &INTEGERLIKE_TEMPLATE)?;
 

	
 
        // Make sure if output is of T then subject is Array<T>
 
        let (progress_expr, progress_subject) =
 
            self.apply_equal2_constraint(ctx, upcast_id, upcast_id, 0, subject_id, 1)?;
 

	
 
        debug_log!(" * After:");
 
        debug_log!("   - Subject type [{}]: {}", progress_subject_base || progress_subject, self.temp_get_display_name(ctx, subject_id));
 
        debug_log!("   - Index   type [{}]: {}", progress_index, self.temp_get_display_name(ctx, index_id));
 
        debug_log!("   - Expr    type [{}]: {}", progress_expr, self.temp_get_display_name(ctx, upcast_id));
 

	
 
        if progress_expr { self.queue_expr_parent(ctx, upcast_id); }
 
        if progress_subject_base || progress_subject { self.queue_expr(ctx, subject_id); }
 
        if progress_index { self.queue_expr(ctx, index_id); }
 

	
 
        Ok(())
 
    }
 

	
 
    fn progress_slicing_expr(&mut self, ctx: &mut Ctx, id: SlicingExpressionId) -> Result<(), ParseError> {
 
        let upcast_id = id.upcast();
 
        let expr = &ctx.heap[id];
 
        let subject_id = expr.subject;
 
        let from_id = expr.from_index;
 
        let to_id = expr.to_index;
 

	
 
        debug_log!("Slicing expr: {}", upcast_id.index);
 
        debug_log!(" * Before:");
 
        debug_log!("   - Subject type: {}", self.temp_get_display_name(ctx, subject_id));
 
        debug_log!("   - FromIdx type: {}", self.temp_get_display_name(ctx, from_id));
 
        debug_log!("   - ToIdx   type: {}", self.temp_get_display_name(ctx, to_id));
 
        debug_log!("   - Expr    type: {}", self.temp_get_display_name(ctx, upcast_id));
 

	
 
        // Make sure subject is arraylike and indices are of equal integerlike
 
        let progress_subject_base = self.apply_forced_constraint(ctx, subject_id, &ARRAYLIKE_TEMPLATE)?;
 
        let progress_idx_base = self.apply_forced_constraint(ctx, from_id, &INTEGERLIKE_TEMPLATE)?;
 
        let progress_subject_base = self.apply_template_constraint(ctx, subject_id, &ARRAYLIKE_TEMPLATE)?;
 
        let progress_idx_base = self.apply_template_constraint(ctx, from_id, &INTEGERLIKE_TEMPLATE)?;
 
        let (progress_from, progress_to) = self.apply_equal2_constraint(ctx, upcast_id, from_id, 0, to_id, 0)?;
 

	
 
        // Make sure if output is of Slice<T> then subject is Array<T>
 
        let progress_expr_base = self.apply_forced_constraint(ctx, upcast_id, &SLICE_TEMPLATE)?;
 
        let progress_expr_base = self.apply_template_constraint(ctx, upcast_id, &SLICE_TEMPLATE)?;
 
        let (progress_expr, progress_subject) =
 
            self.apply_equal2_constraint(ctx, upcast_id, upcast_id, 1, subject_id, 1)?;
 

	
 

	
 
        debug_log!(" * After:");
 
        debug_log!("   - Subject type [{}]: {}", progress_subject_base || progress_subject, self.temp_get_display_name(ctx, subject_id));
 
        debug_log!("   - FromIdx type [{}]: {}", progress_idx_base || progress_from, self.temp_get_display_name(ctx, from_id));
 
        debug_log!("   - ToIdx   type [{}]: {}", progress_idx_base || progress_to, self.temp_get_display_name(ctx, to_id));
 
        debug_log!("   - Expr    type [{}]: {}", progress_expr, self.temp_get_display_name(ctx, upcast_id));
 

	
 
        if progress_expr_base || progress_expr { self.queue_expr_parent(ctx, upcast_id); }
 
        if progress_subject_base || progress_subject { self.queue_expr(ctx, subject_id); }
 
        if progress_idx_base || progress_from { self.queue_expr(ctx, from_id); }
 
        if progress_idx_base || progress_to { self.queue_expr(ctx, to_id); }
 

	
 
        Ok(())
 
    }
 

	
 
    fn progress_select_expr(&mut self, ctx: &mut Ctx, id: SelectExpressionId) -> Result<(), ParseError> {
 
        let upcast_id = id.upcast();
 
        
 
        debug_log!("Select expr: {}", upcast_id.index);
 
        debug_log!(" * Before:");
 
        debug_log!("   - Subject type: {}", self.temp_get_display_name(ctx, ctx.heap[id].subject));
 
        debug_log!("   - Expr    type: {}", self.temp_get_display_name(ctx, upcast_id));
 

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

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

	
 
        fn determine_inference_type_instance<'a>(types: &'a TypeTable, infer_type: &InferenceType) -> Result<Option<&'a DefinedType>, ()> {
 
            for part in &infer_type.parts {
 
                if part.is_marker() || !part.is_concrete() {
 
                    continue;
 
                }
 

	
 
                // Part is concrete, check if it is an instance of something
 
                if let InferenceTypePart::Instance(definition_id, _num_sub) = part {
 
                    // Lookup type definition and ensure the specified field 
 
                    // name exists on the struct
 
                    let definition = types.get_base_definition(definition_id);
 
                    debug_assert!(definition.is_some());
 
                    let definition = definition.unwrap();
 

	
 
                    return Ok(Some(definition))
 
                } else {
 
                    // Expected an instance of something
 
                    return Err(())
 
                }
 
            }
 

	
 
            // Nothing is concrete yet
 
            Ok(None)
 
        }
 

	
 
        if infer_expr.field_or_monomorph_idx < 0 {
 
            // We don't know the field or the definition it is pointing to yet
 
            // Not yet known, check if we can determine it
 
            let subject_type = &self.expr_types[subject_expr_idx as usize].expr_type;
 
            let type_def = determine_inference_type_instance(&ctx.types, subject_type);
 

	
 
            match type_def {
 
                Ok(Some(type_def)) => {
 
                    // Subject type is known, check if it is a
 
                    // struct and the field exists on the struct
 
                    let struct_def = if let DefinedTypeVariant::Struct(struct_def) = &type_def.definition {
 
                        struct_def
 
                    } else {
 
                        return Err(ParseError::new_error_at_span(
 
                            &ctx.module.source, select_expr.field_name.span, format!(
 
                                "Can only apply field access to structs, got a subject of type '{}'",
 
                                subject_type.display_name(&ctx.heap)
 
                            )
 
                        ));
 
                    };
 

	
 
                    let mut struct_def_id = None;
 

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

	
 
                    if struct_def_id.is_none() {
 
                        let ast_struct_def = ctx.heap[type_def.ast_definition].as_struct();
 
                        return Err(ParseError::new_error_at_span(
 
                            &ctx.module.source, select_expr.field_name.span, format!(
 
@@ -1998,196 +2009,196 @@ impl PassTyping {
 
                            )
 
                        ))
 
                    }
 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	
 
        Ok(())
 
    }
 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	
 
                    debug_log!(
 
                        "   - Field {} type | sig: {}, field: {}", field_idx,
 
                        unsafe{&*signature_type}.display_name(&ctx.heap),
 
                        unsafe{&*field_type}.display_name(&ctx.heap)
 
                    );
 
                    if progress_arg {
 
                        self.expr_queued.push_back(field_expr_idx);
 
                    }
 
                }
 
@@ -2251,269 +2262,280 @@ impl PassTyping {
 

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

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

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

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

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

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

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

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

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

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

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

	
 
                progress_expr
 
            },
 
            Literal::Array(data) => {
 
                let expr_elements = data.clone(); // TODO: @performance
 
                debug_log!("Array expr ({} elements): {}", expr_elements.len(), upcast_id.index);
 
                debug_log!(" * Before:");
 
                debug_log!("   - Expr type: {}", self.temp_get_display_name(ctx, upcast_id));
 

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

	
 
                // And the output should be an array of the element types
 
                let mut progress_expr = self.apply_forced_constraint(ctx, upcast_id, &ARRAY_TEMPLATE)?;
 
                let mut progress_expr = self.apply_template_constraint(ctx, upcast_id, &ARRAY_TEMPLATE)?;
 
                if !expr_elements.is_empty() {
 
                    let first_arg_id = expr_elements[0];
 
                    let (inner_expr_progress, arg_progress) = self.apply_equal2_constraint(
 
                        ctx, upcast_id, upcast_id, 1, first_arg_id, 0
 
                    )?;
 

	
 
                    progress_expr = progress_expr || inner_expr_progress;
 

	
 
                    // Note that if the array type progressed the type of the arguments,
 
                    // then we should enqueue this progression function again
 
                    // TODO: @fix Make apply_equal_n accept a start idx as well
 
                    if arg_progress { self.queue_expr(ctx, upcast_id); }
 
                }
 

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

	
 
                progress_expr
 
            },
 
        };
 

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

	
 
        // TODO: FIX!!!!
 
        if progress_expr { self.queue_expr_parent(ctx, upcast_id); }
 

	
 

	
 
        Ok(())
 
    }
 

	
 
    fn progress_cast_expr(&mut self, ctx: &mut Ctx, id: CastExpressionId) -> Result<(), ParseError> {
 
        let upcast_id = id.upcast();
 
        let expr = &ctx.heap[id];
 
        let expr_idx = expr.unique_id_in_definition;
 

	
 
        debug_log!("Casting expr: {}", upcast_id.index);
 
        debug_log!(" * Before:");
 
        debug_log!("   - Expr type:    {}", self.temp_get_display_name(ctx, upcast_id));
 
        debug_log!("   - Subject type: {}", self.temp_get_display_name(ctx, expr.subject));
 

	
 
        // The cast expression might have its output type fixed by the
 
        // programmer, so apply that type to the output. Apart from that casting
 
        // acts like a blocker for two-way inference. So we'll just have to wait
 
        // until we know if the cast is valid.
 
        // TODO: Another thing that has to be updated the moment the type
 
        //  inferencer is fully index/job-based
 
        let infer_type = self.determine_inference_type_from_parser_type_elements(&expr.to_type.elements, true);
 
        let expr_progress = self.apply_forced_constraint(ctx, upcast_id, &infer_type.parts)?;
 
        let expr_progress = self.apply_template_constraint(ctx, upcast_id, &infer_type.parts)?;
 

	
 
        if expr_progress {
 
            self.queue_expr_parent(ctx, upcast_id);
 
        }
 

	
 
        // Check if the two types are compatible
 
        debug_log!(" * After:");
 
        debug_log!("   - Expr type [{}]: {}", expr_progress, self.temp_get_display_name(ctx, upcast_id));
 
        debug_log!("   - Note that the subject type can never be inferred");
 
        debug_log!(" * Decision:");
 

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

	
 
        // Valid casts: (bool, integer, character) can always be cast to one
 
        // another. A cast from a type to itself is also valid.
 
        fn is_bool_int_or_char(parts: &[InferenceTypePart]) -> bool {
 
            return parts.len() == 1 && (
 
                parts[0] == InferenceTypePart::Bool ||
 
                parts[0] == InferenceTypePart::Character ||
 
                parts[0].is_concrete_integer()
 
            );
 
        }
 

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

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

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

	
 
        Ok(())
 
    }
 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	
 
        Ok(())
 
    }
 

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

	
 
        debug_log!("Variable expr '{}': {}", ctx.heap[var_id].identifier.value.as_str(), upcast_id.index);
 
        debug_log!(" * Before:");
 
        debug_log!("   - Var  type: {}", self.var_types.get(&var_id).unwrap().var_type.display_name(&ctx.heap));
 
        debug_log!("   - Expr type: {}", self.temp_get_display_name(ctx, upcast_id));
 

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

	
 
        let infer_res = unsafe{ InferenceType::infer_subtrees_for_both_types(
 
            &mut var_data.var_type as *mut _, 0, expr_type, 0
 
        ) };
 
        if infer_res == DualInferenceResult::Incompatible {
 
            let var_decl = &ctx.heap[var_id];
 
            return Err(ParseError::new_error_at_span(
 
                &ctx.module.source, var_decl.identifier.span, format!(
 
                    "Conflicting types for this variable, previously assigned the type '{}'",
 
                    var_data.var_type.display_name(&ctx.heap)
 
                )
 
            ).with_info_at_span(
 
                &ctx.module.source, var_expr.identifier.span, format!(
 
                    "But inferred to have incompatible type '{}' here",
 
                    expr_type.display_name(&ctx.heap)
 
                )
 
            ))
 
        }
 

	
 
        let progress_var = infer_res.modified_lhs();
 
        let progress_expr = infer_res.modified_rhs();
 

	
 
        if progress_var {
 
            // Let other variable expressions using this type progress as well
 
            for other_expr in var_data.used_at.iter() {
 
                if *other_expr != upcast_id {
 
                    let other_expr_idx = ctx.heap[*other_expr].get_unique_id_in_definition();
 
                    self.expr_queued.push_back(other_expr_idx);
 
                }
 
            }
 

	
 
            // Let a linked port know that our type has updated
 
            if let Some(linked_id) = var_data.linked_var {
 
                // Only perform one-way inference to prevent updating our type,
 
                // this would lead to an inconsistency in the type inference
 
                // algorithm otherwise.
 
                let var_type: *mut _ = &mut var_data.var_type;
 
                let link_data = self.var_types.get_mut(&linked_id).unwrap();
 

	
 
                debug_assert!(
 
                    unsafe{&*var_type}.parts[0] == InferenceTypePart::Input ||
 
                    unsafe{&*var_type}.parts[0] == InferenceTypePart::Output
 
                );
 
                debug_assert!(
 
                    link_data.var_type.parts[0] == InferenceTypePart::Input ||
 
                    link_data.var_type.parts[0] == InferenceTypePart::Output
 
                );
 
                match InferenceType::infer_subtree_for_single_type(&mut link_data.var_type, 1, &unsafe{&*var_type}.parts, 1) {
 
                match InferenceType::infer_subtree_for_single_type(&mut link_data.var_type, 1, &unsafe{&*var_type}.parts, 1, false) {
 
                    SingleInferenceResult::Modified => {
 
                        for other_expr in &link_data.used_at {
 
                            let other_expr_idx = ctx.heap[*other_expr].get_unique_id_in_definition();
 
                            self.expr_queued.push_back(other_expr_idx);
 
                        }
 
                    },
 
                    SingleInferenceResult::Unmodified => {},
 
                    SingleInferenceResult::Incompatible => {
 
                        let var_data = self.var_types.get(&var_id).unwrap();
 
                        let link_data = self.var_types.get(&linked_id).unwrap();
 
                        let var_decl = &ctx.heap[var_id];
 
                        let link_decl = &ctx.heap[linked_id];
 

	
 
                        return Err(ParseError::new_error_at_span(
 
                            &ctx.module.source, var_decl.identifier.span, format!(
 
                                "Conflicting types for this variable, assigned the type '{}'",
 
                                var_data.var_type.display_name(&ctx.heap)
 
                            )
 
                        ).with_info_at_span(
 
                            &ctx.module.source, link_decl.identifier.span, format!(
 
                                "Because it is incompatible with this variable, assigned the type '{}'",
 
                                link_data.var_type.display_name(&ctx.heap)
 
                            )
 
                        ));
 
                    }
 
                }
 
            }
 
        }
 
        if progress_expr { self.queue_expr_parent(ctx, upcast_id); }
 

	
 
        debug_log!(" * After:");
 
        debug_log!("   - Var  type [{}]: {}", progress_var, self.var_types.get(&var_id).unwrap().var_type.display_name(&ctx.heap));
 
        debug_log!("   - Expr type [{}]: {}", progress_expr, self.temp_get_display_name(ctx, upcast_id));
 

	
 

	
 
        Ok(())
 
    }
 

	
 
    fn queue_expr_parent(&mut self, ctx: &Ctx, expr_id: ExpressionId) {
 
        if let ExpressionParent::Expression(parent_expr_id, _) = &ctx.heap[expr_id].parent() {
 
            let expr_idx = ctx.heap[*parent_expr_id].get_unique_id_in_definition();
 
            self.expr_queued.push_back(expr_idx);
 
        }
 
    }
 

	
 
    fn queue_expr(&mut self, ctx: &Ctx, expr_id: ExpressionId) {
 
        let expr_idx = ctx.heap[expr_id].get_unique_id_in_definition();
 
        self.expr_queued.push_back(expr_idx);
 
    }
 

	
 
    /// Applies a forced type constraint: the type associated with the supplied
 
    /// expression will be molded into the provided "template". The template may
 
    /// be fully specified (e.g. a bool) or contain "inference" variables (e.g.
 
    /// an array of T)
 
    fn apply_forced_constraint(
 
    /// Applies a template type constraint: the type associated with the
 
    /// supplied expression will be molded into the provided `template`. But
 
    /// will be considered valid if the template could've been molded into the
 
    /// expression type as well. Hence the template may be fully specified (e.g.
 
    /// a bool) or contain "inference" variables (e.g. an array of T)
 
    fn apply_template_constraint(
 
        &mut self, ctx: &Ctx, expr_id: ExpressionId, template: &[InferenceTypePart]
 
    ) -> Result<bool, ParseError> {
 
        let expr_idx = ctx.heap[expr_id].get_unique_id_in_definition(); // TODO: @Temp
 
        let expr_type = &mut self.expr_types[expr_idx as usize].expr_type;
 
        match InferenceType::infer_subtree_for_single_type(expr_type, 0, template, 0) {
 
        match InferenceType::infer_subtree_for_single_type(expr_type, 0, template, 0, false) {
 
            SingleInferenceResult::Modified => Ok(true),
 
            SingleInferenceResult::Unmodified => Ok(false),
 
            SingleInferenceResult::Incompatible => Err(
 
                self.construct_template_type_error(ctx, expr_id, template)
 
            )
 
        }
 
    }
 

	
 
    fn apply_forced_constraint_types(
 
    fn apply_template_constraint_to_types(
 
        to_infer: *mut InferenceType, to_infer_start_idx: usize,
 
        template: &[InferenceTypePart], template_start_idx: usize
 
    ) -> Result<bool, ()> {
 
        match InferenceType::infer_subtree_for_single_type(
 
            unsafe{ &mut *to_infer }, to_infer_start_idx,
 
            template, template_start_idx
 
            template, template_start_idx, false
 
        ) {
 
            SingleInferenceResult::Modified => Ok(true),
 
            SingleInferenceResult::Unmodified => Ok(false),
 
            SingleInferenceResult::Incompatible => Err(()),
 
        }
 
    }
 

	
 
    /// Applies a forced constraint: the supplied expression's type MUST be
 
    /// inferred from the template, the other way around is considered invalid.
 
    fn apply_forced_constraint(
 
        &mut self, ctx: &Ctx, expr_id: ExpressionId, template: &[InferenceTypePart]
 
    ) -> Result<bool, ParseError> {
 
        let expr_idx = ctx.heap[expr_id].get_unique_id_in_definition();
 
        let expr_type = &mut self.expr_types[expr_idx as usize].expr_type;
 
        match InferenceType::infer_subtree_for_single_type(expr_type, 0, template, 0, true) {
 
            SingleInferenceResult::Modified => Ok(true),
 
            SingleInferenceResult::Unmodified => Ok(false),
 
            SingleInferenceResult::Incompatible => Err(
 
                self.construct_template_type_error(ctx, expr_id, template)
 
            )
 
        }
 
    }
 

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

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

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

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

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

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

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

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

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

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

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

	
 
                modified_sig = modified_sig || modified_at_marker;
 
            // }
 

	
 
            seek_idx = end_idx;
 
        }
 

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

	
 
    /// Applies a type constraint that expects all three provided types to be
 
    /// equal. In case we can make progress in inferring the types then we
 
    /// attempt to do so. If the call is successful then the composition of all
 
    /// types is made equal.
 
    fn apply_equal3_constraint(
 
        &mut self, ctx: &Ctx, expr_id: ExpressionId,
 
        arg1_id: ExpressionId, arg2_id: ExpressionId,
 
        start_idx: usize
 
    ) -> Result<(bool, bool, bool), ParseError> {
 
        // Safety: all points are unique
 
        //         containers may not be modified
 
        let expr_expr_idx = ctx.heap[expr_id].get_unique_id_in_definition(); // TODO: @Temp
 
        let arg1_expr_idx = ctx.heap[arg1_id].get_unique_id_in_definition();
 
        let arg2_expr_idx = ctx.heap[arg2_id].get_unique_id_in_definition();
 

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

	
 
        let expr_res = unsafe{
 
            InferenceType::infer_subtrees_for_both_types(expr_type, start_idx, arg1_type, start_idx)
 
        };
 
        if expr_res == DualInferenceResult::Incompatible {
 
            return Err(self.construct_expr_type_error(ctx, expr_id, arg1_id));
 
        }
 

	
 
        let args_res = unsafe{
 
            InferenceType::infer_subtrees_for_both_types(arg1_type, start_idx, arg2_type, start_idx) };
 
        if args_res == DualInferenceResult::Incompatible {
 
            return Err(self.construct_arg_type_error(ctx, expr_id, arg1_id, arg2_id));
 
        }
 

	
 
        // If all types are compatible, but the second call caused the arg1_type
 
        // to be expanded, then we must also assign this to expr_type.
 
        let mut progress_expr = expr_res.modified_lhs();
 
        let mut progress_arg1 = expr_res.modified_rhs();
 
        let progress_arg2 = args_res.modified_rhs();
 

	
 
        if args_res.modified_lhs() { 
 
            unsafe {
 
                let end_idx = InferenceType::find_subtree_end_idx(&(*arg2_type).parts, start_idx);
 
                let subtree = &((*arg2_type).parts[start_idx..end_idx]);
 
                (*expr_type).replace_subtree(start_idx, subtree);
 
            }
 
            progress_expr = true;
 
            progress_arg1 = true;
 
        }
 

	
 
        Ok((progress_expr, progress_arg1, progress_arg2))
 
    }
 

	
 
    // TODO: @optimize Since we only deal with a single type this might be done
 
    //  a lot more efficiently, methinks (disregarding the allocations here)
 
    fn apply_equal_n_constraint(
 
        &mut self, ctx: &Ctx, expr_id: ExpressionId, args: &[ExpressionId],
 
    ) -> Result<Vec<bool>, ParseError> {
 
        // Early exit
 
        match args.len() {
 
            0 => return Ok(vec!()),         // nothing to progress
 
            1 => return Ok(vec![false]),    // only one type, so nothing to infer
 
            _ => {}
 
        }
 

	
 
        let mut progress = Vec::new();
 
        progress.resize(args.len(), false);
 

	
 
        // Do pairwise inference, keep track of the last entry we made progress
 
        // on. Once done we need to update everything to the most-inferred type.
 
        let mut arg_iter = args.iter();
 
        let mut last_arg_id = *arg_iter.next().unwrap();
 
        let mut last_lhs_progressed = 0;
 
        let mut lhs_arg_idx = 0;
 

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

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

	
 
            if res == DualInferenceResult::Incompatible {
 
                return Err(self.construct_arg_type_error(ctx, expr_id, last_arg_id, *next_arg_id));
 
            }
 

	
 
            if res.modified_lhs() {
 
                // We re-inferred something on the left hand side, so everything
 
                // up until now should be re-inferred.
 
                progress[lhs_arg_idx] = true;
 
                last_lhs_progressed = lhs_arg_idx;
 
            }
 
            progress[lhs_arg_idx + 1] = res.modified_rhs();
 

	
 
            last_arg_id = *next_arg_id;
 
            lhs_arg_idx += 1;
 
        }
 

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

	
 
        Ok(progress)
 
    }
 

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

	
 
        let expr = &ctx.heap[expr_id];
 
        let inference_type = match expr.parent() {
 
            EP::None => {
 
            EP::None =>
 
                // Should have been set by linker
 
                println!("DEBUG: CRAP!\n{:?}", expr);
 
                unreachable!() },
 
                unreachable!(),
 
            EP::ExpressionStmt(_) =>
 
                // Determined during type inference
 
                InferenceType::new(false, false, vec![ITP::Unknown]),
 
            EP::Expression(parent_id, idx_in_parent) => {
 
                // If we are the test expression of a conditional expression,
 
                // then we must resolve to a boolean
 
                let is_conditional = if let Expression::Conditional(_) = &ctx.heap[*parent_id] {
 
                    true
 
                } else {
 
                    false
 
                };
 

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

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

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

	
 
            debug_assert!((infer_expr.extra_data_idx != -1) == needs_extra_data);
 
        }
 

	
 
        Ok(())
 
    }
 

	
 
    fn insert_initial_call_polymorph_data(
 
        &mut self, ctx: &mut Ctx, call_id: CallExpressionId
 
    ) {
 
        // Note: the polymorph variables may be partially specified and may
 
        // contain references to the wrapping definition's (i.e. the proctype
 
        // we are currently visiting) polymorphic arguments.
 
        //
 
        // The arguments of the call may refer to polymorphic variables in the
 
        // definition of the function we're calling, not of the wrapping
 
        // definition. We insert markers in these inferred types to be able to
 
        // map them back and forth to the polymorphic arguments of the function
 
        // we are calling.
 
        let call = &ctx.heap[call_id];
 
        let extra_data_idx = self.expr_types[call.unique_id_in_definition as usize].extra_data_idx; // TODO: @Temp
 
        debug_assert!(extra_data_idx != -1, "insert initial call polymorph data, no preallocated ExtraData");
 

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

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

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

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

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

	
 
    fn insert_initial_struct_polymorph_data(
 
        &mut self, ctx: &mut Ctx, lit_id: LiteralExpressionId,
 
    ) {
 
        use InferenceTypePart as ITP;
 
        let literal = &ctx.heap[lit_id];
 
        let extra_data_idx = self.expr_types[literal.unique_id_in_definition as usize].extra_data_idx; // TODO: @Temp
 
        debug_assert!(extra_data_idx != -1, "initial struct polymorph data, but no preallocated ExtraData");
 
        let literal = ctx.heap[lit_id].value.as_struct();
 

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

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

	
 
@@ -3270,195 +3308,195 @@ impl PassTyping {
 

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

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

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

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

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

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

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

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

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

	
 
        // Generate initial field type
 
        let field_type = self.determine_inference_type_from_parser_type_elements(&definition.fields[field_idx].parser_type.elements, false);
 
        self.extra_data[extra_data_idx as usize] = ExtraData{
 
            expr_id: select_id.upcast(),
 
            definition_id: struct_def_id,
 
            poly_vars,
 
            embedded: vec![InferenceType::new(num_poly_vars != 0, num_poly_vars == 0, struct_parts)],
 
            returned: field_type
 
        };
 
    }
 

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

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

	
 
        for element in elements {
 
            match &element.variant {
 
                // Compiler-only types
 
                PTV::Void => { infer_type.push(ITP::Void); },
 
                PTV::InputOrOutput => { infer_type.push(ITP::PortLike); },
 
                PTV::ArrayLike => { infer_type.push(ITP::ArrayLike); },
 
                PTV::IntegerLike => { infer_type.push(ITP::IntegerLike); },
 
                PTV::InputOrOutput => { infer_type.push(ITP::PortLike); has_inferred = true },
 
                PTV::ArrayLike => { infer_type.push(ITP::ArrayLike); has_inferred = true },
 
                PTV::IntegerLike => { infer_type.push(ITP::IntegerLike); has_inferred = true },
 
                // Builtins
 
                PTV::Message => {
 
                    // TODO: @types Remove the Message -> Byte hack at some point...
 
                    infer_type.push(ITP::Message);
 
                    infer_type.push(ITP::UInt8);
 
                },
 
                PTV::Bool => { infer_type.push(ITP::Bool); },
 
                PTV::UInt8 => { infer_type.push(ITP::UInt8); },
 
                PTV::UInt16 => { infer_type.push(ITP::UInt16); },
 
                PTV::UInt32 => { infer_type.push(ITP::UInt32); },
 
                PTV::UInt64 => { infer_type.push(ITP::UInt64); },
 
                PTV::SInt8 => { infer_type.push(ITP::SInt8); },
 
                PTV::SInt16 => { infer_type.push(ITP::SInt16); },
 
                PTV::SInt32 => { infer_type.push(ITP::SInt32); },
 
                PTV::SInt64 => { infer_type.push(ITP::SInt64); },
 
                PTV::Character => { infer_type.push(ITP::Character); },
 
                PTV::String => { infer_type.push(ITP::String); },
 
                // Special markers
 
                PTV::IntegerLiteral => { unreachable!("integer literal type on variable type"); },
 
                PTV::Inferred => {
 
                    infer_type.push(ITP::Unknown);
 
                    has_inferred = true;
 
                },
 
                // With nested types
 
                PTV::Array => { infer_type.push(ITP::Array); },
 
                PTV::Input => { infer_type.push(ITP::Input); },
 
                PTV::Output => { infer_type.push(ITP::Output); },
 
                PTV::PolymorphicArgument(belongs_to_definition, poly_arg_idx) => {
 
                    let poly_arg_idx = *poly_arg_idx;
 
                    if use_definitions_known_poly_args {
 
                        // Refers to polymorphic argument on procedure we're currently processing.
 
                        // This argument is already known.
 
                        debug_assert_eq!(*belongs_to_definition, self.definition_type.definition_id());
 
                        debug_assert!((poly_arg_idx as usize) < self.poly_vars.len());
 

	
 
                        for concrete_part in &self.poly_vars[poly_arg_idx as usize].parts {
 
                            infer_type.push(ITP::from(*concrete_part));
 
                        }
 
                    } else {
 
                        // Polymorphic argument has to be inferred
 
                        has_markers = true;
 
                        has_inferred = true;
 
                        infer_type.push(ITP::Marker(poly_arg_idx));
 
                        infer_type.push(ITP::Unknown)
 
                    }
 
                },
 
                PTV::Definition(definition_id, num_embedded) => {
 
                    infer_type.push(ITP::Instance(*definition_id, *num_embedded));
 
                }
 
            }
 
        }
 

	
 
        InferenceType::new(has_markers, !has_inferred, infer_type)
 
    }
 

	
 
    /// Construct an error when an expression's type does not match. This
 
    /// happens if we infer the expression type from its arguments (e.g. the
 
    /// expression type of an addition operator is the type of the arguments)
 
    /// But the expression type was already set due to our parent (e.g. an
 
    /// "if statement" or a "logical not" always expecting a boolean)
 
    fn construct_expr_type_error(
 
        &self, ctx: &Ctx, expr_id: ExpressionId, arg_id: ExpressionId
 
    ) -> ParseError {
 
        // TODO: Expand and provide more meaningful information for humans
 
        let expr = &ctx.heap[expr_id];
 
        let arg_expr = &ctx.heap[arg_id];
 
        let expr_idx = expr.get_unique_id_in_definition();
 
        let arg_expr_idx = arg_expr.get_unique_id_in_definition();
 
        let expr_type = &self.expr_types[expr_idx as usize].expr_type;
 
        let arg_type = &self.expr_types[arg_expr_idx as usize].expr_type;
 

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

	
 
    fn construct_arg_type_error(
 
        &self, ctx: &Ctx, expr_id: ExpressionId,
 
        arg1_id: ExpressionId, arg2_id: ExpressionId
 
    ) -> ParseError {
 
        let expr = &ctx.heap[expr_id];
 
        let arg1 = &ctx.heap[arg1_id];
 
        let arg2 = &ctx.heap[arg2_id];
 

	
 
        let arg1_idx = arg1.get_unique_id_in_definition();
 
        let arg1_type = &self.expr_types[arg1_idx as usize].expr_type;
 
        let arg2_idx = arg2.get_unique_id_in_definition();
 
        let arg2_type = &self.expr_types[arg2_idx as usize].expr_type;
 
@@ -3618,135 +3656,135 @@ impl PassTyping {
 
                        InferenceType::partial_display_name(&ctx.heap, section_b)
 
                    )
 
                );
 
        }
 

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

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

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

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

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

	
 
    #[test]
 
    fn test_single_part_inference() {
 
        // lhs argument inferred from rhs
 
        let pairs = [
 
            (ITP::NumberLike, ITP::UInt8),
 
            (ITP::IntegerLike, ITP::SInt32),
 
            (ITP::Unknown, ITP::UInt64),
 
            (ITP::Unknown, ITP::String)
 
        ];
 
        for (lhs, rhs) in pairs.iter() {
 
            // Using infer-both
 
            let mut lhs_type = IT::new(false, false, vec![lhs.clone()]);
 
            let mut rhs_type = IT::new(false, true, vec![rhs.clone()]);
 
            let result = unsafe{ IT::infer_subtrees_for_both_types(
 
                &mut lhs_type, 0, &mut rhs_type, 0
 
            ) };
 
            assert_eq!(DualInferenceResult::First, result);
 
            assert_eq!(lhs_type.parts, rhs_type.parts);
 

	
 
            // Using infer-single
 
            let mut lhs_type = IT::new(false, false, vec![lhs.clone()]);
 
            let rhs_type = IT::new(false, true, vec![rhs.clone()]);
 
            let result = IT::infer_subtree_for_single_type(
 
                &mut lhs_type, 0, &rhs_type.parts, 0
 
                &mut lhs_type, 0, &rhs_type.parts, 0, false
 
            );
 
            assert_eq!(SingleInferenceResult::Modified, result);
 
            assert_eq!(lhs_type.parts, rhs_type.parts);
 
        }
 
    }
 

	
 
    #[test]
 
    fn test_multi_part_inference() {
 
        let pairs = [
 
            (vec![ITP::ArrayLike, ITP::NumberLike], vec![ITP::Slice, ITP::SInt8]),
 
            (vec![ITP::Unknown], vec![ITP::Input, ITP::Array, ITP::String]),
 
            (vec![ITP::PortLike, ITP::SInt32], vec![ITP::Input, ITP::SInt32]),
 
            (vec![ITP::Unknown], vec![ITP::Output, ITP::SInt32]),
 
            (
 
                vec![ITP::Instance(Id::new(0), 2), ITP::Input, ITP::Unknown, ITP::Output, ITP::Unknown],
 
                vec![ITP::Instance(Id::new(0), 2), ITP::Input, ITP::Array, ITP::SInt32, ITP::Output, ITP::SInt32]
 
            )
 
        ];
 

	
 
        for (lhs, rhs) in pairs.iter() {
 
            let mut lhs_type = IT::new(false, false, lhs.clone());
 
            let mut rhs_type = IT::new(false, true, rhs.clone());
 
            let result = unsafe{ IT::infer_subtrees_for_both_types(
 
                &mut lhs_type, 0, &mut rhs_type, 0
 
            ) };
 
            assert_eq!(DualInferenceResult::First, result);
 
            assert_eq!(lhs_type.parts, rhs_type.parts);
 

	
 
            let mut lhs_type = IT::new(false, false, lhs.clone());
 
            let rhs_type = IT::new(false, true, rhs.clone());
 
            let result = IT::infer_subtree_for_single_type(
 
                &mut lhs_type, 0, &rhs_type.parts, 0
 
                &mut lhs_type, 0, &rhs_type.parts, 0, false
 
            );
 
            assert_eq!(SingleInferenceResult::Modified, result);
 
            assert_eq!(lhs_type.parts, rhs_type.parts)
 
        }
 
    }
 
}
 
\ No newline at end of file

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

0 comments (0 inline, 0 general)