From 2451a3ca7e4d12df9c87efd29e8c77589a608b12 2022-02-14 18:39:50 From: MH Date: 2022-02-14 18:39:50 Subject: [PATCH] WIP: Refactored most of type table, pending one bugfix --- diff --git a/src/collections/raw_vec.rs b/src/collections/raw_vec.rs index c1b4806d59ab89348ef4c6225d1660cf1a1291aa..a506831c8f86d3fe80bc93e9030a7b89a1fbeaef 100644 --- a/src/collections/raw_vec.rs +++ b/src/collections/raw_vec.rs @@ -140,9 +140,7 @@ impl Drop for RawVec { let (_, layout) = self.current_layout(); unsafe { dealloc(self.base as *mut u8, layout); - if cfg!(debug_assertions) { - self.base = ptr::null_mut(); - } + dbg_code!({ self.base = ptr::null_mut(); }); } } } diff --git a/src/protocol/eval/executor.rs b/src/protocol/eval/executor.rs index ac845a93c1640ab5270a557c106b806c13b5618b..9bda8f7069a6e45e9f51dcb19b4f8757f94517f2 100644 --- a/src/protocol/eval/executor.rs +++ b/src/protocol/eval/executor.rs @@ -27,7 +27,7 @@ pub(crate) enum ExprInstruction { #[derive(Debug, Clone)] pub(crate) struct Frame { pub(crate) definition: DefinitionId, - pub(crate) monomorph_idx: i32, + pub(crate) monomorph_type_id: TypeId, pub(crate) position: StatementId, pub(crate) expr_stack: VecDeque, // hack for expression evaluation, evaluated by popping from back pub(crate) expr_values: VecDeque, // hack for expression results, evaluated by popping from front/back @@ -36,7 +36,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: DefinitionId, monomorph_idx: i32) -> Self { + pub fn new(heap: &Heap, definition_id: DefinitionId, monomorph_type_id: TypeId) -> Self { let definition = &heap[definition_id]; let (outer_scope_id, first_statement_id) = match definition { Definition::Component(definition) => (definition.scope, definition.body), @@ -64,7 +64,7 @@ impl Frame { Frame{ definition: definition_id, - monomorph_idx, + monomorph_type_id, position: first_statement_id.upcast(), expr_stack: VecDeque::with_capacity(128), expr_values: VecDeque::with_capacity(128), @@ -211,7 +211,7 @@ pub enum EvalContinuation { // Returned only in non-sync mode ComponentTerminated, SyncBlockStart, - NewComponent(DefinitionId, i32, ValueGroup), + NewComponent(DefinitionId, TypeId, ValueGroup), NewChannel, } @@ -224,14 +224,14 @@ pub struct Prompt { } impl Prompt { - pub fn new(_types: &TypeTable, heap: &Heap, def: DefinitionId, monomorph_idx: i32, args: ValueGroup) -> Self { + pub fn new(_types: &TypeTable, heap: &Heap, def: DefinitionId, 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, monomorph_idx); + let new_frame = Frame::new(heap, def, type_id); let max_stack_size = new_frame.max_stack_size; prompt.frames.push(new_frame); args.into_store(&mut prompt.store); @@ -479,7 +479,7 @@ impl Prompt { }, Expression::Select(expr) => { let subject= cur_frame.expr_values.pop_back().unwrap(); - let mono_data = types.get_procedure_monomorph(cur_frame.monomorph_idx); + let mono_data = types.get_procedure_monomorph(cur_frame.monomorph_type_id); let field_idx = mono_data.expr_data[expr.unique_id_in_definition as usize].field_or_monomorph_idx as u32; // Note: same as above: clone if value lives on expr stack, simply @@ -527,7 +527,7 @@ impl Prompt { } Literal::Integer(lit_value) => { use ConcreteTypePart as CTP; - let def_types = types.get_procedure_monomorph(cur_frame.monomorph_idx); + let def_types = types.get_procedure_monomorph(cur_frame.monomorph_type_id); let concrete_type = &def_types.expr_data[expr.unique_id_in_definition as usize].expr_type; debug_assert_eq!(concrete_type.parts.len(), 1); @@ -575,7 +575,7 @@ impl Prompt { cur_frame.expr_values.push_back(value); }, Expression::Cast(expr) => { - let mono_data = types.get_procedure_monomorph(cur_frame.monomorph_idx); + let mono_data = types.get_procedure_monomorph(cur_frame.monomorph_type_id); let output_type = &mono_data.expr_data[expr.unique_id_in_definition as usize].expr_type; // Typechecking reduced this to two cases: either we @@ -766,11 +766,11 @@ impl Prompt { } // Determine the monomorph index of the function we're calling - let mono_data = types.get_procedure_monomorph(cur_frame.monomorph_idx); + let mono_data = types.get_procedure_monomorph(cur_frame.monomorph_type_id); let call_data = &mono_data.expr_data[expr.unique_id_in_definition as usize]; // Push the new frame and reserve its stack size - let new_frame = Frame::new(heap, expr.definition, call_data.field_or_monomorph_idx); + let new_frame = Frame::new(heap, expr.definition, call_data.type_id); let new_stack_size = new_frame.max_stack_size; self.frames.push(new_frame); self.store.cur_stack_boundary = new_stack_boundary; @@ -834,13 +834,13 @@ impl Prompt { Statement::Local(stmt) => { match stmt { LocalStatement::Memory(stmt) => { - if cfg!(debug_assertions) { + dbg_code!({ let variable = &heap[stmt.variable]; debug_assert!(match self.store.read_ref(ValueId::Stack(variable.unique_id_in_scope as u32)) { Value::Unassigned => false, _ => true, }); - } + }); cur_frame.position = stmt.next; Ok(EvalContinuation::Stepping) @@ -1030,7 +1030,7 @@ impl Prompt { "mismatch in expr stack size and number of arguments for new statement" ); - let mono_data = types.get_procedure_monomorph(cur_frame.monomorph_idx); + let mono_data = types.get_procedure_monomorph(cur_frame.monomorph_type_id); let expr_data = &mono_data.expr_data[call_expr.unique_id_in_definition as usize]; // Note that due to expression value evaluation they exist in @@ -1052,7 +1052,7 @@ impl Prompt { cur_frame.position = stmt.next; - Ok(EvalContinuation::NewComponent(call_expr.definition, expr_data.field_or_monomorph_idx, argument_group)) + Ok(EvalContinuation::NewComponent(call_expr.definition, expr_data.type_id, argument_group)) }, Statement::Expression(stmt) => { // The expression has just been completely evaluated. Some diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index c7c4ba07d617734fe1aaab99fcbe75c5634f550b..1b22d31e02ca3dd2218e5327823727bb2a53e821 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -16,6 +16,8 @@ use crate::protocol::input_source::*; use crate::protocol::parser::*; use crate::protocol::type_table::*; +pub use parser::type_table::TypeId; + /// A protocol description module pub struct Module { pub(crate) source: InputSource, @@ -106,7 +108,7 @@ impl ProtocolDescription { // - check number of arguments by retrieving the one instantiated // monomorph let concrete_type = ConcreteType{ parts: vec![ConcreteTypePart::Component(definition_id, 0)] }; - let mono_index = self.types.get_procedure_monomorph_index(&definition_id, &concrete_type.parts).unwrap(); + 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() { return Err(ComponentCreationError::InvalidNumArguments); diff --git a/src/protocol/parser/pass_definitions.rs b/src/protocol/parser/pass_definitions.rs index ab5dd5343d577ce389b7cc0dba2eac1ea651868e..6a97cc63dd144569b731a0250ff410a327728d4a 100644 --- a/src/protocol/parser/pass_definitions.rs +++ b/src/protocol/parser/pass_definitions.rs @@ -478,7 +478,6 @@ impl PassDefinitions { scope: ScopeId::new_invalid(), }; - let false_body_scope_id = false_body.scope; Some(false_body) } else { None diff --git a/src/protocol/parser/pass_typing.rs b/src/protocol/parser/pass_typing.rs index dace9be4d17124fe5a046dd1368aafef815f140f..b83adc287c4c02f7241be33d32ad7e1242939206 100644 --- a/src/protocol/parser/pass_typing.rs +++ b/src/protocol/parser/pass_typing.rs @@ -224,13 +224,13 @@ impl InferenceType { /// Generates a new InferenceType. The two boolean flags will be checked in /// debug mode. fn new(has_marker: bool, is_done: bool, parts: Vec) -> Self { - if cfg!(debug_assertions) { + dbg_code!({ debug_assert!(!parts.is_empty()); let parts_body_marker = parts.iter().any(|v| v.is_marker()); debug_assert_eq!(has_marker, parts_body_marker); let parts_done = parts.iter().all(|v| v.is_concrete()); debug_assert_eq!(is_done, parts_done, "{:?}", parts); - } + }); Self{ has_marker, is_done, parts } } @@ -827,7 +827,7 @@ pub(crate) struct ResolveQueueElement { // the polymorphic arguments to the procedure. pub(crate) root_id: RootId, pub(crate) definition_id: DefinitionId, - pub(crate) reserved_monomorph_idx: i32, + pub(crate) reserved_type_id: TypeId, } pub(crate) type ResolveQueue = Vec; @@ -836,8 +836,9 @@ pub(crate) type ResolveQueue = Vec; struct InferenceExpression { expr_type: InferenceType, // result type from expression expr_id: ExpressionId, // expression that is evaluated - field_or_monomorph_idx: i32, // index of field, of index of monomorph array in type table + field_or_monomorph_idx: i32, // index of field extra_data_idx: i32, // index of extra data needed for inference + type_id: TypeId, // when applicable indexes into type table } impl Default for InferenceExpression { @@ -847,6 +848,7 @@ impl Default for InferenceExpression { expr_id: ExpressionId::new_invalid(), field_or_monomorph_idx: -1, extra_data_idx: -1, + type_id: TypeId::new_invalid(), } } } @@ -855,7 +857,7 @@ impl Default for InferenceExpression { /// that all expressions have the appropriate types. pub(crate) struct PassTyping { // Current definition we're typechecking. - reserved_idx: i32, + reserved_type_id: TypeId, definition_type: DefinitionType, poly_vars: Vec, // Buffers for iteration over various types @@ -919,7 +921,7 @@ impl VarData { impl PassTyping { pub(crate) fn new() -> Self { PassTyping { - reserved_idx: -1, + reserved_type_id: TypeId::new_invalid(), definition_type: DefinitionType::Function(FunctionDefinitionId::new_invalid()), poly_vars: Vec::new(), var_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_LARGE), @@ -960,11 +962,11 @@ impl PassTyping { if let Some(first_concrete_part) = first_concrete_part { let concrete_type = ConcreteType{ parts: vec![first_concrete_part] }; - let reserved_idx = ctx.types.reserve_procedure_monomorph_index(definition_id, concrete_type); + let type_id = ctx.types.reserve_procedure_monomorph_type_id(definition_id, concrete_type); queue.push(ResolveQueueElement{ root_id, definition_id: *definition_id, - reserved_monomorph_idx: reserved_idx, + reserved_type_id: type_id, }) } } @@ -978,11 +980,11 @@ impl PassTyping { debug_assert!(self.poly_vars.is_empty()); // Prepare for visiting the definition - self.reserved_idx = element.reserved_monomorph_idx; + self.reserved_type_id = element.reserved_type_id; let proc_base = ctx.types.get_base_definition(&element.definition_id).unwrap(); if proc_base.is_polymorph { - let monomorph = ctx.types.get_monomorph(element.reserved_monomorph_idx); + let monomorph = ctx.types.get_monomorph(element.reserved_type_id); for poly_arg in monomorph.concrete_type.embedded_iter(0) { self.poly_vars.push(ConcreteType{ parts: Vec::from(poly_arg) }); } @@ -996,7 +998,7 @@ impl PassTyping { } fn reset(&mut self) { - self.reserved_idx = -1; + self.reserved_type_id = TypeId::new_invalid(); self.definition_type = DefinitionType::Function(FunctionDefinitionId::new_invalid()); self.poly_vars.clear(); self.var_types.clear(); @@ -1576,19 +1578,19 @@ impl PassTyping { ctx, extra_data.expr_id, &extra_data.poly_vars, first_concrete_part )?; - match ctx.types.get_procedure_monomorph_index(&definition_id, &concrete_type.parts) { - Some(reserved_idx) => { + match ctx.types.get_procedure_monomorph_type_id(&definition_id, &concrete_type.parts) { + Some(type_id) => { // Already typechecked, or already put into the resolve queue - infer_expr.field_or_monomorph_idx = reserved_idx; + infer_expr.type_id = type_id; }, None => { // Not typechecked yet, so add an entry in the queue - let reserved_idx = ctx.types.reserve_procedure_monomorph_index(&definition_id, concrete_type); - infer_expr.field_or_monomorph_idx = reserved_idx; + let reserved_type_id = ctx.types.reserve_procedure_monomorph_type_id(&definition_id, concrete_type); + infer_expr.type_id = reserved_type_id; queue.push(ResolveQueueElement { root_id: ctx.heap[definition_id].defined_in(), definition_id, - reserved_monomorph_idx: reserved_idx, + reserved_type_id, }); } } @@ -1604,8 +1606,8 @@ impl PassTyping { let concrete_type = 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(ctx.modules, ctx.heap, ctx.arch, definition_id, concrete_type)?; - infer_expr.field_or_monomorph_idx = mono_index; + let type_id = ctx.types.add_monomorphed_type(ctx.modules, ctx.heap, ctx.arch, definition_id, concrete_type)?; + infer_expr.type_id = type_id; }, Expression::Select(_) => { debug_assert!(infer_expr.field_or_monomorph_idx >= 0); @@ -1629,7 +1631,7 @@ impl PassTyping { }, }; - let target = ctx.types.get_procedure_monomorph_mut(self.reserved_idx); + let target = ctx.types.get_procedure_monomorph_mut(self.reserved_type_id); debug_assert!(target.arg_types.is_empty()); // makes sure we never queue a procedure's type inferencing twice debug_assert!(target.expr_data.is_empty()); @@ -1649,7 +1651,8 @@ impl PassTyping { infer_expr.expr_type.write_concrete_type(&mut concrete); target.expr_data.push(MonomorphExpression{ expr_type: concrete, - field_or_monomorph_idx: infer_expr.field_or_monomorph_idx + field_or_monomorph_idx: infer_expr.field_or_monomorph_idx, + type_id: infer_expr.type_id, }); } diff --git a/src/protocol/parser/pass_validation_linking.rs b/src/protocol/parser/pass_validation_linking.rs index e7e2345d34b94f8b63535a1f5a9b1ca7c892c9a9..30bd3bb11139bc5d185f8c7effa239adc70fbd65 100644 --- a/src/protocol/parser/pass_validation_linking.rs +++ b/src/protocol/parser/pass_validation_linking.rs @@ -1383,12 +1383,12 @@ impl Visitor for PassValidationLinking { Expression::Literal(lit_expr) => { // Only struct, unions, tuples and arrays can // have subexpressions, so we're always fine - if cfg!(debug_assertions) { + dbg_code!({ match lit_expr.value { Literal::Struct(_) | Literal::Union(_) | Literal::Array(_) | Literal::Tuple(_) => {}, _ => unreachable!(), } - } + }); true }, diff --git a/src/protocol/parser/type_table.rs b/src/protocol/parser/type_table.rs index 5e318dc6461b3d687874b5580aa71b2f8f872c21..2c41358e636475fcb6570524a23047baa4072bf6 100644 --- a/src/protocol/parser/type_table.rs +++ b/src/protocol/parser/type_table.rs @@ -38,6 +38,7 @@ use std::fmt::{Formatter, Result as FmtResult}; use std::collections::HashMap; +use std::hash::{Hash, Hasher}; use crate::protocol::ast::*; use crate::protocol::parser::symbol_table::SymbolScope; @@ -224,22 +225,14 @@ pub struct MonomorphExpression { // monomorph index for polymorphic function calls or literals. Negative // values are never used, but used to catch programming errors. pub(crate) field_or_monomorph_idx: i32, + pub(crate) type_id: TypeId, } //------------------------------------------------------------------------------ // Type monomorph storage //------------------------------------------------------------------------------ -/// Generic monomorph has a specific concrete type, a size and an alignment. -/// Extra data is in the `MonomorphVariant` per kind of type. -pub(crate) struct TypeMonomorph { - pub concrete_type: ConcreteType, - pub size: usize, - pub alignment: usize, - pub variant: MonomorphVariant, -} - -pub(crate) enum MonomorphVariant { +pub(crate) enum MonoTypeVariant { Enum, // no extra data Struct(StructMonomorph), Union(UnionMonomorph), @@ -247,45 +240,45 @@ pub(crate) enum MonomorphVariant { Tuple(TupleMonomorph), } -impl MonomorphVariant { +impl MonoTypeVariant { fn as_struct_mut(&mut self) -> &mut StructMonomorph { match self { - MonomorphVariant::Struct(v) => v, + MonoTypeVariant::Struct(v) => v, _ => unreachable!(), } } pub(crate) fn as_union(&self) -> &UnionMonomorph { match self { - MonomorphVariant::Union(v) => v, + MonoTypeVariant::Union(v) => v, _ => unreachable!(), } } fn as_union_mut(&mut self) -> &mut UnionMonomorph { match self { - MonomorphVariant::Union(v) => v, + MonoTypeVariant::Union(v) => v, _ => unreachable!(), } } fn as_tuple_mut(&mut self) -> &mut TupleMonomorph { match self { - MonomorphVariant::Tuple(v) => v, + MonoTypeVariant::Tuple(v) => v, _ => unreachable!(), } } fn as_procedure(&self) -> &ProcedureMonomorph { match self { - MonomorphVariant::Procedure(v) => v, + MonoTypeVariant::Procedure(v) => v, _ => unreachable!(), } } fn as_procedure_mut(&mut self) -> &mut ProcedureMonomorph { match self { - MonomorphVariant::Procedure(v) => v, + MonoTypeVariant::Procedure(v) => v, _ => unreachable!(), } } @@ -297,7 +290,8 @@ pub struct StructMonomorph { } pub struct StructMonomorphField { - pub concrete_type: ConcreteType, + pub type_id: TypeId, + concrete_type: ConcreteType, pub size: usize, pub alignment: usize, pub offset: usize, @@ -323,7 +317,8 @@ pub struct UnionMonomorphVariant { } pub struct UnionMonomorphEmbedded { - pub concrete_type: ConcreteType, + pub type_id: TypeId, + concrete_type: ConcreteType, // Note that the meaning of the offset (and alignment) depend on whether or // not the variant lives on the stack/heap. If it lives on the stack then // they refer to the offset from the start of the union value (so the first @@ -351,208 +346,113 @@ pub struct TupleMonomorph { } pub struct TupleMonomorphMember { - pub concrete_type: ConcreteType, + pub type_id: TypeId, + concrete_type: ConcreteType, pub size: usize, pub alignment: usize, pub offset: usize, } -/// Key used to perform lookups in the monomorph table. It computes a hash of -/// the type while not taking the unused polymorphic variables of the base type -/// into account (e.g. `struct Foo{ A field }`, here `B` is an unused -/// polymorphic variable). -struct MonomorphKey { - parts: Vec, - in_use: Vec, // TODO: @Performance, limit num args and use two `u64` as bitflags or something +/// Generic unique type ID. Every monomorphed type and every non-polymorphic +/// type will have one of these associated with it. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct TypeId(i64); + +impl TypeId { + pub(crate) fn new_invalid() -> Self { + return Self(-1); + } } -use std::hash::*; +/// A monomorphed type (or non-polymorphic type's) memory layout and information +/// regarding associated types (like a struct's field type). +pub struct MonoType { + pub type_id: TypeId, + pub concrete_type: ConcreteType, + pub size: usize, + pub alignment: usize, + pub(crate) variant: MonoTypeVariant +} -impl Hash for MonomorphKey { - fn hash(&self, state: &mut H) { - // if `in_use` is empty, then we may assume the type is not polymorphic - // (or all types are in use) - if self.in_use.is_empty() { - self.parts.hash(state); - } else { - // type is polymorphic - self.parts[0].hash(state); - - // note: hash is computed in a unique way, because practically - // speaking `in_use` is fixed per base type. So we cannot have the - // same base type (hence: a type with the same DefinitionId) with - // different different polymorphic variables in use. - let mut in_use_index = 0; - for section in ConcreteTypeIter::new(self.parts.as_slice(), 0) { - if self.in_use[in_use_index] { - section.hash(state); - } - in_use_index += 1; - } +impl MonoType { + #[inline] + fn new_empty(type_id: TypeId, concrete_type: ConcreteType, variant: MonoTypeVariant) -> Self { + return Self { + type_id, concrete_type, + size: 0, + alignment: 0, + variant, } } -} -impl PartialEq for MonomorphKey { - fn eq(&self, other: &Self) -> bool { - if self.in_use.is_empty() { - let temp_result = self.parts == other.parts; - return temp_result; + /// Little internal helper function as a reminder: if alignment is 0, then + /// the size/alignment are not actually computed yet! + #[inline] + fn get_size_alignment(&self) -> Option<(usize, usize)> { + if self.alignment == 0 { + return None } else { - // Outer type does not match - if self.parts[0] != other.parts[0] { - return false; - } - - debug_assert_eq!(self.parts[0].num_embedded() as usize, self.in_use.len()); - let mut iter_self = ConcreteTypeIter::new(self.parts.as_slice(), 0); - let mut iter_other = ConcreteTypeIter::new(other.parts.as_slice(), 0); - let mut index = 0; - while let Some(section_self) = iter_self.next() { - let section_other = iter_other.next().unwrap(); - let in_use = self.in_use[index]; - index += 1; - - if !in_use { - continue; - } - - if section_self != section_other { - return false; - } - } - - return true; + return Some((self.size, self.alignment)); } } } -impl Eq for MonomorphKey {} - -use std::cell::UnsafeCell; - -/// Lookup table for monomorphs. Wrapped in a special struct because we don't -/// want to allocate for each lookup (what we really want is a HashMap that -/// exposes its CompareFn and HashFn, but whatevs). -pub(crate) struct MonomorphTable { - lookup: HashMap, // indexes into `monomorphs` - pub(crate) monomorphs: Vec, - // We use an UnsafeCell because this is only used internally per call to - // `get_monomorph_index` calls. This is safe because `&TypeMonomorph`s - // retrieved for this class remain valid when the key is mutated and the - // type table is not multithreaded. - // - // I added this because we don't want to allocate for each lookup, hence we - // need a reusable `key` internal to this class. This in turn makes - // `get_monomorph_index` a mutable call. Now the code that calls this - // function (even though we're not mutating the table!) needs a lot of extra - // boilerplate. I opted for the `UnsafeCell` instead of the boilerplate. - key: UnsafeCell, +/// Special structure that acts like the lookup key for `ConcreteType` instances +/// that have already been added to the type table before. +#[derive(Clone)] +struct MonoSearchKey { + parts: Vec<(bool, ConcreteTypePart)>, } -// TODO: Clean this up: somehow prevent the `key`, but also do not allocate for -// each "get_monomorph_index" -unsafe impl Send for MonomorphTable{} -unsafe impl Sync for MonomorphTable{} - -impl MonomorphTable { - fn new() -> Self { - return Self { - lookup: HashMap::with_capacity(256), - monomorphs: Vec::with_capacity(256), - key: UnsafeCell::new(MonomorphKey{ - parts: Vec::with_capacity(32), - in_use: Vec::with_capacity(32), - }), - } - } - - fn insert_with_zero_size_and_alignment(&mut self, concrete_type: ConcreteType, in_use: &[PolymorphicVariable], variant: MonomorphVariant) -> i32 { - let key = MonomorphKey{ - parts: Vec::from(concrete_type.parts.as_slice()), - in_use: in_use.iter().map(|v| v.is_in_use).collect(), +impl MonoSearchKey { + fn with_capacity(capacity: usize) -> Self { + return MonoSearchKey{ + parts: Vec::with_capacity(capacity), }; - let index = self.monomorphs.len(); - let _result = self.lookup.insert(key, index as i32); - debug_assert!(_result.is_none()); // did not exist yet - self.monomorphs.push(TypeMonomorph{ - concrete_type, - size: 0, - alignment: 0, - variant, - }); - - return index as i32; } - fn get_monomorph_index(&self, parts: &[ConcreteTypePart], in_use: &[PolymorphicVariable]) -> Option { - let key = unsafe { - // Clear-and-extend to, at some point, prevent future allocations - let key = &mut *self.key.get(); - key.parts.clear(); - key.parts.extend_from_slice(parts); - key.in_use.clear(); - key.in_use.extend(in_use.iter().map(|v| v.is_in_use)); - - &*key - }; + /// Sets the search key based on a single concrete type and its polymorphic + /// variables. + fn set(&mut self, concrete_type_parts: &[ConcreteTypePart], poly_var_in_use: &[PolymorphicVariable]) { + self.set_top_type(concrete_type_parts[0]); - match self.lookup.get(key) { - Some(index) => return Some(*index), - None => return None, + let mut poly_var_index = 0; + for subtype in ConcreteTypeIter::new(concrete_type_parts, 0) { + let in_use = poly_var_in_use[poly_var_index].is_in_use; + poly_var_index += 1; + self.push_subtype(subtype, in_use); } - } - #[inline] - fn get(&self, index: i32) -> &TypeMonomorph { - debug_assert!(index >= 0); - return &self.monomorphs[index as usize]; + debug_assert_eq!(poly_var_index, poly_var_in_use.len()); } - #[inline] - fn get_mut(&mut self, index: i32) -> &mut TypeMonomorph { - debug_assert!(index >= 0); - return &mut self.monomorphs[index as usize]; + /// Starts setting the search key based on an initial top-level type, + /// programmer must call `push_subtype` the appropriate number of times + /// after calling this function + fn set_top_type(&mut self, type_part: ConcreteTypePart) { + self.parts.clear(); + self.parts.push((true, type_part)); } - fn get_monomorph_size_alignment(&self, index: i32) -> Option<(usize, usize)> { - let monomorph = self.get(index); - if monomorph.size == 0 && monomorph.alignment == 0 { - // If both are zero, then we wish to mean: we haven't actually - // computed the size and alignment yet. So: - return None; - } else { - return Some((monomorph.size, monomorph.alignment)); + fn push_subtype(&mut self, concrete_type: &[ConcreteTypePart], in_use: bool) { + for part in concrete_type { + self.parts.push((in_use, *part)); } } -} -//------------------------------------------------------------------------------ -// Monomorph Type Storage -//------------------------------------------------------------------------------ - -/// Generic unique type ID. Every monomorphed type and every non-polymorphic -/// type will have one of these associated with it. -pub struct TypeId(i64); + fn push_subtree(&mut self, concrete_type: &[ConcreteTypePart], poly_var_in_use: &[PolymorphicVariable]) { + self.parts.push((true, concrete_type[0])); + let mut poly_var_index = 0; + for subtype in ConcreteTypeIter::new(concrete_type, 0) { + let in_use = poly_var_in_use[poly_var_index].is_in_use; + poly_var_index += 1; + self.push_subtype(subtype, in_use); + } -impl TypeId { - pub(crate) fn new_invalid() -> Self { - return Self(-1); + debug_assert_eq!(poly_var_index, poly_var_in_use.len()); } } -/// A monomorphed type (or non-polymorphic type's) memory layout and information -/// regarding associated types (like a struct's field type). -pub struct MonoType { - concrete_type: ConcreteType, -} - -/// Special structure that acts like the lookup key for `ConcreteType` instances -/// that have already been added to the type table before. -struct MonoSearchKey { - parts: Vec<(bool, ConcreteTypePart)>, -} - impl Hash for MonoSearchKey { fn hash(&self, state: &mut H) { for index in 0..self.parts.len() { @@ -573,13 +473,15 @@ impl PartialEq for MonoSearchKey { while self_index < self.parts.len() && other_index < other.parts.len() { let (self_in_use, self_part) = &self.parts[self_index]; let (other_in_use, other_part) = &other.parts[self_index]; - + let self_end_index = ConcreteType::type_parts_subtree_end_idx(&self.parts, ) if self_in_use == other_in_use { - if self_in_use { + if *self_in_use { // Both are in use, so both should be equal if self_part != other_part { return false; } + + self_index } // else: both not in use, so we don't care } else { // No agreement on importance of parts. This is practically @@ -587,7 +489,7 @@ impl PartialEq for MonoSearchKey { unreachable!(); } - self_index += 1; + self_index = ConcreteType::type_parts_subtree_end_idx(); other_index += 1; } @@ -597,6 +499,8 @@ impl PartialEq for MonoSearchKey { } } +impl Eq for MonoSearchKey{} + //------------------------------------------------------------------------------ // Type table //------------------------------------------------------------------------------ @@ -604,7 +508,7 @@ impl PartialEq for MonoSearchKey { // Programmer note: keep this struct free of dynamically allocated memory #[derive(Clone)] struct TypeLoopBreadcrumb { - monomorph_idx: i32, + type_id: TypeId, next_member: u32, next_embedded: u32, // for unions, the index into the variant's embedded types } @@ -612,7 +516,7 @@ struct TypeLoopBreadcrumb { // Programmer note: keep this struct free of dynamically allocated memory #[derive(Clone)] struct MemoryBreadcrumb { - monomorph_idx: i32, + type_id: TypeId, next_member: u32, next_embedded: u32, first_size_alignment_idx: u32, @@ -632,21 +536,25 @@ enum MemoryLayoutResult { // TODO: @Optimize, initial memory-unoptimized implementation struct TypeLoopEntry { - monomorph_idx: i32, + type_id: TypeId, is_union: bool, } struct TypeLoop { - members: Vec + members: Vec, } +type DefinitionMap = HashMap; +type MonoTypeMap = HashMap; +type MonoTypeArray = Vec; + pub struct TypeTable { // Lookup from AST DefinitionId to a defined type. Also lookups for // concrete type to monomorphs - pub(crate) definition_lookup: HashMap, - pub(crate) mono_type_lookup: HashMap, - pub(crate) mono_types: Vec, - pub(crate) mono_lookup: MonomorphTable, + pub(crate) definition_lookup: DefinitionMap, + mono_type_lookup: MonoTypeMap, + pub(crate) mono_types: MonoTypeArray, + mono_search_key: MonoSearchKey, // Breadcrumbs left behind while trying to find type loops. Also used to // determine sizes of types when all type loops are detected. type_loop_breadcrumbs: Vec, @@ -666,7 +574,7 @@ impl TypeTable { definition_lookup: HashMap::with_capacity(128), mono_type_lookup: HashMap::with_capacity(128), mono_types: Vec::with_capacity(128), - mono_lookup: MonomorphTable::new(), + mono_search_key: MonoSearchKey::with_capacity(32), type_loop_breadcrumbs: Vec::with_capacity(32), type_loops: Vec::with_capacity(8), encountered_types: Vec::with_capacity(32), @@ -686,11 +594,11 @@ impl TypeTable { debug_assert!(modules.iter().all(|m| m.phase >= ModuleCompilationPhase::DefinitionsParsed)); debug_assert!(self.definition_lookup.is_empty()); - if cfg!(debug_assertions) { + dbg_code!({ for (index, module) in modules.iter().enumerate() { debug_assert_eq!(index, module.root_id.index as usize); } - } + }); // Use context to guess hashmap size of the base types let reserve_size = ctx.heap.definitions.len(); @@ -710,14 +618,14 @@ impl TypeTable { } } - debug_assert_eq!(self.definition_lookup.len(), reserve_size, "mismatch in reserved size of type table"); // NOTE: Temp fix for builtin functions + debug_assert_eq!(self.definition_lookup.len(), reserve_size, "mismatch in reserved size of type table"); for module in modules.iter_mut() { module.phase = ModuleCompilationPhase::TypesAddedToTable; } // Go through all types again, lay out all types that are not - // polymorphic. This might cause us to lay out types that are monomorphs - // of polymorphic types. + // polymorphic. This might cause us to lay out monomorphized polymorphs + // if these were member types of non-polymorphic types. for definition_idx in 0..ctx.heap.definitions.len() { let definition_id = ctx.heap.definitions.get_id(definition_idx); let poly_type = self.definition_lookup.get(&definition_id).unwrap(); @@ -729,8 +637,9 @@ impl TypeTable { // If here then the type is a data type without polymorphic // variables, but we might have instantiated it already, so: let concrete_parts = [ConcreteTypePart::Instance(definition_id, 0)]; - let mono_index = self.mono_lookup.get_monomorph_index(&concrete_parts, &[]); - if mono_index.is_none() { + self.mono_search_key.set(&concrete_parts, &[]); + 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, ConcreteType{ @@ -755,32 +664,34 @@ impl TypeTable { /// Returns the index into the monomorph type array if the procedure type /// already has a (reserved) monomorph. - /// FIXME: This really shouldn't be called from within the runtime. See UnsafeCell in MonomorphTable #[inline] - pub(crate) fn get_procedure_monomorph_index(&self, definition_id: &DefinitionId, type_parts: &[ConcreteTypePart]) -> Option { + pub(crate) fn get_procedure_monomorph_type_id(&self, definition_id: &DefinitionId, type_parts: &[ConcreteTypePart]) -> Option { + // Cannot use internal search key due to mutability issues. But this + // method should end up being deprecated at some point anyway. let base_type = self.definition_lookup.get(definition_id).unwrap(); - return self.mono_lookup.get_monomorph_index(type_parts, &base_type.poly_vars); + let mut search_key = MonoSearchKey::with_capacity(type_parts.len()); + search_key.set(type_parts, &base_type.poly_vars); + + return self.mono_type_lookup.get(&search_key).copied(); } #[inline] - pub(crate) fn get_monomorph(&self, monomorph_index: i32) -> &TypeMonomorph { - return self.mono_lookup.get(monomorph_index); + pub(crate) fn get_monomorph(&self, type_id: TypeId) -> &MonoType { + return &self.mono_types[type_id.0 as usize]; } /// Returns a mutable reference to a procedure's monomorph expression data. /// Used by typechecker to fill in previously reserved type information #[inline] - pub(crate) fn get_procedure_monomorph_mut(&mut self, monomorph_index: i32) -> &mut ProcedureMonomorph { - debug_assert!(monomorph_index >= 0); - let monomorph = self.mono_lookup.get_mut(monomorph_index); - return monomorph.variant.as_procedure_mut(); + pub(crate) fn get_procedure_monomorph_mut(&mut self, type_id: TypeId) -> &mut ProcedureMonomorph { + let mono_type = &mut self.mono_types[type_id.0 as usize]; + return mono_type.variant.as_procedure_mut(); } #[inline] - pub(crate) fn get_procedure_monomorph(&self, monomorph_index: i32) -> &ProcedureMonomorph { - debug_assert!(monomorph_index >= 0); - let monomorph = self.mono_lookup.get(monomorph_index); - return monomorph.variant.as_procedure(); + pub(crate) fn get_procedure_monomorph(&self, type_id: TypeId) -> &ProcedureMonomorph { + let mono_type = &self.mono_types[type_id.0 as usize]; + return mono_type.variant.as_procedure(); } /// Reserves space for a monomorph of a polymorphic procedure. The index @@ -788,44 +699,45 @@ impl TypeTable { /// 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, concrete_type: ConcreteType) -> i32 { + pub(crate) fn reserve_procedure_monomorph_type_id(&mut self, definition_id: &DefinitionId, concrete_type: ConcreteType) -> TypeId { + let type_id = TypeId(self.mono_types.len() as i64); let base_type = self.definition_lookup.get_mut(definition_id).unwrap(); - let mono_index = self.mono_lookup.insert_with_zero_size_and_alignment( - concrete_type, &base_type.poly_vars, MonomorphVariant::Procedure(ProcedureMonomorph{ - arg_types: Vec::new(), - expr_data: Vec::new(), - }) - ); + self.mono_search_key.set(&concrete_type.parts, &base_type.poly_vars); + + debug_assert!(!self.mono_type_lookup.contains_key(&self.mono_search_key)); + 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{ + arg_types: Vec::new(), + expr_data: Vec::new(), + }))); - return mono_index; + return type_id; } - /// 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. - /// TODO: Fix signature - pub(crate) fn add_data_monomorph( - &mut self, modules: &[Module], heap: &Heap, arch: &TargetArch, definition_id: DefinitionId, concrete_type: ConcreteType - ) -> Result { - debug_assert_eq!(definition_id, get_concrete_type_definition(&concrete_type)); - - // Check if the monomorph already exists - let poly_type = self.definition_lookup.get_mut(&definition_id).unwrap(); - if let Some(idx) = self.mono_lookup.get_monomorph_index(&concrete_type.parts, &poly_type.poly_vars) { - return Ok(idx); + /// 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, + definition_id: DefinitionId, concrete_type: ConcreteType + ) -> Result { + debug_assert_eq!(definition_id, get_concrete_type_definition(&concrete_type.parts).unwrap()); + + // Check if the concrete type was already added + let definition = self.definition_lookup.get(&definition_id).unwrap(); + let poly_var_in_use = &definition.poly_vars; + self.mono_search_key.set(&concrete_type.parts, poly_var_in_use.as_slice()); + if let Some(type_id) = self.mono_type_lookup.get(&self.mono_search_key) { + return Ok(*type_id); } - // Doesn't exist, so instantiate a monomorph and determine its memory - // layout. + // Concrete type needs to be added self.detect_and_resolve_type_loops_for(modules, heap, concrete_type)?; - let mono_idx = self.encountered_types[0].monomorph_idx; + let type_id = self.encountered_types[0].type_id; self.lay_out_memory_for_encountered_types(arch); - return Ok(mono_idx as i32); + return Ok(type_id); } - /// - //-------------------------------------------------------------------------- // Building base types //-------------------------------------------------------------------------- @@ -1255,7 +1167,10 @@ impl TypeTable { debug_assert!(self.encountered_types.is_empty()); // Push the initial breadcrumb - let initial_breadcrumb = self.check_member_for_type_loops(&concrete_type); + let initial_breadcrumb = Self::check_member_for_type_loops( + &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); } else { @@ -1268,12 +1183,12 @@ impl TypeTable { let breadcrumb_idx = self.type_loop_breadcrumbs.len() - 1; let mut breadcrumb = self.type_loop_breadcrumbs[breadcrumb_idx].clone(); - let monomorph = self.mono_lookup.get(breadcrumb.monomorph_idx); - let resolve_result = match &monomorph.variant { - MonomorphVariant::Enum => { + let mono_type = &self.mono_types[breadcrumb.type_id.0 as usize]; + let resolve_result = match &mono_type.variant { + MonoTypeVariant::Enum => { TypeLoopResult::TypeExists }, - MonomorphVariant::Union(monomorph) => { + MonoTypeVariant::Union(monomorph) => { let num_variants = monomorph.variants.len() as u32; let mut union_result = TypeLoopResult::TypeExists; @@ -1283,7 +1198,10 @@ impl TypeTable { while breadcrumb.next_embedded < num_embedded { let mono_embedded = &mono_variant.embedded[breadcrumb.next_embedded as usize]; - union_result = self.check_member_for_type_loops(&mono_embedded.concrete_type); + union_result = Self::check_member_for_type_loops( + &self.type_loop_breadcrumbs, &self.definition_lookup, &self.mono_type_lookup, + &mut self.mono_search_key, &mono_embedded.concrete_type + ); if union_result != TypeLoopResult::TypeExists { // In type loop or new breadcrumb pushed, so @@ -1300,13 +1218,16 @@ impl TypeTable { union_result }, - MonomorphVariant::Struct(monomorph) => { + MonoTypeVariant::Struct(monomorph) => { let num_fields = monomorph.fields.len() as u32; let mut struct_result = TypeLoopResult::TypeExists; while breadcrumb.next_member < num_fields { let mono_field = &monomorph.fields[breadcrumb.next_member as usize]; - struct_result = self.check_member_for_type_loops(&mono_field.concrete_type); + struct_result = Self::check_member_for_type_loops( + &self.type_loop_breadcrumbs, &self.definition_lookup, &self.mono_type_lookup, + &mut self.mono_search_key, &mono_field.concrete_type + ); if struct_result != TypeLoopResult::TypeExists { // Type loop or breadcrumb pushed, so break out of @@ -1319,14 +1240,17 @@ impl TypeTable { struct_result }, - MonomorphVariant::Procedure(_) => unreachable!(), - MonomorphVariant::Tuple(monomorph) => { + MonoTypeVariant::Procedure(_) => unreachable!(), + MonoTypeVariant::Tuple(monomorph) => { let num_members = monomorph.members.len() as u32; let mut tuple_result = TypeLoopResult::TypeExists; while breadcrumb.next_member < num_members { let tuple_member = &monomorph.members[breadcrumb.next_member as usize]; - tuple_result = self.check_member_for_type_loops(&tuple_member.concrete_type); + tuple_result = Self::check_member_for_type_loops( + &self.type_loop_breadcrumbs, &self.definition_lookup, &self.mono_type_lookup, + &mut self.mono_search_key, &tuple_member.concrete_type + ); if tuple_result != TypeLoopResult::TypeExists { break; @@ -1363,24 +1287,22 @@ impl TypeTable { let breadcrumb = &mut self.type_loop_breadcrumbs[breadcrumb_idx]; let mut is_union = false; - let monomorph = self.mono_lookup.get_mut(breadcrumb.monomorph_idx); - // TODO: Match on monomorph directly here - match &mut monomorph.variant { - MonomorphVariant::Union(monomorph) => { - // 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 variant = &mut monomorph.variants[breadcrumb.next_member as usize]; - variant.lives_on_heap = true; - breadcrumb.next_embedded += 1; - is_union = true; - contains_union = true; - }, - _ => {}, // else: we don't care for now - } + // Check if type loop member is a union that may be + // broken up by moving some of its members to the heap. + let mono_type = &mut self.mono_types[breadcrumb.type_id.0 as usize]; + if let MonoTypeVariant::Union(union_type) = &mut mono_type.variant { + // Mark the variant that caused the loop as heap + // allocated to break the type loop. + let variant = &mut union_type.variants[breadcrumb.next_member as usize]; + variant.lives_on_heap = true; + breadcrumb.next_embedded += 1; + + is_union = true; + contains_union = true; + } // else: we don't care about the type for now loop_members.push(TypeLoopEntry{ - monomorph_idx: breadcrumb.monomorph_idx, + type_id: breadcrumb.type_id, is_union }); } @@ -1391,7 +1313,7 @@ impl TypeTable { // type loop error. This is because otherwise our // breadcrumb resolver ends up in an infinite loop. return Err(construct_type_loop_error( - self, &new_type_loop, modules, heap + &self.mono_types, &new_type_loop, modules, heap )); } @@ -1409,17 +1331,17 @@ impl TypeTable { // 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], heap: &Heap, mono_lookup: &MonomorphTable, - definition_id: DefinitionId, monomorph_idx: i32, index_in_loop: usize + modules: &'a [Module], heap: &Heap, mono_types: &MonoTypeArray, + definition_id: DefinitionId, mono_type_id: TypeId, 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 = &mono_lookup.get(monomorph_idx).concrete_type; + let mono_type = &mono_types[mono_type_id.0 as usize]; + let type_name = mono_type.concrete_type.display_name(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 \ @@ -1443,16 +1365,7 @@ impl TypeTable { ); } - fn retrieve_definition_id_if_possible(parts: &[ConcreteTypePart]) -> DefinitionId { - match &parts[0] { - ConcreteTypePart::Instance(v, _) | - ConcreteTypePart::Function(v, _) | - ConcreteTypePart::Component(v, _) => *v, - _ => DefinitionId::new_invalid(), - } - } - - fn construct_type_loop_error(table: &TypeTable, type_loop: &TypeLoop, modules: &[Module], heap: &Heap) -> ParseError { + fn construct_type_loop_error(mono_types: &MonoTypeArray, type_loop: &TypeLoop, modules: &[Module], heap: &Heap) -> ParseError { // Seek first entry to produce parse error. Then continue builder // pattern. This is the error case so efficiency can go home. let mut parse_error = None; @@ -1461,13 +1374,17 @@ impl TypeTable { let first_entry = &type_loop.members[next_member_index]; next_member_index += 1; - let first_definition_id = retrieve_definition_id_if_possible(&table.mono_lookup.get(first_entry.monomorph_idx).concrete_type.parts); - if first_definition_id.is_invalid() { + // Retrieve definition of first type in loop + let first_mono_type = &mono_types[first_entry.type_id.0 as usize]; + let first_definition_id = get_concrete_type_definition(&first_mono_type.concrete_type.parts); + if first_definition_id.is_none() { continue; } + let first_definition_id = first_definition_id.unwrap(); + // Produce error message for first type in loop let (first_module, first_span, first_message) = type_loop_source_span_and_message( - modules, heap, &table.mono_lookup, first_definition_id, first_entry.monomorph_idx, 0 + modules, heap, mono_types, first_definition_id, first_entry.type_id, 0 ); parse_error = Some(ParseError::new_error_at_span(first_module, first_span, first_message)); break; @@ -1478,13 +1395,15 @@ impl TypeTable { let mut error_counter = 1; for member_idx in next_member_index..type_loop.members.len() { let entry = &type_loop.members[member_idx]; - let definition_id = retrieve_definition_id_if_possible(&table.mono_lookup.get(entry.monomorph_idx).concrete_type.parts); - if definition_id.is_invalid() { - continue; // dont display tuples + let mono_type = &mono_types[entry.type_id.0 as usize]; + let definition_id = get_concrete_type_definition(&mono_type.concrete_type.parts); + if definition_id.is_none() { + continue; } + let definition_id = definition_id.unwrap(); let (module, span, message) = type_loop_source_span_and_message( - modules, heap, &table.mono_lookup, definition_id, entry.monomorph_idx, error_counter + modules, heap, mono_types, definition_id, entry.type_id, error_counter ); parse_error = parse_error.with_info_at_span(module, span, message); error_counter += 1; @@ -1499,9 +1418,9 @@ impl TypeTable { for entry in &type_loop.members { if entry.is_union { - let monomorph = self.mono_lookup.get(entry.monomorph_idx).variant.as_union(); - 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); + let mono_type = self.mono_types[entry.type_id.0 as usize].variant.as_union(); + debug_assert!(!mono_type.variants.is_empty()); // otherwise it couldn't be part of the type loop + let has_stack_variant = mono_type.variants.iter().any(|variant| !variant.lives_on_heap); if has_stack_variant { can_be_broken = true; break; @@ -1511,7 +1430,7 @@ impl TypeTable { if !can_be_broken { // Construct a type loop error - return Err(construct_type_loop_error(self, type_loop, modules, heap)); + return Err(construct_type_loop_error(&self.mono_types, type_loop, modules, heap)); } } @@ -1529,35 +1448,39 @@ impl TypeTable { /// don't do any modifications of internal types here. Hence: if we /// return `PushBreadcrumb` then call `handle_new_breadcrumb_for_type_loops` /// to take care of storing the appropriate types. - fn check_member_for_type_loops(&self, definition_type: &ConcreteType) -> TypeLoopResult { + fn check_member_for_type_loops( + breadcrumbs: &[TypeLoopBreadcrumb], definition_map: &DefinitionMap, mono_type_map: &MonoTypeMap, + mono_key: &mut MonoSearchKey, concrete_type: &ConcreteType + ) -> TypeLoopResult { use ConcreteTypePart as CTP; // Depending on the type, lookup if the type has already been visited // (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!(!definition_type.parts.is_empty()); - let (definition_id, monomorph_index) = match &definition_type.parts[0] { + debug_assert!(!concrete_type.parts.is_empty()); + let (definition_id, type_id) = match &concrete_type.parts[0] { CTP::Tuple(_) => { - let monomorph_index = self.mono_lookup.get_monomorph_index(&definition_type.parts, &[]); - - (DefinitionId::new_invalid(), monomorph_index) + 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, _) | CTP::Function(definition_id, _) | CTP::Component(definition_id, _) => { - let base_type = self.definition_lookup.get(definition_id).unwrap(); - let monomorph_index = self.mono_lookup.get_monomorph_index(&definition_type.parts, &base_type.poly_vars); + 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, monomorph_index) + (*definition_id, type_id) }, _ => { return TypeLoopResult::TypeExists }, }; - if let Some(monomorph_index) = monomorph_index { - for (breadcrumb_idx, breadcrumb) in self.type_loop_breadcrumbs.iter().enumerate() { - if breadcrumb.monomorph_idx == monomorph_index { + if let Some(type_id) = type_id { + for (breadcrumb_idx, breadcrumb) in breadcrumbs.iter().enumerate() { + if breadcrumb.type_id == type_id { return TypeLoopResult::TypeLoop(breadcrumb_idx); } } @@ -1567,55 +1490,59 @@ impl TypeTable { // Type is not yet known, so we need to insert it into the lookup and // push a new breadcrumb. - return TypeLoopResult::PushBreadcrumb(definition_id, definition_type.clone()); + return TypeLoopResult::PushBreadcrumb(definition_id, concrete_type.clone()); } /// Handles the `PushBreadcrumb` result for a `check_member_for_type_loops` - /// call. - fn handle_new_breadcrumb_for_type_loops(&mut self, definition_id: DefinitionId, definition_type: ConcreteType) { + /// 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) { use DefinedTypeVariant as DTV; use ConcreteTypePart as CTP; let mut is_union = false; - let monomorph_index = match &definition_type.parts[0] { + let type_id = match &concrete_type.parts[0] { 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); - for section in ConcreteTypeIter::new(&definition_type.parts, 0) { + for section in ConcreteTypeIter::new(&concrete_type.parts, 0) { members.push(TupleMonomorphMember{ + type_id: TypeId::new_invalid(), concrete_type: ConcreteType{ parts: Vec::from(section) }, size: 0, alignment: 0, offset: 0 }); } - let mono_index = self.mono_lookup.insert_with_zero_size_and_alignment( - definition_type, &[], - MonomorphVariant::Tuple(TupleMonomorph{ - members, - }) - ); - mono_index + let type_id = TypeId(self.mono_types.len() as i64); + Self::set_search_key_to_tuple(&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::new_empty(type_id, concrete_type, MonoTypeVariant::Tuple(TupleMonomorph{ members }))); + + type_id }, CTP::Instance(_check_definition_id, _) => { debug_assert_eq!(definition_id, *_check_definition_id); // because this is how `definition_id` was determined - let base_type = self.definition_lookup.get_mut(&definition_id).unwrap(); - let monomorph_index = match &mut base_type.definition { + + Self::set_search_key_to_type(&mut self.mono_search_key, &self.definition_lookup, &concrete_type.parts); + let base_type = self.definition_lookup.get(&definition_id).unwrap(); + let type_id = match &base_type.definition { DTV::Enum(definition) => { // The enum is a bit exceptional in that when we insert // it we we will immediately set its size/alignment: // there is nothing to compute here. debug_assert!(definition.size != 0 && definition.alignment != 0); - let mono_index = self.mono_lookup.insert_with_zero_size_and_alignment( - definition_type, &base_type.poly_vars, MonomorphVariant::Enum - ); - let mono_type = self.mono_lookup.get_mut(mono_index); + 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::new_empty(type_id, concrete_type, MonoTypeVariant::Enum)); + + let mono_type = &mut self.mono_types[type_id.0 as usize]; mono_type.size = definition.size; mono_type.alignment = definition.alignment; - mono_index + type_id }, DTV::Union(definition) => { // Create all the variants with their concrete types @@ -1623,8 +1550,9 @@ impl TypeTable { 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); + let mono_concrete = Self::construct_concrete_type(poly_embedded, &concrete_type); mono_embedded.push(UnionMonomorphEmbedded{ + type_id: TypeId::new_invalid(), concrete_type: mono_concrete, size: 0, alignment: 0, @@ -1638,24 +1566,27 @@ impl TypeTable { }) } - let mono_index = self.mono_lookup.insert_with_zero_size_and_alignment( - definition_type, &base_type.poly_vars, - MonomorphVariant::Union(UnionMonomorph{ - variants: mono_variants, - tag_size: definition.tag_size, - heap_size: 0, - heap_alignment: 0 - }) - ); + let type_id = TypeId(self.mono_types.len() as i64); + let tag_size = definition.tag_size; + 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::new_empty(type_id, concrete_type, MonoTypeVariant::Union(UnionMonomorph{ + variants: mono_variants, + tag_size, + heap_size: 0, + heap_alignment: 0, + }))); is_union = true; - mono_index + type_id }, DTV::Struct(definition) => { + // Create fields 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); + let mono_concrete = Self::construct_concrete_type(&poly_field.parser_type, &concrete_type); mono_fields.push(StructMonomorphField{ + type_id: TypeId::new_invalid(), concrete_type: mono_concrete, size: 0, alignment: 0, @@ -1663,30 +1594,28 @@ impl TypeTable { }) } - let mono_index = self.mono_lookup.insert_with_zero_size_and_alignment( - definition_type, &base_type.poly_vars, - MonomorphVariant::Struct(StructMonomorph{ fields: mono_fields }) - ); + 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::new_empty(type_id, concrete_type, MonoTypeVariant::Struct(StructMonomorph{ + fields: mono_fields, + }))); - mono_index + type_id }, DTV::Function(_) | DTV::Component(_) => { unreachable!("pushing type resolving breadcrumb for procedure type") }, }; - monomorph_index + type_id }, _ => unreachable!(), }; - self.encountered_types.push(TypeLoopEntry{ - monomorph_idx: monomorph_index, - is_union, - }); - + self.encountered_types.push(TypeLoopEntry{ type_id, is_union }); self.type_loop_breadcrumbs.push(TypeLoopBreadcrumb{ - monomorph_idx: monomorph_index, + type_id, next_member: 0, next_embedded: 0, }); @@ -1737,7 +1666,7 @@ impl TypeTable { // 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.variant { - debug_assert_eq!(_container_definition_id, get_concrete_type_definition(container_type)); + debug_assert_eq!(_container_definition_id, get_concrete_type_definition(&container_type.parts).unwrap()); let mut container_iter = container_type.embedded_iter(0); for _ in 0..poly_arg_idx { @@ -1784,7 +1713,7 @@ impl TypeTable { // were detecting type loops) let first_entry = &self.encountered_types[0]; self.memory_layout_breadcrumbs.push(MemoryBreadcrumb{ - monomorph_idx: first_entry.monomorph_idx, + type_id: first_entry.type_id, next_member: 0, next_embedded: 0, first_size_alignment_idx: 0, @@ -1795,16 +1724,16 @@ impl TypeTable { let cur_breadcrumb_idx = self.memory_layout_breadcrumbs.len() - 1; let mut breadcrumb = self.memory_layout_breadcrumbs[cur_breadcrumb_idx].clone(); - let mono_type = self.mono_lookup.get(breadcrumb.monomorph_idx); + let mono_type = &self.mono_types[breadcrumb.type_id.0 as usize]; match &mono_type.variant { - MonomorphVariant::Enum => { + MonoTypeVariant::Enum => { // Size should already be computed - if cfg!(debug_assertions) { - let mono_type = self.mono_lookup.get(breadcrumb.monomorph_idx); + dbg_code!({ + let mono_type = &self.mono_types[breadcrumb.type_id.0 as usize]; debug_assert!(mono_type.size != 0 && mono_type.alignment != 0); - } + }); }, - MonomorphVariant::Union(mono_type) => { + MonoTypeVariant::Union(mono_type) => { // Retrieve size/alignment of each embedded type. We do not // compute the offsets or total type sizes yet. let num_variants = mono_type.variants.len() as u32; @@ -1819,7 +1748,12 @@ impl TypeTable { let num_embedded = mono_variant.embedded.len() as u32; while breadcrumb.next_embedded < num_embedded { let mono_embedded = &mono_variant.embedded[breadcrumb.next_embedded as usize]; - match self.get_memory_layout_or_breadcrumb(arch, &mono_embedded.concrete_type.parts) { + let layout_result = Self::get_memory_layout_or_breadcrumb( + &self.definition_lookup, &self.mono_type_lookup, &self.mono_types, + &mut self.mono_search_key, arch, &mono_embedded.concrete_type.parts, + self.size_alignment_stack.len() + ); + match layout_result { MemoryLayoutResult::TypeExists(size, alignment) => { self.size_alignment_stack.push((size, alignment)); }, @@ -1845,15 +1779,15 @@ impl TypeTable { let mut max_size = mono_type.tag_size; let mut max_alignment = mono_type.tag_size; - let mono_info = self.mono_lookup.get_mut(breadcrumb.monomorph_idx); - let mono_type = mono_info.variant.as_union_mut(); + let mono_type = &mut self.mono_types[breadcrumb.type_id.0 as usize]; + let union_type = mono_type.variant.as_union_mut(); let mut size_alignment_idx = breadcrumb.first_size_alignment_idx as usize; - for variant in &mut mono_type.variants { + for variant in &mut union_type.variants { // We're doing stack computations, so always start with // the tag size/alignment. - let mut variant_offset = mono_type.tag_size; - let mut variant_alignment = mono_type.tag_size; + let mut variant_offset = union_type.tag_size; + let mut variant_alignment = union_type.tag_size; if variant.lives_on_heap { // Variant lives on heap, so just a pointer @@ -1883,18 +1817,23 @@ impl TypeTable { max_alignment = max_alignment.max(variant_alignment); } - mono_info.size = max_size; - mono_info.alignment = max_alignment; + mono_type.size = max_size; + mono_type.alignment = max_alignment; self.size_alignment_stack.truncate(breadcrumb.first_size_alignment_idx as usize); }, - MonomorphVariant::Struct(mono_type) => { + MonoTypeVariant::Struct(mono_type) => { // Retrieve size and alignment of each struct member. We'll // compute the offsets once all of those are known let num_fields = mono_type.fields.len() as u32; while breadcrumb.next_member < num_fields { let mono_field = &mono_type.fields[breadcrumb.next_member as usize]; - match self.get_memory_layout_or_breadcrumb(arch, &mono_field.concrete_type.parts) { + let layout_result = Self::get_memory_layout_or_breadcrumb( + &self.definition_lookup, &self.mono_type_lookup, &self.mono_types, + &mut self.mono_search_key, arch, &mono_field.concrete_type.parts, + self.size_alignment_stack.len() + ); + match layout_result { MemoryLayoutResult::TypeExists(size, alignment) => { self.size_alignment_stack.push((size, alignment)) }, @@ -1912,11 +1851,11 @@ impl TypeTable { let mut cur_offset = 0; let mut max_alignment = 1; - let mono_info = self.mono_lookup.get_mut(breadcrumb.monomorph_idx); - let mono_type = mono_info.variant.as_struct_mut(); + let mono_type = &mut self.mono_types[breadcrumb.type_id.0 as usize]; + let struct_type = mono_type.variant.as_struct_mut(); let mut size_alignment_idx = breadcrumb.first_size_alignment_idx as usize; - for field in &mut mono_type.fields { + for field in &mut struct_type.fields { let (size, alignment) = self.size_alignment_stack[size_alignment_idx]; field.size = size; field.alignment = alignment; @@ -1929,18 +1868,23 @@ impl TypeTable { max_alignment = max_alignment.max(alignment); } - mono_info.size = cur_offset; - mono_info.alignment = max_alignment; + mono_type.size = cur_offset; + mono_type.alignment = max_alignment; self.size_alignment_stack.truncate(breadcrumb.first_size_alignment_idx as usize); }, - MonomorphVariant::Procedure(_) => { + MonoTypeVariant::Procedure(_) => { unreachable!(); }, - MonomorphVariant::Tuple(mono_type) => { + MonoTypeVariant::Tuple(mono_type) => { let num_members = mono_type.members.len() as u32; while breadcrumb.next_member < num_members { let mono_member = &mono_type.members[breadcrumb.next_member as usize]; - match self.get_memory_layout_or_breadcrumb(arch, &mono_member.concrete_type.parts) { + let layout_result = Self::get_memory_layout_or_breadcrumb( + &self.definition_lookup, &self.mono_type_lookup, &self.mono_types, + &mut self.mono_search_key, arch, &mono_member.concrete_type.parts, + self.size_alignment_stack.len() + ); + match layout_result { MemoryLayoutResult::TypeExists(size, alignment) => { self.size_alignment_stack.push((size, alignment)); }, @@ -1958,15 +1902,15 @@ impl TypeTable { let mut cur_offset = 0; let mut max_alignment = 1; - let mono_info = self.mono_lookup.get_mut(breadcrumb.monomorph_idx); - let mono_type = mono_info.variant.as_tuple_mut(); + let mono_type = &mut self.mono_types[breadcrumb.type_id.0 as usize]; + let mono_tuple = mono_type.variant.as_tuple_mut(); let mut size_alignment_index = breadcrumb.first_size_alignment_idx as usize; for member_index in 0..num_members { let (member_size, member_alignment) = self.size_alignment_stack[size_alignment_index]; align_offset_to(&mut cur_offset, member_alignment); size_alignment_index += 1; - let member = &mut mono_type.members[member_index as usize]; + let member = &mut mono_tuple.members[member_index as usize]; member.size = member_size; member.alignment = member_alignment; member.offset = cur_offset; @@ -1975,8 +1919,8 @@ impl TypeTable { max_alignment = max_alignment.max(member_alignment); } - mono_info.size = cur_offset; - mono_info.alignment = max_alignment; + mono_type.size = cur_offset; + mono_type.alignment = max_alignment; self.size_alignment_stack.truncate(breadcrumb.first_size_alignment_idx as usize); }, } @@ -1998,7 +1942,7 @@ impl TypeTable { // First pass, use buffer to store size/alignment to prevent // borrowing issues. - let mono_type = self.mono_lookup.get(entry.monomorph_idx).variant.as_union(); + let mono_type = self.mono_types[entry.type_id.0 as usize].variant.as_union(); for variant in &mono_type.variants { if !variant.lives_on_heap { continue; @@ -2007,17 +1951,22 @@ impl TypeTable { debug_assert!(!variant.embedded.is_empty()); for embedded in &variant.embedded { - match self.get_memory_layout_or_breadcrumb(arch, &embedded.concrete_type.parts) { + let layout_result = Self::get_memory_layout_or_breadcrumb( + &self.definition_lookup, &self.mono_type_lookup, &self.mono_types, + &mut self.mono_search_key, arch, &embedded.concrete_type.parts, + self.size_alignment_stack.len() + ); + match layout_result { MemoryLayoutResult::TypeExists(size, alignment) => { self.size_alignment_stack.push((size, alignment)); }, - _ => unreachable!(), + _ => unreachable!(), // type was not truly infinite, so type must have been found } } } // Second pass, apply the size/alignment values in our buffer - let mono_type = self.mono_lookup.get_mut(entry.monomorph_idx).variant.as_union_mut(); + let mono_type = self.mono_types[entry.type_id.0 as usize].variant.as_union_mut(); let mut max_size = 0; let mut max_alignment = 1; @@ -2063,7 +2012,13 @@ impl TypeTable { /// is called *after* type loops have been succesfully resolved. Hence we /// may assume that all monomorph entries exist, but we may not assume that /// those entries already have their size/alignment computed. - fn get_memory_layout_or_breadcrumb(&self, arch: &TargetArch, parts: &[ConcreteTypePart]) -> MemoryLayoutResult { + // Passed parameters are messy. But need to strike balance between borrowing + // and allocations in hot loops. So it is what it is. + fn get_memory_layout_or_breadcrumb( + definition_map: &DefinitionMap, mono_type_map: &MonoTypeMap, mono_types: &MonoTypeArray, + search_key: &mut MonoSearchKey, arch: &TargetArch, parts: &[ConcreteTypePart], + size_alignment_stack_len: usize, + ) -> MemoryLayoutResult { use ConcreteTypePart as CTP; debug_assert!(!parts.is_empty()); @@ -2086,32 +2041,36 @@ impl TypeTable { CTP::Input => arch.port_size_alignment, CTP::Output => arch.port_size_alignment, CTP::Tuple(_) => { - let mono_index = self.mono_lookup.get_monomorph_index(parts, &[]).unwrap(); - if let Some((size, alignment)) = self.mono_lookup.get_monomorph_size_alignment(mono_index) { + Self::set_search_key_to_tuple(search_key, definition_map, parts); + let type_id = mono_type_map.get(&search_key).copied().unwrap(); + let mono_type = &mono_types[type_id.0 as usize]; + if let Some((size, alignment)) = mono_type.get_size_alignment() { return MemoryLayoutResult::TypeExists(size, alignment); } else { return MemoryLayoutResult::PushBreadcrumb(MemoryBreadcrumb{ - monomorph_idx: mono_index, + type_id, next_member: 0, next_embedded: 0, - first_size_alignment_idx: self.size_alignment_stack.len() as u32, + first_size_alignment_idx: size_alignment_stack_len as u32, }) } }, CTP::Instance(definition_id, _) => { // Retrieve entry and the specific monomorph index by applying // the full concrete type. - let entry = self.definition_lookup.get(&definition_id).unwrap(); - let mono_index = self.mono_lookup.get_monomorph_index(parts, &entry.poly_vars).unwrap(); + let definition_type = definition_map.get(&definition_id).unwrap(); + search_key.set(parts, &definition_type.poly_vars); + let type_id = mono_type_map.get(&search_key).copied().unwrap(); + let mono_type = &mono_types[type_id.0 as usize]; - if let Some((size, alignment)) = self.mono_lookup.get_monomorph_size_alignment(mono_index) { + if let Some((size, alignment)) = mono_type.get_size_alignment() { return MemoryLayoutResult::TypeExists(size, alignment); } else { return MemoryLayoutResult::PushBreadcrumb(MemoryBreadcrumb{ - monomorph_idx: mono_index, + type_id, next_member: 0, next_embedded: 0, - first_size_alignment_idx: self.size_alignment_stack.len() as u32, + first_size_alignment_idx: size_alignment_stack_len as u32, }); } }, @@ -2175,6 +2134,45 @@ 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 { + match type_parts[0] { + ConcreteTypePart::Tuple(_) => { + Self::set_search_key_to_tuple(search_key, definition_map, type_parts); + return true; + }, + ConcreteTypePart::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(_, _) => { + todo!("implement function pointers") + }, + _ => return false, + } + } + + fn set_search_key_to_tuple(search_key: &mut MonoSearchKey, definition_map: &DefinitionMap, type_parts: &[ConcreteTypePart]) { + dbg_code!({ + let is_tuple = if let ConcreteTypePart::Tuple(_) = type_parts[0] { true } else { false }; + assert!(is_tuple); + }); + search_key.set_top_type(type_parts[0]); + for subtree in ConcreteTypeIter::new(type_parts, 0) { + if let Some(definition_id) = get_concrete_type_definition(subtree) { + // A definition, so retrieve poly var usage info + let definition_type = definition_map.get(&definition_id).unwrap(); + search_key.push_subtree(subtree, &definition_type.poly_vars); + } else { + // Not a definition, so all type information is important + search_key.push_subtype(subtree, true); + } + } + } } #[inline] @@ -2186,11 +2184,15 @@ fn align_offset_to(offset: &mut usize, alignment: usize) { } #[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() +fn get_concrete_type_definition(concrete_parts: &[ConcreteTypePart]) -> Option { + match concrete_parts[0] { + ConcreteTypePart::Instance(definition_id, _) | + ConcreteTypePart::Function(definition_id, _) | + ConcreteTypePart::Component(definition_id, _) => { + return Some(definition_id); + }, + _ => { + return None; + }, } } \ No newline at end of file diff --git a/src/protocol/tests/parser_monomorphs.rs b/src/protocol/tests/parser_monomorphs.rs index 2926b727bfa3360ea90779f6de4d5f43ab281694..76e6004ddd79e3bad7b879cbeb65c9e21c87a139 100644 --- a/src/protocol/tests/parser_monomorphs.rs +++ b/src/protocol/tests/parser_monomorphs.rs @@ -71,8 +71,8 @@ fn test_enum_monomorphs() { } " ).for_enum("Answer", |e| { e - .assert_num_monomorphs(1) - .assert_has_monomorph("Answer"); + .assert_has_monomorph("2Answer") + .assert_num_monomorphs(1); }); } diff --git a/src/protocol/tests/utils.rs b/src/protocol/tests/utils.rs index 461d2207031d776df65f16a2c52952449c5a7e4a..d599014400b8b193e8bded700bdcba2fdfeafb65 100644 --- a/src/protocol/tests/utils.rs +++ b/src/protocol/tests/utils.rs @@ -296,8 +296,8 @@ impl<'a> StructTester<'a> { pub(crate) fn assert_size_alignment(mut self, monomorph: &str, size: usize, alignment: usize) -> Self { self = self.assert_has_monomorph(monomorph); let (mono_idx, _) = has_monomorph(self.ctx, self.ast_def.this.upcast(), monomorph); - let mono_idx = mono_idx.unwrap(); - let mono = self.ctx.types.get_monomorph(mono_idx); + let type_id = mono_idx.unwrap(); + let mono = self.ctx.types.get_monomorph(type_id); assert!( mono.size == size && mono.alignment == alignment, @@ -702,7 +702,7 @@ impl<'a> FunctionTester<'a> { // Assuming the function is not polymorphic let definition_id = self.def.this.upcast(); let func_type = [ConcreteTypePart::Function(definition_id, 0)]; - let mono_index = self.ctx.types.get_procedure_monomorph_index(&definition_id, &func_type).unwrap(); + let mono_index = self.ctx.types.get_procedure_monomorph_type_id(&definition_id, &func_type).unwrap(); let mut prompt = Prompt::new(&self.ctx.types, &self.ctx.heap, self.def.this.upcast(), mono_index, ValueGroup::new_stack(Vec::new())); let mut call_context = FakeRunContext{}; @@ -816,7 +816,7 @@ fn get_procedure_monomorph<'a>(heap: &Heap, types: &'a TypeTable, definition_id: unreachable!() }; - let mono_index = types.get_procedure_monomorph_index(&definition_id, &func_type).unwrap(); + let mono_index = types.get_procedure_monomorph_type_id(&definition_id, &func_type).unwrap(); let mono_data = types.get_procedure_monomorph(mono_index); mono_data @@ -926,7 +926,7 @@ fn has_equal_num_monomorphs(ctx: TestCtx, num: usize, definition_id: DefinitionI // Again: inefficient, but its testing code let mut num_on_type = 0; - for mono in &ctx.types.mono_lookup.monomorphs { + for mono in &ctx.types.mono_types { match &mono.concrete_type.parts[0] { ConcreteTypePart::Instance(def_id, _) | ConcreteTypePart::Function(def_id, _) | @@ -942,13 +942,13 @@ fn has_equal_num_monomorphs(ctx: TestCtx, num: usize, definition_id: DefinitionI (num_on_type == num, num_on_type) } -fn has_monomorph(ctx: TestCtx, definition_id: DefinitionId, serialized_monomorph: &str) -> (Option, String) { +fn has_monomorph(ctx: TestCtx, definition_id: DefinitionId, serialized_monomorph: &str) -> (Option, String) { // Note: full_buffer is just for error reporting let mut full_buffer = String::new(); let mut has_match = None; full_buffer.push('['); - let mut append_to_full_buffer = |concrete_type: &ConcreteType, mono_idx: usize| { + let mut append_to_full_buffer = |concrete_type: &ConcreteType, type_id: TypeId| { if full_buffer.len() != 1 { full_buffer.push_str(", "); } @@ -957,14 +957,14 @@ fn has_monomorph(ctx: TestCtx, definition_id: DefinitionId, serialized_monomorph let first_idx = full_buffer.len(); full_buffer.push_str(concrete_type.display_name(ctx.heap).as_str()); if &full_buffer[first_idx..] == serialized_monomorph { - has_match = Some(mono_idx as i32); + has_match = Some(type_id); } full_buffer.push('"'); }; // Bit wasteful, but this is (temporary?) testing code: - for (mono_idx, mono) in ctx.types.mono_lookup.monomorphs.iter().enumerate() { + for (mono_idx, mono) in ctx.types.mono_types.iter().enumerate() { let got_definition_id = match &mono.concrete_type.parts[0] { ConcreteTypePart::Instance(v, _) | ConcreteTypePart::Function(v, _) | @@ -972,7 +972,7 @@ fn has_monomorph(ctx: TestCtx, definition_id: DefinitionId, serialized_monomorph _ => DefinitionId::new_invalid(), }; if got_definition_id == definition_id { - append_to_full_buffer(&mono.concrete_type, mono_idx); + append_to_full_buffer(&mono.concrete_type, mono.type_id); } } diff --git a/src/runtime/connector.rs b/src/runtime/connector.rs index e9bce7685c94520e6df7cf873184512aee407e1f..f495ce8faa4812601639dba56217514833284aec 100644 --- a/src/runtime/connector.rs +++ b/src/runtime/connector.rs @@ -463,7 +463,7 @@ impl ConnectorPDL { return ConnectorScheduling::Immediate; }, - EvalContinuation::NewComponent(definition_id, monomorph_idx, arguments) => { + EvalContinuation::NewComponent(definition_id, type_id, arguments) => { // Note: we're relinquishing ownership of ports. But because // we are in non-sync mode the scheduler will handle and check // port ownership transfer. @@ -473,7 +473,7 @@ impl ConnectorPDL { let new_prompt = Prompt::new( &sched_ctx.runtime.protocol_description.types, &sched_ctx.runtime.protocol_description.heap, - definition_id, monomorph_idx, arguments + definition_id, type_id, arguments ); let new_component = ConnectorPDL::new(new_prompt); comp_ctx.push_component(new_component, comp_ctx.workspace_ports.clone()); diff --git a/src/runtime/consensus.rs b/src/runtime/consensus.rs index 1147f447ef8a25ef9a8b8e0754c36b4527efdfc0..616fc07fbfa1db8177754241900e984f7aa1f667 100644 --- a/src/runtime/consensus.rs +++ b/src/runtime/consensus.rs @@ -328,13 +328,13 @@ impl Consensus { let branch = &mut self.branch_annotations[branch_id.index as usize]; let port_info = ctx.get_port_by_id(source_port_id).unwrap(); - if cfg!(debug_assertions) { + dbg_code!({ // Check for consistent mapping let port = branch.channel_mapping.iter() .find(|v| v.channel_id == port_info.channel_id) .unwrap(); debug_assert!(port.expected_firing == None || port.expected_firing == Some(true)); - } + }); // Check for ports that are being sent debug_assert!(self.workspace_ports.is_empty()); diff --git a/src/runtime2/component/component_pdl.rs b/src/runtime2/component/component_pdl.rs index 8fdb556af27fe536f23f6d19a592551b097e4450..a698dbff39a72205436cb435ab9cd0bf7709cb84 100644 --- a/src/runtime2/component/component_pdl.rs +++ b/src/runtime2/component/component_pdl.rs @@ -255,11 +255,11 @@ impl CompPDL { self.handle_sync_start(sched_ctx, comp_ctx); return Ok(CompScheduling::Immediate); }, - EC::NewComponent(definition_id, monomorph_idx, arguments) => { + EC::NewComponent(definition_id, type_id, arguments) => { debug_assert_eq!(self.mode, Mode::NonSync); self.create_component_and_transfer_ports( sched_ctx, comp_ctx, - definition_id, monomorph_idx, arguments + definition_id, type_id, arguments ); return Ok(CompScheduling::Requeue); }, @@ -311,7 +311,7 @@ impl CompPDL { /// Handles decision from the consensus round. This will cause a change in /// the internal `Mode`, such that the next call to `run` can take the /// appropriate next steps. - fn handle_sync_decision(&mut self, sched_ctx: &SchedulerCtx, comp_ctx: &mut CompCtx, decision: SyncRoundDecision) { + fn handle_sync_decision(&mut self, sched_ctx: &SchedulerCtx, _comp_ctx: &mut CompCtx, decision: SyncRoundDecision) { sched_ctx.log(&format!("Handling sync decision: {:?} (in mode {:?})", decision, self.mode)); let is_success = match decision { SyncRoundDecision::None => { @@ -598,7 +598,7 @@ impl CompPDL { fn create_component_and_transfer_ports( &mut self, sched_ctx: &SchedulerCtx, creator_ctx: &mut CompCtx, - definition_id: DefinitionId, monomorph_index: i32, mut arguments: ValueGroup + definition_id: DefinitionId, type_id: TypeId, mut arguments: ValueGroup ) { struct PortPair{ creator_handle: LocalPortHandle, @@ -692,7 +692,7 @@ impl CompPDL { // to message exchanges between remote peers. let prompt = Prompt::new( &sched_ctx.runtime.protocol.types, &sched_ctx.runtime.protocol.heap, - definition_id, monomorph_index, arguments, + definition_id, type_id, arguments, ); let component = CompPDL::new(prompt, port_id_pairs.len()); let (created_key, component) = sched_ctx.runtime.finish_create_pdl_component( diff --git a/src/runtime2/component/consensus.rs b/src/runtime2/component/consensus.rs index 4683ff164f11a5d64815a14a18ef5d94395ad24e..3ad9244b7ed94ce7b697a704fce159a7358b7d5d 100644 --- a/src/runtime2/component/consensus.rs +++ b/src/runtime2/component/consensus.rs @@ -99,7 +99,7 @@ impl SolutionCombiner { /// Combines the currently stored global solution (if any) with the newly /// provided local solution. Make sure to check the `has_decision` return /// value afterwards. - fn combine_with_local_solution(&mut self, comp_id: CompId, solution: SyncLocalSolution) { + fn combine_with_local_solution(&mut self, _comp_id: CompId, solution: SyncLocalSolution) { debug_assert_ne!(self.solution.decision, SyncRoundDecision::Solution); // Combine partial solution with the local solution entries