Changeset - 8a2f9184afc3
[Not reviewed]
0 1 0
mh - 4 years ago 2021-07-26 09:48:20
contact@maxhenger.nl
tiny changes to memory layout
1 file changed with 11 insertions and 8 deletions:
0 comments (0 inline, 0 general)
src/protocol/parser/type_table.rs
Show inline comments
 
@@ -557,203 +557,203 @@ impl TypeTable {
 
                        parts: vec![ConcreteTypePart::Instance(definition_id, 0)]
 
                    },
 
                )?;
 
                self.lay_out_memory_for_encountered_types(ctx);
 
            }
 
        }
 

	
 
        Ok(())
 
    }
 

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

	
 
    /// Returns the index into the monomorph type array if the procedure type
 
    /// already has a (reserved) monomorph.
 
    pub(crate) fn get_procedure_monomorph_index(&self, definition_id: &DefinitionId, types: &Vec<ConcreteType>) -> Option<i32> {
 
        let def = self.lookup.get(definition_id).unwrap();
 
        if def.is_polymorph {
 
            let monos = def.definition.procedure_monomorphs();
 
            return monos.iter()
 
                .position(|v| v.poly_args == *types)
 
                .map(|v| v as i32);
 
        } else {
 
            // We don't actually care about the types
 
            let monos = def.definition.procedure_monomorphs();
 
            if monos.is_empty() {
 
                return None
 
            } else {
 
                return Some(0)
 
            }
 
        }
 
    }
 

	
 
    /// Returns a mutable reference to a procedure's monomorph expression data.
 
    /// Used by typechecker to fill in previously reserved type information
 
    pub(crate) fn get_procedure_expression_data_mut(&mut self, definition_id: &DefinitionId, monomorph_idx: i32) -> &mut ProcedureMonomorph {
 
        debug_assert!(monomorph_idx >= 0);
 
        let def = self.lookup.get_mut(definition_id).unwrap();
 
        let monomorphs = def.definition.procedure_monomorphs_mut();
 
        return &mut monomorphs[monomorph_idx as usize];
 
    }
 

	
 
    pub(crate) fn get_procedure_expression_data(&self, definition_id: &DefinitionId, monomorph_idx: i32) -> &ProcedureMonomorph {
 
        debug_assert!(monomorph_idx >= 0);
 
        let def = self.lookup.get(definition_id).unwrap();
 
        let monomorphs = def.definition.procedure_monomorphs();
 
        return &monomorphs[monomorph_idx as usize];
 
    }
 

	
 
    /// Reserves space for a monomorph of a polymorphic procedure. The index
 
    /// will point into a (reserved) slot of the array of expression types. The
 
    /// monomorph may NOT exist yet (because the reservation implies that we're
 
    /// going to be performing typechecking on it, and we don't want to
 
    /// check the same monomorph twice)
 
    pub(crate) fn reserve_procedure_monomorph_index(&mut self, definition_id: &DefinitionId, types: Option<Vec<ConcreteType>>) -> i32 {
 
        let def = self.lookup.get_mut(definition_id).unwrap();
 
        if let Some(types) = types {
 
            // Expecting a polymorphic procedure
 
            let monos = def.definition.procedure_monomorphs_mut();
 
            debug_assert!(def.is_polymorph);
 
            debug_assert!(def.poly_vars.len() == types.len());
 
            debug_assert!(monos.iter().find(|v| v.poly_args == types).is_none());
 

	
 
            let mono_idx = monos.len();
 
            monos.push(ProcedureMonomorph{ poly_args: types, expr_data: Vec::new() });
 

	
 
            return mono_idx as i32;
 
        } else {
 
            // Expecting a non-polymorphic procedure
 
            let monos = def.definition.procedure_monomorphs_mut();
 
            debug_assert!(!def.is_polymorph);
 
            debug_assert!(def.poly_vars.is_empty());
 
            debug_assert!(monos.is_empty());
 

	
 
            monos.push(ProcedureMonomorph{ poly_args: Vec::new(), expr_data: Vec::new() });
 

	
 
            return 0;
 
        }
 
    }
 

	
 
    /// Adds a datatype polymorph to the type table. Will not add the
 
    /// monomorph if it is already present, or if the type's polymorphic
 
    /// variables are all unused.
 
    pub(crate) fn add_data_monomorph(
 
        &mut self, modules: &[Module], ctx: &PassCtx, definition_id: &DefinitionId, concrete_type: ConcreteType
 
    ) -> Result<i32, ParseError> {
 
        debug_assert_eq!(*definition_id, get_concrete_type_definition(&concrete_type));
 

	
 
        // Check if the monomorph already exists
 
        let poly_type = self.lookup.get_mut(definition_id).unwrap();
 
        if let Some(idx) = poly_type.get_monomorph_index(&concrete_type) {
 
            return idx as i32;
 
            return Ok(idx as i32);
 
        }
 

	
 
        // Doesn't exist, so instantiate a monomorph and determine its memory
 
        // layout.
 
        self.detect_and_resolve_type_loops_for(modules, ctx, concrete_type)?;
 
        debug_assert_eq!(self.encountered_types[0].definition_id, definition_id);
 
        let mono_idx = self.encountered_types[0].monomorph_idx;
 
        self.lay_out_memory_for_encountered_types(ctx);
 

	
 
        return mono_idx as i32;
 
        return Ok(mono_idx as i32);
 
    }
 

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

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

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

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

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

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

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

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

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

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

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

	
 
        return Ok(());
 
    }
 

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

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

	
 
            let has_embedded = !variant.value.is_empty();
 
@@ -988,389 +988,389 @@ impl TypeTable {
 
                // Types that are not constructable, or types that are not
 
                // allowed (and checked earlier)
 
                PTV::IntegerLiteral | PTV::Inferred => {
 
                    unreachable!("illegal ParserTypeVariant within type definition");
 
                },
 
                // Finally, user-defined types
 
                PTV::Definition(definition_id, _) => {
 
                    let definition = &ctx.heap[definition_id];
 
                    if !(definition.is_struct() || definition.is_enum() || definition.is_union()) {
 
                        let source = &modules[base_definition_root_id.index as usize].source;
 
                        return Err(ParseError::new_error_str_at_span(
 
                            source, element.element_span, "expected a datatype (a struct, enum or union)"
 
                        ));
 
                    }
 

	
 
                    // Otherwise, we're fine
 
                }
 
            }
 
        }
 

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

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

	
 
        Ok(())
 
    }
 

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

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

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

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

	
 
    /// Internal function that will detect type loops and check if they're
 
    /// resolvable. If so then the appropriate union variants will be marked as
 
    /// "living on heap". If not then a `ParseError` will be returned
 
    fn detect_and_resolve_type_loops_for(&mut self, modules: &[Module], ctx: &PassCtx, concrete_type: ConcreteType) -> Result<(), ParseError> {
 
    fn detect_and_resolve_type_loops_for(&mut self, modules: &[Module], heap: &Heap, concrete_type: ConcreteType) -> Result<(), ParseError> {
 
        use DefinedTypeVariant as DTV;
 

	
 
        debug_assert!(self.breadcrumbs.is_empty());
 
        debug_assert!(self.type_loops.is_empty());
 
        debug_assert!(self.encountered_types.is_empty());
 

	
 
        // Push the initial breadcrumb
 
        let _initial_result = self.push_breadcrumb_for_type_loops(get_concrete_type_definition(&concrete_type), &concrete_type);
 
        debug_assert_eq!(_initial_result, BreadcrumbResult::PushedBreadcrumb);
 

	
 
        // Enter into the main resolving loop
 
        while !self.breadcrumbs.is_empty() {
 
            let breadcrumb_idx = self.breadcrumbs.len() - 1;
 
            let breadcrumb = self.breadcrumbs.last_mut().unwrap();
 

	
 
            let poly_type = self.lookup.get(&breadcrumb.definition_id).unwrap();
 

	
 
            // TODO: Misuse of BreadcrumbResult enum?
 
            let resolve_result = match &poly_type.definition {
 
                DTV::Enum(_) => {
 
                    BreadcrumbResult::TypeExists
 
                },
 
                DTV::Union(definition) => {
 
                    let monomorph = &definition.monomorphs[breadcrumb.monomorph_idx];
 
                    let num_variants = monomorph.variants.len();
 

	
 
                    let mut union_result = BreadcrumbResult::TypeExists;
 

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

	
 
                        while breadcrumb.next_embedded < num_embedded {
 
                            let mono_embedded = &mono_variant.embedded[breadcrumb.next_embedded];
 
                            union_result = self.push_breadcrumb_for_type_loops(poly_type.ast_definition, &mono_embedded.concrete_type);
 

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

	
 
                            breadcrumb.next_embedded += 1;
 
                        }
 

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

	
 
                    union_result
 
                },
 
                DTV::Struct(definition) => {
 
                    let monomorph = &definition.monomorphs[breadcrumb.monomorph_idx];
 
                    let num_fields = monomorph.fields.len();
 

	
 
                    let mut struct_result = BreadcrumbResult::TypeExists;
 
                    while breadcrumb.next_member < num_fields {
 
                        let mono_field = &monomorph.fields[breadcrumb.next_member];
 
                        struct_result = self.push_breadcrumb_for_type_loops(poly_type.ast_definition, &mono_field.concrete_type);
 

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

	
 
                        breadcrumb.next_member += 1;
 
                    }
 

	
 
                    struct_result
 
                },
 
                DTV::Function(_) | DTV::Component(_) => unreachable!(),
 
            };
 

	
 
            // Handle the result of attempting to resolve the current breadcrumb
 
            match resolve_result {
 
                BreadcrumbResult::TypeExists => {
 
                    // We finished parsing the type
 
                    self.breadcrumbs.pop();
 
                },
 
                BreadcrumbResult::PushedBreadcrumb => {
 
                    // We recurse into the member type, since the breadcrumb is
 
                    // already pushed we don't take any action here.
 
                },
 
                BreadcrumbResult::TypeLoop(first_idx) => {
 
                    // We're in a type loop. Add the type loop
 
                    let mut loop_members = Vec::with_capacity(self.breadcrumbs.len() - first_idx);
 
                    for breadcrumb_idx in first_idx..self.breadcrumbs.len() {
 
                        let breadcrumb = &mut self.breadcrumbs[breadcrumb_idx];
 
                        let mut is_union = false;
 

	
 
                        let entry = self.lookup.get_mut(&breadcrumb.definition_id).unwrap();
 
                        match &mut entry.definition {
 
                            DTV::Union(definition) => {
 
                                // Mark the currently processed variant as requiring heap
 
                                // allocation, then advance the *embedded* type. The loop above
 
                                // will then take care of advancing it to the next *member*.
 
                                let monomorph = &mut definition.monomorphs[breadcrumb.monomorph_idx];
 
                                let variant = &mut monomorph.variants[breadcrumb.next_member];
 
                                variant.lives_on_heap = true;
 
                                breadcrumb.next_embedded += 1;
 
                                is_union = true;
 
                            },
 
                            _ => {}, // else: we don't care for now
 
                        }
 

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

	
 
                    self.type_loops.push(TypeLoop{ members: loop_members });
 
                }
 
            }
 
        }
 

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

	
 
        // The next step is to figure out if all of the type loops can be
 
        // broken. A type loop can be broken if at least one union exists in the
 
        // loop and that union ended up having variants that are not part of
 
        // a type loop.
 
        fn type_loop_source_span_and_message<'a>(
 
            modules: &'a [Module], ctx: &PassCtx, defined_type: &DefinedType, monomorph_idx: usize, index_in_loop: usize
 
            modules: &'a [Module], heap: &Heap, defined_type: &DefinedType, monomorph_idx: usize, index_in_loop: usize
 
        ) -> (&'a InputSource, InputSpan, String) {
 
            // Note: because we will discover the type loop the *first* time we
 
            // instantiate a monomorph with the provided polymorphic arguments
 
            // (not all arguments are actually used in the type). We don't have
 
            // to care about a second instantiation where certain unused
 
            // polymorphic arguments are different.
 
            let monomorph_type = match &defined_type.definition {
 
                DTV::Union(definition) => &definition.monomorphs[monomorph_idx].concrete_type,
 
                DTV::Struct(definition) => &definition.monomorphs[monomorph_idx].concrete_type,
 
                DTV::Enum(_) | DTV::Function(_) | DTV::Component(_) =>
 
                    unreachable!(), // impossible to have an enum/procedure in a type loop
 
            };
 

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

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

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

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

	
 
            for entry in &type_loop.members {
 
                if entry.is_union {
 
                    let base_type = self.lookup.get(&entry.definition_id).unwrap();
 
                    let monomorph = &base_type.definition.as_union().monomorphs[entry.monomorph_idx];
 

	
 
                    debug_assert!(!monomorph.variants.is_empty()); // otherwise it couldn't be part of the type loop
 
                    let has_stack_variant = monomorph.variants.iter().any(|variant| !variant.lives_on_heap);
 
                    if has_stack_variant {
 
                        can_be_broken = true;
 
                    }
 
                }
 
            }
 

	
 
            if !can_be_broken {
 
                // Construct a type loop error
 
                let first_entry = &type_loop.members[0];
 
                let first_type = self.lookup.get(&first_entry.definition_id).unwrap();
 
                let (first_module, first_span, first_message) = type_loop_source_span_and_message(
 
                    modules, ctx, first_type, first_entry.monomorph_idx, 0
 
                    modules, heap, first_type, first_entry.monomorph_idx, 0
 
                );
 
                let mut parse_error = ParseError::new_error_at_span(first_module, first_span, first_message);
 

	
 
                for member_idx in 1..type_loop.members.len() {
 
                    let entry = &type_loop.members[member_idx];
 
                    let entry_type = self.lookup.get(&first_entry.definition_id).unwrap();
 
                    let (module, span, message) = type_loop_source_span_and_message(
 
                        modules, ctx, entry_type, entry.monomorph_idx, member_idx
 
                        modules, heap, entry_type, entry.monomorph_idx, member_idx
 
                    );
 
                    parse_error = parse_error.with_info_at_span(module, span, message);
 
                }
 

	
 
                return Err(parse_error);
 
            }
 
        }
 

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

	
 
        return Ok(());
 
    }
 

	
 
    // TODO: Pass in definition_type by value?
 
    fn push_breadcrumb_for_type_loops(&mut self, definition_id: DefinitionId, definition_type: &ConcreteType) -> BreadcrumbResult {
 
        use DefinedTypeVariant as DTV;
 

	
 
        let mut base_type = self.lookup.get_mut(&definition_id).unwrap();
 
        if let Some(mono_idx) = base_type.get_monomorph_index(&definition_type) {
 
            // Monomorph is already known. Check if it is present in the
 
            // breadcrumbs. If so, then we are in a type loop
 
            for (breadcrumb_idx, breadcrumb) in self.breadcrumbs.iter().enumerate() {
 
                if breadcrumb.definition_id == definition_id && breadcrumb.monomorph_idx == mono_idx {
 
                    return BreadcrumbResult::TypeLoop(breadcrumb_idx);
 
                }
 
            }
 

	
 
            return BreadcrumbResult::TypeExists;
 
        }
 

	
 
        // Type is not yet known, so we need to insert it into the lookup and
 
        // push a new breadcrumb.
 
        let mut is_union = false;
 
        let monomorph_idx = match &mut base_type.definition {
 
            DTV::Enum(definition) => {
 
                debug_assert!(definition.monomorphs.is_empty());
 
                definition.monomorphs.push(EnumMonomorph{
 
                    concrete_type: definition_type.clone(),
 
                });
 
                0
 
            },
 
            DTV::Union(definition) => {
 
                // Create all the variants with their concrete types
 
                let mut mono_variants = Vec::with_capacity(definition.variants.len());
 
                for poly_variant in &definition.variants {
 
                    let mut mono_embedded = Vec::with_capacity(poly_variant.embedded.len());
 
                    for poly_embedded in &poly_variant.embedded {
 
                        let mono_concrete = Self::construct_concrete_type(poly_embedded, definition_type);
 
                        mono_embedded.push(UnionMonomorphEmbedded{
 
                            concrete_type: mono_concrete,
 
                            size: 0,
 
                            alignment: 0,
 
                            offset: 0
 
                        });
 
                    }
 

	
 
                    mono_variants.push(UnionMonomorphVariant{
 
                        lives_on_heap: false,
 
                        embedded: mono_embedded,
 
                    })
 
                }
 

	
 
                let mono_idx = definition.monomorphs.len();
 
                definition.monomorphs.push(UnionMonomorph{
 
                    concrete_type: definition_type.clone(),
 
                    variants: mono_variants,
 
                    stack_size: 0,
 
                    stack_alignment: 0,
 
                    heap_size: 0,
 
                    heap_alignment: 0
 
                });
 

	
 
                is_union = true;
 
                mono_idx
 
            },
 
            DTV::Struct(definition) => {
 
                let mut mono_fields = Vec::with_capacity(definition.fields.len());
 
                for poly_field in &definition.fields {
 
                    let mono_concrete = Self::construct_concrete_type(&poly_field.parser_type, definition_type);
 
                    mono_fields.push(StructMonomorphField{
 
                        concrete_type: mono_concrete,
 
                        size: 0,
 
                        alignment: 0,
 
                        offset: 0
 
                    })
 
                }
 

	
 
                let mono_idx = definition.monomorphs.len();
 
                definition.monomorphs.push(StructMonomorph{
 
                    concrete_type: definition_type.clone(),
 
                    fields: mono_fields,
 
                    size: 0,
 
                    alignment: 0
 
                });
 
@@ -1624,171 +1624,174 @@ impl TypeTable {
 
            if !entry.is_union {
 
                continue;
 
            }
 

	
 
            let poly_type = self.lookup.get_mut(&entry.definition_id).unwrap();
 
            match &mut poly_type.definition {
 
                DTV::Union(definition) => {
 
                    let mono_type = &mut definition.monomorphs[entry.monomorph_idx];
 
                    let mut max_size = 0;
 
                    let mut max_alignment = 1;
 

	
 
                    for variant in &mut mono_type.variants {
 
                        if !variant.lives_on_heap {
 
                            continue;
 
                        }
 

	
 
                        let mut variant_offset = 0;
 
                        let mut variant_alignment = 1;
 
                        debug_assert!(!variant.embedded.is_empty());
 

	
 
                        for embedded in &mut variant.embedded {
 
                            match self.get_memory_layout_or_breadcrumb(ctx, &embedded.concrete_type) {
 
                                MemoryLayoutResult::TypeExists(size, alignment) => {
 
                                    embedded.size = size;
 
                                    embedded.alignment = alignment;
 

	
 
                                    align_offset_to(&mut variant_offset, alignment);
 
                                    embedded.alignment = variant_offset;
 

	
 
                                    variant_offset += size;
 
                                    variant_alignment = variant_alignment.max(alignment);
 
                                },
 
                                _ => unreachable!(),
 
                            }
 
                        }
 

	
 
                        // Update heap size/alignment
 
                        max_size = max_size.max(variant_offset);
 
                        max_alignment = max_alignment.max(variant_alignment);
 
                    }
 

	
 
                    if max_size != 0 {
 
                        // At least one entry lives on the heap
 
                        mono_type.heap_size = max_size;
 
                        mono_type.heap_alignment = max_alignment;
 
                    }
 
                },
 
                _ => unreachable!(),
 
            }
 
        }
 

	
 
        // And now, we're actually, properly, done
 
        self.encountered_types.clear();
 
    }
 

	
 
    fn get_memory_layout_or_breadcrumb(&self, ctx: &PassCtx, concrete_type: &ConcreteType) -> MemoryLayoutResult {
 
        use ConcreteTypePart as CTP;
 

	
 
        // Before we do any fancy shenanigans, we need to check if the concrete
 
        // type actually requires laying out memory.
 
        debug_assert!(!concrete_type.parts.is_empty());
 
        let (builtin_size, builtin_alignment) = match concrete_type.parts[0] {
 
            CTP::Void   => (0, 1),
 
            CTP::Message => ctx.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 => ctx.arch.string_size_alignment,
 
            CTP::Array => ctx.arch.array_size_alignment,
 
            CTP::Slice => ctx.arch.array_size_alignment,
 
            CTP::Input => ctx.arch.port_size_alignment,
 
            CTP::Output => ctx.arch.port_size_alignment,
 
            CTP::Instance(definition_id, _) => {
 
                // Special case where we explicitly return to simplify the
 
                // return case for the builtins.
 
                let entry = self.lookup.get(&definition_id).unwrap();
 
                let monomorph_idx = entry.get_monomorph_index(concrete_type).unwrap();
 

	
 
                if let Some((size, alignment)) = entry.get_monomorph_size_alignment(monomorph_idx) {
 
                    // Type has been layed out in memory
 
                    return MemoryLayoutResult::TypeExists(size, alignment);
 
                } else {
 
                    return MemoryLayoutResult::PushBreadcrumb(TypeLoopBreadcrumb {
 
                        definition_id,
 
                        monomorph_idx,
 
                        next_member: 0,
 
                        next_embedded: 0,
 
                    });
 
                }
 
            },
 
            CTP::Function(_, _) | CTP::Component(_, _) => {
 
                todo!("storage for 'function pointers'");
 
            }
 
        };
 

	
 
        return MemoryLayoutResult::TypeExists(builtin_size, builtin_alignment);
 
    }
 

	
 
    /// 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) {
 
                (ConcreteTypePart::UInt16, 2)
 
            } else if max_val <= (u32::MAX as i64) {
 
                (ConcreteTypePart::UInt32, 4)
 
            } else {
 
                (ConcreteTypePart::UInt64, 8)
 
            }
 
        } else {
 
            // Must be a signed integer
 
            if min_val >= (i8::MIN as i64) && max_val <= (i8::MAX as i64) {
 
                (ConcreteTypePart::SInt8, 1)
 
            } else if min_val >= (i16::MIN as i64) && max_val <= (i16::MAX as i64) {
 
                (ConcreteTypePart::SInt16, 2)
 
            } else if min_val >= (i32::MIN as i64) && max_val <= (i32::MAX as i64) {
 
                (ConcreteTypePart::SInt32, 4)
 
            } else {
 
                (ConcreteTypePart::SInt64, 8)
 
            }
 
        };
 

	
 
        return (ConcreteType{ parts: vec![part] }, size);
 
    }
 

	
 
    //--------------------------------------------------------------------------
 
    // Small utilities
 
    //--------------------------------------------------------------------------
 

	
 
    fn create_polymorphic_variables(variables: &[Identifier]) -> Vec<PolymorphicVariable> {
 
        let mut result = Vec::with_capacity(variables.len());
 
        for variable in variables.iter() {
 
            result.push(PolymorphicVariable{ identifier: variable.clone(), is_in_use: false });
 
        }
 

	
 
        result
 
    }
 

	
 
    fn mark_used_polymorphic_variables(poly_vars: &mut Vec<PolymorphicVariable>, parser_type: &ParserType) {
 
        for element in &parser_type.elements {
 
            if let ParserTypeVariant::PolymorphicArgument(_, idx) = &element.variant {
 
                poly_vars[*idx as usize].is_in_use = true;
 
            }
 
        }
 
    }
 
}
 

	
 
#[inline] fn align_offset_to(offset: &mut usize, alignment: usize) {
 
    debug_assert!(alignment > 0);
 
    let alignment_min_1 = alignment - 1;
 
    *offset += alignment_min_1;
 
    *offset &= !(alignment_min_1);
 
}
 

	
 
#[inline] fn get_concrete_type_definition(concrete: &ConcreteType) -> DefinitionId {
 
    if let ConcreteTypePart::Instance(definition_id, _) = concrete.parts[0] {
 
        return definition_id;
 
    } else {
 
        debug_assert!(false, "passed {:?} to the type table", concrete);
 
        return DefinitionId::new_invalid()
 
    }
 
}
 
\ No newline at end of file
0 comments (0 inline, 0 general)