use crate::collections::*; use crate::protocol::*; use super::visitor::*; pub(crate) struct PassRewriting { current_scope: ScopeId, current_procedure_id: ProcedureDefinitionId, definition_buffer: ScopedBuffer, statement_buffer: ScopedBuffer, call_expr_buffer: ScopedBuffer, expression_buffer: ScopedBuffer, scope_buffer: ScopedBuffer, } impl PassRewriting { pub(crate) fn new() -> Self { Self{ current_scope: ScopeId::new_invalid(), current_procedure_id: ProcedureDefinitionId::new_invalid(), definition_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_LARGE), statement_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_SMALL), call_expr_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_SMALL), expression_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_SMALL), scope_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_SMALL), } } } impl Visitor for PassRewriting { fn visit_module(&mut self, ctx: &mut Ctx) -> VisitorResult { let module = ctx.module(); debug_assert_eq!(module.phase, ModuleCompilationPhase::Typed); let root_id = module.root_id; let root = &ctx.heap[root_id]; let definition_section = self.definition_buffer.start_section_initialized(&root.definitions); for definition_index in 0..definition_section.len() { let definition_id = definition_section[definition_index]; self.visit_definition(ctx, definition_id)?; } definition_section.forget(); ctx.module_mut().phase = ModuleCompilationPhase::Rewritten; return Ok(()) } // --- Visiting procedures fn visit_procedure_definition(&mut self, ctx: &mut Ctx, id: ProcedureDefinitionId) -> VisitorResult { let definition = &ctx.heap[id]; if definition.source.is_builtin() { return Ok(()); } let body_id = definition.body; self.current_scope = definition.scope; self.current_procedure_id = id; return self.visit_block_stmt(ctx, body_id); } // --- Visiting statements (that are not the select statement) fn visit_block_stmt(&mut self, ctx: &mut Ctx, id: BlockStatementId) -> VisitorResult { let block_stmt = &ctx.heap[id]; let stmt_section = self.statement_buffer.start_section_initialized(&block_stmt.statements); self.current_scope = block_stmt.scope; for stmt_idx in 0..stmt_section.len() { self.visit_stmt(ctx, stmt_section[stmt_idx])?; } stmt_section.forget(); return Ok(()) } fn visit_labeled_stmt(&mut self, ctx: &mut Ctx, id: LabeledStatementId) -> VisitorResult { let labeled_stmt = &ctx.heap[id]; let body_id = labeled_stmt.body; return self.visit_stmt(ctx, body_id); } fn visit_if_stmt(&mut self, ctx: &mut Ctx, id: IfStatementId) -> VisitorResult { let if_stmt = &ctx.heap[id]; let true_case = if_stmt.true_case; let false_case = if_stmt.false_case; self.current_scope = true_case.scope; self.visit_stmt(ctx, true_case.body)?; if let Some(false_case) = false_case { self.current_scope = false_case.scope; self.visit_stmt(ctx, false_case.body)?; } return Ok(()) } fn visit_while_stmt(&mut self, ctx: &mut Ctx, id: WhileStatementId) -> VisitorResult { let while_stmt = &ctx.heap[id]; let body_id = while_stmt.body; self.current_scope = while_stmt.scope; return self.visit_stmt(ctx, body_id); } fn visit_synchronous_stmt(&mut self, ctx: &mut Ctx, id: SynchronousStatementId) -> VisitorResult { let sync_stmt = &ctx.heap[id]; let body_id = sync_stmt.body; self.current_scope = sync_stmt.scope; return self.visit_stmt(ctx, body_id); } // --- Visiting the select statement fn visit_select_stmt(&mut self, ctx: &mut Ctx, id: SelectStatementId) -> VisitorResult { // Utility for the last stage of rewriting process. Note that caller // still needs to point the end of the if-statement to the end of the // replacement statement of the select statement. fn transform_select_case_code( ctx: &mut Ctx, containing_procedure_id: ProcedureDefinitionId, select_id: SelectStatementId, case_index: usize, select_var_id: VariableId, select_var_type_id: TypeIdReference ) -> (IfStatementId, EndIfStatementId, ScopeId) { // Retrieve statement IDs associated with case let case = &ctx.heap[select_id].cases[case_index]; let case_guard_id = case.guard; let case_body_id = case.body; let case_scope_id = case.scope; // Create the if-statement for the result of the select statement let compare_expr_id = create_ast_equality_comparison_expr(ctx, containing_procedure_id, select_var_id, select_var_type_id, case_index as u64); let true_case = IfStatementCase{ body: case_guard_id, // which is linked up to the body scope: case_scope_id, }; let (if_stmt_id, end_if_stmt_id) = create_ast_if_stmt(ctx, compare_expr_id.upcast(), true_case, None); // Link up body statement to end-if set_ast_statement_next(ctx, case_body_id, end_if_stmt_id.upcast()); return (if_stmt_id, end_if_stmt_id, case_scope_id); } // Precreate the block that will end up containing all of the // transformed statements. Also precreate the scope associated with it let (outer_block_id, outer_end_block_id, outer_scope_id) = create_ast_block_stmt(ctx, Vec::new()); // The "select" and the "end select" statement will act like trampolines // that jump to the replacement block. So set the child/parent // relationship already. // --- for the statements let select_stmt = &mut ctx.heap[id]; select_stmt.next = outer_block_id.upcast(); let end_select_stmt_id = select_stmt.end_select; let select_stmt_relative_pos = select_stmt.relative_pos_in_parent; let outer_end_block_stmt = &mut ctx.heap[outer_end_block_id]; outer_end_block_stmt.next = end_select_stmt_id.upcast(); // --- for the scopes link_new_child_to_existing_parent_scope(ctx, &mut self.scope_buffer, self.current_scope, outer_scope_id, select_stmt_relative_pos); // Create statements that will create temporary variables for all of the // ports passed to the "get" calls in the select case guards. let select_stmt = &ctx.heap[id]; let total_num_cases = select_stmt.cases.len(); let mut total_num_ports = 0; let end_select_stmt_id = select_stmt.end_select; let _end_select = &ctx.heap[end_select_stmt_id]; // Put heap IDs into temporary buffers to handle borrowing rules let mut call_id_section = self.call_expr_buffer.start_section(); let mut expr_id_section = self.expression_buffer.start_section(); for case in select_stmt.cases.iter() { total_num_ports += case.involved_ports.len(); for (call_id, expr_id) in case.involved_ports.iter().copied() { call_id_section.push(call_id); expr_id_section.push(expr_id); } } // Transform all of the call expressions by takings its argument (the // port from which we `get`) and turning it into a temporary variable. let mut transformed_stmts = Vec::with_capacity(total_num_ports); // TODO: Recompute this preallocated length, put assert at the end let mut locals = Vec::with_capacity(total_num_ports); for port_var_idx in 0..call_id_section.len() { let get_call_expr_id = call_id_section[port_var_idx]; let port_expr_id = expr_id_section[port_var_idx]; let port_type_index = ctx.heap[port_expr_id].type_index(); let port_type_ref = TypeIdReference::IndirectSameAsExpr(port_type_index); // Move the port expression such that it gets assigned to a temporary variable let variable_id = create_ast_variable(ctx, outer_scope_id); let variable_decl_stmt_id = create_ast_variable_declaration_stmt(ctx, self.current_procedure_id, variable_id, port_type_ref, port_expr_id); // Replace the original port expression in the call with a reference // to the replacement variable let variable_expr_id = create_ast_variable_expr(ctx, self.current_procedure_id, variable_id, port_type_ref); let call_expr = &mut ctx.heap[get_call_expr_id]; call_expr.arguments[0] = variable_expr_id.upcast(); transformed_stmts.push(variable_decl_stmt_id.upcast().upcast()); locals.push((variable_id, port_type_ref)); } // Insert runtime calls that facilitate the semantics of the select // block. // Create the call that indicates the start of the select block { let num_cases_expression_id = create_ast_literal_integer_expr(ctx, self.current_procedure_id, total_num_cases as u64, ctx.arch.uint32_type_id); let num_ports_expression_id = create_ast_literal_integer_expr(ctx, self.current_procedure_id, total_num_ports as u64, ctx.arch.uint32_type_id); let arguments = vec![ num_cases_expression_id.upcast(), num_ports_expression_id.upcast() ]; let call_expression_id = create_ast_call_expr(ctx, InputSpan::new(), self.current_procedure_id, Method::SelectStart, &mut self.expression_buffer, arguments); let call_statement_id = create_ast_expression_stmt(ctx, call_expression_id.upcast()); transformed_stmts.push(call_statement_id.upcast()); } // Create calls for each select case that will register the ports that // we are waiting on at the runtime. { let mut total_port_index = 0; for case_index in 0..total_num_cases { let case = &ctx.heap[id].cases[case_index]; let case_num_ports = case.involved_ports.len(); for case_port_index in 0..case_num_ports { // Retrieve original span in case of error reporting for the // inserted function call let original_get_call_id = ctx.heap[id].cases[case_index].involved_ports[case_port_index].0; let original_get_call_span = ctx.heap[original_get_call_id].full_span; // Arguments to runtime call let (port_variable_id, port_variable_type) = locals[total_port_index]; // so far this variable contains the temporary variables for the port expressions let case_index_expr_id = create_ast_literal_integer_expr(ctx, self.current_procedure_id, case_index as u64, ctx.arch.uint32_type_id); let port_index_expr_id = create_ast_literal_integer_expr(ctx, self.current_procedure_id, case_port_index as u64, ctx.arch.uint32_type_id); let port_variable_expr_id = create_ast_variable_expr(ctx, self.current_procedure_id, port_variable_id, port_variable_type); let runtime_call_arguments = vec![ case_index_expr_id.upcast(), port_index_expr_id.upcast(), port_variable_expr_id.upcast() ]; // Create runtime call, then store it let runtime_call_expr_id = create_ast_call_expr(ctx, original_get_call_span, self.current_procedure_id, Method::SelectRegisterCasePort, &mut self.expression_buffer, runtime_call_arguments); let runtime_call_stmt_id = create_ast_expression_stmt(ctx, runtime_call_expr_id.upcast()); transformed_stmts.push(runtime_call_stmt_id.upcast()); total_port_index += 1; } } } // Create the variable that will hold the result of a completed select // block. Then create the runtime call that will produce this result let select_variable_id = create_ast_variable(ctx, outer_scope_id); let select_variable_type = TypeIdReference::DirectTypeId(ctx.arch.uint32_type_id); locals.push((select_variable_id, select_variable_type)); { let runtime_call_expr_id = create_ast_call_expr(ctx, InputSpan::new(), self.current_procedure_id, Method::SelectWait, &mut self.expression_buffer, Vec::new()); let variable_stmt_id = create_ast_variable_declaration_stmt(ctx, self.current_procedure_id, select_variable_id, select_variable_type, runtime_call_expr_id.upcast()); transformed_stmts.push(variable_stmt_id.upcast().upcast()); } call_id_section.forget(); expr_id_section.forget(); // Now we transform each of the select block case's guard and code into // a chained if-else statement. let relative_pos = transformed_stmts.len() as i32; if total_num_cases > 0 { let (if_stmt_id, end_if_stmt_id, scope_id) = transform_select_case_code(ctx, self.current_procedure_id, id, 0, select_variable_id, select_variable_type); link_existing_child_to_new_parent_scope(ctx, &mut self.scope_buffer, outer_scope_id, scope_id, relative_pos); let first_end_if_stmt = &mut ctx.heap[end_if_stmt_id]; first_end_if_stmt.next = outer_end_block_id.upcast(); let mut last_if_stmt_id = if_stmt_id; let mut last_end_if_stmt_id = end_if_stmt_id; let mut last_parent_scope_id = outer_scope_id; let mut last_relative_pos = transformed_stmts.len() as i32 + 1; transformed_stmts.push(last_if_stmt_id.upcast()); for case_index in 1..total_num_cases { let (if_stmt_id, end_if_stmt_id, scope_id) = transform_select_case_code(ctx, self.current_procedure_id, id, case_index, select_variable_id, select_variable_type); let false_case_scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::If(last_if_stmt_id, false))); link_existing_child_to_new_parent_scope(ctx, &mut self.scope_buffer, false_case_scope_id, scope_id, 0); link_new_child_to_existing_parent_scope(ctx, &mut self.scope_buffer, last_parent_scope_id, false_case_scope_id, last_relative_pos); set_ast_if_statement_false_body(ctx, last_if_stmt_id, last_end_if_stmt_id, IfStatementCase{ body: if_stmt_id.upcast(), scope: false_case_scope_id }); let end_if_stmt = &mut ctx.heap[end_if_stmt_id]; end_if_stmt.next = last_end_if_stmt_id.upcast(); last_if_stmt_id = if_stmt_id; last_end_if_stmt_id = end_if_stmt_id; last_parent_scope_id = false_case_scope_id; last_relative_pos = 0; } } // Final steps: set the statements of the replacement block statement, // link all of those statements together, and update the scopes. let first_stmt_id = transformed_stmts[0]; let mut last_stmt_id = transformed_stmts[0]; for stmt_id in transformed_stmts.iter().skip(1).copied() { set_ast_statement_next(ctx, last_stmt_id, stmt_id); last_stmt_id = stmt_id; } if total_num_cases == 0 { // If we don't have any cases, then we didn't connect the statements // up to the end of the outer block, so do that here set_ast_statement_next(ctx, last_stmt_id, outer_end_block_id.upcast()); } let outer_block_stmt = &mut ctx.heap[outer_block_id]; outer_block_stmt.next = first_stmt_id; outer_block_stmt.statements = transformed_stmts; return Ok(()) } } // ----------------------------------------------------------------------------- // Utilities to create compiler-generated AST nodes // ----------------------------------------------------------------------------- #[derive(Clone, Copy)] enum TypeIdReference { DirectTypeId(TypeId), IndirectSameAsExpr(i32), // by type index } fn create_ast_variable(ctx: &mut Ctx, scope_id: ScopeId) -> VariableId { let variable_id = ctx.heap.alloc_variable(|this| Variable{ this, kind: VariableKind::Local, parser_type: ParserType{ elements: Vec::new(), full_span: InputSpan::new(), }, identifier: Identifier::new_empty(InputSpan::new()), relative_pos_in_parent: -1, unique_id_in_scope: -1, }); let scope = &mut ctx.heap[scope_id]; scope.variables.push(variable_id); return variable_id; } fn create_ast_variable_expr(ctx: &mut Ctx, containing_procedure_id: ProcedureDefinitionId, variable_id: VariableId, variable_type_id: TypeIdReference) -> VariableExpressionId { let variable_type_index = add_new_procedure_expression_type(ctx, containing_procedure_id, variable_type_id); return ctx.heap.alloc_variable_expression(|this| VariableExpression{ this, identifier: Identifier::new_empty(InputSpan::new()), declaration: Some(variable_id), used_as_binding_target: false, parent: ExpressionParent::None, type_index: variable_type_index, }); } /// Creates an AST call expression. The provided expression span is useful for /// the cases where we perform compiler-builtin function calls that, when they /// fail, should provide an error pointing at a specific point in the source /// code. The `containing_procedure_id` is the procedure whose instructions are /// going to contain this new call expression. fn create_ast_call_expr(ctx: &mut Ctx, span: InputSpan, containing_procedure_id: ProcedureDefinitionId, method: Method, buffer: &mut ScopedBuffer, arguments: Vec) -> CallExpressionId { let call_type_id = match method { Method::SelectStart => ctx.arch.void_type_id, Method::SelectRegisterCasePort => ctx.arch.void_type_id, Method::SelectWait => ctx.arch.uint32_type_id, // TODO: Not pretty, this. Pretty error prone _ => unreachable!(), // if this goes of, add the appropriate method here. }; let expression_ids = buffer.start_section_initialized(&arguments); let call_type_index = add_new_procedure_expression_type(ctx, containing_procedure_id, TypeIdReference::DirectTypeId(call_type_id)); let call_expression_id = ctx.heap.alloc_call_expression(|this| CallExpression{ func_span: InputSpan::new(), this, full_span: span, parser_type: ParserType{ elements: Vec::new(), full_span: InputSpan::new(), }, method, arguments, procedure: ProcedureDefinitionId::new_invalid(), parent: ExpressionParent::None, type_index: call_type_index, }); for argument_index in 0..expression_ids.len() { let argument_id = expression_ids[argument_index]; let argument_expr = &mut ctx.heap[argument_id]; *argument_expr.parent_mut() = ExpressionParent::Expression(call_expression_id.upcast(), argument_index as u32); } return call_expression_id; } fn create_ast_literal_integer_expr(ctx: &mut Ctx, containing_procedure_id: ProcedureDefinitionId, unsigned_value: u64, type_id: TypeId) -> LiteralExpressionId { let literal_type_index = add_new_procedure_expression_type(ctx, containing_procedure_id, TypeIdReference::DirectTypeId(type_id)); return ctx.heap.alloc_literal_expression(|this| LiteralExpression{ this, span: InputSpan::new(), value: Literal::Integer(LiteralInteger{ unsigned_value, negated: false, }), parent: ExpressionParent::None, type_index: literal_type_index, }); } fn create_ast_equality_comparison_expr( ctx: &mut Ctx, containing_procedure_id: ProcedureDefinitionId, variable_id: VariableId, variable_type: TypeIdReference, value: u64 ) -> BinaryExpressionId { let var_expr_id = create_ast_variable_expr(ctx, containing_procedure_id, variable_id, variable_type); let int_expr_id = create_ast_literal_integer_expr(ctx, containing_procedure_id, value, ctx.arch.uint32_type_id); let cmp_type_index = add_new_procedure_expression_type(ctx, containing_procedure_id, TypeIdReference::DirectTypeId(ctx.arch.bool_type_id)); let cmp_expr_id = ctx.heap.alloc_binary_expression(|this| BinaryExpression{ this, operator_span: InputSpan::new(), full_span: InputSpan::new(), left: var_expr_id.upcast(), operation: BinaryOperator::Equality, right: int_expr_id.upcast(), parent: ExpressionParent::None, type_index: cmp_type_index, }); let var_expr = &mut ctx.heap[var_expr_id]; var_expr.parent = ExpressionParent::Expression(cmp_expr_id.upcast(), 0); let int_expr = &mut ctx.heap[int_expr_id]; int_expr.parent = ExpressionParent::Expression(cmp_expr_id.upcast(), 1); return cmp_expr_id; } fn create_ast_expression_stmt(ctx: &mut Ctx, expression_id: ExpressionId) -> ExpressionStatementId { let statement_id = ctx.heap.alloc_expression_statement(|this| ExpressionStatement{ this, span: InputSpan::new(), expression: expression_id, next: StatementId::new_invalid(), }); let expression = &mut ctx.heap[expression_id]; *expression.parent_mut() = ExpressionParent::ExpressionStmt(statement_id); return statement_id; } fn create_ast_variable_declaration_stmt( ctx: &mut Ctx, containing_procedure_id: ProcedureDefinitionId, variable_id: VariableId, variable_type: TypeIdReference, initial_value_expr_id: ExpressionId ) -> MemoryStatementId { // Create the assignment expression, assigning the initial value to the variable let variable_expr_id = create_ast_variable_expr(ctx, containing_procedure_id, variable_id, variable_type); let void_type_index = add_new_procedure_expression_type(ctx, containing_procedure_id, TypeIdReference::DirectTypeId(ctx.arch.void_type_id)); let assignment_expr_id = ctx.heap.alloc_assignment_expression(|this| AssignmentExpression{ this, operator_span: InputSpan::new(), full_span: InputSpan::new(), left: variable_expr_id.upcast(), operation: AssignmentOperator::Set, right: initial_value_expr_id, parent: ExpressionParent::None, type_index: void_type_index, }); // Create the memory statement let memory_stmt_id = ctx.heap.alloc_memory_statement(|this| MemoryStatement{ this, span: InputSpan::new(), variable: variable_id, initial_expr: assignment_expr_id, next: StatementId::new_invalid(), }); // Set all parents which we can access let variable_expr = &mut ctx.heap[variable_expr_id]; variable_expr.parent = ExpressionParent::Expression(assignment_expr_id.upcast(), 0); let value_expr = &mut ctx.heap[initial_value_expr_id]; *value_expr.parent_mut() = ExpressionParent::Expression(assignment_expr_id.upcast(), 1); let assignment_expr = &mut ctx.heap[assignment_expr_id]; assignment_expr.parent = ExpressionParent::Memory(memory_stmt_id); return memory_stmt_id; } fn create_ast_block_stmt(ctx: &mut Ctx, statements: Vec) -> (BlockStatementId, EndBlockStatementId, ScopeId) { let block_stmt_id = ctx.heap.alloc_block_statement(|this| BlockStatement{ this, span: InputSpan::new(), statements, end_block: EndBlockStatementId::new_invalid(), scope: ScopeId::new_invalid(), next: StatementId::new_invalid(), }); let end_block_stmt_id = ctx.heap.alloc_end_block_statement(|this| EndBlockStatement{ this, start_block: block_stmt_id, next: StatementId::new_invalid(), }); let scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::Block(block_stmt_id))); let block_stmt = &mut ctx.heap[block_stmt_id]; block_stmt.end_block = end_block_stmt_id; block_stmt.scope = scope_id; return (block_stmt_id, end_block_stmt_id, scope_id); } fn create_ast_if_stmt(ctx: &mut Ctx, condition_expression_id: ExpressionId, true_case: IfStatementCase, false_case: Option) -> (IfStatementId, EndIfStatementId) { // Create if statement and the end-if statement let if_stmt_id = ctx.heap.alloc_if_statement(|this| IfStatement{ this, span: InputSpan::new(), test: condition_expression_id, true_case, false_case, end_if: EndIfStatementId::new_invalid() }); let end_if_stmt_id = ctx.heap.alloc_end_if_statement(|this| EndIfStatement{ this, start_if: if_stmt_id, next: StatementId::new_invalid(), }); // Link the statements up as much as we can let if_stmt = &mut ctx.heap[if_stmt_id]; if_stmt.end_if = end_if_stmt_id; let condition_expr = &mut ctx.heap[condition_expression_id]; *condition_expr.parent_mut() = ExpressionParent::If(if_stmt_id); return (if_stmt_id, end_if_stmt_id); } /// Sets the false body for a given fn set_ast_if_statement_false_body(ctx: &mut Ctx, if_statement_id: IfStatementId, end_if_statement_id: EndIfStatementId, false_case: IfStatementCase) { // Point if-statement to "false body" let if_stmt = &mut ctx.heap[if_statement_id]; debug_assert!(if_stmt.false_case.is_none()); // simplifies logic, not necessary if_stmt.false_case = Some(false_case); // Point end of false body to the end of the if statement set_ast_statement_next(ctx, false_case.body, end_if_statement_id.upcast()); } /// Sets the specified AST statement's control flow such that it will be /// followed by the target statement. This may seem obvious, but may imply that /// a statement associated with, but different from, the source statement is /// modified. fn set_ast_statement_next(ctx: &mut Ctx, source_stmt_id: StatementId, target_stmt_id: StatementId) { let source_stmt = &mut ctx.heap[source_stmt_id]; match source_stmt { Statement::Block(stmt) => { let end_id = stmt.end_block; ctx.heap[end_id].next = target_stmt_id }, Statement::EndBlock(stmt) => stmt.next = target_stmt_id, Statement::Local(stmt) => { match stmt { LocalStatement::Memory(stmt) => stmt.next = target_stmt_id, LocalStatement::Channel(stmt) => stmt.next = target_stmt_id, } }, Statement::Labeled(stmt) => { let body_id = stmt.body; set_ast_statement_next(ctx, body_id, target_stmt_id); }, Statement::If(stmt) => { let end_id = stmt.end_if; ctx.heap[end_id].next = target_stmt_id; }, Statement::EndIf(stmt) => stmt.next = target_stmt_id, Statement::While(stmt) => { let end_id = stmt.end_while; ctx.heap[end_id].next = target_stmt_id; }, Statement::EndWhile(stmt) => stmt.next = target_stmt_id, Statement::Break(_stmt) => {}, Statement::Continue(_stmt) => {}, Statement::Synchronous(stmt) => { let end_id = stmt.end_sync; ctx.heap[end_id].next = target_stmt_id; }, Statement::EndSynchronous(stmt) => { stmt.next = target_stmt_id; }, Statement::Fork(_) | Statement::EndFork(_) => { todo!("remove fork from language"); }, Statement::Select(stmt) => { let end_id = stmt.end_select; ctx.heap[end_id].next = target_stmt_id; }, Statement::EndSelect(stmt) => stmt.next = target_stmt_id, Statement::Return(_stmt) => {}, Statement::Goto(_stmt) => {}, Statement::New(stmt) => stmt.next = target_stmt_id, Statement::Expression(stmt) => stmt.next = target_stmt_id, } } /// Links a new scope to an existing scope as its child. fn link_new_child_to_existing_parent_scope(ctx: &mut Ctx, scope_buffer: &mut ScopedBuffer, parent_scope_id: ScopeId, child_scope_id: ScopeId, relative_pos_hint: i32) { let child_scope = &mut ctx.heap[child_scope_id]; debug_assert!(child_scope.parent.is_none()); child_scope.parent = Some(parent_scope_id); child_scope.relative_pos_in_parent = relative_pos_hint; add_child_scope_to_parent(ctx, scope_buffer, parent_scope_id, child_scope_id, relative_pos_hint); } /// Relinks an existing scope to a new scope as its child. Will also break the /// link of the child scope's old parent. fn link_existing_child_to_new_parent_scope(ctx: &mut Ctx, scope_buffer: &mut ScopedBuffer, new_parent_scope_id: ScopeId, child_scope_id: ScopeId, new_relative_pos_in_parent: i32) { let child_scope = &mut ctx.heap[child_scope_id]; let old_parent_scope_id = child_scope.parent.unwrap(); child_scope.parent = Some(new_parent_scope_id); child_scope.relative_pos_in_parent = new_relative_pos_in_parent; // Remove from old parent let old_parent = &mut ctx.heap[old_parent_scope_id]; let scope_index = old_parent.nested.iter() .position(|v| *v == child_scope_id) .unwrap(); old_parent.nested.remove(scope_index); // Add to new parent add_child_scope_to_parent(ctx, scope_buffer, new_parent_scope_id, child_scope_id, new_relative_pos_in_parent); } /// Will add a child scope to a parent scope using the relative position hint. fn add_child_scope_to_parent(ctx: &mut Ctx, scope_buffer: &mut ScopedBuffer, parent_scope_id: ScopeId, child_scope_id: ScopeId, relative_pos_hint: i32) { let parent_scope = &ctx.heap[parent_scope_id]; let existing_scope_ids = scope_buffer.start_section_initialized(&parent_scope.nested); let mut insert_pos = existing_scope_ids.len(); for index in 0..existing_scope_ids.len() { let existing_scope_id = existing_scope_ids[index]; let existing_scope = &ctx.heap[existing_scope_id]; if relative_pos_hint <= existing_scope.relative_pos_in_parent { insert_pos = index; break; } } existing_scope_ids.forget(); let parent_scope = &mut ctx.heap[parent_scope_id]; parent_scope.nested.insert(insert_pos, child_scope_id); } fn add_new_procedure_expression_type(ctx: &mut Ctx, procedure_id: ProcedureDefinitionId, type_id: TypeIdReference) -> i32 { let procedure = &mut ctx.heap[procedure_id]; let type_index = procedure.monomorphs[0].expr_info.len(); match type_id { TypeIdReference::DirectTypeId(type_id) => { for monomorph in procedure.monomorphs.iter_mut() { debug_assert_eq!(monomorph.expr_info.len(), type_index); monomorph.expr_info.push(ExpressionInfo{ type_id, variant: ExpressionInfoVariant::Generic }); } }, TypeIdReference::IndirectSameAsExpr(source_type_index) => { for monomorph in procedure.monomorphs.iter_mut() { debug_assert_eq!(monomorph.expr_info.len(), type_index); let copied_expr_info = monomorph.expr_info[source_type_index as usize]; monomorph.expr_info.push(copied_expr_info) } } } return type_index as i32; }