diff --git a/src/collections/string_pool.rs b/src/collections/string_pool.rs index fb897861609d1cef87d11cc845c627fd07d968c8..2b9f7856adfdc27897aa5027b8f5bbb8d55d1255 100644 --- a/src/collections/string_pool.rs +++ b/src/collections/string_pool.rs @@ -152,6 +152,13 @@ impl Drop for StringPool { } } +// String pool cannot be cloned, and the created `StringRef` instances remain +// allocated until the end of the program, so it is always safe to send. It is +// also sync in the sense that it becomes an immutable thing after compilation, +// but lets not derive that if we would ever become a multithreaded compiler in +// the future. +unsafe impl Send for StringPool {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs index f8901415933fa689dfb5715d11935f6f7b33e79f..481f96acee8f19c9f3cec88100d31fb90a067adc 100644 --- a/src/ffi/mod.rs +++ b/src/ffi/mod.rs @@ -232,6 +232,8 @@ pub unsafe extern "C" fn connector_add_port_pair( #[no_mangle] pub unsafe extern "C" fn connector_add_component( connector: &mut Connector, + module_ptr: *const u8, + module_len: usize, ident_ptr: *const u8, ident_len: usize, ports_ptr: *const PortId, @@ -239,6 +241,7 @@ pub unsafe extern "C" fn connector_add_component( ) -> c_int { StoredError::tl_clear(); match connector.add_component( + &*slice_from_raw_parts(module_ptr, module_len), &*slice_from_raw_parts(ident_ptr, ident_len), &*slice_from_raw_parts(ports_ptr, ports_len), ) { diff --git a/src/protocol/eval/error.rs b/src/protocol/eval/error.rs new file mode 100644 index 0000000000000000000000000000000000000000..f2f91e0838e03731c64a3f4b782cea88fff6dc20 --- /dev/null +++ b/src/protocol/eval/error.rs @@ -0,0 +1,111 @@ +use std::fmt; + +use crate::protocol::{ + ast::*, + Module, + input_source::{ErrorStatement, StatementKind} +}; +use super::executor::*; + +/// Represents a stack frame recorded in an error +#[derive(Debug)] +pub struct EvalFrame { + pub line: u32, + pub module_name: String, + pub procedure: String, // function or component + pub is_func: bool, +} + +impl fmt::Display for EvalFrame { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let func_or_comp = if self.is_func { + "function " + } else { + "component" + }; + + if self.module_name.is_empty() { + write!(f, "{} {}:{}", func_or_comp, &self.procedure, self.line) + } else { + write!(f, "{} {}:{}:{}", func_or_comp, &self.module_name, &self.procedure, self.line) + } + } +} + +/// Represents an error that ocurred during evaluation. Contains error +/// statements just like in parsing errors. Additionally may display the current +/// execution state. +#[derive(Debug)] +pub struct EvalError { + pub(crate) statements: Vec, + pub(crate) frames: Vec, +} + +impl EvalError { + pub(crate) fn new_error_at_expr(prompt: &Prompt, modules: &[Module], heap: &Heap, expr_id: ExpressionId, msg: String) -> EvalError { + // Create frames + debug_assert!(!prompt.frames.is_empty()); + let mut frames = Vec::with_capacity(prompt.frames.len()); + let mut last_module_source = &modules[0].source; + for frame in prompt.frames.iter() { + let definition = &heap[frame.definition]; + let statement = &heap[frame.position]; + let statement_span = statement.span(); + + let (root_id, procedure, is_func) = match definition { + Definition::Function(def) => { + (def.defined_in, def.identifier.value.as_str().to_string(), true) + }, + Definition::Component(def) => { + (def.defined_in, def.identifier.value.as_str().to_string(), false) + }, + _ => unreachable!("construct stack frame with definition pointing to data type") + }; + + // Lookup module name, if it has one + let module = modules.iter().find(|m| m.root_id == root_id).unwrap(); + let module_name = if let Some(name) = &module.name { + name.as_str().to_string() + } else { + String::new() + }; + + last_module_source = &module.source; + frames.push(EvalFrame{ + line: statement_span.begin.line, + module_name, + procedure, + is_func + }); + } + + let expr = &heap[expr_id]; + let statements = vec![ + ErrorStatement::from_source_at_span(StatementKind::Error, last_module_source, expr.span(), msg) + ]; + + EvalError{ statements, frames } + } +} + +impl fmt::Display for EvalError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Display error statement(s) + self.statements[0].fmt(f)?; + for statement in self.statements.iter().skip(1) { + writeln!(f)?; + statement.fmt(f)?; + } + + // Display stack trace + writeln!(f)?; + writeln!(f, " +- Stack trace:")?; + for frame in self.frames.iter().rev() { + write!(f, " | ")?; + frame.fmt(f)?; + writeln!(f)?; + } + + Ok(()) + } +} \ No newline at end of file diff --git a/src/protocol/eval/executor.rs b/src/protocol/eval/executor.rs index 239d890bff7cb47c2f6000277c2b39f825e91fb5..aaa8ff51668fbea70e82fe55e9c6a9cf2e7686c4 100644 --- a/src/protocol/eval/executor.rs +++ b/src/protocol/eval/executor.rs @@ -3,6 +3,7 @@ use std::collections::VecDeque; use super::value::*; use super::store::*; +use super::error::*; use crate::protocol::*; use crate::protocol::ast::*; @@ -24,10 +25,10 @@ pub(crate) enum ExprInstruction { #[derive(Debug, Clone)] pub(crate) struct Frame { - definition: DefinitionId, - position: StatementId, - expr_stack: VecDeque, // hack for expression evaluation, evaluated by popping from back - expr_values: VecDeque, // hack for expression results, evaluated by popping from front/back + pub(crate) definition: DefinitionId, + 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 } impl Frame { @@ -150,7 +151,8 @@ impl Frame { } } -type EvalResult = Result; +type EvalResult = Result; + pub enum EvalContinuation { Stepping, Inconsistent, @@ -184,7 +186,7 @@ impl Prompt { prompt } - pub(crate) fn step(&mut self, heap: &Heap, ctx: &mut EvalContext) -> EvalResult { + pub(crate) fn step(&mut self, heap: &Heap, modules: &[Module], ctx: &mut EvalContext) -> EvalResult { // Helper function to transfer multiple values from the expression value // array into a heap region (e.g. constructing arrays or structs). fn transfer_expression_values_front_into_heap(cur_frame: &mut Frame, store: &mut Store, num_values: usize) -> HeapPos { @@ -206,6 +208,25 @@ impl Prompt { heap_pos } + // Helper function to make sure that an index into an aray is valid. + fn array_inclusive_index_is_invalid(store: &Store, array_heap_pos: u32, idx: i64) -> bool { + let array_len = store.heap_regions[array_heap_pos as usize].values.len(); + return idx < 0 || idx >= array_len as i64; + } + + fn array_exclusive_index_is_invalid(store: &Store, array_heap_pos: u32, idx: i64) -> bool { + let array_len = store.heap_regions[array_heap_pos as usize].values.len(); + return idx < 0 || idx > array_len as i64; + } + + fn construct_array_error(prompt: &Prompt, modules: &[Module], heap: &Heap, expr_id: ExpressionId, heap_pos: u32, idx: i64) -> EvalError { + let array_len = prompt.store.heap_regions[heap_pos as usize].values.len(); + return EvalError::new_error_at_expr( + prompt, modules, heap, expr_id, + format!("index {} is out of bounds: array length is {}", idx, array_len) + ) + } + // Checking if we're at the end of execution let cur_frame = self.frames.last_mut().unwrap(); if cur_frame.position.is_invalid() { @@ -266,7 +287,6 @@ impl Prompt { self.store.drop_value(val.get_heap_pos()); }, Expression::Indexing(expr) => { - // TODO: Out of bounds checking // Evaluate index. Never heap allocated so we do // not have to drop it. let index = cur_frame.expr_values.pop_back().unwrap(); @@ -274,14 +294,14 @@ impl Prompt { debug_assert!(index.is_integer()); let index = if index.is_signed_integer() { - index.as_signed_integer() as u32 + index.as_signed_integer() as i64 } else { - index.as_unsigned_integer() as u32 + index.as_unsigned_integer() as i64 }; let subject = cur_frame.expr_values.pop_back().unwrap(); - let (deallocate_heap_pos, value_to_push) = match subject { + let (deallocate_heap_pos, value_to_push, subject_heap_pos) = match subject { Value::Ref(value_ref) => { // Our expression stack value is a reference to something that // exists in the normal stack/heap. We don't want to deallocate @@ -289,14 +309,23 @@ impl Prompt { let subject = self.store.read_ref(value_ref); let subject_heap_pos = subject.as_array(); - (None, Value::Ref(ValueId::Heap(subject_heap_pos, index))) + if array_inclusive_index_is_invalid(&self.store, subject_heap_pos, index) { + return Err(construct_array_error(self, modules, heap, expr_id, subject_heap_pos, index)); + } + + (None, Value::Ref(ValueId::Heap(subject_heap_pos, index as u32)), subject_heap_pos) }, _ => { // Our value lives on the expression stack, hence we need to // clone whatever we're referring to. Then drop the subject. let subject_heap_pos = subject.as_array(); - let subject_indexed = Value::Ref(ValueId::Heap(subject_heap_pos, index)); - (Some(subject_heap_pos), self.store.clone_value(subject_indexed)) + + if array_inclusive_index_is_invalid(&self.store, subject_heap_pos, index) { + return Err(construct_array_error(self, modules, heap, expr_id, subject_heap_pos, index)); + } + + let subject_indexed = Value::Ref(ValueId::Heap(subject_heap_pos, index as u32)); + (Some(subject_heap_pos), self.store.clone_value(subject_indexed), subject_heap_pos) }, }; @@ -304,8 +333,59 @@ impl Prompt { self.store.drop_value(deallocate_heap_pos); }, Expression::Slicing(expr) => { - // TODO: Out of bounds checking - todo!("implement slicing") + // Evaluate indices + let from_index = cur_frame.expr_values.pop_back().unwrap(); + let from_index = self.store.maybe_read_ref(&from_index); + let to_index = cur_frame.expr_values.pop_back().unwrap(); + let to_index = self.store.maybe_read_ref(&to_index); + + debug_assert!(from_index.is_integer() && to_index.is_integer()); + let from_index = if from_index.is_signed_integer() { + from_index.as_signed_integer() + } else { + from_index.as_unsigned_integer() as i64 + }; + let to_index = if to_index.is_signed_integer() { + to_index.as_signed_integer() + } else { + to_index.as_unsigned_integer() as i64 + }; + + // Dereference subject if needed + let subject = cur_frame.expr_values.pop_back().unwrap(); + let deref_subject = self.store.maybe_read_ref(&subject); + + // Slicing needs to produce a copy anyway (with the + // current evaluator implementation) + let array_heap_pos = deref_subject.as_array(); + if array_inclusive_index_is_invalid(&self.store, array_heap_pos, from_index) { + return Err(construct_array_error(self, modules, heap, expr.from_index, array_heap_pos, from_index)); + } + if array_exclusive_index_is_invalid(&self.store, array_heap_pos, to_index) { + return Err(construct_array_error(self, modules, heap, expr.to_index, array_heap_pos, to_index)); + } + + // Again: would love to push directly, but rust... + let new_heap_pos = self.store.alloc_heap(); + debug_assert!(self.store.heap_regions[new_heap_pos as usize].values.is_empty()); + if to_index > from_index { + let from_index = from_index as usize; + let to_index = to_index as usize; + let mut values = Vec::with_capacity(to_index - from_index); + for idx in from_index..to_index { + let value = self.store.heap_regions[array_heap_pos as usize].values[idx].clone(); + values.push(self.store.clone_value(value)); + } + + self.store.heap_regions[new_heap_pos as usize].values = values; + + } // else: empty range + + cur_frame.expr_values.push_back(Value::Array(new_heap_pos)); + + // Dropping the original subject, because we don't + // want to drop something on the stack + self.store.drop_value(subject.get_heap_pos()); }, Expression::Select(expr) => { let subject= cur_frame.expr_values.pop_back().unwrap(); @@ -359,7 +439,7 @@ impl Prompt { CTP::SInt16 => Value::SInt16(lit_value.unsigned_value as i16), CTP::SInt32 => Value::SInt32(lit_value.unsigned_value as i32), CTP::SInt64 => Value::SInt64(lit_value.unsigned_value as i64), - _ => unreachable!(), + _ => unreachable!("got concrete type {:?} for integer literal at expr {:?}", &expr.concrete_type, expr_id), } } Literal::Struct(lit_value) => { @@ -619,7 +699,7 @@ impl Prompt { assert!( cur_frame.expr_values.is_empty(), "This is a debugging assertion that will fail if you perform expressions without \ - assigning to anything. This should be completely valid, and this assertions should be \ + assigning to anything. This should be completely valid, and this assertion should be \ replaced by something that clears the expression values if needed, but I'll keep this \ in for now for debugging purposes." ); diff --git a/src/protocol/eval/mod.rs b/src/protocol/eval/mod.rs index 5c7b066b229f28ca73911ecd278d4d73e4fbf5f6..33f8e6bb9897331a06b763b82df5da99ce7c19c0 100644 --- a/src/protocol/eval/mod.rs +++ b/src/protocol/eval/mod.rs @@ -23,7 +23,9 @@ pub(crate) mod value; pub(crate) mod store; pub(crate) mod executor; +pub(crate) mod error; +pub use error::EvalError; pub use value::{Value, ValueGroup}; pub(crate) use store::{Store}; pub use executor::{EvalContinuation, Prompt}; diff --git a/src/protocol/eval/value.rs b/src/protocol/eval/value.rs index 74e48556f09b6af6c33ab29a2c00744d3c218493..3aa4511601ee08e072ea0464fdf4f39535977965 100644 --- a/src/protocol/eval/value.rs +++ b/src/protocol/eval/value.rs @@ -534,6 +534,7 @@ pub(crate) fn apply_equality_operator(store: &Store, lhs: &Value, rhs: &Value) - Value::SInt16(v) => *v == rhs.as_sint16(), Value::SInt32(v) => *v == rhs.as_sint32(), Value::SInt64(v) => *v == rhs.as_sint64(), + Value::Array(lhs_pos) => eval_equality_heap(store, *lhs_pos, rhs.as_array()), Value::Enum(v) => *v == rhs.as_enum(), Value::Union(lhs_tag, lhs_pos) => { let (rhs_tag, rhs_pos) = rhs.as_union(); diff --git a/src/protocol/input_source.rs b/src/protocol/input_source.rs index b3eb530d5c0239c2995b518292930027916e8df5..0c26d2f4f8e291ffc1998c2eb5f6dae7ede85787 100644 --- a/src/protocol/input_source.rs +++ b/src/protocol/input_source.rs @@ -213,7 +213,7 @@ pub enum ContextKind { } #[derive(Debug)] -pub struct ParseErrorStatement { +pub struct ErrorStatement { pub(crate) statement_kind: StatementKind, pub(crate) context_kind: ContextKind, pub(crate) start_line: u32, @@ -225,7 +225,7 @@ pub struct ParseErrorStatement { pub(crate) message: String, } -impl ParseErrorStatement { +impl ErrorStatement { fn from_source_at_pos(statement_kind: StatementKind, source: &InputSource, position: InputPosition, message: String) -> Self { // Seek line start and end let line_start = source.lookup_line_start_offset(position.line); @@ -247,7 +247,7 @@ impl ParseErrorStatement { } } - fn from_source_at_span(statement_kind: StatementKind, source: &InputSource, span: InputSpan, message: String) -> Self { + pub(crate) fn from_source_at_span(statement_kind: StatementKind, source: &InputSource, span: InputSpan, message: String) -> Self { debug_assert!(span.end.line >= span.begin.line); debug_assert!(span.end.offset >= span.begin.offset); @@ -285,7 +285,7 @@ impl ParseErrorStatement { } } -impl fmt::Display for ParseErrorStatement { +impl fmt::Display for ErrorStatement { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Write kind of statement and message match self.statement_kind { @@ -402,7 +402,7 @@ impl fmt::Display for ParseErrorStatement { #[derive(Debug)] pub struct ParseError { - pub(crate) statements: Vec + pub(crate) statements: Vec } impl fmt::Display for ParseError { @@ -423,36 +423,36 @@ impl fmt::Display for ParseError { impl ParseError { pub fn new_error_at_pos(source: &InputSource, position: InputPosition, message: String) -> Self { - Self{ statements: vec!(ParseErrorStatement::from_source_at_pos( + Self{ statements: vec!(ErrorStatement::from_source_at_pos( StatementKind::Error, source, position, message )) } } pub fn new_error_str_at_pos(source: &InputSource, position: InputPosition, message: &str) -> Self { - Self{ statements: vec!(ParseErrorStatement::from_source_at_pos( + Self{ statements: vec!(ErrorStatement::from_source_at_pos( StatementKind::Error, source, position, message.to_string() )) } } pub fn new_error_at_span(source: &InputSource, span: InputSpan, message: String) -> Self { - Self{ statements: vec!(ParseErrorStatement::from_source_at_span( + Self{ statements: vec!(ErrorStatement::from_source_at_span( StatementKind::Error, source, span, message )) } } pub fn new_error_str_at_span(source: &InputSource, span: InputSpan, message: &str) -> Self { - Self{ statements: vec!(ParseErrorStatement::from_source_at_span( + Self{ statements: vec!(ErrorStatement::from_source_at_span( StatementKind::Error, source, span, message.to_string() )) } } pub fn with_at_pos(mut self, error_type: StatementKind, source: &InputSource, position: InputPosition, message: String) -> Self { - self.statements.push(ParseErrorStatement::from_source_at_pos(error_type, source, position, message)); + self.statements.push(ErrorStatement::from_source_at_pos(error_type, source, position, message)); self } pub fn with_at_span(mut self, error_type: StatementKind, source: &InputSource, span: InputSpan, message: String) -> Self { - self.statements.push(ParseErrorStatement::from_source_at_span(error_type, source, span, message.to_string())); + self.statements.push(ErrorStatement::from_source_at_span(error_type, source, span, message.to_string())); self } diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 2747d065b40721b46e1792c71f730f561f766aaf..12f5acc3386ab4f83bafd844bfbdbd471661953e 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -7,18 +7,27 @@ mod parser; pub(crate) mod ast; pub(crate) mod ast_printer; +use std::sync::Mutex; + +use crate::collections::{StringPool, StringRef}; use crate::common::*; use crate::protocol::ast::*; use crate::protocol::eval::*; use crate::protocol::input_source::*; use crate::protocol::parser::*; +/// A protocol description module +pub struct Module { + pub(crate) source: InputSource, + pub(crate) root_id: RootId, + pub(crate) name: Option>, +} /// Description of a protocol object, used to configure new connectors. #[repr(C)] pub struct ProtocolDescription { + modules: Vec, heap: Heap, - source: InputSource, - root: RootId, + pool: Mutex, } #[derive(Debug, Clone)] pub(crate) struct ComponentState { @@ -51,28 +60,46 @@ impl ProtocolDescription { } debug_assert_eq!(parser.modules.len(), 1, "only supporting one module here for now"); - let module = parser.modules.remove(0); - let root = module.root_id; - let source = module.source; - return Ok(ProtocolDescription { heap: parser.heap, source, root }); + let modules: Vec = parser.modules.into_iter() + .map(|module| Module{ + source: module.source, + root_id: module.root_id, + name: module.name.map(|(_, name)| name) + }) + .collect(); + + return Ok(ProtocolDescription { + modules, + heap: parser.heap, + pool: Mutex::new(parser.string_pool), + }); } pub(crate) fn component_polarities( &self, + module_name: &[u8], identifier: &[u8], ) -> Result, AddComponentError> { use AddComponentError::*; - let h = &self.heap; - let root = &h[self.root]; - let def = root.get_definition_ident(h, identifier); + + let module_root = self.lookup_module_root(module_name); + if module_root.is_none() { + return Err(AddComponentError::NoSuchModule); + } + let module_root = module_root.unwrap(); + + let root = &self.heap[module_root]; + let def = root.get_definition_ident(&self.heap, identifier); if def.is_none() { return Err(NoSuchComponent); } - let def = &h[def.unwrap()]; + + let def = &self.heap[def.unwrap()]; if !def.is_component() { return Err(NoSuchComponent); } + for ¶m in def.parameters().iter() { - let param = &h[param]; + let param = &self.heap[param]; let first_element = ¶m.parser_type.elements[0]; match first_element.variant { @@ -82,9 +109,10 @@ impl ProtocolDescription { } } } + let mut result = Vec::new(); for ¶m in def.parameters().iter() { - let param = &h[param]; + let param = &self.heap[param]; let first_element = ¶m.parser_type.elements[0]; if first_element.variant == ParserTypeVariant::Input { @@ -98,18 +126,34 @@ impl ProtocolDescription { Ok(result) } // expects port polarities to be correct - pub(crate) fn new_component(&self, identifier: &[u8], ports: &[PortId]) -> ComponentState { + pub(crate) fn new_component(&self, module_name: &[u8], identifier: &[u8], ports: &[PortId]) -> ComponentState { let mut args = Vec::new(); - for (&x, y) in ports.iter().zip(self.component_polarities(identifier).unwrap()) { + for (&x, y) in ports.iter().zip(self.component_polarities(module_name, identifier).unwrap()) { match y { Polarity::Getter => args.push(Value::Input(x)), Polarity::Putter => args.push(Value::Output(x)), } } - let h = &self.heap; - let root = &h[self.root]; - let def = root.get_definition_ident(h, identifier).unwrap(); - ComponentState { prompt: Prompt::new(h, def, ValueGroup::new_stack(args)) } + + let module_root = self.lookup_module_root(module_name).unwrap(); + let root = &self.heap[module_root]; + let def = root.get_definition_ident(&self.heap, identifier).unwrap(); + ComponentState { prompt: Prompt::new(&self.heap, def, ValueGroup::new_stack(args)) } + } + + fn lookup_module_root(&self, module_name: &[u8]) -> Option { + for module in self.modules.iter() { + match &module.name { + Some(name) => if name.as_bytes() == module_name { + return Some(module.root_id); + }, + None => if module_name.is_empty() { + return Some(module.root_id); + } + } + } + + return None; } } impl ComponentState { @@ -120,9 +164,12 @@ impl ComponentState { ) -> NonsyncBlocker { let mut context = EvalContext::Nonsync(context); loop { - let result = self.prompt.step(&pd.heap, &mut context); + let result = self.prompt.step(&pd.heap, &pd.modules, &mut context); match result { - Err(_) => todo!("error handling"), + Err(err) => { + println!("Evaluation error:\n{}", err); + panic!("proper error handling when component fails"); + }, Ok(cont) => match cont { EvalContinuation::Stepping => continue, EvalContinuation::Inconsistent => return NonsyncBlocker::Inconsistent, @@ -175,9 +222,12 @@ impl ComponentState { ) -> SyncBlocker { let mut context = EvalContext::Sync(context); loop { - let result = self.prompt.step(&pd.heap, &mut context); + let result = self.prompt.step(&pd.heap, &pd.modules, &mut context); match result { - Err(_) => todo!("error handling"), + Err(err) => { + println!("Evaluation error:\n{}", err); + panic!("proper error handling when component fails"); + }, Ok(cont) => match cont { EvalContinuation::Stepping => continue, EvalContinuation::Inconsistent => return SyncBlocker::Inconsistent, diff --git a/src/protocol/parser/pass_definitions.rs b/src/protocol/parser/pass_definitions.rs index 4c2a8f46d2e9d4ca369bfa5b84f6bd5066d71073..d7855320b863892fe06bb07d16b79105557f6c89 100644 --- a/src/protocol/parser/pass_definitions.rs +++ b/src/protocol/parser/pass_definitions.rs @@ -396,7 +396,7 @@ impl PassDefinitions { let end_if = ctx.heap.alloc_end_if_statement(|this| EndIfStatement{ this, start_if: id, next: StatementId::new_invalid() }); - section.push(id.upcast()); + section.push(end_if.upcast()); let if_stmt = &mut ctx.heap[id]; if_stmt.end_if = end_if; @@ -407,7 +407,7 @@ impl PassDefinitions { let end_while = ctx.heap.alloc_end_while_statement(|this| EndWhileStatement{ this, start_while: id, next: StatementId::new_invalid() }); - section.push(id.upcast()); + section.push(end_while.upcast()); let while_stmt = &mut ctx.heap[id]; while_stmt.end_while = end_while; @@ -424,6 +424,7 @@ impl PassDefinitions { let end_sync = ctx.heap.alloc_end_synchronous_statement(|this| EndSynchronousStatement{ this, start_sync: id, next: StatementId::new_invalid() }); + section.push(end_sync.upcast()); let sync_stmt = &mut ctx.heap[id]; sync_stmt.end_sync = end_sync; diff --git a/src/protocol/parser/pass_typing.rs b/src/protocol/parser/pass_typing.rs index 94f8f0026575ac6f8c04687afe91180408558ad9..3fed21e6c2d46a0a16b5c949f6c9d508b1a3e81b 100644 --- a/src/protocol/parser/pass_typing.rs +++ b/src/protocol/parser/pass_typing.rs @@ -45,6 +45,10 @@ /// 6. Investigate different ways of performing the type-on-type inference, /// maybe there is a better way then flattened trees + markers? +macro_rules! debug_log_enabled { + () => { false }; +} + macro_rules! debug_log { ($format:literal) => { enabled_debug_print!(false, "types", $format); @@ -146,7 +150,6 @@ impl InferenceTypePart { } fn is_concrete_number(&self) -> bool { - // TODO: @float use InferenceTypePart as ITP; match self { ITP::UInt8 | ITP::UInt16 | ITP::UInt32 | ITP::UInt64 | @@ -612,7 +615,7 @@ impl InferenceType { /// Performs the conversion of the inference type into a concrete type. /// By calling this function you must make sure that no unspecified types /// (e.g. Unknown or IntegerLike) exist in the type. - fn write_concrete_type(&self, concrete_type: &mut ConcreteType) { + fn write_concrete_type(&self, concrete_type: &mut ConcreteType, discard_marked_types: bool) { use InferenceTypePart as ITP; use ConcreteTypePart as CTP; @@ -626,10 +629,15 @@ impl InferenceType { let part = &self.parts[idx]; let converted_part = match part { ITP::MarkerDefinition(marker) => { - // Outer markers are converted to regular markers, we - // completely remove the type subtree that follows it - idx = InferenceType::find_subtree_end_idx(&self.parts, idx + 1); - concrete_type.parts.push(CTP::Marker(*marker)); + // When annotating the AST we keep the markers. When + // determining types for monomorphs we instead want to + // keep the type (not the markers) + if discard_marked_types { + idx = InferenceType::find_subtree_end_idx(&self.parts, idx + 1); + concrete_type.parts.push(CTP::Marker(*marker)); + } else { + idx += 1; + } continue; }, ITP::MarkerBody(_) => { @@ -875,7 +883,8 @@ pub(crate) struct PassTyping { expr_queued: HashSet, } -// TODO: @rename used for calls and struct literals, maybe union literals? +// TODO: @Rename, this is used for a lot of type inferencing. It seems like +// there is a different underlying architecture waiting to surface. struct ExtraData { /// Progression of polymorphic variables (if any) poly_vars: Vec, @@ -1008,6 +1017,17 @@ impl Visitor2 for PassTyping { debug_log!("{}", "-".repeat(50)); debug_log!("Visiting function '{}': {}", func_def.identifier.value.as_str(), id.0.index); + if debug_log_enabled!() { + debug_log!("Polymorphic variables:"); + for (idx, poly_var) in self.poly_vars.iter().enumerate() { + let mut infer_type_parts = Vec::new(); + for concrete_part in &poly_var.parts { + infer_type_parts.push(InferenceTypePart::from(*concrete_part)); + } + let infer_type = InferenceType::new(false, true, infer_type_parts); + debug_log!(" - [{:03}] {:?}", idx, infer_type.display_name(&ctx.heap)); + } + } debug_log!("{}", "-".repeat(50)); for param_id in func_def.parameters.clone() { @@ -1382,11 +1402,11 @@ impl PassTyping { if !already_checked { let concrete_type = ctx.heap[*expr_id].get_type_mut(); - expr_type.write_concrete_type(concrete_type); + expr_type.write_concrete_type(concrete_type, true); } else { if cfg!(debug_assertions) { let mut concrete_type = ConcreteType::default(); - expr_type.write_concrete_type(&mut concrete_type); + expr_type.write_concrete_type(&mut concrete_type, true); debug_assert_eq!(*ctx.heap[*expr_id].get_type(), concrete_type); } } @@ -1427,7 +1447,7 @@ impl PassTyping { } let mut concrete_type = ConcreteType::default(); - poly_type.write_concrete_type(&mut concrete_type); + poly_type.write_concrete_type(&mut concrete_type, false); monomorph_types.insert(poly_idx, concrete_type); } @@ -1463,7 +1483,7 @@ impl PassTyping { Literal::Struct(literal) => &literal.definition, Literal::Enum(literal) => &literal.definition, Literal::Union(literal) => &literal.definition, - _ => unreachable!("post-inference monomorph for non-struct, non-enum literal") + _ => unreachable!("post-inference monomorph for non-struct, non-enum, non-union literal") }; if !ctx.types.has_monomorph(definition_id, &monomorph_types) { ctx.types.add_monomorph(definition_id, monomorph_types); @@ -1785,7 +1805,7 @@ impl PassTyping { // Make sure if output is of T then subject is Array let (progress_expr, progress_subject) = - self.apply_equal2_constraint(ctx, upcast_id, upcast_id, 0, subject_id, 1)?; + self.apply_equal2_constraint(ctx, upcast_id, upcast_id, 0, subject_id, 0)?; debug_log!(" * After:"); @@ -2816,8 +2836,6 @@ impl PassTyping { // We already have an entry, this happens if our parent fixed // our type (e.g. we're used in a conditional expression's test) // but we have a different type. - // TODO: Is this ever called? Seems like it can't - debug_assert!(false, "I am actually called, my ID is {}", expr_id.index); let old_type = preexisting.get_mut(); if let SingleInferenceResult::Incompatible = InferenceType::infer_subtree_for_single_type( old_type, 0, &inference_type.parts, 0 diff --git a/src/protocol/parser/type_table.rs b/src/protocol/parser/type_table.rs index cdcee8542c0118e8bac44be0ab5a4851d51db122..4f3a355b7747bb149160ed9b2f80ec346348f7e8 100644 --- a/src/protocol/parser/type_table.rs +++ b/src/protocol/parser/type_table.rs @@ -327,7 +327,8 @@ impl TypeTable { let definition = &ctx.heap[definition_id]; let can_pop_breadcrumb = match definition { - // TODO: @cleanup Borrow rules hax + // Bit ugly, since we already have the definition, but we need + // to work around rust borrowing rules... Definition::Enum(_) => self.resolve_base_enum_definition(modules, ctx, root_id, definition_id), Definition::Union(_) => self.resolve_base_union_definition(modules, ctx, root_id, definition_id), Definition::Struct(_) => self.resolve_base_struct_definition(modules, ctx, root_id, definition_id), diff --git a/src/protocol/tests/eval_calls.rs b/src/protocol/tests/eval_calls.rs index c5ee0ea3f76e521b80950084514a5c802b7ba42f..5a261115429234916d32645a65ec6986d80741c8 100644 --- a/src/protocol/tests/eval_calls.rs +++ b/src/protocol/tests/eval_calls.rs @@ -10,7 +10,7 @@ fn test_function_call() { return add_two(5); } ").for_function("foo", |f| { - f.call(Some(Value::UInt32(7))); + f.call_ok(Some(Value::UInt32(7))); }); println!("\n\n\n\n\n\n\n"); @@ -25,6 +25,6 @@ fn test_function_call() { auto result = add_two(initial); return initial == 5 && result == 7; }").for_function("foo", |f| { - f.call(Some(Value::Bool(true))); + f.call_ok(Some(Value::Bool(true))); }); } \ No newline at end of file diff --git a/src/protocol/tests/eval_operators.rs b/src/protocol/tests/eval_operators.rs index 96393fcf8ad85e10409432e24801b1080918ba02..9c66802dfa223e55970add4d91992002e34d9bb3 100644 --- a/src/protocol/tests/eval_operators.rs +++ b/src/protocol/tests/eval_operators.rs @@ -16,7 +16,7 @@ fn test_assignment_operators() { fn perform_test(name: &str, source: String, expected_value: Value) { Tester::new_single_source_expect_ok(name, source) .for_function("foo", move |f| { - f.call(Some(expected_value)); + f.call_ok(Some(expected_value)); }); } @@ -100,7 +100,7 @@ fn test_binary_integer_operators() { fn perform_test(test_name: &str, value_type: &str, code: &str, expected_value: Value) { Tester::new_single_source_expect_ok(test_name, construct_source(value_type, code)) .for_function("foo", move |f| { - f.call(Some(expected_value)); + f.call_ok(Some(expected_value)); }); } diff --git a/src/protocol/tests/eval_silly.rs b/src/protocol/tests/eval_silly.rs index ef3688cd7673147e0bc939fc03e6f908ebef4d32..4cd310ac8d269c6a857e573d07e789272e052cfc 100644 --- a/src/protocol/tests/eval_silly.rs +++ b/src/protocol/tests/eval_silly.rs @@ -54,45 +54,111 @@ fn test_concatenate_operator() { auto has_correct_fields = check_values(total, 3, 3, 4) && check_values(total, 4, 1, 2); - return is_equal && !is_not_equal && has_correct_fields; + auto array_check = lhs == rhs && total == total; + return is_equal && !is_not_equal && has_correct_fields && array_check; } " ).for_function("foo", |f| { - f.call(Some(Value::Bool(true))); + f.call_ok(Some(Value::Bool(true))); }); } #[test] -fn test_struct_fields() { - Tester::new_single_source_expect_ok("struct field access", -" -struct Nester { - T v, -} +fn test_slicing_magic() { + // TODO: Reimplement polymorphism, then retest with polymorphic types + Tester::new_single_source_expect_ok("slicing", " + struct Holder { + u32[] left, + u32[] right, + } -func make(T inner) -> Nester { - return Nester{ v: inner }; -} + func create_array(u32 first_index, u32 last_index) -> u32[] { + auto result = {}; + while (first_index < last_index) { + // Absolutely rediculous, but we don't have builtin array functions yet... + result = result @ { first_index }; + first_index += 1; + } + return result; + } + + func create_holder(u32 left_first, u32 left_last, u32 right_first, u32 right_last) -> Holder { + return Holder{ + left: create_array(left_first, left_last), + right: create_array(right_first, right_last) + }; + } -func modify(Nester outer, T inner) -> Nester { - outer.v = inner; - return outer; + // Another silly thing, we first slice the full thing. Then subslice a single + // element, then concatenate. We always return an array of two things. + func slicing_magic(Holder holder, u32 left_base, u32 left_amount, u32 right_base, u32 right_amount) -> u32[] { + auto left = holder.left[left_base..left_base + left_amount]; + auto right = holder.right[right_base..right_base + right_amount]; + return left[0..1] @ right[0..1]; + } + + func foo() -> u32 { + // left array will be [0, 1, 2, ...] and right array will be [2, 3, 4, ...] + auto created = create_holder(0, 5, 2, 8); + + // in a convoluted fashion select the value 3 from the lhs and the value 3 from the rhs + auto result = slicing_magic(create_holder(0, 5, 2, 8), 3, 2, 1, 2); + + // and return 3 + 3 + return result[0] + result[1]; + } + ").for_function("foo", |f| { + f.call_ok(Some(Value::UInt32(6))); + }); } -func foo() -> bool { - // Single depth modification - auto original1 = make(5); - auto modified1 = modify(original1, 2); - auto success1 = original1.v == 5 && modified1.v == 2; +#[test] +fn test_struct_fields() { + Tester::new_single_source_expect_ok("struct field access", " + struct Nester { + T v, + } + + func make(T inner) -> Nester { + return Nester{ v: inner }; + } + + func modify(Nester outer, T inner) -> Nester { + outer.v = inner; + return outer; + } + + func foo() -> bool { + // Single depth modification + auto original1 = make(5); + auto modified1 = modify(original1, 2); + auto success1 = original1.v == 5 && modified1.v == 2; - // Multiple levels of modification - auto original2 = make(make(make(make(true)))); - auto modified2 = modify(original2.v, make(make(false))); // strip one Nester level - auto success2 = original2.v.v.v.v == true && modified2.v.v.v == false; + // Multiple levels of modification + auto original2 = make(make(make(make(true)))); + auto modified2 = modify(original2.v, make(make(false))); // strip one Nester level + auto success2 = original2.v.v.v.v == true && modified2.v.v.v == false; - return success1 && success2; + return success1 && success2; + } + ").for_function("foo", |f| { + f.call_ok(Some(Value::Bool(true))); + }); } -").for_function("foo", |f| { - f.call(Some(Value::Bool(true))); + +#[test] +fn test_index_error() { + Tester::new_single_source_expect_ok("indexing error", " + func check_array(u32[] vals, u32 idx) -> u32 { + return vals[idx]; + } + + func foo() -> u32 { + auto array = {1, 2, 3, 4, 5, 6, 7}; + check_array(array, 7); + return array[0]; + } + ").for_function("foo", |f| { + f.call_err("index 7 is out of bounds: array length is 7"); }); } \ No newline at end of file diff --git a/src/protocol/tests/parser_imports.rs b/src/protocol/tests/parser_imports.rs index 7db92555292b4e29f855c9c2bf7fd63f9e7ad59b..811d2cdb0736b3ddd02b17e8300fc9176c1bc686 100644 --- a/src/protocol/tests/parser_imports.rs +++ b/src/protocol/tests/parser_imports.rs @@ -83,18 +83,17 @@ fn test_single_symbol_import() { .compile() .expect_ok(); - // TODO: Re-enable once std lib is properly implemented - // Tester::new("import all") - // .with_source(" - // #module external - // struct Foo { s32 field } - // ") - // .with_source(" - // import external::*; - // s32 caller() { return Foo{field:0}.field; } - // ") - // .compile() - // .expect_ok(); + Tester::new("import all") + .with_source(" + #module external + struct Foo { s32 field } + ") + .with_source(" + import external::*; + func caller() -> s32 { return Foo{field:0}.field; } + ") + .compile() + .expect_ok(); } #[test] diff --git a/src/protocol/tests/parser_inference.rs b/src/protocol/tests/parser_inference.rs index bfc15f41d6b1fa9c9ba7ad9b6ff7b2a79c124534..cef508c29080d7e35397d131699fbdb975610550 100644 --- a/src/protocol/tests/parser_inference.rs +++ b/src/protocol/tests/parser_inference.rs @@ -391,7 +391,7 @@ fn test_failed_polymorph_inference() { .assert_msg_has(2, "inferred it to 's32'"); }); - // TODO: Needs better error messages anyway, but this failed before + // Silly regression test Tester::new_single_source_expect_err( "nested field access inference mismatch", " diff --git a/src/protocol/tests/parser_validation.rs b/src/protocol/tests/parser_validation.rs index bc6d059c5e2a3de48e42595a44266bffbce77247..14c1bb9856a7ad5c1a8fa2b9bc1ab77803786980 100644 --- a/src/protocol/tests/parser_validation.rs +++ b/src/protocol/tests/parser_validation.rs @@ -1,7 +1,6 @@ /// parser_validation.rs /// /// Simple tests for the validation phase -/// TODO: If semicolon behind struct definition: should be fine... use super::*; diff --git a/src/protocol/tests/utils.rs b/src/protocol/tests/utils.rs index 36a068cde8afa9a987d60c0cc7a1d09542add123..aacf1f8f77596c5a4cca46a0e3b9a29163f9dafb 100644 --- a/src/protocol/tests/utils.rs +++ b/src/protocol/tests/utils.rs @@ -1,9 +1,10 @@ use crate::collections::StringPool; use crate::protocol::{ + Module, ast::*, input_source::*, parser::{ - *, + Parser, type_table::TypeTable, symbol_table::SymbolTable, token_parsing::*, @@ -132,7 +133,11 @@ impl AstOkTester { fn new(test_name: String, parser: Parser) -> Self { Self { test_name, - modules: parser.modules, + modules: parser.modules.into_iter().map(|module| Module{ + source: module.source, + root_id: module.root_id, + name: module.name.map(|(_, name)| name) + }).collect(), heap: parser.heap, symbols: parser.symbol_table, types: parser.type_table, @@ -573,26 +578,28 @@ impl<'a> FunctionTester<'a> { self } - pub(crate) fn call(self, expected_result: Option) -> Self { + pub(crate) fn call_ok(self, expected_result: Option) -> Self { use crate::protocol::*; use crate::runtime::*; - let mut prompt = Prompt::new(&self.ctx.heap, self.def.this.upcast(), ValueGroup::new_stack(Vec::new())); - let mut call_context = EvalContext::None; - loop { - let result = prompt.step(&self.ctx.heap, &mut call_context).unwrap(); - match result { - EvalContinuation::Stepping => {}, - _ => break, + let (prompt, result) = self.eval_until_end(); + match result { + Ok(_) => { + assert!( + prompt.store.stack.len() > 0, // note: stack never shrinks + "[{}] No value on stack after calling function for {}", + self.ctx.test_name, self.assert_postfix() + ); + }, + Err(err) => { + assert!( + false, + "[{}] Expected call to succeed, but got {:?} for {}", + self.ctx.test_name, err, self.assert_postfix() + ) } } - assert!( - prompt.store.stack.len() > 0, // note: stack never shrinks - "[{}] No value on stack after calling function for {}", - self.ctx.test_name, self.assert_postfix() - ); - if let Some(expected_result) = expected_result { debug_assert!(expected_result.get_heap_pos().is_none(), "comparing against heap thingamajigs is not yet implemented"); assert!( @@ -605,6 +612,49 @@ impl<'a> FunctionTester<'a> { self } + // Keeping this simple for now, will likely change + pub(crate) fn call_err(self, expected_result: &str) -> Self { + use crate::protocol::*; + use crate::runtime::*; + + let (_, result) = self.eval_until_end(); + match result { + Ok(_) => { + assert!( + false, + "[{}] Expected an error, but evaluation finished successfully for {}", + self.ctx.test_name, self.assert_postfix() + ); + }, + Err(err) => { + println!("DEBUG: Got evaluation error:\n{}", err); + debug_assert_eq!(err.statements.len(), 1); + assert!( + err.statements[0].message.contains(&expected_result), + "[{}] Expected error message to contain '{}', but it was '{}' for {}", + self.ctx.test_name, expected_result, err.statements[0].message, self.assert_postfix() + ); + } + } + + self + } + + fn eval_until_end(&self) -> (Prompt, Result) { + use crate::protocol::*; + use crate::runtime::*; + + let mut prompt = Prompt::new(&self.ctx.heap, self.def.this.upcast(), ValueGroup::new_stack(Vec::new())); + let mut call_context = EvalContext::None; + loop { + let result = prompt.step(&self.ctx.heap, &self.ctx.modules, &mut call_context); + match result { + Ok(EvalContinuation::Stepping) => {}, + _ => return (prompt, result), + } + } + } + fn assert_postfix(&self) -> String { format!("Function{{ name: {} }}", self.def.identifier.value.as_str()) } diff --git a/src/runtime/error.rs b/src/runtime/error.rs index 1284844b8dbbeb6f808f3c37c23095758a84ea32..59bca37f40dc3ddf0db08b6f5c800ded8618b10c 100644 --- a/src/runtime/error.rs +++ b/src/runtime/error.rs @@ -17,6 +17,7 @@ pub enum ConnectError { #[derive(Eq, PartialEq, Copy, Clone, Debug)] pub enum AddComponentError { DuplicatePort(PortId), + NoSuchModule, NoSuchComponent, NonPortTypeParameters, CannotMovePort(PortId), diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 2cebca9b308e8d556f4b84a1385d90d50715f8a5..95bc3e85c6f4df6c9a221764f5eb8ad46745159e 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -654,6 +654,7 @@ impl Connector { /// This function panics if the connector's (large) component id space is exhausted. pub fn add_component( &mut self, + module_name: &[u8], identifier: &[u8], ports: &[PortId], ) -> Result<(), AddComponentError> { @@ -663,7 +664,7 @@ impl Connector { if let Some(port) = duplicate_port(ports) { return Err(Ace::DuplicatePort(port)); } - let expected_polarities = cu.proto_description.component_polarities(identifier)?; + let expected_polarities = cu.proto_description.component_polarities(module_name, identifier)?; if expected_polarities.len() != ports.len() { return Err(Ace::WrongNumberOfParamaters { expected: expected_polarities.len() }); } @@ -680,7 +681,7 @@ impl Connector { // create a new component and identifier let Connector { phased, unphased: cu } = self; let new_cid = cu.ips.id_manager.new_component_id(); - cu.proto_components.insert(new_cid, cu.proto_description.new_component(identifier, ports)); + cu.proto_components.insert(new_cid, cu.proto_description.new_component(module_name, identifier, ports)); // update the ownership of moved ports for port in ports.iter() { match cu.ips.port_info.map.get_mut(port) { diff --git a/src/runtime/tests.rs b/src/runtime/tests.rs index d95b032827b69cdbfe2b1e33fc7a2df77f1ec0b9..dca5f781d734bc85e5d1d00f946c05c7b69240a6 100644 --- a/src/runtime/tests.rs +++ b/src/runtime/tests.rs @@ -89,7 +89,7 @@ fn new_sync() { let test_log_path = Path::new("./logs/new_sync"); let mut c = file_logged_connector(0, test_log_path); let [o, i] = c.new_port_pair(); - c.add_component(b"sync", &[i, o]).unwrap(); + c.add_component(b"", b"sync", &[i, o]).unwrap(); } #[test] @@ -340,7 +340,7 @@ fn cannot_use_moved_ports() { let test_log_path = Path::new("./logs/cannot_use_moved_ports"); let mut c = file_logged_connector(0, test_log_path); let [p, g] = c.new_port_pair(); - c.add_component(b"sync", &[g, p]).unwrap(); + c.add_component(b"", b"sync", &[g, p]).unwrap(); c.connect(SEC1).unwrap(); c.put(p, TEST_MSG.clone()).unwrap_err(); c.get(g).unwrap_err(); @@ -356,7 +356,7 @@ fn sync_sync() { let mut c = file_logged_connector(0, test_log_path); let [p0, g0] = c.new_port_pair(); let [p1, g1] = c.new_port_pair(); - c.add_component(b"sync", &[g0, p1]).unwrap(); + c.add_component(b"", b"sync", &[g0, p1]).unwrap(); c.connect(SEC1).unwrap(); c.put(p0, TEST_MSG.clone()).unwrap(); c.get(g1).unwrap(); @@ -408,7 +408,7 @@ fn distributed_msg_bounce() { c.new_net_port(Putter, sock_addrs[0], Active).unwrap(), c.new_net_port(Getter, sock_addrs[1], Active).unwrap(), ]; - c.add_component(b"sync", &[g, p]).unwrap(); + c.add_component(b"", b"sync", &[g, p]).unwrap(); c.connect(SEC1).unwrap(); c.sync(SEC1).unwrap(); }); @@ -572,7 +572,7 @@ fn together() { let p2 = c.new_net_port(Getter, sock_addrs[0], Passive).unwrap(); let p3 = c.new_net_port(Putter, sock_addrs[1], Active).unwrap(); let [p4, p5] = c.new_port_pair(); - c.add_component(b"together", &[p1, p2, p3, p4]).unwrap(); + c.add_component(b"", b"together", &[p1, p2, p3, p4]).unwrap(); c.connect(SEC1).unwrap(); c.put(p0, TEST_MSG.clone()).unwrap(); c.get(p5).unwrap(); @@ -585,7 +585,7 @@ fn together() { let p2 = c.new_net_port(Getter, sock_addrs[1], Passive).unwrap(); let p3 = c.new_net_port(Putter, sock_addrs[0], Active).unwrap(); let [p4, p5] = c.new_port_pair(); - c.add_component(b"together", &[p1, p2, p3, p4]).unwrap(); + c.add_component(b"", b"together", &[p1, p2, p3, p4]).unwrap(); c.connect(SEC1).unwrap(); c.put(p0, TEST_MSG.clone()).unwrap(); c.get(p5).unwrap(); @@ -877,7 +877,7 @@ fn ac_not_b() { let p1 = c.new_net_port(Getter, sock_addrs[1], Passive).unwrap(); let [a, b] = c.new_port_pair(); - c.add_component(b"ac_not_b", &[p0, p1, a]).unwrap(); + c.add_component(b"", b"ac_not_b", &[p0, p1, a]).unwrap(); c.connect(SEC1).unwrap(); @@ -985,7 +985,7 @@ fn pdl_reo_fifo1full() { let mut c = file_logged_configured_connector(0, test_log_path, Arc::new(pd)); let [_p0, g0] = c.new_port_pair(); let [p1, g1] = c.new_port_pair(); - c.add_component(b"fifo1full", &[g0, p1]).unwrap(); + c.add_component(b"", b"fifo1full", &[g0, p1]).unwrap(); c.connect(None).unwrap(); c.get(g1).unwrap(); c.sync(None).unwrap(); @@ -1008,7 +1008,7 @@ fn pdl_msg_consensus() { let mut c = file_logged_configured_connector(0, test_log_path, Arc::new(pd)); let [p0, g0] = c.new_port_pair(); let [p1, g1] = c.new_port_pair(); - c.add_component(b"msgconsensus", &[g0, g1]).unwrap(); + c.add_component(b"", b"msgconsensus", &[g0, g1]).unwrap(); c.connect(None).unwrap(); c.put(p0, Payload::from(b"HELLO" as &[_])).unwrap(); c.put(p1, Payload::from(b"HELLO" as &[_])).unwrap(); @@ -1043,7 +1043,7 @@ fn sequencer3_prim() { let [p0, g0] = c.new_port_pair(); let [p1, g1] = c.new_port_pair(); let [p2, g2] = c.new_port_pair(); - c.add_component(b"sequencer3", &[p0, p1, p2]).unwrap(); + c.add_component(b"", b"sequencer3", &[p0, p1, p2]).unwrap(); c.connect(None).unwrap(); let which_of_three = move |c: &mut Connector| { @@ -1109,7 +1109,7 @@ fn sequencer3_comp() { let [p0, g0] = c.new_port_pair(); let [p1, g1] = c.new_port_pair(); let [p2, g2] = c.new_port_pair(); - c.add_component(b"sequencer3", &[p0, p1, p2]).unwrap(); + c.add_component(b"", b"sequencer3", &[p0, p1, p2]).unwrap(); c.connect(None).unwrap(); let which_of_three = move |c: &mut Connector| { @@ -1167,7 +1167,7 @@ fn xrouter_prim() { let [p0, g0] = c.new_port_pair(); let [p1, g1] = c.new_port_pair(); let [p2, g2] = c.new_port_pair(); - c.add_component(b"xrouter", &[g0, p1, p2]).unwrap(); + c.add_component(b"", b"xrouter", &[g0, p1, p2]).unwrap(); c.connect(None).unwrap(); let now = std::time::Instant::now(); @@ -1235,7 +1235,7 @@ fn xrouter_comp() { let [p0, g0] = c.new_port_pair(); let [p1, g1] = c.new_port_pair(); let [p2, g2] = c.new_port_pair(); - c.add_component(b"xrouter", &[g0, p1, p2]).unwrap(); + c.add_component(b"", b"xrouter", &[g0, p1, p2]).unwrap(); c.connect(None).unwrap(); let now = std::time::Instant::now(); @@ -1274,7 +1274,7 @@ fn count_stream() { // setup a session between (a) native, and (b) sequencer3, connected by 3 ports. let [p0, g0] = c.new_port_pair(); - c.add_component(b"count_stream", &[p0]).unwrap(); + c.add_component(b"", b"count_stream", &[p0]).unwrap(); c.connect(None).unwrap(); for expecting in 0u8..16 { @@ -1304,7 +1304,7 @@ fn for_msg_byte() { // setup a session between (a) native, and (b) sequencer3, connected by 3 ports. let [p0, g0] = c.new_port_pair(); - c.add_component(b"for_msg_byte", &[p0]).unwrap(); + c.add_component(b"", b"for_msg_byte", &[p0]).unwrap(); c.connect(None).unwrap(); for expecting in 0u8..8 { @@ -1344,7 +1344,7 @@ fn eq_causality() { */ let [p0, g0] = c.new_port_pair(); let [p1, g1] = c.new_port_pair(); - c.add_component(b"eq", &[g0, g1, p1]).unwrap(); + c.add_component(b"", b"eq", &[g0, g1, p1]).unwrap(); /* V--------. @@ -1353,7 +1353,7 @@ fn eq_causality() { */ let [p2, g2] = c.new_port_pair(); let [p3, g3] = c.new_port_pair(); - c.add_component(b"eq", &[g3, g2, p3]).unwrap(); + c.add_component(b"", b"eq", &[g3, g2, p3]).unwrap(); c.connect(None).unwrap(); for _ in 0..4 { @@ -1432,7 +1432,7 @@ fn eq_no_causality() { */ let [p0, g0] = c.new_port_pair(); let [p1, g1] = c.new_port_pair(); - c.add_component(b"eq", &[g0, g1, p1]).unwrap(); + c.add_component(b"", b"eq", &[g0, g1, p1]).unwrap(); /* V--------. @@ -1441,7 +1441,7 @@ fn eq_no_causality() { */ let [p2, g2] = c.new_port_pair(); let [p3, g3] = c.new_port_pair(); - c.add_component(b"eq", &[g3, g2, p3]).unwrap(); + c.add_component(b"", b"eq", &[g3, g2, p3]).unwrap(); c.connect(None).unwrap(); for _ in 0..32 {