Changeset - 291ee7a49c03
[Not reviewed]
0 8 0
mh - 3 years ago 2022-02-15 14:58:17
contact@maxhenger.nl
Add concept of TypeId for builtin types as well
8 files changed with 137 insertions and 69 deletions:
0 comments (0 inline, 0 general)
src/collections/string_pool.rs
Show inline comments
 
@@ -22,25 +22,25 @@ impl<'a> StringRef<'a> {
 
    pub(crate) fn new(data: &'a [u8]) -> StringRef<'a> {
 
        // This is an internal (compiler) function: so debug_assert that the
 
        // string is valid ascii. Most commonly the input will come from the
 
        // code's source file, which is checked for ASCII-ness anyway.
 
        debug_assert!(data.is_ascii());
 
        let length = data.len();
 
        let data = data.as_ptr();
 
        StringRef{ data, length, _phantom: PhantomData }
 
    }
 

	
 
    /// `new_empty` creates a empty StringRef. It is a null pointer with a
 
    /// length of zero.
 
    pub(crate) fn new_empty() -> StringRef<'static> {
 
    pub(crate) const fn new_empty() -> StringRef<'static> {
 
        StringRef{ data: null(), length: 0, _phantom: PhantomData }
 
    }
 

	
 
    pub fn as_str(&self) -> &'a str {
 
        unsafe {
 
            let slice = std::slice::from_raw_parts::<'a, u8>(self.data, self.length);
 
            std::str::from_utf8_unchecked(slice)
 
        }
 
    }
 

	
 
    pub fn as_bytes(&self) -> &'a [u8] {
 
        unsafe {
src/protocol/ast.rs
Show inline comments
 
@@ -336,25 +336,25 @@ pub struct ImportSymbols {
 
    pub module: Identifier,
 
    pub module_id: RootId,
 
    pub symbols: Vec<AliasedSymbol>,
 
}
 

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

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

	
 
impl PartialEq for Identifier {
 
    fn eq(&self, other: &Self) -> bool {
 
        return self.value == other.value
 
    }
 
}
 
@@ -501,43 +501,44 @@ pub enum ConcreteTypePart {
 
    Void,
 
    // Builtin types without nested types
 
    Message,
 
    Bool,
 
    UInt8, UInt16, UInt32, UInt64,
 
    SInt8, SInt16, SInt32, SInt64,
 
    Character, String,
 
    // Builtin types with one nested type
 
    Array,
 
    Slice,
 
    Input,
 
    Output,
 
    Pointer,
 
    // Tuple: variable number of nested types, will never be 1
 
    Tuple(u32),
 
    // User defined type with any number of nested types
 
    Instance(DefinitionId, u32),    // instance of data type
 
    Function(DefinitionId, u32),    // instance of function
 
    Component(DefinitionId, u32),   // instance of a connector
 
}
 

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

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

	
 
#[derive(Debug, Clone, Eq, PartialEq)]
 
pub struct ConcreteType {
 
    pub(crate) parts: Vec<ConcreteTypePart>
 
@@ -626,24 +627,28 @@ impl ConcreteType {
 
            CTP::Input => {
 
                target.push_str(KW_TYPE_IN_PORT_STR);
 
                target.push('<');
 
                idx = Self::render_type_part_at(parts, heap, idx, target);
 
                target.push('>');
 
            },
 
            CTP::Output => {
 
                target.push_str(KW_TYPE_OUT_PORT_STR);
 
                target.push('<');
 
                idx = Self::render_type_part_at(parts, heap, idx, target);
 
                target.push('>');
 
            },
 
            CTP::Pointer => {
 
                target.push('*');
 
                idx = Self::render_type_part_at(parts, heap, idx, target);
 
            }
 
            CTP::Tuple(num_parts) => {
 
                target.push('(');
 
                if num_parts != 0 {
 
                    idx = Self::render_type_part_at(parts, heap, idx, target);
 
                    for _ in 1..num_parts {
 
                        target.push(',');
 
                        idx = Self::render_type_part_at(parts, heap, idx, target);
 
                    }
 
                }
 
                target.push(')');
 
            },
 
            CTP::Instance(definition_id, num_poly_args) |
src/protocol/ast_printer.rs
Show inline comments
 
@@ -948,35 +948,36 @@ fn write_parser_type(target: &mut String, heap: &Heap, t: &ParserType) {
 

	
 
fn write_concrete_type(target: &mut String, heap: &Heap, def_id: DefinitionId, t: &ConcreteType) {
 
    use ConcreteTypePart as CTP;
 

	
 
    fn write_concrete_part(target: &mut String, heap: &Heap, def_id: DefinitionId, t: &ConcreteType, mut idx: usize) -> usize {
 
        if idx >= t.parts.len() {
 
            return idx;
 
        }
 

	
 
        match &t.parts[idx] {
 
            CTP::Void => target.push_str("void"),
 
            CTP::Message => target.push_str("msg"),
 
            CTP::Bool => target.push_str("bool"),
 
            CTP::Bool => target.push_str(KW_TYPE_BOOL_STR),
 
            CTP::UInt8 => target.push_str(KW_TYPE_UINT8_STR),
 
            CTP::UInt16 => target.push_str(KW_TYPE_UINT16_STR),
 
            CTP::UInt32 => target.push_str(KW_TYPE_UINT32_STR),
 
            CTP::UInt64 => target.push_str(KW_TYPE_UINT64_STR),
 
            CTP::SInt8 => target.push_str(KW_TYPE_SINT8_STR),
 
            CTP::SInt16 => target.push_str(KW_TYPE_SINT16_STR),
 
            CTP::SInt32 => target.push_str(KW_TYPE_SINT32_STR),
 
            CTP::SInt64 => target.push_str(KW_TYPE_SINT64_STR),
 
            CTP::Character => target.push_str(KW_TYPE_CHAR_STR),
 
            CTP::String => target.push_str(KW_TYPE_STRING_STR),
 
            CTP::Pointer => target.push('*'),
 
            CTP::Array => {
 
                idx = write_concrete_part(target, heap, def_id, t, idx + 1);
 
                target.push_str("[]");
 
            },
 
            CTP::Slice => {
 
                idx = write_concrete_part(target, heap, def_id, t, idx + 1);
 
                target.push_str("[..]");
 
            }
 
            CTP::Input => {
 
                target.push_str("in<");
 
                idx = write_concrete_part(target, heap, def_id, t, idx + 1);
 
                target.push('>');
src/protocol/input_source.rs
Show inline comments
 
@@ -15,25 +15,25 @@ impl InputPosition {
 
}
 

	
 
#[derive(Debug, Clone, Copy)]
 
pub struct InputSpan {
 
    pub begin: InputPosition,
 
    pub end: InputPosition,
 
}
 

	
 
impl InputSpan {
 
    // This must only be used if you're sure that the span will not be involved
 
    // in creating an error message.
 
    #[inline]
 
    pub fn new() -> InputSpan {
 
    pub const fn new() -> InputSpan {
 
        InputSpan{ begin: InputPosition{ line: 0, offset: 0 }, end: InputPosition{ line: 0, offset: 0 }}
 
    }
 

	
 
    #[inline]
 
    pub fn from_positions(begin: InputPosition, end: InputPosition) -> Self {
 
        Self { begin, end }
 
    }
 
}
 

	
 
/// Wrapper around source file with optional filename. Ensures that the file is
 
/// only scanned once.
 
pub struct InputSource {
src/protocol/mod.rs
Show inline comments
 
@@ -138,25 +138,25 @@ impl ProtocolDescription {
 
                    return Some(module.root_id);
 
                }
 
            }
 
        }
 

	
 
        return None;
 
    }
 

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

	
 
        match &expected.parts[expected_idx] {
 
            CTP::Void | CTP::Message | CTP::Slice | CTP::Function(_, _) | CTP::Component(_, _) => unreachable!(),
 
            CTP::Void | CTP::Message | CTP::Slice | CTP::Pointer | CTP::Function(_, _) | CTP::Component(_, _) => unreachable!(),
 
            CTP::Bool => if let Value::Bool(_) = argument { true } else { false },
 
            CTP::UInt8 => if let Value::UInt8(_) = argument { true } else { false },
 
            CTP::UInt16 => if let Value::UInt16(_) = argument { true } else { false },
 
            CTP::UInt32 => if let Value::UInt32(_) = argument { true } else { false },
 
            CTP::UInt64 => if let Value::UInt64(_) = argument { true } else { false },
 
            CTP::SInt8 => if let Value::SInt8(_) = argument { true } else { false },
 
            CTP::SInt16 => if let Value::SInt16(_) = argument { true } else { false },
 
            CTP::SInt32 => if let Value::SInt32(_) = argument { true } else { false },
 
            CTP::SInt64 => if let Value::SInt64(_) = argument { true } else { false },
 
            CTP::Character => if let Value::Char(_) = argument { true } else { false },
 
            CTP::String => {
 
                // Match outer string type and embedded character types
src/protocol/parser/mod.rs
Show inline comments
 
@@ -16,30 +16,31 @@ mod visitor;
 
use tokens::*;
 
use crate::collections::*;
 
use visitor::Visitor;
 
use pass_tokenizer::PassTokenizer;
 
use pass_symbols::PassSymbols;
 
use pass_imports::PassImport;
 
use pass_definitions::PassDefinitions;
 
use pass_validation_linking::PassValidationLinking;
 
use pass_typing::{PassTyping, ResolveQueue};
 
use pass_rewriting::PassRewriting;
 
use pass_stack_size::PassStackSize;
 
use symbol_table::*;
 
use type_table::TypeTable;
 
use type_table::*;
 

	
 
use crate::protocol::ast::*;
 
use crate::protocol::input_source::*;
 

	
 
use crate::protocol::ast_printer::ASTWriter;
 
use crate::protocol::parser::type_table::PolymorphicVariable;
 

	
 
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
 
pub enum ModuleCompilationPhase {
 
    Tokenized,              // source is tokenized
 
    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
 
    Rewritten,              // Special AST nodes are rewritten into regular AST nodes
 
    // When we continue with the compiler:
 
@@ -48,34 +49,68 @@ pub enum ModuleCompilationPhase {
 

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

	
 
// TODO: This is kind of wrong. Because when we're producing bytecode we would
 
//       like the bytecode itself to not have the notion of the size of a pointer
 
//       type. But until I figure out what we do want I'll just set everything
 
//       to a 64-bit architecture.
 
pub struct TargetArch {
 
    pub array_size_alignment: (usize, usize),
 
    pub slice_size_alignment: (usize, usize),
 
    pub string_size_alignment: (usize, usize),
 
    pub port_size_alignment: (usize, usize),
 
    pub pointer_size_alignment: (usize, usize),
 
    pub void_type_id: TypeId,
 
    pub message_type_id: TypeId,
 
    pub bool_type_id: TypeId,
 
    pub uint8_type_id: TypeId,
 
    pub uint16_type_id: TypeId,
 
    pub uint32_type_id: TypeId,
 
    pub uint64_type_id: TypeId,
 
    pub sint8_type_id: TypeId,
 
    pub sint16_type_id: TypeId,
 
    pub sint32_type_id: TypeId,
 
    pub sint64_type_id: TypeId,
 
    pub char_type_id: TypeId,
 
    pub string_type_id: TypeId,
 
    pub array_type_id: TypeId,
 
    pub slice_type_id: TypeId,
 
    pub input_type_id: TypeId,
 
    pub output_type_id: TypeId,
 
    pub pointer_type_id: TypeId,
 
}
 

	
 
impl TargetArch {
 
    fn new() -> Self {
 
        return Self{
 
            void_type_id: TypeId::new_invalid(),
 
            bool_type_id: TypeId::new_invalid(),
 
            message_type_id: TypeId::new_invalid(),
 
            uint8_type_id: TypeId::new_invalid(),
 
            uint16_type_id: TypeId::new_invalid(),
 
            uint32_type_id: TypeId::new_invalid(),
 
            uint64_type_id: TypeId::new_invalid(),
 
            sint8_type_id: TypeId::new_invalid(),
 
            sint16_type_id: TypeId::new_invalid(),
 
            sint32_type_id: TypeId::new_invalid(),
 
            sint64_type_id: TypeId::new_invalid(),
 
            char_type_id: TypeId::new_invalid(),
 
            string_type_id: TypeId::new_invalid(),
 
            array_type_id: TypeId::new_invalid(),
 
            slice_type_id: TypeId::new_invalid(),
 
            input_type_id: TypeId::new_invalid(),
 
            output_type_id: TypeId::new_invalid(),
 
            pointer_type_id: TypeId::new_invalid(),
 
        }
 
    }
 
}
 

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

	
 
pub struct Parser {
 
    // Storage of all information created/gathered during compilation.
 
    pub(crate) heap: Heap,
 
@@ -106,35 +141,51 @@ impl Parser {
 
            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(),
 
            pass_rewriting: PassRewriting::new(),
 
            pass_stack_size: PassStackSize::new(),
 
            write_ast_to: None,
 
            arch: TargetArch {
 
                array_size_alignment: (3*8, 8), // pointer, length, capacity
 
                slice_size_alignment: (2*8, 8), // pointer, length
 
                string_size_alignment: (3*8, 8), // pointer, length, capacity
 
                port_size_alignment: (3*4, 4), // two u32s: connector + port ID
 
                pointer_size_alignment: (8, 8),
 
            }
 
            arch: TargetArch::new(),
 
        };
 

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

	
 
        // Insert builtin types
 
        // TODO: At some point use correct values for size/alignment
 
        parser.arch.void_type_id    = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::Void], false, 0, 1);
 
        parser.arch.message_type_id = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::Message], false, 24, 8);
 
        parser.arch.bool_type_id    = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::Bool], false, 1, 1);
 
        parser.arch.uint8_type_id   = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::UInt8], false, 1, 1);
 
        parser.arch.uint16_type_id  = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::UInt16], false, 2, 2);
 
        parser.arch.uint32_type_id  = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::UInt32], false, 4, 4);
 
        parser.arch.uint64_type_id  = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::UInt64], false, 8, 8);
 
        parser.arch.sint8_type_id   = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::SInt8], false, 1, 1);
 
        parser.arch.sint16_type_id  = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::SInt16], false, 2, 2);
 
        parser.arch.sint32_type_id  = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::SInt32], false, 4, 4);
 
        parser.arch.sint64_type_id  = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::SInt64], false, 8, 8);
 
        parser.arch.char_type_id    = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::Character], false, 4, 4);
 
        parser.arch.string_type_id  = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::String], false, 24, 8);
 
        parser.arch.array_type_id   = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::Array, ConcreteTypePart::Void], true, 24, 8);
 
        parser.arch.slice_type_id   = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::Slice, ConcreteTypePart::Void], true, 16, 4);
 
        parser.arch.input_type_id   = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::Input, ConcreteTypePart::Void], true, 8, 8);
 
        parser.arch.output_type_id  = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::Output, ConcreteTypePart::Void], true, 8, 8);
 
        parser.arch.pointer_type_id = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::Pointer, ConcreteTypePart::Void], true, 8, 8);
 

	
 
        // Insert builtin functions
 
        fn quick_type(variants: &[ParserTypeVariant]) -> ParserType {
 
            let mut t = ParserType{ elements: Vec::with_capacity(variants.len()), full_span: InputSpan::new() };
 
            for variant in variants {
 
                t.elements.push(ParserTypeElement{ element_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)]))
 
@@ -279,28 +330,44 @@ impl Parser {
 

	
 
        // Write out desired information
 
        if let Some(filename) = &self.write_ast_to {
 
            let mut writer = ASTWriter::new();
 
            let mut file = std::fs::File::create(std::path::Path::new(filename)).unwrap();
 
            writer.write_ast(&mut file, &self.heap);
 
        }
 

	
 
        Ok(())
 
    }
 
}
 

	
 
fn insert_builtin_type(type_table: &mut TypeTable, parts: Vec<ConcreteTypePart>, has_poly_var: bool, size: usize, alignment: usize) -> TypeId {
 
    const POLY_VARS: [PolymorphicVariable; 1] = [PolymorphicVariable{
 
        identifier: Identifier::new_empty(InputSpan::new()),
 
        is_in_use: false,
 
    }];
 

	
 
    let concrete_type = ConcreteType{ parts };
 
    let poly_var = if has_poly_var {
 
        POLY_VARS.as_slice()
 
    } else {
 
        &[]
 
    };
 

	
 
    return type_table.add_builtin_type(concrete_type, poly_var, size, alignment);
 
}
 

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

	
 
    p: &mut Parser, func_name: &str, polymorphic: &[&str], arg_and_return_fn: T
 
) {
 
    let mut poly_vars = Vec::with_capacity(polymorphic.len());
 
    for poly_var in polymorphic {
 
        poly_vars.push(Identifier{ span: InputSpan::new(), value: p.string_pool.intern(poly_var.as_bytes()) });
 
    }
 

	
 
    let func_ident_ref = p.string_pool.intern(func_name.as_bytes());
 
    let func_id = p.heap.alloc_function_definition(|this| FunctionDefinition{
 
        this,
 
        defined_in: RootId::new_invalid(),
 
        builtin: true,
 
        span: InputSpan::new(),
 
        identifier: Identifier{ span: InputSpan::new(), value: func_ident_ref.clone() },
src/protocol/parser/pass_typing.rs
Show inline comments
 
@@ -3774,24 +3774,25 @@ impl PassTyping {
 
                CTP::SInt16 => parser_type.push(ITP::SInt16),
 
                CTP::SInt32 => parser_type.push(ITP::SInt32),
 
                CTP::SInt64 => parser_type.push(ITP::SInt64),
 
                CTP::Character => parser_type.push(ITP::Character),
 
                CTP::String => {
 
                    parser_type.push(ITP::String);
 
                    parser_type.push(ITP::Character)
 
                },
 
                CTP::Array => parser_type.push(ITP::Array),
 
                CTP::Slice => parser_type.push(ITP::Slice),
 
                CTP::Input => parser_type.push(ITP::Input),
 
                CTP::Output => parser_type.push(ITP::Output),
 
                CTP::Pointer => unreachable!("pointer type during concrete to inference type conversion"),
 
                CTP::Tuple(num) => parser_type.push(ITP::Tuple(*num)),
 
                CTP::Instance(id, num) => parser_type.push(ITP::Instance(*id, *num)),
 
                CTP::Function(_, _) => unreachable!("function type during concrete to inference type conversion"),
 
                CTP::Component(_, _) => unreachable!("component type during concrete to inference type conversion"),
 
            }
 
        }
 
    }
 

	
 
    /// 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
src/protocol/parser/type_table.rs
Show inline comments
 
@@ -131,26 +131,26 @@ impl DefinedTypeVariant {
 
        }
 
    }
 

	
 
    pub(crate) fn as_union(&self) -> &UnionType {
 
        match self {
 
            DefinedTypeVariant::Union(v) => v,
 
            _ => unreachable!("Cannot convert {} to union variant", self.type_class())
 
        }
 
    }
 
}
 

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

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

	
 
        return type_id;
 
    }
 

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

	
 
        return type_id;
 
    }
 
@@ -1785,24 +1786,26 @@ impl TypeTable {
 
        // stores an index into the `size_alignment_stack`, which will be used
 
        // to store intermediate size/alignment pairs until all members are
 
        // resolved. Note that this `size_alignment_stack` is NOT an
 
        // optimization, we're working around borrowing rules here.
 

	
 
        // Just finished type loop detection, so we're left with the encountered
 
        // types only
 
        debug_assert!(self.type_loops.is_empty());
 
        debug_assert!(!self.encountered_types.is_empty());
 
        debug_assert!(self.memory_layout_breadcrumbs.is_empty());
 
        debug_assert!(self.size_alignment_stack.is_empty());
 

	
 
        let (ptr_size, ptr_align) = self.mono_types[arch.pointer_type_id.0 as usize].get_size_alignment().unwrap();
 

	
 
        // Push the first entry (the type we originally started with when we
 
        // were detecting type loops)
 
        let first_entry = &self.encountered_types[0];
 
        self.memory_layout_breadcrumbs.push(MemoryBreadcrumb{
 
            type_id: first_entry.type_id,
 
            next_member: 0,
 
            next_embedded: 0,
 
            first_size_alignment_idx: 0,
 
        });
 

	
 
        // Enter the main resolving loop
 
        'breadcrumb_loop: while !self.memory_layout_breadcrumbs.is_empty() {
 
@@ -1867,25 +1870,24 @@ impl TypeTable {
 
                    let mono_type = &mut self.mono_types[breadcrumb.type_id.0 as usize];
 
                    let union_type = mono_type.variant.as_union_mut();
 
                    let mut size_alignment_idx = breadcrumb.first_size_alignment_idx as usize;
 

	
 
                    for variant in &mut union_type.variants {
 
                        // We're doing stack computations, so always start with
 
                        // the tag size/alignment.
 
                        let mut variant_offset = union_type.tag_size;
 
                        let mut variant_alignment = union_type.tag_size;
 

	
 
                        if variant.lives_on_heap {
 
                            // Variant lives on heap, so just a pointer
 
                            let (ptr_size, ptr_align) = arch.pointer_size_alignment;
 
                            align_offset_to(&mut variant_offset, ptr_align);
 

	
 
                            variant_offset += ptr_size;
 
                            variant_alignment = variant_alignment.max(ptr_align);
 
                        } else {
 
                            // Variant lives on stack, so walk all embedded
 
                            // types.
 
                            for embedded in &mut variant.embedded {
 
                                let (size, alignment) = self.size_alignment_stack[size_alignment_idx];
 
                                embedded.size = size;
 
                                embedded.alignment = alignment;
 
                                size_alignment_idx += 1;
 
@@ -2098,82 +2100,74 @@ impl TypeTable {
 
    /// may assume that all monomorph entries exist, but we may not assume that
 
    /// those entries already have their size/alignment computed.
 
    // Passed parameters are messy. But need to strike balance between borrowing
 
    // and allocations in hot loops. So it is what it is.
 
    fn get_memory_layout_or_breadcrumb(
 
        definition_map: &DefinitionMap, mono_type_map: &MonoTypeMap, mono_types: &MonoTypeArray,
 
        search_key: &mut MonoSearchKey, arch: &TargetArch, parts: &[ConcreteTypePart],
 
        size_alignment_stack_len: usize,
 
    ) -> MemoryLayoutResult {
 
        use ConcreteTypePart as CTP;
 

	
 
        debug_assert!(!parts.is_empty());
 
        let (builtin_size, builtin_alignment) = match parts[0] {
 
            CTP::Void   => (0, 1),
 
            CTP::Message => arch.array_size_alignment,
 
            CTP::Bool   => (1, 1),
 
            CTP::UInt8  => (1, 1),
 
            CTP::UInt16 => (2, 2),
 
            CTP::UInt32 => (4, 4),
 
            CTP::UInt64 => (8, 8),
 
            CTP::SInt8  => (1, 1),
 
            CTP::SInt16 => (2, 2),
 
            CTP::SInt32 => (4, 4),
 
            CTP::SInt64 => (8, 8),
 
            CTP::Character => (4, 4),
 
            CTP::String => arch.string_size_alignment,
 
            CTP::Array => arch.array_size_alignment,
 
            CTP::Slice => arch.array_size_alignment,
 
            CTP::Input => arch.port_size_alignment,
 
            CTP::Output => arch.port_size_alignment,
 
        let type_id = match parts[0] {
 
            CTP::Void      => arch.void_type_id,
 
            CTP::Message   => arch.message_type_id,
 
            CTP::Bool      => arch.bool_type_id,
 
            CTP::UInt8     => arch.uint8_type_id,
 
            CTP::UInt16    => arch.uint16_type_id,
 
            CTP::UInt32    => arch.uint32_type_id,
 
            CTP::UInt64    => arch.uint64_type_id,
 
            CTP::SInt8     => arch.sint8_type_id,
 
            CTP::SInt16    => arch.sint16_type_id,
 
            CTP::SInt32    => arch.sint32_type_id,
 
            CTP::SInt64    => arch.sint64_type_id,
 
            CTP::Character => arch.char_type_id,
 
            CTP::String    => arch.string_type_id,
 
            CTP::Array     => arch.array_type_id,
 
            CTP::Slice     => arch.slice_type_id,
 
            CTP::Input     => arch.input_type_id,
 
            CTP::Output    => arch.output_type_id,
 
            CTP::Pointer   => arch.pointer_type_id,
 
            CTP::Tuple(_) => {
 
                Self::set_search_key_to_tuple(search_key, definition_map, parts);
 
                let type_id = mono_type_map.get(&search_key).copied().unwrap();
 
                let mono_type = &mono_types[type_id.0 as usize];
 
                if let Some((size, alignment)) = mono_type.get_size_alignment() {
 
                    return MemoryLayoutResult::TypeExists(size, alignment);
 
                } else {
 
                    return MemoryLayoutResult::PushBreadcrumb(MemoryBreadcrumb{
 
                        type_id,
 
                        next_member: 0,
 
                        next_embedded: 0,
 
                        first_size_alignment_idx: size_alignment_stack_len as u32,
 
                    })
 
                }
 

	
 
                type_id
 
            },
 
            CTP::Instance(definition_id, _) => {
 
                // Retrieve entry and the specific monomorph index by applying
 
                // the full concrete type.
 
                let definition_type = definition_map.get(&definition_id).unwrap();
 
                search_key.set(parts, &definition_type.poly_vars);
 
                let type_id = mono_type_map.get(&search_key).copied().unwrap();
 
                let mono_type = &mono_types[type_id.0 as usize];
 

	
 
                if let Some((size, alignment)) = mono_type.get_size_alignment() {
 
                    return MemoryLayoutResult::TypeExists(size, alignment);
 
                } else {
 
                    return MemoryLayoutResult::PushBreadcrumb(MemoryBreadcrumb{
 
                        type_id,
 
                        next_member: 0,
 
                        next_embedded: 0,
 
                        first_size_alignment_idx: size_alignment_stack_len as u32,
 
                    });
 
                }
 
                type_id
 
            },
 
            CTP::Function(_, _) | CTP::Component(_, _) => {
 
                todo!("storage for 'function pointers'");
 
            }
 
        };
 

	
 
        return MemoryLayoutResult::TypeExists(builtin_size, builtin_alignment);
 
        let mono_type = &mono_types[type_id.0 as usize];
 
        if let Some((size, alignment)) = mono_type.get_size_alignment() {
 
            return MemoryLayoutResult::TypeExists(size, alignment);
 
        } else {
 
            return MemoryLayoutResult::PushBreadcrumb(MemoryBreadcrumb{
 
                type_id,
 
                next_member: 0,
 
                next_embedded: 0,
 
                first_size_alignment_idx: size_alignment_stack_len as u32,
 
            });
 
        }
 
    }
 

	
 
    /// Returns tag concrete type (always a builtin integer type), the size of
 
    /// that type in bytes (and implicitly, its alignment)
 
    fn variant_tag_type_from_values(min_val: i64, max_val: i64) -> (ConcreteType, usize) {
 
        debug_assert!(min_val <= max_val);
 

	
 
        let (part, size) = if min_val >= 0 {
 
            // Can be an unsigned integer
 
            if max_val <= (u8::MAX as i64) {
 
                (ConcreteTypePart::UInt8, 1)
 
            } else if max_val <= (u16::MAX as i64) {
0 comments (0 inline, 0 general)