diff --git a/src/protocol/inputsource.rs b/src/protocol/inputsource.rs index dda400c8befa547e4f817aa9f675d863c7835ad9..b070fe7e5d57d55cb2e4a28692ddf265c1364feb 100644 --- a/src/protocol/inputsource.rs +++ b/src/protocol/inputsource.rs @@ -151,7 +151,7 @@ impl fmt::Display for InputSource { pub struct InputPosition { line: usize, column: usize, - offset: usize, + pub(crate) offset: usize, } impl InputPosition { diff --git a/src/protocol/parser/type_resolver.rs b/src/protocol/parser/type_resolver.rs index 32fb18786d3a7e29dd2d7b904d0a85719538e96c..8026f5a77aeaa37785acaf06035c479848cfd828 100644 --- a/src/protocol/parser/type_resolver.rs +++ b/src/protocol/parser/type_resolver.rs @@ -655,8 +655,13 @@ impl InferenceType { use InferenceTypePart as ITP; match &parts[idx] { - ITP::MarkerDefinition(_) | ITP::MarkerBody(_) => { - idx = Self::write_display_name(buffer, heap, parts, idx + 1) + ITP::MarkerDefinition(thing) => { + buffer.push_str(&format!("{{D:{}}}", *thing)); + idx = Self::write_display_name(buffer, heap, parts, idx + 1); + }, + ITP::MarkerBody(thing) => { + buffer.push_str(&format!("{{B:{}}}", *thing)); + idx = Self::write_display_name(buffer, heap, parts, idx + 1); }, ITP::Unknown => buffer.push_str("?"), ITP::NumberLike => buffer.push_str("num?"), @@ -1860,14 +1865,14 @@ impl TypeResolvingVisitor { let signature_type: *mut _ = &mut poly_data.embedded[0]; let subject_type: *mut _ = self.expr_types.get_mut(&subject_id).unwrap(); - let progress_subject = Self::apply_equal2_polyvar_constraint( + let progress_subject = Self::apply_equal2_polyvar_constraint(&ctx.heap, poly_data, &poly_progress, signature_type, subject_type ); let signature_type: *mut _ = &mut poly_data.returned; let expr_type: *mut _ = self.expr_types.get_mut(&upcast_id).unwrap(); - let progress_expr = Self::apply_equal2_polyvar_constraint( + let progress_expr = Self::apply_equal2_polyvar_constraint(&ctx.heap, poly_data, &poly_progress, signature_type, expr_type ); @@ -2006,7 +2011,7 @@ impl TypeResolvingVisitor { let field_expr_id = data.fields[field_idx].value; let field_type: *mut _ = self.expr_types.get_mut(&field_expr_id).unwrap(); - let progress_arg = Self::apply_equal2_polyvar_constraint( + let progress_arg = Self::apply_equal2_polyvar_constraint(&ctx.heap, extra, &poly_progress, signature_type, field_type ); @@ -2024,7 +2029,7 @@ impl TypeResolvingVisitor { let signature_type: *mut _ = &mut extra.returned; let expr_type: *mut _ = self.expr_types.get_mut(&upcast_id).unwrap(); - let progress_expr = Self::apply_equal2_polyvar_constraint( + let progress_expr = Self::apply_equal2_polyvar_constraint(&ctx.heap, extra, &poly_progress, signature_type, expr_type ); @@ -2109,6 +2114,9 @@ impl TypeResolvingVisitor { // reapplying the polymorph type to each argument type and the return // type should always succeed. debug_log!(" * During (reinferring from progressed polyvars):"); + for (poly_idx, poly_var) in extra.poly_vars.iter().enumerate() { + debug_log!(" - Poly {} | sig: {}", poly_idx, poly_var.display_name(&ctx.heap)); + } // TODO: @performance If the algorithm is changed to be more "on demand // argument re-evaluation", instead of "all-argument re-evaluation", // then this is no longer true @@ -2117,7 +2125,7 @@ impl TypeResolvingVisitor { let arg_expr_id = expr.arguments[arg_idx]; let arg_type: *mut _ = self.expr_types.get_mut(&arg_expr_id).unwrap(); - let progress_arg = Self::apply_equal2_polyvar_constraint( + let progress_arg = Self::apply_equal2_polyvar_constraint(&ctx.heap, extra, &poly_progress, signature_type, arg_type ); @@ -2136,7 +2144,7 @@ impl TypeResolvingVisitor { let signature_type: *mut _ = &mut extra.returned; let ret_type: *mut _ = self.expr_types.get_mut(&upcast_id).unwrap(); - let progress_ret = Self::apply_equal2_polyvar_constraint( + let progress_ret = Self::apply_equal2_polyvar_constraint(&ctx.heap, extra, &poly_progress, signature_type, ret_type ); debug_log!( @@ -2406,6 +2414,7 @@ impl TypeResolvingVisitor { /// /// This function returns true if the expression's type has been progressed fn apply_equal2_polyvar_constraint( + heap: &Heap, polymorph_data: &ExtraData, polymorph_progress: &HashSet, signature_type: *mut InferenceType, expr_type: *mut InferenceType ) -> bool { @@ -2425,6 +2434,7 @@ impl TypeResolvingVisitor { if polymorph_progress.contains(&poly_idx) { // Need to match subtrees let polymorph_type = &polymorph_data.poly_vars[poly_idx]; + debug_log!(" - DEBUG: Applying {} to '{}' from '{}'", polymorph_type.display_name(heap), InferenceType::partial_display_name(heap, &signature_type.parts[start_idx..]), signature_type.display_name(heap)); let modified_at_marker = Self::apply_forced_constraint_types( signature_type, start_idx, &polymorph_type.parts, 0 diff --git a/src/protocol/tests/mod.rs b/src/protocol/tests/mod.rs index 813d5ff82042c0030b1215fb3ff6dc3ce714f9bb..016c773671e0a092fa43042f13ba6e8b94f3b48f 100644 --- a/src/protocol/tests/mod.rs +++ b/src/protocol/tests/mod.rs @@ -1,5 +1,6 @@ mod utils; mod lexer; mod parser_validation; +mod parser_inference; pub(crate) use utils::{Tester}; \ No newline at end of file diff --git a/src/protocol/tests/parser_inference.rs b/src/protocol/tests/parser_inference.rs index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..59eaf4f5d9eab7803804a32ed07fbca10c89fc95 100644 --- a/src/protocol/tests/parser_inference.rs +++ b/src/protocol/tests/parser_inference.rs @@ -0,0 +1,158 @@ +/// parser_inference.rs +/// +/// Simple tests for the type inferences + +use super::*; + +#[test] +fn test_integer_inference() { + Tester::new_single_source_expect_ok( + "by arguments", + " + int call(byte b, short s, int i, long l) { + auto b2 = b; + auto s2 = s; + auto i2 = i; + auto l2 = l; + return i2; + } + " + ).for_function("call", |f| { f + .for_variable("b2", |v| { v + .assert_parser_type("auto") + .assert_concrete_type("byte"); + }) + .for_variable("s2", |v| { v + .assert_parser_type("auto") + .assert_concrete_type("short"); + }) + .for_variable("i2", |v| { v + .assert_parser_type("auto") + .assert_concrete_type("int"); + }) + .for_variable("l2", |v| { v + .assert_parser_type("auto") + .assert_concrete_type("long"); + }); + }); + + Tester::new_single_source_expect_ok( + "by assignment", + " + int call() { + byte b1 = 0; short s1 = 0; int i1 = 0; long l1 = 0; + auto b2 = b1; + auto s2 = s1; + auto i2 = i1; + auto l2 = l1; + return 0; + }" + ).for_function("call", |f| { f + .for_variable("b2", |v| { v + .assert_parser_type("auto") + .assert_concrete_type("byte"); + }) + .for_variable("s2", |v| { v + .assert_parser_type("auto") + .assert_concrete_type("short"); + }) + .for_variable("i2", |v| { v + .assert_parser_type("auto") + .assert_concrete_type("int"); + }) + .for_variable("l2", |v| { v + .assert_parser_type("auto") + .assert_concrete_type("long"); + }); + }); +} + +#[test] +fn test_struct_inference() { + // Tester::new_single_source_expect_ok( + // "by function calls", + // " + // struct Pair{ T1 first, T2 second } + // Pair construct(T1 first, T2 second) { + // return Pair{ first: first, second: second }; + // } + // int fix_t1(Pair arg) { return 0; } + // int fix_t2(Pair arg) { return 0; } + // int test() { + // auto first = 0; + // auto second = 1; + // auto pair = construct(first, second); + // fix_t1(pair); + // fix_t2(pair); + // return 0; + // } + // " + // ).for_function("test", |f| { f + // .for_variable("first", |v| { v + // .assert_parser_type("auto") + // .assert_concrete_type("byte"); + // }) + // .for_variable("second", |v| { v + // .assert_parser_type("auto") + // .assert_concrete_type("int"); + // }) + // .for_variable("pair", |v| { v + // .assert_parser_type("auto") + // .assert_concrete_type("Pair"); + // }); + // }); + + // Tester::new_single_source_expect_ok( + // "by field access", + // " + // struct Pair{ T1 first, T2 second } + // Pair construct(T1 first, T2 second) { + // return Pair{ first: first, second: second }; + // } + // int test() { + // auto first = 0; + // auto second = 1; + // auto pair = construct(first, second); + // byte assign_first = 0; + // long assign_second = 1; + // pair.first = assign_first; + // pair.second = assign_second; + // return 0; + // } + // " + // ).for_function("test", |f| { f + // .for_variable("first", |v| { v + // .assert_parser_type("auto") + // .assert_concrete_type("byte"); + // }) + // .for_variable("second", |v| { v + // .assert_parser_type("auto") + // .assert_concrete_type("long"); + // }) + // .for_variable("pair", |v| { v + // .assert_parser_type("auto") + // .assert_concrete_type("Pair"); + // }); + // }); + + Tester::new_single_source_expect_ok( + "by nested field access", + " + struct Node{ T1 l, T2 r } + Node construct(T1 l, T2 r) { return Node{ l: l, r: r }; } + int fix_poly(Node a) { return 0; } + int test() { + byte assigned = 0; + auto thing = construct(assigned, construct(0, 1)); + fix_poly(thing.r); + thing.r.r = assigned; + return 0; + } + ", + ).for_function("test", |f| { f + .for_variable("thing", |v| { v + .assert_parser_type("auto") + .assert_concrete_type("Pair>"); + }); + }); +} \ No newline at end of file diff --git a/src/protocol/tests/parser_validation.rs b/src/protocol/tests/parser_validation.rs index 6a414915213d4489b8501aab995fefb260e97779..112e6264b41ec11ead733918afbb01359437cad0 100644 --- a/src/protocol/tests/parser_validation.rs +++ b/src/protocol/tests/parser_validation.rs @@ -64,7 +64,7 @@ fn test_correct_struct_instance() { " ); - Tester::new_single_source_expr_ok( + Tester::new_single_source_expect_ok( "multiple fields, different explicit polymorph", " struct Pair{ T1 first, T2 second } diff --git a/src/protocol/tests/utils.rs b/src/protocol/tests/utils.rs index e530437f860aadee15fe7196090177f6668a3b49..0cc0c0a36cccf4e4cda17159e0317da0004e9ddc 100644 --- a/src/protocol/tests/utils.rs +++ b/src/protocol/tests/utils.rs @@ -75,6 +75,7 @@ impl AstTesterResult { AstTesterResult::Ok(v) => v, AstTesterResult::Err(err) => { let wrapped = ErrorTester{ test_name: &err.test_name, error: &err.error }; + println!("DEBUG: Full error:\n{}", &err.error); assert!( false, "[{}] Expected compilation to succeed, but it failed with {}", @@ -182,7 +183,7 @@ impl<'a> StructTester<'a> { } pub(crate) fn assert_num_fields(self, num: usize) -> Self { - debug_assert_eq!( + assert_eq!( num, self.def.fields.len(), "[{}] Expected {} struct fields, but found {} for {}", self.test_name, num, self.def.fields.len(), self.assert_postfix() @@ -235,7 +236,7 @@ impl<'a> StructFieldTester<'a> { pub(crate) fn assert_parser_type(self, expected: &str) -> Self { let mut serialized_type = String::new(); serialize_parser_type(&mut serialized_type, &self.heap, self.def.parser_type); - debug_assert_eq!( + assert_eq!( expected, &serialized_type, "[{}] Expected type '{}', but got '{}' for {}", self.test_name, expected, &serialized_type, self.assert_postfix() @@ -265,9 +266,10 @@ impl<'a> FunctionTester<'a> { } pub(crate) fn for_variable(self, name: &str, f: F) -> Self { + // Find the memory statement in order to find the local let mem_stmt_id = seek_stmt( self.heap, self.def.body, - |stmt| { + &|stmt| { if let Statement::Local(local) = stmt { if let LocalStatement::Memory(memory) = local { let local = &self.heap[memory.variable]; @@ -281,26 +283,106 @@ impl<'a> FunctionTester<'a> { } ); - match mem_stmt_id { - Some(mem_stmt_id) => { - // TODO: Retrieve shit - }, - None => { - // TODO: Throw error + assert!( + mem_stmt_id.is_some(), "[{}] Failed to find variable '{}' in {}", + self.test_name, name, self.assert_postfix() + ); + + let mem_stmt_id = mem_stmt_id.unwrap(); + let local_id = self.heap[mem_stmt_id].as_memory().variable; + let local = &self.heap[local_id]; + + // Find the assignment expression that follows it + let assignment_id = seek_expr_in_stmt( + self.heap, self.def.body, + &|expr| { + if let Expression::Assignment(assign_expr) = expr { + if let Expression::Variable(variable_expr) = &self.heap[assign_expr.left] { + if variable_expr.position.offset == local.identifier.position.offset { + return true; + } + } + } + + false } - } + ); + + assert!( + assignment_id.is_some(), "[{}] Failed to find assignment to variable '{}' in {}", + self.test_name, name, self.assert_postfix() + ); + + let assignment = &self.heap[assignment_id.unwrap()]; + + // Construct tester and pass to tester function + let tester = VariableTester::new( + self.test_name, self.def.this.upcast(), local, + assignment.as_assignment(), self.heap + ); + f(tester); + + self + } + + fn assert_postfix(&self) -> String { + format!( + "Function{{ name: {} }}", + &String::from_utf8_lossy(&self.def.identifier.value) + ) } } pub(crate) struct VariableTester<'a> { test_name: &'a str, - def: &'a Local, + definition_id: DefinitionId, + local: &'a Local, assignment: &'a AssignmentExpression, heap: &'a Heap, } +impl<'a> VariableTester<'a> { + fn new( + test_name: &'a str, definition_id: DefinitionId, local: &'a Local, assignment: &'a AssignmentExpression, heap: &'a Heap + ) -> Self { + Self{ test_name, definition_id, local, assignment, heap } + } + pub(crate) fn assert_parser_type(self, expected: &str) -> Self { + let mut serialized = String::new(); + serialize_parser_type(&mut serialized, self.heap, self.local.parser_type); + + assert_eq!( + expected, &serialized, + "[{}] Expected parser type '{}', but got '{}' for {}", + self.test_name, expected, &serialized, self.assert_postfix() + ); + self + } + + pub(crate) fn assert_concrete_type(self, expected: &str) -> Self { + let mut serialized = String::new(); + serialize_concrete_type( + &mut serialized, self.heap, self.definition_id, + &self.assignment.concrete_type + ); + + assert_eq!( + expected, &serialized, + "[{}] Expected concrete type '{}', but got '{}' for {}", + self.test_name, expected, &serialized, self.assert_postfix() + ); + self + } + + fn assert_postfix(&self) -> String { + format!( + "Variable{{ name: {} }}", + &String::from_utf8_lossy(&self.local.identifier.value) + ) + } +} //------------------------------------------------------------------------------ // Interface for failed compilation @@ -444,7 +526,75 @@ fn serialize_parser_type(buffer: &mut String, heap: &Heap, id: ParserTypeId) { } } -fn seek_stmt bool>(heap: &Heap, start: StatementId, f: F) -> Option { +fn serialize_concrete_type(buffer: &mut String, heap: &Heap, def: DefinitionId, concrete: &ConcreteType) { + // Retrieve polymorphic variables, if present (since we're dealing with a + // concrete type we only expect procedure types) + let poly_vars = match &heap[def] { + Definition::Function(func) => &func.poly_vars, + Definition::Component(comp) => &comp.poly_vars, + _ => unreachable!("Error in testing utility: did not expect non-procedure type for concrete type serialization"), + }; + + fn serialize_recursive( + buffer: &mut String, heap: &Heap, poly_vars: &Vec, concrete: &ConcreteType, mut idx: usize + ) -> usize { + use ConcreteTypePart as CTP; + + let part = &concrete.parts[idx]; + match part { + CTP::Marker(poly_idx) => { + buffer.push_str(&String::from_utf8_lossy(&poly_vars[*poly_idx].value)); + }, + CTP::Void => buffer.push_str("void"), + CTP::Message => buffer.push_str("msg"), + CTP::Bool => buffer.push_str("bool"), + CTP::Byte => buffer.push_str("byte"), + CTP::Short => buffer.push_str("short"), + CTP::Int => buffer.push_str("int"), + CTP::Long => buffer.push_str("long"), + CTP::String => buffer.push_str("string"), + CTP::Array => { + idx = serialize_recursive(buffer, heap, poly_vars, concrete, idx + 1); + buffer.push_str("[]"); + idx += 1; + }, + CTP::Slice => { + idx = serialize_recursive(buffer, heap, poly_vars, concrete, idx + 1); + buffer.push_str("[..]"); + idx += 1; + }, + CTP::Input => { + buffer.push_str("in<"); + idx = serialize_recursive(buffer, heap, poly_vars, concrete, idx + 1); + buffer.push('>'); + idx += 1; + }, + CTP::Output => { + buffer.push_str("out<"); + idx = serialize_recursive(buffer, heap, poly_vars, concrete, idx + 1); + buffer.push('>'); + idx += 1 + }, + CTP::Instance(definition_id, num_sub) => { + let definition_name = heap[*definition_id].identifier(); + buffer.push_str(&String::from_utf8_lossy(&definition_name.value)); + buffer.push('<'); + for sub_idx in 0..*num_sub { + if sub_idx != 0 { buffer.push(','); } + idx = serialize_recursive(buffer, heap, poly_vars, concrete, idx + 1); + } + buffer.push('>'); + idx += 1; + } + } + + idx + } + + serialize_recursive(buffer, heap, poly_vars, concrete, 0); +} + +fn seek_stmt bool>(heap: &Heap, start: StatementId, f: &F) -> Option { let stmt = &heap[start]; if f(stmt) { return Some(start); } @@ -476,7 +626,7 @@ fn seek_stmt bool>(heap: &Heap, start: StatementId, f: F) - matched } -fn seek_expr_in_expr bool>(heap: &Heap, start: ExpressionId, f: F) -> Option { +fn seek_expr_in_expr bool>(heap: &Heap, start: ExpressionId, f: &F) -> Option { let expr = &heap[start]; if f(expr) { return Some(start); } @@ -523,7 +673,7 @@ fn seek_expr_in_expr bool>(heap: &Heap, start: ExpressionI None }, Expression::Literal(expr) => { - if let Literal::Struct(lit) = expr.value { + if let Literal::Struct(lit) = &expr.value { for field in &lit.fields { if let Some(id) = seek_expr_in_expr(heap, field.value, f) { return Some(id) @@ -546,7 +696,7 @@ fn seek_expr_in_expr bool>(heap: &Heap, start: ExpressionI } } -fn seek_expr_in_stmt bool>(heap: &Heap, start: StatementId, f: F) -> Option { +fn seek_expr_in_stmt bool>(heap: &Heap, start: StatementId, f: &F) -> Option { let stmt = &heap[start]; match stmt {