From 979f0c33417c97bffeb164f94d6baa01de3f4b42 2022-02-24 18:21:06 From: MH Date: 2022-02-24 18:21:06 Subject: [PATCH] WIP: Use monomorph index lookup, prepare builtin procedures --- diff --git a/src/collections/string_pool.rs b/src/collections/string_pool.rs index ad281e630f0bba4c2d192f01f878509d7ae1b0cf..58fc38bd5524f9abf76135fc40b9d3f4066d414f 100644 --- a/src/collections/string_pool.rs +++ b/src/collections/string_pool.rs @@ -171,7 +171,7 @@ mod tests { fn display_empty_string_ref() { // Makes sure that null pointer inside StringRef will not cause issues let v = StringRef::new_empty(); - let val = format!("{}{:?}", v, v); + let _val = format!("{}{:?}", v, v); // calls Format and Debug on StringRef } #[test] diff --git a/src/protocol/ast.rs b/src/protocol/ast.rs index 61bb609a30e47f16729f36900525d3e3745007f0..e137985fa110c6e2cea9a0f83be7cedf79aef88d 100644 --- a/src/protocol/ast.rs +++ b/src/protocol/ast.rs @@ -807,7 +807,7 @@ pub struct Variable { pub unique_id_in_scope: i32, // Temporary fix until proper bytecode/asm is generated } -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum Definition { Struct(StructDefinition), Enum(EnumDefinition), @@ -1018,6 +1018,7 @@ pub enum ProcedureKind { /// Monomorphed instantiation of a procedure (or the sole instantiation of a /// non-polymorphic procedure). +#[derive(Debug)] pub struct ProcedureDefinitionMonomorph { pub argument_types: Vec, pub expr_info: Vec @@ -1032,6 +1033,7 @@ impl ProcedureDefinitionMonomorph { } } +#[derive(Debug, Clone, Copy)] pub struct ExpressionInfo { pub type_id: TypeId, pub variant: ExpressionInfoVariant, @@ -1046,7 +1048,7 @@ impl ExpressionInfo { } } -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Copy)] pub enum ExpressionInfoVariant { Generic, Procedure(TypeId, u32), // procedure TypeID and its monomorph index @@ -1073,7 +1075,7 @@ impl ExpressionInfoVariant { /// components. // Note that we will have function definitions for builtin functions as well. In // that case the span, the identifier span and the body are all invalid. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct ProcedureDefinition { pub this: ProcedureDefinitionId, pub defined_in: RootId, @@ -1090,8 +1092,6 @@ pub struct ProcedureDefinition { pub body: BlockStatementId, // Monomorphization of typed procedures pub monomorphs: Vec, - // Validation/linking - pub num_expressions_in_body: i32, } impl ProcedureDefinition { @@ -1109,7 +1109,6 @@ impl ProcedureDefinition { scope: ScopeId::new_invalid(), body: BlockStatementId::new_invalid(), monomorphs: Vec::new(), - num_expressions_in_body: -1, } } } diff --git a/src/protocol/eval/executor.rs b/src/protocol/eval/executor.rs index a3ff4fdb1d881b97dd0aebbfe8115871e5a5f51c..913be03fa88e408a2ac4bc3b71832f87bfbfaf8d 100644 --- a/src/protocol/eval/executor.rs +++ b/src/protocol/eval/executor.rs @@ -37,7 +37,7 @@ pub(crate) struct Frame { impl Frame { /// Creates a new execution frame. Does not modify the stack in any way. - pub fn new(heap: &Heap, definition_id: ProcedureDefinitionId, monomorph_type_id: TypeId) -> Self { + pub fn new(heap: &Heap, definition_id: ProcedureDefinitionId, monomorph_type_id: TypeId, monomorph_index: u32) -> Self { let definition = &heap[definition_id]; let outer_scope_id = definition.scope; let first_statement_id = definition.body; @@ -63,6 +63,7 @@ impl Frame { Frame{ definition: definition_id, monomorph_type_id, + monomorph_index: monomorph_index as usize, position: first_statement_id.upcast(), expr_stack: VecDeque::with_capacity(128), expr_values: VecDeque::with_capacity(128), @@ -222,14 +223,15 @@ pub struct Prompt { } impl Prompt { - pub fn new(_types: &TypeTable, heap: &Heap, def: ProcedureDefinitionId, type_id: TypeId, args: ValueGroup) -> Self { + pub fn new(types: &TypeTable, heap: &Heap, def: ProcedureDefinitionId, type_id: TypeId, args: ValueGroup) -> Self { let mut prompt = Self{ frames: Vec::new(), store: Store::new(), }; // Maybe do typechecking in the future? - let new_frame = Frame::new(heap, def, type_id); + let monomorph_index = types.get_monomorph(type_id).variant.as_procedure().monomorph_index; + let new_frame = Frame::new(heap, def, type_id, monomorph_index); let max_stack_size = new_frame.max_stack_size; prompt.frames.push(new_frame); args.into_store(&mut prompt.store); @@ -767,10 +769,10 @@ impl Prompt { // Determine the monomorph index of the function we're calling let mono_data = &heap[cur_frame.definition].monomorphs[cur_frame.monomorph_index]; - let type_id = mono_data.expr_info[expr.type_index as usize].variant.as_procedure().0; + let (type_id, monomorph_index) = mono_data.expr_info[expr.type_index as usize].variant.as_procedure(); // Push the new frame and reserve its stack size - let new_frame = Frame::new(heap, expr.procedure, type_id); + let new_frame = Frame::new(heap, expr.procedure, type_id, monomorph_index); let new_stack_size = new_frame.max_stack_size; self.frames.push(new_frame); self.store.cur_stack_boundary = new_stack_boundary; @@ -1029,7 +1031,7 @@ impl Prompt { ); let mono_data = &heap[cur_frame.definition].monomorphs[cur_frame.monomorph_index]; - let type_id = mono_data.expr_info[expr.type_index as usize].variant.as_procedure().0; + let type_id = mono_data.expr_info[call_expr.type_index as usize].variant.as_procedure().0; // Note that due to expression value evaluation they exist in // reverse order on the stack. diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 9a41b28c25581af0f1467043b1f9c21082f5e794..28b49cea928f7667630eb86e32814cbb8792360b 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -108,15 +108,17 @@ impl ProtocolDescription { // - check number of arguments by retrieving the one instantiated // monomorph let concrete_type = ConcreteType{ parts: vec![ConcreteTypePart::Component(ast_definition.this, 0)] }; - let mono_index = self.types.get_procedure_monomorph_type_id(&definition_id, &concrete_type.parts).unwrap(); - let mono_type = self.types.get_procedure_monomorph(mono_index); - if mono_type.arg_types.len() != arguments.values.len() { + let procedure_type_id = self.types.get_procedure_monomorph_type_id(&definition_id, &concrete_type.parts).unwrap(); + let procedure_monomorph_index = self.types.get_monomorph(procedure_type_id).variant.as_procedure().monomorph_index; + let monomorph_info = &ast_definition.monomorphs[procedure_monomorph_index as usize]; + if monomorph_info.argument_types.len() != arguments.values.len() { return Err(ComponentCreationError::InvalidNumArguments); } // - for each argument try to make sure the types match for arg_idx in 0..arguments.values.len() { - let expected_type = &mono_type.arg_types[arg_idx]; + let expected_type_id = monomorph_info.argument_types[arg_idx]; + let expected_type = &self.types.get_monomorph(expected_type_id).concrete_type; let provided_value = &arguments.values[arg_idx]; if !self.verify_same_type(expected_type, 0, &arguments, provided_value) { return Err(ComponentCreationError::InvalidArgumentType(arg_idx)); @@ -125,7 +127,7 @@ impl ProtocolDescription { // By now we're sure that all of the arguments are correct. So create // the connector. - return Ok(Prompt::new(&self.types, &self.heap, ast_definition.this, mono_index, arguments)); + return Ok(Prompt::new(&self.types, &self.heap, ast_definition.this, procedure_type_id, arguments)); } fn lookup_module_root(&self, module_name: &[u8]) -> Option { diff --git a/src/protocol/parser/mod.rs b/src/protocol/parser/mod.rs index 9ef01e4a58319347b06620e5b7012a140d093c5d..f8099e570194ba10d7b2caadbf7ec7786b472e32 100644 --- a/src/protocol/parser/mod.rs +++ b/src/protocol/parser/mod.rs @@ -298,7 +298,7 @@ impl Parser { types: &mut self.type_table, arch: &self.arch, }; - PassTyping::queue_module_definitions(&mut ctx, &mut queue); + self.pass_typing.queue_module_definitions(&mut ctx, &mut queue); }; while !queue.is_empty() { let top = queue.pop().unwrap(); @@ -352,16 +352,21 @@ fn insert_builtin_type(type_table: &mut TypeTable, parts: Vec, &[] }; - return type_table.add_builtin_type(concrete_type, poly_var, size, alignment); + return type_table.add_builtin_data_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 (Vec<(&'static str, ParserType)>, ParserType)> ( p: &mut Parser, func_name: &str, polymorphic: &[&str], arg_and_return_fn: T ) { - let mut poly_vars = Vec::with_capacity(polymorphic.len()); + // Insert into AST (to get an ID), also prepare the polymorphic variables + // we need later for the type table + let mut ast_poly_vars = Vec::with_capacity(polymorphic.len()); + let mut type_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 identifier = Identifier{ span: InputSpan::new(), value: p.string_pool.intern(poly_var.as_bytes()) } ; + ast_poly_vars.push(identifier.clone()); + type_poly_vars.push(PolymorphicVariable{ identifier, is_in_use: false }); } let func_ident_ref = p.string_pool.intern(func_name.as_bytes()); @@ -372,14 +377,15 @@ fn insert_builtin_function (Vec<(&'static str, P kind: ProcedureKind::Function, span: InputSpan::new(), identifier: Identifier{ span: InputSpan::new(), value: func_ident_ref.clone() }, - poly_vars, + poly_vars: ast_poly_vars, return_type: None, parameters: Vec::new(), scope: ScopeId::new_invalid(), body: BlockStatementId::new_invalid(), - num_expressions_in_body: -1, + monomorphs: Vec::new(), }); + // Modify AST with more information about the procedure let (arguments, return_type) = arg_and_return_fn(procedure_id); let mut parameters = Vec::with_capacity(arguments.len()); @@ -400,6 +406,7 @@ fn insert_builtin_function (Vec<(&'static str, P func.parameters = parameters; func.return_type = Some(return_type); + // Insert into symbol table p.symbol_table.insert_symbol(SymbolScope::Global, Symbol{ name: func_ident_ref, variant: SymbolVariant::Definition(SymbolDefinition{ @@ -412,4 +419,13 @@ fn insert_builtin_function (Vec<(&'static str, P definition_id: procedure_id.upcast(), }) }).unwrap(); + + // Insert into type table + let mut concrete_type = ConcreteType::default(); + concrete_type.parts.push(ConcreteTypePart::Function(procedure_id, type_poly_vars.len() as u32)); + + for _ in 0..type_poly_vars.len() { + concrete_type.parts.push(ConcreteTypePart::Void); // doesn't matter (I hope...) + } + p.type_table.add_builtin_procedure_type(concrete_type, &type_poly_vars); } \ No newline at end of file diff --git a/src/protocol/parser/pass_rewriting.rs b/src/protocol/parser/pass_rewriting.rs index 724f9d0c42354424329169b1510e4520b4720c19..5884cdca726ff74bb13f9733d73ab3bd8e5f8f6d 100644 --- a/src/protocol/parser/pass_rewriting.rs +++ b/src/protocol/parser/pass_rewriting.rs @@ -294,35 +294,6 @@ impl Visitor for PassRewriting { } } -impl PassRewriting { - fn create_runtime_call_statement(&self, ctx: &mut Ctx, method: Method, arguments: Vec) -> (CallExpressionId, ExpressionStatementId) { - let call_expr_id = ctx.heap.alloc_call_expression(|this| CallExpression{ - this, - func_span: InputSpan::new(), - full_span: InputSpan::new(), - parser_type: ParserType{ - elements: Vec::new(), - full_span: InputSpan::new(), - }, - method, - arguments, - procedure: ProcedureDefinitionId::new_invalid(), - parent: ExpressionParent::None, - }); - let call_stmt_id = ctx.heap.alloc_expression_statement(|this| ExpressionStatement{ - this, - span: InputSpan::new(), - expression: call_expr_id.upcast(), - next: StatementId::new_invalid(), - }); - - let call_expr = &mut ctx.heap[call_expr_id]; - call_expr.parent = ExpressionParent::ExpressionStmt(call_stmt_id); - - return (call_expr_id, call_stmt_id); - } -} - // ----------------------------------------------------------------------------- // Utilities to create compiler-generated AST nodes // ----------------------------------------------------------------------------- diff --git a/src/protocol/parser/pass_typing.rs b/src/protocol/parser/pass_typing.rs index 4015356be244b9435be552673f0d20939e9d7e64..0ce59543f294a55e55c3f573a53640c36b0b76ad 100644 --- a/src/protocol/parser/pass_typing.rs +++ b/src/protocol/parser/pass_typing.rs @@ -1026,6 +1026,7 @@ pub(crate) struct PassTyping { stmt_buffer: ScopedBuffer, bool_buffer: ScopedBuffer, index_buffer: ScopedBuffer, + definition_buffer: ScopedBuffer, poly_progress_buffer: ScopedBuffer, // Mapping from parser type to inferred type. We attempt to continue to // specify these types until we're stuck or we've fully determined the type. @@ -1104,6 +1105,7 @@ impl PassTyping { stmt_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_LARGE), bool_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_SMALL), index_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_SMALL), + definition_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_LARGE), poly_progress_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_SMALL), infer_nodes: Vec::with_capacity(BUFFER_INIT_CAP_LARGE), poly_data: Vec::with_capacity(BUFFER_INIT_CAP_SMALL), @@ -1112,12 +1114,14 @@ impl PassTyping { } } - pub(crate) fn queue_module_definitions(ctx: &mut Ctx, queue: &mut ResolveQueue) { + pub(crate) fn queue_module_definitions(&mut self, ctx: &mut Ctx, queue: &mut ResolveQueue) { debug_assert_eq!(ctx.module().phase, ModuleCompilationPhase::ValidatedAndLinked); let root_id = ctx.module().root_id; let root = &ctx.heap.protocol_descriptions[root_id]; - for definition_id in &root.definitions { - let definition = &ctx.heap[*definition_id]; + let definitions_section = self.definition_buffer.start_section_initialized(&root.definitions); + + for definition_id in definitions_section.iter_copied() { + let definition = &ctx.heap[definition_id]; let first_concrete_part_and_procedure_id = match definition { Definition::Procedure(definition) => { @@ -1140,15 +1144,17 @@ impl PassTyping { procedure.monomorphs.push(ProcedureDefinitionMonomorph::new_invalid()); let concrete_type = ConcreteType{ parts: vec![first_concrete_part] }; - let type_id = ctx.types.reserve_procedure_monomorph_type_id(definition_id, concrete_type, monomorph_index); + let type_id = ctx.types.reserve_procedure_monomorph_type_id(&definition_id, concrete_type, monomorph_index); queue.push(ResolveQueueElement{ root_id, - definition_id: *definition_id, + definition_id, reserved_type_id: type_id, reserved_monomorph_index: monomorph_index, }) } } + + definitions_section.forget(); } pub(crate) fn handle_module_definition( @@ -2014,7 +2020,7 @@ impl PassTyping { // expression information to the AST. If this is the first time we're // visiting this procedure then we assign expression indices as well. let procedure = &ctx.heap[self.procedure_id]; - let num_infer_nodes = self.infer_nodes().len(); + let num_infer_nodes = self.infer_nodes.len(); let mut monomorph = ProcedureDefinitionMonomorph{ argument_types: Vec::with_capacity(procedure.parameters.len()), expr_info: Vec::with_capacity(num_infer_nodes), @@ -2026,7 +2032,6 @@ impl PassTyping { for infer_node in self.infer_nodes.iter_mut() { // Determine type ID let expr = &ctx.heap[infer_node.expr_id]; - let poly_data = &self.poly_data[infer_node.poly_data_index as usize]; // TODO: Maybe optimize? Split insertion up into lookup, then clone // if needed? @@ -2039,16 +2044,19 @@ impl PassTyping { let info_variant = if let Expression::Call(expr) = expr { // Construct full function type. If not yet typechecked then // queue it for typechecking. + let poly_data = &self.poly_data[infer_node.poly_data_index as usize]; debug_assert!(expr.method.is_user_defined() || expr.method.is_public_builtin()); + let procedure_id = expr.procedure; let num_poly_vars = poly_data.poly_vars.len() as u32; + let first_part = match expr.method { - Method::UserFunction => ConcreteTypePart::Function(expr.procedure, num_poly_vars), - Method::UserComponent => ConcreteTypePart::Component(expr.procedure, num_poly_vars), - _ => ConcreteTypePart::Function(expr.procedure, num_poly_vars), + Method::UserFunction => ConcreteTypePart::Function(procedure_id, num_poly_vars), + Method::UserComponent => ConcreteTypePart::Component(procedure_id, num_poly_vars), + _ => ConcreteTypePart::Function(procedure_id, num_poly_vars), }; - let definition_id = expr.procedure.upcast(); + let definition_id = procedure_id.upcast(); let signature_type = poly_data_type_to_concrete_type( ctx, infer_node.expr_id, &poly_data.poly_vars, first_part )?; @@ -2059,7 +2067,7 @@ impl PassTyping { (type_id, monomorph_index) } else { // Procedure is not yet typechecked, reserve a TypeID and a monomorph index - let procedure_to_check = &mut ctx.heap[expr.procedure]; + let procedure_to_check = &mut ctx.heap[procedure_id]; let monomorph_index = procedure_to_check.monomorphs.len() as u32; procedure_to_check.monomorphs.push(ProcedureDefinitionMonomorph::new_invalid()); let type_id = ctx.types.reserve_procedure_monomorph_type_id(&definition_id, signature_type, monomorph_index); @@ -2074,7 +2082,7 @@ impl PassTyping { }; ExpressionInfoVariant::Procedure(type_id, monomorph_index) - } else if let Expression::Select(expr) = expr { + } else if let Expression::Select(_expr) = expr { ExpressionInfoVariant::Select(infer_node.field_index) } else { ExpressionInfoVariant::Generic @@ -2085,6 +2093,7 @@ impl PassTyping { } // Write the types of the arguments + let procedure = &ctx.heap[self.procedure_id]; for parameter_id in procedure.parameters.iter().copied() { let mut concrete = ConcreteType::default(); let var_data = self.var_data.iter().find(|v| v.var_id == parameter_id).unwrap(); @@ -2096,10 +2105,11 @@ impl PassTyping { // Determine if we have already assigned type indices to the expressions // before (the indices that, for a monomorph, can retrieve the type of // the expression). - let has_type_indices = self.reserved_monomorph_index == 0; + let has_type_indices = self.reserved_monomorph_index > 0; if has_type_indices { // already have indices, so resize and then index into it - monomorph.expr_info.resize_with(num_infer_nodes, ExpressionInfo::new_invalid()); + debug_assert!(monomorph.expr_info.is_empty()); + monomorph.expr_info.resize(num_infer_nodes, ExpressionInfo::new_invalid()); for infer_node in self.infer_nodes.iter() { let type_index = ctx.heap[infer_node.expr_id].type_index(); monomorph.expr_info[type_index as usize] = infer_node.as_expression_info(); @@ -2242,7 +2252,6 @@ impl PassTyping { let someone_is_str = expr_is_str || arg1_is_str || arg2_is_str; let someone_is_not_str = expr_is_not_str || arg1_is_not_str || arg2_is_not_str; - println!("DEBUG: Running concat, is_str = {}, is_not_str = {}", someone_is_str, someone_is_not_str); // Note: this statement is an expression returning the progression bools let (node_progress, arg1_progress, arg2_progress) = if someone_is_str { // One of the arguments is a string, then all must be strings @@ -2313,7 +2322,6 @@ impl PassTyping { // Same as array indexing: result depends on whether subject is string // or array let (is_string, is_not_string) = self.type_is_certainly_or_certainly_not_string(node_index); - println!("DEBUG: Running slicing, is_str = {}, is_not_str = {}", is_string, is_not_string); let (node_progress, subject_progress) = if is_string { // Certainly a string ( @@ -2970,7 +2978,6 @@ impl PassTyping { /// not a string (false, true), or still unknown (false, false). fn type_is_certainly_or_certainly_not_string(&self, node_index: InferNodeIndex) -> (bool, bool) { let expr_type = &self.infer_nodes[node_index].expr_type; - println!("DEBUG: Running test on {:?}", expr_type.parts); let mut part_index = 0; while part_index < expr_type.parts.len() { let part = &expr_type.parts[part_index]; diff --git a/src/protocol/parser/pass_validation_linking.rs b/src/protocol/parser/pass_validation_linking.rs index 27e54b7192d2d66775b95d2c5474e1d3dad0316d..697c6770543d5178b575178b9d5f84d83ac7556e 100644 --- a/src/protocol/parser/pass_validation_linking.rs +++ b/src/protocol/parser/pass_validation_linking.rs @@ -123,7 +123,6 @@ impl PassValidationLinking { proc_kind: ProcedureKind::Function, must_be_assignable: None, relative_pos_in_parent: 0, - next_expr_index: 0, control_flow_stmts: Vec::with_capacity(BUFFER_INIT_CAP_SMALL), variable_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_SMALL), definition_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_SMALL), @@ -147,7 +146,6 @@ impl PassValidationLinking { self.expr_parent = ExpressionParent::None; self.must_be_assignable = None; self.relative_pos_in_parent = 0; - self.next_expr_index = 0; self.control_flow_stmts.clear(); } } @@ -213,11 +211,6 @@ impl Visitor for PassValidationLinking { self.visit_block_stmt(ctx, body_id)?; self.pop_scope(old_scope); - // Assign total number of expressions and assign an in-block unique ID - // to each of the locals in the procedure. - let definition = &mut ctx.heap[id]; - definition.num_expressions_in_body = self.next_expr_index; - self.resolve_pending_control_flow_targets(ctx)?; Ok(()) @@ -624,7 +617,6 @@ impl Visitor for PassValidationLinking { let right_expr_id = assignment_expr.right; let old_expr_parent = self.expr_parent; assignment_expr.parent = old_expr_parent; - self.next_expr_index += 1; self.expr_parent = ExpressionParent::Expression(upcast_id, 0); self.must_be_assignable = Some(assignment_expr.operator_span); diff --git a/src/protocol/parser/type_table.rs b/src/protocol/parser/type_table.rs index 08f9a12d4ccb7e7a19b92190a7da7c2ae10e7fe4..579443f42433b6563c5ffe4dd69bd04db02c38b1 100644 --- a/src/protocol/parser/type_table.rs +++ b/src/protocol/parser/type_table.rs @@ -293,6 +293,7 @@ pub struct UnionMonomorphEmbedded { /// stores the expression type data from the typechecking/inferencing pass. pub struct ProcedureMonomorph { pub monomorph_index: u32, + pub builtin: bool, } /// Tuple monomorph. Again a kind of exception because one cannot define a named @@ -522,6 +523,8 @@ impl Eq for MonoSearchKey{} // Type table //------------------------------------------------------------------------------ +const POLY_VARS_IN_USE: [PolymorphicVariable; 1] = [PolymorphicVariable{ identifier: Identifier::new_empty(InputSpan::new()), is_in_use: true }]; + // Programmer note: keep this struct free of dynamically allocated memory #[derive(Clone)] struct TypeLoopBreadcrumb { @@ -657,7 +660,7 @@ impl TypeTable { let type_id = self.mono_type_lookup.get(&self.mono_search_key); if type_id.is_none() { self.detect_and_resolve_type_loops_for( - modules, ctx.heap, + modules, ctx.heap, ctx.arch, ConcreteType{ parts: vec![ConcreteTypePart::Instance(definition_id, 0)] }, @@ -712,6 +715,7 @@ impl TypeTable { self.mono_type_lookup.insert(self.mono_search_key.clone(), type_id); self.mono_types.push(MonoType::new_empty(type_id, concrete_type, MonoTypeVariant::Procedure(ProcedureMonomorph{ monomorph_index, + builtin: false, }))); return type_id; @@ -719,7 +723,7 @@ impl TypeTable { /// Adds a builtin type to the type table. As this is only called by the /// compiler during setup we assume it cannot fail. - pub(crate) fn add_builtin_type(&mut self, concrete_type: ConcreteType, poly_vars: &[PolymorphicVariable], size: usize, alignment: usize) -> TypeId { + pub(crate) fn add_builtin_data_type(&mut self, concrete_type: ConcreteType, poly_vars: &[PolymorphicVariable], size: usize, alignment: usize) -> TypeId { self.mono_search_key.set(&concrete_type.parts, poly_vars); debug_assert!(!self.mono_type_lookup.contains_key(&self.mono_search_key)); debug_assert_ne!(alignment, 0); @@ -736,29 +740,39 @@ impl TypeTable { return type_id; } + /// Adds a builtin procedure to the type table. + pub(crate) fn add_builtin_procedure_type(&mut self, concrete_type: ConcreteType, poly_vars: &[PolymorphicVariable]) -> TypeId { + self.mono_search_key.set(&concrete_type.parts, poly_vars); + debug_assert!(!self.mono_type_lookup.contains_key(&self.mono_search_key)); + let type_id = TypeId(self.mono_types.len() as i64); + self.mono_type_lookup.insert(self.mono_search_key.clone(), type_id); + self.mono_types.push(MonoType{ + type_id, + concrete_type, + size: 0, + alignment: 0, + variant: MonoTypeVariant::Procedure(ProcedureMonomorph{ + monomorph_index: u32::MAX, + builtin: true, + }) + }); + + return type_id; + } + /// Adds a monomorphed type to the type table. If it already exists then the /// previous entry will be used. pub(crate) fn add_monomorphed_type( &mut self, modules: &[Module], heap: &Heap, arch: &TargetArch, concrete_type: ConcreteType ) -> Result { - let poly_vars = match get_concrete_type_definition(&concrete_type.parts) { - Some(definition_id) => { - let definition = self.definition_lookup.get(&definition_id).unwrap(); - definition.poly_vars.as_slice() - }, - None => { - &[] - } - }; - // Check if the concrete type was already added - self.mono_search_key.set(&concrete_type.parts, poly_vars); + Self::set_search_key_to_type(&mut self.mono_search_key, &self.definition_lookup, &concrete_type.parts); if let Some(type_id) = self.mono_type_lookup.get(&self.mono_search_key) { return Ok(*type_id); } // Concrete type needs to be added - self.detect_and_resolve_type_loops_for(modules, heap, concrete_type)?; + self.detect_and_resolve_type_loops_for(modules, heap, arch, concrete_type)?; let type_id = self.encountered_types[0].type_id; self.lay_out_memory_for_encountered_types(arch); @@ -1124,7 +1138,7 @@ impl TypeTable { /// Internal function that will detect type loops and check if they're /// resolvable. If so then the appropriate union variants will be marked as /// "living on heap". If not then a `ParseError` will be returned - fn detect_and_resolve_type_loops_for(&mut self, modules: &[Module], heap: &Heap, concrete_type: ConcreteType) -> Result<(), ParseError> { + fn detect_and_resolve_type_loops_for(&mut self, modules: &[Module], heap: &Heap, arch: &TargetArch, concrete_type: ConcreteType) -> Result<(), ParseError> { // Programmer notes: what happens here is the we call // `check_member_for_type_loops` for a particular type's member, and // then take action using the return value: @@ -1155,11 +1169,12 @@ impl TypeTable { &self.type_loop_breadcrumbs, &self.definition_lookup, &self.mono_type_lookup, &mut self.mono_search_key, &concrete_type ); + if let TypeLoopResult::PushBreadcrumb(definition_id, concrete_type) = initial_breadcrumb { - self.handle_new_breadcrumb_for_type_loops(definition_id, concrete_type); + self.handle_new_breadcrumb_for_type_loops(arch, definition_id, concrete_type); } else { - unreachable!(); - } + unreachable!() + }; // Enter into the main resolving loop while !self.type_loop_breadcrumbs.is_empty() { @@ -1259,7 +1274,7 @@ impl TypeTable { TypeLoopResult::PushBreadcrumb(definition_id, concrete_type) => { // We recurse into the member type. self.type_loop_breadcrumbs[breadcrumb_idx] = breadcrumb; - self.handle_new_breadcrumb_for_type_loops(definition_id, concrete_type); + self.handle_new_breadcrumb_for_type_loops(arch, definition_id, concrete_type); }, TypeLoopResult::TypeLoop(first_idx) => { // Because we will be modifying breadcrumbs within the @@ -1445,29 +1460,14 @@ impl TypeTable { // (i.e. either already has its memory layed out, or is part of a type // loop because we've already visited the type) debug_assert!(!concrete_type.parts.is_empty()); - let (definition_id, type_id) = match &concrete_type.parts[0] { - CTP::Tuple(_) => { - Self::set_search_key_to_tuple(mono_key, definition_map, &concrete_type.parts); - let type_id = mono_type_map.get(&mono_key).copied(); - (DefinitionId::new_invalid(), type_id) - }, - CTP::Instance(definition_id, _) => { - let definition_type = definition_map.get(definition_id).unwrap(); - mono_key.set(&concrete_type.parts, &definition_type.poly_vars); - let type_id = mono_type_map.get(&mono_key).copied(); - - (*definition_id, type_id) - }, - CTP::Function(_, _) | - CTP::Component(_, _) => { - todo!("function pointers") - }, - _ => { - return TypeLoopResult::TypeExists - }, + let definition_id = if let ConcreteTypePart::Instance(definition_id, _) = concrete_type.parts[0] { + definition_id + } else { + DefinitionId::new_invalid() }; - if let Some(type_id) = type_id { + Self::set_search_key_to_type(mono_key, definition_map, &concrete_type.parts); + if let Some(type_id) = mono_type_map.get(mono_key).copied() { for (breadcrumb_idx, breadcrumb) in breadcrumbs.iter().enumerate() { if breadcrumb.type_id == type_id { return TypeLoopResult::TypeLoop(breadcrumb_idx); @@ -1485,13 +1485,58 @@ impl TypeTable { /// Handles the `PushBreadcrumb` result for a `check_member_for_type_loops` /// call. Will preallocate entries in the monomorphed type storage (with /// all memory properties zeroed). - fn handle_new_breadcrumb_for_type_loops(&mut self, definition_id: DefinitionId, concrete_type: ConcreteType) { + fn handle_new_breadcrumb_for_type_loops(&mut self, arch: &TargetArch, definition_id: DefinitionId, concrete_type: ConcreteType) { use DefinedTypeVariant as DTV; use ConcreteTypePart as CTP; let mut is_union = false; let type_id = match &concrete_type.parts[0] { + // Builtin types + CTP::Void | CTP::Message | CTP::Bool | + CTP::UInt8 | CTP::UInt16 | CTP::UInt32 | CTP::UInt64 | + CTP::SInt8 | CTP::SInt16 | CTP::SInt32 | CTP::SInt64 | + CTP::Character | CTP::String | + CTP::Array | CTP::Slice | CTP::Input | CTP::Output | CTP::Pointer => { + // Insert the entry for the builtin type, we should be able to + // immediately "steal" the size from the preinserted builtins. + let base_type_id = match &concrete_type.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, + _ => unreachable!(), + }; + let base_type = &self.mono_types[base_type_id.0 as usize]; + + let type_id = TypeId(self.mono_types.len() as i64); + Self::set_search_key_to_type(&mut self.mono_search_key, &self.definition_lookup, &concrete_type.parts); + self.mono_type_lookup.insert(self.mono_search_key.clone(), type_id); + self.mono_types.push(MonoType{ + type_id, + concrete_type, + size: base_type.size, + alignment: base_type.alignment, + variant: MonoTypeVariant::Builtin + }); + + type_id + }, + // User-defined types CTP::Tuple(num_embedded) => { debug_assert!(definition_id.is_invalid()); // because tuples do not have an associated `DefinitionId` let mut members = Vec::with_capacity(*num_embedded as usize); @@ -1599,7 +1644,7 @@ impl TypeTable { type_id }, - _ => unreachable!(), + CTP::Function(_, _) | CTP::Component(_, _) => todo!("function pointers"), }; self.encountered_types.push(TypeLoopEntry{ type_id, is_union }); @@ -1692,7 +1737,9 @@ impl TypeTable { // optimization, we're working around borrowing rules here. // Just finished type loop detection, so we're left with the encountered - // types only + // types only. If we don't have any (a builtin type's monomorph was + // added to the type table) then this function shouldn't be called at + // all. debug_assert!(self.type_loops.is_empty()); debug_assert!(!self.encountered_types.is_empty()); debug_assert!(self.memory_layout_breadcrumbs.is_empty()); @@ -2117,24 +2164,35 @@ impl TypeTable { } } - /// Sets the search key. If `false` is returned then the provided type is a - /// builtin type. If `true` is returned then we're dealing with a user- - /// defined type. - fn set_search_key_to_type(search_key: &mut MonoSearchKey, definition_map: &DefinitionMap, type_parts: &[ConcreteTypePart]) -> bool { + /// Sets the search key to a specific type. + fn set_search_key_to_type(search_key: &mut MonoSearchKey, definition_map: &DefinitionMap, type_parts: &[ConcreteTypePart]) { + use ConcreteTypePart as CTP; + match type_parts[0] { - ConcreteTypePart::Tuple(_) => { + // Builtin types without any embedded types + CTP::Void | CTP::Message | CTP::Bool | + CTP::UInt8 | CTP::UInt16 | CTP::UInt32 | CTP::UInt64 | + CTP::SInt8 | CTP::SInt16 | CTP::SInt32 | CTP::SInt64 | + CTP::Character | CTP::String => { + debug_assert_eq!(type_parts.len(), 1); + search_key.set_top_type(type_parts[0]); + }, + // Builtin types with a single nested type + CTP::Array | CTP::Slice | CTP::Input | CTP::Output | CTP::Pointer => { + debug_assert_eq!(type_parts.len(), 2); + search_key.set(type_parts, &POLY_VARS_IN_USE[..1]) + }, + // User-defined types + CTP::Tuple(_) => { Self::set_search_key_to_tuple(search_key, definition_map, type_parts); - return true; }, - ConcreteTypePart::Instance(definition_id, _) => { + CTP::Instance(definition_id, _) => { let definition_type = definition_map.get(&definition_id).unwrap(); search_key.set(type_parts, &definition_type.poly_vars); - return true; }, - ConcreteTypePart::Function(_, _) | ConcreteTypePart::Component(_, _) => { + CTP::Function(_, _) | CTP::Component(_, _) => { todo!("implement function pointers") }, - _ => return false, } } diff --git a/src/protocol/tests/utils.rs b/src/protocol/tests/utils.rs index c39dedfc5eeac57e4908f14b69524ce22b87bbd3..3767cdc6efdb6d417b055eb1724470c94929a0d4 100644 --- a/src/protocol/tests/utils.rs +++ b/src/protocol/tests/utils.rs @@ -788,7 +788,7 @@ impl<'a> ExpressionTester<'a> { let mono_proc = get_procedure_monomorph(&self.ctx.heap, &self.ctx.types, self.definition_id); let mono_index = mono_proc.monomorph_index; let mono_data = &self.ctx.heap[self.definition_id].as_procedure().monomorphs[mono_index as usize]; - let expr_info = &mono_data.expr_info[self.var_expr.type_index as usize]; + let expr_info = &mono_data.expr_info[self.expr.type_index() as usize]; let concrete_type = &self.ctx.types.get_monomorph(expr_info.type_id).concrete_type; // Serialize and check type @@ -819,7 +819,7 @@ fn get_procedure_monomorph<'a>(heap: &Heap, types: &'a TypeTable, definition_id: }; let mono_index = types.get_procedure_monomorph_type_id(&definition_id, &func_type).unwrap(); - let mono_data = types.get_procedure_monomorph(mono_index); + let mono_data = types.get_monomorph(mono_index).variant.as_procedure(); mono_data } diff --git a/src/runtime2/store/component.rs b/src/runtime2/store/component.rs index 0370e5956540d36a4784546678e35ab0dca7408f..65e0bbc75d1999b762b56ff7a78cc44f5cb36c2c 100644 --- a/src/runtime2/store/component.rs +++ b/src/runtime2/store/component.rs @@ -534,7 +534,7 @@ mod tests { } else { // Must destroy let stored_index = new_value as usize % stored.len(); - let (el_index, el_value) = stored.remove(stored_index); + let (el_index, _el_value) = stored.remove(stored_index); store.destroy(el_index); } } diff --git a/src/runtime2/tests/mod.rs b/src/runtime2/tests/mod.rs index ce820c8a0167bf00581b238e07141be8c6703995..007407b42484808e587f36c85deed563a28efd0d 100644 --- a/src/runtime2/tests/mod.rs +++ b/src/runtime2/tests/mod.rs @@ -25,7 +25,7 @@ fn test_component_creation() { ").expect("compilation"); let rt = Runtime::new(1, true, pd); - for i in 0..20 { + for _i in 0..20 { create_component(&rt, "", "nothing_at_all", no_args()); } }