Changeset - 63d65f7baf18
[Not reviewed]
0 4 0
MH - 4 years ago 2021-07-22 15:22:00
contact@maxhenger.nl
stuck again in the abstractions, pending rewrite of visitor
4 files changed with 75 insertions and 37 deletions:
0 comments (0 inline, 0 general)
src/protocol/ast.rs
Show inline comments
 
@@ -485,40 +485,44 @@ pub enum ConcreteTypePart {
 
    // 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,
 
    // User defined type with any number of nested types
 
    Instance(DefinitionId, u32),
 
    Instance(DefinitionId, u32),    // instance of data type
 
    Function(DefinitionId, u32),    // instance of function
 
    Component(DefinitionId, u32),   // instance of a connector
 
}
 

	
 
impl ConcreteTypePart {
 
    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 =>
 
                1,
 
            Instance(_, num_embedded) => *num_embedded
 
            Instance(_, num_embedded) => *num_embedded,
 
            Function(_, num_embedded) => *num_embedded,
 
            Component(_, num_embedded) => *num_embedded,
 
        }
 
    }
 
}
 

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

	
 
impl Default for ConcreteType {
 
    fn default() -> Self {
 
        Self{ parts: Vec::new() }
 
@@ -592,25 +596,27 @@ impl ConcreteType {
 
                CTP::Input => {
 
                    target.push_str(KW_TYPE_IN_PORT_STR);
 
                    target.push('<');
 
                    idx = display_part(parts, heap, idx, target);
 
                    target.push('>');
 
                },
 
                CTP::Output => {
 
                    target.push_str(KW_TYPE_OUT_PORT_STR);
 
                    target.push('<');
 
                    idx = display_part(parts, heap, idx, target);
 
                    target.push('>');
 
                },
 
                CTP::Instance(definition_id, num_poly_args) => {
 
                CTP::Instance(definition_id, num_poly_args) |
 
                CTP::Function(definition_id, num_poly_args) |
 
                CTP::Component(definition_id, num_poly_args) => {
 
                    let definition = &heap[definition_id];
 
                    target.push_str(definition.identifier().value.as_str());
 

	
 
                    if num_poly_args != 0 {
 
                        target.push('<');
 
                        for poly_arg_idx in 0..num_poly_args {
 
                            if poly_arg_idx != 0 {
 
                                target.push(',');
 
                                idx = display_part(parts, heap, idx, target);
 
                            }
 
                        }
 
                        target.push('>');
src/protocol/parser/pass_definitions.rs
Show inline comments
 
@@ -215,25 +215,26 @@ impl PassDefinitions {
 
                    |source, iter, ctx| {
 
                        let poly_vars = ctx.heap[definition_id].poly_vars();
 
                        consume_parser_type(
 
                            source, iter, &ctx.symbols, &ctx.heap, poly_vars,
 
                            module_scope, definition_id, false, 0
 
                        )
 
                    },
 
                    &mut types_section, "an embedded type", Some(&mut close_pos)
 
                )?;
 
                let value = if has_embedded {
 
                    types_section.into_vec()
 
                } else {
 
                    types_section.forget()
 
                    types_section.forget();
 
                    Vec::new()
 
                };
 

	
 
                Ok(UnionVariantDefinition{
 
                    span: InputSpan::from_positions(identifier.span.begin, close_pos),
 
                    identifier,
 
                    value
 
                })
 
            },
 
            &mut variants_section, "a union variant", "a list of union variants", None
 
        )?;
 

	
 
        // Transfer to AST
src/protocol/parser/pass_typing.rs
Show inline comments
 
@@ -1382,56 +1382,71 @@ impl PassTyping {
 
        expr_type.display_name(&ctx.heap)
 
    }
 

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

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

	
 
            let mut concrete_full_parts = Vec::with_capacity(1 + num_inference_parts);
 
            concrete_full_parts.push(first_concrete_part);
 

	
 
            let mut concrete_poly_args = Vec::with_capacity(inference.len());
 

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

	
 
                let mut concrete_type = ConcreteType::default();
 
                poly_type.write_concrete_type(&mut concrete_type);
 
                concrete.push(concrete_type);
 

	
 
                concrete_full_parts.extend_from_slice(&concrete_type.parts);
 
                concrete_poly_args.push(concrete_type);
 
            }
 

	
 
            Ok(concrete)
 
            Ok((ConcreteType{ parts: concrete_full_parts }, concrete_poly_args))
 
        }
 

	
 
        // Inference is now done. But we may still have uninferred types. So we
 
        // check for these.
 
        for (infer_expr_idx, infer_expr) in self.expr_types.iter_mut().enumerate() {
 
            let expr_type = &mut infer_expr.expr_type;
 
            if !expr_type.is_done {
 
                // Auto-infer numberlike/integerlike types to a regular int
 
                if expr_type.parts.len() == 1 && expr_type.parts[0] == InferenceTypePart::IntegerLike {
 
                    expr_type.parts[0] = InferenceTypePart::SInt32;
 
                    self.expr_queued.push_back(infer_expr_idx as i32);
 
                } else {
 
@@ -1449,31 +1464,39 @@ impl PassTyping {
 
            if infer_expr.extra_data_idx < 0 { continue; }
 

	
 
            // Extra data is attached, perform typechecking and transfer
 
            // resolved information to the expression
 
            let extra_data = &self.extra_data[infer_expr.extra_data_idx as usize];
 
            if extra_data.poly_vars.is_empty() { continue; }
 

	
 
            // Note that only call and literal expressions need full inference.
 
            // Select expressions also use `extra_data`, but only for temporary
 
            // storage of the struct type whose field it is selecting.
 
            match &ctx.heap[extra_data.expr_id] {
 
                Expression::Call(expr) => {
 
                    if expr.method != Method::UserFunction && expr.method != Method::UserComponent {
 
                    // Check if it is not a builtin function. If not, then
 
                    // construct the first part of the concrete type.
 
                    let first_concrete_part = if expr.method == Method::UserFunction {
 
                        ConcreteTypePart::Function(expr.definition, extra_data.poly_vars.len() as u32)
 
                    } else if expr.method == Method::UserComponent {
 
                        ConcreteTypePart::Component(expr.definition, extra_data.poly_vars.len() as u32)
 
                    } else {
 
                        // Builtin function
 
                        continue;
 
                    }
 
                    };
 

	
 
                    let definition_id = expr.definition;
 
                    let poly_types = poly_inference_to_concrete_type(ctx, extra_data.expr_id, &extra_data.poly_vars)?;
 
                    let (concrete_type, poly_types) = inference_type_to_concrete_type(
 
                        ctx, extra_data.expr_id, &extra_data.poly_vars, first_concrete_part
 
                    )?;
 

	
 
                    match ctx.types.get_procedure_monomorph_index(&definition_id, &poly_types) {
 
                        Some(reserved_idx) => {
 
                            // Already typechecked, or already put into the resolve queue
 
                            infer_expr.field_or_monomorph_idx = reserved_idx;
 
                        },
 
                        None => {
 
                            // Not typechecked yet, so add an entry in the queue
 
                            let reserved_idx = ctx.types.reserve_procedure_monomorph_index(&definition_id, Some(poly_types.clone()));
 
                            infer_expr.field_or_monomorph_idx = reserved_idx;
 
                            queue.push(ResolveQueueElement{
 
                                root_id: ctx.heap[definition_id].defined_in(),
 
@@ -1482,27 +1505,29 @@ impl PassTyping {
 
                                reserved_monomorph_idx: reserved_idx,
 
                            });
 
                        }
 
                    }
 
                },
 
                Expression::Literal(expr) => {
 
                    let definition_id = match &expr.value {
 
                        Literal::Enum(lit) => lit.definition,
 
                        Literal::Union(lit) => lit.definition,
 
                        Literal::Struct(lit) => lit.definition,
 
                        _ => unreachable!(),
 
                    };
 

	
 
                    let poly_types = poly_inference_to_concrete_type(ctx, extra_data.expr_id, &extra_data.poly_vars)?;
 
                    let mono_index = ctx.types.add_data_monomorph(&definition_id, poly_types);
 
                    let first_concrete_part = ConcreteTypePart::Instance(definition_id, extra_data.poly_vars.len() as u32);
 
                    let (concrete_type, poly_types) = inference_type_to_concrete_type(
 
                        ctx, extra_data.expr_id, &extra_data.poly_vars, first_concrete_part
 
                    )?;
 
                    let mono_index = ctx.types.add_data_monomorph(modules, ctx, &definition_id, concrete_type)?;
 
                    infer_expr.field_or_monomorph_idx = mono_index;
 
                },
 
                Expression::Select(_) => {
 
                    debug_assert!(infer_expr.field_or_monomorph_idx >= 0);
 
                },
 
                _ => {
 
                    unreachable!("handling extra data for expression {:?}", &ctx.heap[extra_data.expr_id]);
 
                }
 
            }
 
        }
 

	
 
        // If we did any implicit type forcing, then our queue isn't empty
 
@@ -3510,24 +3535,26 @@ impl PassTyping {
 
                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::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
 
    /// "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 {
src/protocol/parser/type_table.rs
Show inline comments
 
@@ -444,25 +444,25 @@ pub struct MonomorphExpression {
 

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

	
 
struct TypeLoopBreadcrumb {
 
    definition_id: DefinitionId,
 
    monomorph_idx: usize,
 
    next_member: usize,
 
    next_embedded: usize, // for unions, the index into the variant's embedded types
 
}
 

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

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

	
 
// TODO: @Optimize, initial memory-unoptimized implementation
 
@@ -633,40 +633,43 @@ impl TypeTable {
 
            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, definition_id: &DefinitionId, types: Vec<ConcreteType>) -> i32 {
 
        let def = self.lookup.get_mut(definition_id).unwrap();
 
        if !def.is_polymorph {
 
            // Not a polymorph, or polyvars are not used in type definition
 
            return 0;
 
        }
 

	
 
        let monos = def.definition.data_monomorphs_mut();
 
        if let Some(index) = monos.iter().position(|v| v.poly_args == types) {
 
            // We already know about this monomorph
 
            return index as i32;
 
        }
 
    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;
 
        }
 

	
 
        // 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);
 

	
 
        let index = monos.len();
 
        monos.push(DataMonomorph{ poly_args: types });
 
        return index as i32;
 
        return 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;
 

	
 
@@ -1161,25 +1164,26 @@ impl TypeTable {
 
                },
 
                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;
 

	
 
                        match self.lookup.get_mut(&breadcrumb.definition_id).unwrap() {
 
                        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
 
                        }
 
@@ -1365,25 +1369,25 @@ impl TypeTable {
 

	
 
                let mono_idx = definition.monomorphs.len();
 
                definition.monomorphs.push(StructMonomorph{
 
                    concrete_type: definition_type.clone(),
 
                    fields: mono_fields,
 
                    size: 0,
 
                    alignment: 0
 
                });
 

	
 
                mono_idx
 
            },
 
            DTV::Function(_) | DTV::Component(_) => {
 
                unreachable!("pushing type resolving breadcrumb for procedure type {:?}", base_type)
 
                unreachable!("pushing type resolving breadcrumb for procedure type")
 
            },
 
        };
 

	
 
        self.breadcrumbs.push(TypeLoopBreadcrumb{
 
            definition_id,
 
            monomorph_idx,
 
            next_member: 0,
 
            next_embedded: 0
 
        });
 

	
 
        // With the breadcrumb constructed we know this is a new type, so we
 
        // also need to add an entry in the list of all encountered types
 
@@ -1430,29 +1434,29 @@ impl TypeTable {
 
        }
 

	
 
        let mut parts = Vec::with_capacity(member_type.elements.len()); // usually a correct estimation, might not be
 
        for member_part in &member_type.elements {
 
            // Check if we have a regular builtin type
 
            if let Some(part) = parser_to_concrete_part(&member_part.variant) {
 
                parts.push(part);
 
                continue;
 
            }
 

	
 
            // Not builtin, but if all code is working correctly, we only care
 
            // about the polymorphic argument at this point.
 
            if let PTV::PolymorphicArgument(_container_definition_id, poly_arg_idx) = member_part {
 
                debug_assert_eq!(*_container_definition_id, get_concrete_type_definition(container_type));
 
            if let PTV::PolymorphicArgument(_container_definition_id, poly_arg_idx) = member_part.variant {
 
                debug_assert_eq!(_container_definition_id, get_concrete_type_definition(container_type));
 

	
 
                let mut container_iter = container_type.embedded_iter(0);
 
                for _ in 0..*poly_arg_idx {
 
                for _ in 0..poly_arg_idx {
 
                    container_iter.next();
 
                }
 

	
 
                let poly_section = container_iter.next().unwrap();
 
                parts.extend(poly_section);
 

	
 
                continue;
 
            }
 

	
 
            unreachable!("unexpected type part {:?} from {:?}", member_part, member_type);
 
        }
 

	
0 comments (0 inline, 0 general)