From 337b7c720307d10296bc5b220f38264714c447a5 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Sat, 9 Dec 2023 11:10:46 -0500 Subject: [PATCH] feat: modules: Handle module args in let. --- src/ast/mod.rs | 151 +++++++++++-- src/ast/typecheck/complex_module.ucg | 5 + src/ast/typecheck/infer_module_arg.ucg | 5 + src/ast/typecheck/mod.rs | 286 ++++++++++++++++++------- src/ast/typecheck/test.rs | 203 ++++++++++++------ 5 files changed, 477 insertions(+), 173 deletions(-) create mode 100644 src/ast/typecheck/complex_module.ucg create mode 100644 src/ast/typecheck/infer_module_arg.ucg diff --git a/src/ast/mod.rs b/src/ast/mod.rs index e86fe4d..f46ac6d 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -259,10 +259,16 @@ pub enum ImportShape { Unresolved(PositionedItem>), } +#[derive(PartialEq, Debug, Clone)] +pub enum NarrowingShape { + Narrowed(Vec), + Any, +} + #[derive(PartialEq, Debug, Clone)] pub struct NarrowedShape { pub pos: Position, - pub types: Vec, + pub types: NarrowingShape, } impl NarrowedShape { @@ -271,7 +277,10 @@ impl NarrowedShape { } pub fn new_with_pos(types: Vec, pos: Position) -> Self { - Self { pos, types } + Self { + pos, + types: NarrowingShape::Narrowed(types), + } } pub fn with_pos(mut self, pos: Position) -> Self { @@ -280,12 +289,19 @@ impl NarrowedShape { } pub fn merge_in_shape(&mut self, shape: Shape, symbol_table: &mut BTreeMap, Shape>) { - for s in self.types.iter() { - if s.equivalent(&shape, symbol_table) { - return; + match &mut self.types { + NarrowingShape::Narrowed(ref mut types) => { + for s in types.iter() { + if s.equivalent(&shape, symbol_table) { + return; + } + } + types.push(shape); + } + NarrowingShape::Any => { + self.types = NarrowingShape::Narrowed(vec![shape]); } } - self.types.push(shape) } } @@ -315,11 +331,49 @@ impl Shape { | (Shape::Int(_), Shape::Int(_)) | (Shape::Hole(_), Shape::Hole(_)) | (Shape::Float(_), Shape::Float(_)) => true, - (Shape::Narrowed(left_slist), Shape::Narrowed(right_slist)) - | (Shape::List(left_slist), Shape::List(right_slist)) => { - for ls in left_slist.types.iter() { + ( + Shape::List(NarrowedShape { + pos: _, + types: NarrowingShape::Any, + }), + Shape::List(NarrowedShape { + pos: _, + types: NarrowingShape::Any, + }), + ) + | ( + Shape::Narrowed(NarrowedShape { + pos: _, + types: NarrowingShape::Any, + }), + Shape::Narrowed(NarrowedShape { + pos: _, + types: NarrowingShape::Any, + }), + ) => true, + ( + Shape::List(NarrowedShape { + pos: _, + types: NarrowingShape::Narrowed(left_slist), + }), + Shape::List(NarrowedShape { + pos: _, + types: NarrowingShape::Narrowed(right_slist), + }), + ) + | ( + Shape::Narrowed(NarrowedShape { + pos: _, + types: NarrowingShape::Narrowed(left_slist), + }), + Shape::Narrowed(NarrowedShape { + pos: _, + types: NarrowingShape::Narrowed(right_slist), + }), + ) => { + for ls in left_slist.iter() { let mut found = false; - for rs in right_slist.types.iter() { + for rs in right_slist.iter() { if ls.equivalent(rs, symbol_table) { found = true; break; @@ -331,6 +385,46 @@ impl Shape { } true } + ( + Shape::List(NarrowedShape { + pos: _, + types: NarrowingShape::Any, + }), + Shape::List(NarrowedShape { + pos: _, + types: NarrowingShape::Narrowed(right_slist), + }), + ) + | ( + Shape::Narrowed(NarrowedShape { + pos: _, + types: NarrowingShape::Any, + }), + Shape::Narrowed(NarrowedShape { + pos: _, + types: NarrowingShape::Narrowed(right_slist), + }), + ) => true, + ( + Shape::List(NarrowedShape { + pos, + types: NarrowingShape::Narrowed(left_slist), + }), + Shape::List(NarrowedShape { + pos: _, + types: NarrowingShape::Any, + }), + ) + | ( + Shape::Narrowed(NarrowedShape { + pos, + types: NarrowingShape::Narrowed(left_slist), + }), + Shape::Narrowed(NarrowedShape { + pos: _, + types: NarrowingShape::Any, + }), + ) => true, (Shape::Tuple(left_slist), Shape::Tuple(right_slist)) => { for (lt, ls) in left_slist.val.iter() { let mut found = false; @@ -433,14 +527,24 @@ impl Shape { right: &Shape, symbol_table: &mut BTreeMap, Shape>, ) -> Shape { - let left_iter = left_slist.types.iter(); - let right_iter = right_slist.types.iter(); - if is_list_subset(left_iter, right_slist, symbol_table) { - self.clone() - } else if is_list_subset(right_iter, left_slist, symbol_table) { - right.clone() - } else { - Shape::TypeErr(right.pos().clone(), "Incompatible List Shapes".to_owned()) + match (&left_slist.types, &right_slist.types) { + ( + NarrowingShape::Narrowed(ref left_types), + NarrowingShape::Narrowed(ref right_types), + ) => { + let left_iter = left_types.iter(); + let right_iter = right_types.iter(); + if is_list_subset(left_iter, right_slist, symbol_table) { + self.clone() + } else if is_list_subset(right_iter, left_slist, symbol_table) { + right.clone() + } else { + Shape::TypeErr(right.pos().clone(), "Incompatible List Shapes".to_owned()) + } + } + (NarrowingShape::Narrowed(_), NarrowingShape::Any) + | (NarrowingShape::Any, NarrowingShape::Any) => self.clone(), + (NarrowingShape::Any, NarrowingShape::Narrowed(_)) => right.clone(), } } @@ -486,10 +590,11 @@ impl Shape { Shape::Int(_) => Shape::Int(pos.clone()), Shape::Float(_) => Shape::Float(pos.clone()), Shape::Boolean(_) => Shape::Boolean(pos.clone()), - Shape::List(lst) => Shape::List(NarrowedShape::new_with_pos(lst.types, pos)), + Shape::List(NarrowedShape { pos: _, types: NarrowingShape::Narrowed(lst) }) => Shape::List(NarrowedShape::new_with_pos(lst, pos)), + Shape::List(NarrowedShape { pos: _, types: NarrowingShape::Any }) => Shape::List(NarrowedShape {pos, types: NarrowingShape::Any}), Shape::Tuple(flds) => Shape::Tuple(PositionedItem::new(flds.val, pos)), Shape::Func(_) | Shape::Module(_) => self.clone(), - Shape::Narrowed(pi) => Shape::Narrowed(pi.with_pos(pos)), + Shape::Narrowed(narrowed_shape) => Shape::Narrowed(narrowed_shape.with_pos(pos)), Shape::Hole(pi) => Shape::Hole(pi.with_pos(pos)), Shape::Import(ImportShape::Resolved(_, s)) => { Shape::Import(ImportShape::Resolved(pos, s)) @@ -535,6 +640,10 @@ fn is_list_subset( left_slist: &NarrowedShape, symbol_table: &mut BTreeMap, Shape>, ) -> bool { + let left_slist = match &left_slist.types { + NarrowingShape::Any => return true, + NarrowingShape::Narrowed(ref list) => list, + }; let right_subset = loop { let mut matches = false; let ls = if let Some(ls) = right_iter.next() { @@ -542,7 +651,7 @@ fn is_list_subset( } else { break true; }; - for rs in left_slist.types.iter() { + for rs in left_slist.iter() { let s = ls.narrow(rs, symbol_table); if let Shape::TypeErr(_, _) = s { // noop diff --git a/src/ast/typecheck/complex_module.ucg b/src/ast/typecheck/complex_module.ucg new file mode 100644 index 0000000..f3aa3b9 --- /dev/null +++ b/src/ast/typecheck/complex_module.ucg @@ -0,0 +1,5 @@ +module{ + arg=0, +} => (foo) { + let foo = mod.arg; +}; diff --git a/src/ast/typecheck/infer_module_arg.ucg b/src/ast/typecheck/infer_module_arg.ucg new file mode 100644 index 0000000..6339999 --- /dev/null +++ b/src/ast/typecheck/infer_module_arg.ucg @@ -0,0 +1,5 @@ +module{ + arg={}, +} => (foo) { + let foo = mod.arg.bar + 1; +}; diff --git a/src/ast/typecheck/mod.rs b/src/ast/typecheck/mod.rs index 5107cc0..7232115 100644 --- a/src/ast/typecheck/mod.rs +++ b/src/ast/typecheck/mod.rs @@ -24,7 +24,7 @@ use crate::error::{BuildError, ErrorType}; use super::{ BinaryExprType, BinaryOpDef, CastType, CopyDef, FuncDef, ImportShape, ModuleDef, ModuleShape, - NarrowedShape, NotDef, Position, PositionedItem, SelectDef, + NarrowedShape, NarrowingShape, NotDef, Position, PositionedItem, SelectDef, TupleShape, }; /// Trait for shape derivation. @@ -69,41 +69,35 @@ impl DeriveShape for FuncDef { impl DeriveShape for ModuleDef { fn derive_shape(&self, symbol_table: &mut BTreeMap, Shape>) -> Shape { - let sym_table: BTreeMap, Shape> = self - .arg_set - .iter() - .map(|(tok, expr)| (tok.fragment.clone(), expr.derive_shape(symbol_table))) - .collect(); - let sym_positions: BTreeSet>> = - self.arg_set.iter().map(|(tok, _)| tok.into()).collect(); + let mut sym_table: BTreeMap, Shape> = BTreeMap::new(); + let mod_key: Rc = "mod".into(); + let mut mod_shape: TupleShape = Vec::new(); + if let Some(pos) = self.arg_set.first().map(|(t, _)| t.pos.clone()) { + mod_shape = self + .arg_set + .iter() + .map(|(tok, expr)| (tok.into(), expr.derive_shape(symbol_table))) + .collect(); + sym_table.insert( + mod_key.clone(), + Shape::Tuple(PositionedItem::new(mod_shape.clone(), pos)), + ); + } + // TODO(jwall): We should modify the shape_list when we can continue narrowing a type. let mut checker = Checker::new().with_symbol_table(sym_table); checker.walk_statement_list(self.statements.clone().iter_mut().collect()); if let Some(mut expr) = self.out_expr.clone() { checker.walk_expression(&mut expr); } else { - // TODO(jwall): We need to construct a tuple from the let statements here. + // TODO(jwall): Remove this when we require out expressions. } let ret = Box::new( checker .pop_shape() .expect("There should always be a return type here"), ); - let mut items = Vec::new(); - let sym_table = checker - .result() - .expect("There should aways be a symbol_table here"); - for pos_key in sym_positions { - let key = pos_key.val.clone(); - items.push(( - pos_key, - sym_table - .get(&key) - .expect("This should always have a valid shape") - .clone(), - )); - } Shape::Module(ModuleShape { - items, + items: dbg!(mod_shape), ret, }) } @@ -117,10 +111,8 @@ impl DeriveShape for SelectDef { tuple, pos: _, } = self; - let mut narrowed_shape = NarrowedShape { - pos: self.pos.clone(), - types: Vec::with_capacity(tuple.len()), - }; + let mut narrowed_shape = + NarrowedShape::new_with_pos(Vec::with_capacity(tuple.len()), self.pos.clone()); for (_, expr) in tuple { let shape = expr.derive_shape(symbol_table); narrowed_shape.merge_in_shape(shape, symbol_table); @@ -147,24 +139,40 @@ fn derive_include_shape( fn derive_not_shape(def: &NotDef, symbol_table: &mut BTreeMap, Shape>) -> Shape { let shape = def.expr.as_ref().derive_shape(symbol_table); - if let Shape::Boolean(_) = &shape { - return Shape::Boolean(def.pos.clone()); - } else if let Shape::Hole(_) = &shape { - return Shape::Boolean(def.pos.clone()); - } else if let Shape::Narrowed(shape_list) = &shape { - for s in shape_list.types.iter() { - if let Shape::Boolean(_) = s { - return Shape::Boolean(def.pos.clone()); + match &shape { + Shape::Boolean(_) => { + return Shape::Boolean(def.pos.clone()); + } + Shape::Hole(_) => { + return Shape::Boolean(def.pos.clone()); + } + Shape::Narrowed(NarrowedShape { + pos: _, + types: NarrowingShape::Any, + }) => { + return Shape::Boolean(def.pos.clone()); + } + Shape::Narrowed(NarrowedShape { + pos: _, + types: NarrowingShape::Narrowed(shape_list), + }) => { + for s in shape_list.iter() { + if let Shape::Boolean(_) = s { + return Shape::Boolean(def.pos.clone()); + } } } - }; - Shape::TypeErr( + _ => { + // noop + } + } + return Shape::TypeErr( def.pos.clone(), format!( "Expected Boolean value in Not expression but got: {:?}", shape ), - ) + ); } fn derive_copy_shape(def: &CopyDef, symbol_table: &mut BTreeMap, Shape>) -> Shape { @@ -187,19 +195,40 @@ fn derive_copy_shape(def: &CopyDef, symbol_table: &mut BTreeMap, Shape>) Shape::Tuple(PositionedItem::new(vec![], pi.pos.clone())), Shape::Module(ModuleShape { items: vec![], - ret: Box::new(Shape::Narrowed(NarrowedShape { - pos: pi.pos.clone(), - types: vec![], - })), + ret: Box::new(Shape::Narrowed(NarrowedShape::new_with_pos( + vec![], + pi.pos.clone(), + ))), }), Shape::Import(ImportShape::Unresolved(pi.clone())), ], pi.pos.clone(), )), - Shape::Narrowed(potentials) => { + Shape::Narrowed(NarrowedShape { + pos, + types: NarrowingShape::Any, + }) => Shape::Narrowed(NarrowedShape::new_with_pos( + vec![ + Shape::Tuple(PositionedItem { + pos: def.pos.clone(), + val: Vec::new(), + }), + Shape::Module(ModuleShape { + items: vec![], + ret: Box::new(Shape::Narrowed(NarrowedShape { + pos: def.pos.clone(), + types: NarrowingShape::Any, + })), + }), + ], + def.pos.clone(), + )), + Shape::Narrowed(NarrowedShape { + pos, + types: NarrowingShape::Narrowed(potentials), + }) => { // 1. Do the possible shapes include tuple, module, or import? let filtered = potentials - .types .iter() .filter_map(|v| match v { Shape::Tuple(_) | Shape::Module(_) | Shape::Import(_) | Shape::Hole(_) => { @@ -284,48 +313,20 @@ impl DeriveShape for Expression { def.path.pos.clone(), ))), Expression::Binary(def) => { + dbg!(&def); let left_shape = def.left.derive_shape(symbol_table); - let right_shape = def.right.derive_shape(symbol_table); - // We need to do somethig different if it's a ShapeKind::DOT + // We need to do somethig different if it's a BinaryExprType::DOT if def.kind == BinaryExprType::DOT { - dbg!(&def); - // left_shape can be assumed to be of type tuple. - // If left_shape is not known it can be inferred to be a tuple with right - // shapes symbol as a field name. - if let Shape::Hole(p) = left_shape { - dbg!(&p); - if let Shape::Hole(pi) = right_shape { - dbg!(&pi); - let derived_shape = Shape::Tuple(PositionedItem::new( - // TODO(jeremy): This needs to be a token... - vec![( - pi.into(), - Shape::Narrowed(NarrowedShape { - pos: p.pos.clone(), - types: Vec::new(), - }), - )], - p.pos.clone(), - )); - symbol_table.insert(p.val.clone(), derived_shape); - return Shape::Narrowed(NarrowedShape { - pos: p.pos.clone(), - types: Vec::new(), - }); - } - } else if let Shape::Tuple(fields_pi) = left_shape { - dbg!(&fields_pi); - if let Shape::Hole(pi) = right_shape { - dbg!(&pi); - for (sym, shape) in fields_pi.val { - if pi.val == sym.val { - return shape; - } - } - } + // TODO(jwall): We should update the symbol table with the new + // left shape type if it was a symbol + let shape = + derive_dot_expression(&def.pos, &left_shape, &def.right, symbol_table); + if let Expression::Simple(Value::Symbol(pi)) = def.left.as_ref() { + symbol_table.insert(pi.val.clone(), shape.clone()); } - Shape::TypeErr(def.pos.clone(), "Invalid Tuple field selector".to_owned()) + shape } else { + let right_shape = def.right.derive_shape(symbol_table); left_shape.narrow(&right_shape, symbol_table) } } @@ -342,6 +343,125 @@ impl DeriveShape for Expression { } } +fn derive_dot_expression( + pos: &Position, + left_shape: &Shape, + right_expr: &Expression, + symbol_table: &mut BTreeMap, Shape>, +) -> Shape { + // left shape is symbol or tuple or array. + // right shape is symbol, str, number, grouped expression + // left_shape can be assumed to be of type tuple. + // If left_shape is not known it can be inferred to be a tuple with right + // shapes symbol as a field name. + match (left_shape, right_expr) { + // These all recurse down the dot chain + ( + Shape::Tuple(tshape), + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::DOT, + left, + right, + pos, + }), + ) => { + let next_left_shape = left.derive_shape(symbol_table); + let mut tshape = tshape.clone(); + if let Shape::Hole(pi) = next_left_shape { + tshape.val.push(( + pi.clone(), + Shape::Narrowed(NarrowedShape::new_with_pos(vec![], pos.clone())), + )); + } + return Shape::Tuple(tshape); + } + ( + Shape::List(lshape), + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::DOT, + left, + right, + pos, + }), + ) => { + let next_left_shape = left.derive_shape(symbol_table); + if let Shape::Int(_) = next_left_shape { + return Shape::List(lshape.clone()); + }; + if let Shape::Hole(pi) = next_left_shape { + return Shape::Tuple(PositionedItem { + pos: pi.pos.clone(), + val: vec![( + pi.clone(), + Shape::Narrowed(NarrowedShape { + pos: pi.pos.clone(), + types: NarrowingShape::Any, + }), + )], + }); + } else { + todo!() + }; + } + ( + Shape::Narrowed(narrowed_shape), + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::DOT, + left, + right, + pos, + }), + ) => { + let next_left_shape = left.derive_shape(symbol_table); + let types = match &narrowed_shape.types { + NarrowingShape::Any => todo!(), + NarrowingShape::Narrowed(ref types) => types, + }; + match next_left_shape { + Shape::Hole(_) | Shape::Int(_) | Shape::Str(_) => { + if types + .iter() + .any(|s| s.type_name() == "tuple" || s.type_name() == "list") + { + todo!() + } + todo!() + } + _ => { + todo!() + } + } + } + ( + Shape::Hole(_), + Expression::Binary(BinaryOpDef { + kind: BinaryExprType::DOT, + left, + right, + pos, + }), + ) => { + todo!() + } + + // These do not recurse down the dot chain + (Shape::Tuple(_), Expression::Simple(Value::Str(pi))) + | (Shape::Tuple(_), Expression::Simple(Value::Symbol(pi))) => todo!(), + (Shape::List(_), Expression::Simple(Value::Symbol(pi))) => todo!(), + (Shape::List(_), Expression::Simple(Value::Int(pi))) => todo!(), + (Shape::Hole(_), Expression::Simple(Value::Str(pi))) => todo!(), + (Shape::Hole(_), Expression::Simple(Value::Symbol(pi))) => todo!(), + (Shape::Hole(_), Expression::Simple(Value::Int(pi))) => todo!(), + (Shape::Narrowed(_), Expression::Simple(Value::Symbol(pi))) => todo!(), + (Shape::Narrowed(_), Expression::Simple(Value::Str(pi))) => todo!(), + (Shape::Narrowed(_), Expression::Simple(Value::Int(pi))) => todo!(), + (_, Expression::Grouped(expr, _)) => { + return derive_dot_expression(pos, left_shape, expr.as_ref(), symbol_table) + } + (_, _) => return Shape::TypeErr(pos.clone(), "Invalid Tuple field selector".to_owned()), + } +} + impl DeriveShape for Value { fn derive_shape(&self, symbol_table: &mut BTreeMap, Shape>) -> Shape { match self { diff --git a/src/ast/typecheck/test.rs b/src/ast/typecheck/test.rs index ecfaabe..8e106c0 100644 --- a/src/ast/typecheck/test.rs +++ b/src/ast/typecheck/test.rs @@ -32,7 +32,11 @@ macro_rules! assert_type_success { checker.walk_statement_list(expr.iter_mut().collect()); let maybe_shape = checker.pop_shape(); let result = checker.result(); - assert!(result.is_ok(), "We expect this to typecheck successfully."); + assert!( + result.is_ok(), + "We expect this to typecheck successfully. {:?}", + result + ); assert!(maybe_shape.is_some(), "We got a shape out of it"); assert_eq!(maybe_shape.unwrap(), $shape); }}; @@ -205,10 +209,10 @@ fn infer_symbol_type_test() { Shape::Tuple(PositionedItem::new( vec![( PositionedItem::new(foo.clone(), Position::new(1, 5, 4)), - Shape::Narrowed(NarrowedShape { - pos: Position::new(0, 0, 0), - types: Vec::new(), - }), + Shape::Narrowed(NarrowedShape::new_with_pos( + Vec::new(), + Position::new(0, 0, 0), + )), )], Position::new(0, 0, 0), )), @@ -289,29 +293,23 @@ fn infer_func_type_test() { fn infer_select_shape() { assert_type_success!( r#"select (foo) => { true = "foo", false = 1 };"#, - Shape::Narrowed(NarrowedShape { - pos: Position { + Shape::Narrowed(NarrowedShape::new_with_pos( + vec![ + Shape::Str(Position::new(1, 26, 25)), + Shape::Int(Position::new(1, 41, 40)), + ], + Position { file: None, line: 1, column: 1, offset: 0 }, - types: vec![ - Shape::Str(Position::new(1, 26, 25)), - Shape::Int(Position::new(1, 41, 40)), - ] - }) + )) ); assert_type_success!( r#"select (foo) => { true = "foo", false = { } };"#, - Shape::Narrowed(NarrowedShape { - pos: Position { - file: None, - line: 1, - column: 1, - offset: 0 - }, - types: vec![ + Shape::Narrowed(NarrowedShape::new_with_pos( + vec![ Shape::Str(Position::new(1, 26, 25)), Shape::Tuple(PositionedItem { pos: Position { @@ -323,18 +321,18 @@ fn infer_select_shape() { val: vec![], }), ], - }) - ); - assert_type_success!( - r#"select (foo) => { true = { }, false = { } };"#, - Shape::Narrowed(NarrowedShape { - pos: Position { + Position { file: None, line: 1, column: 1, offset: 0 }, - types: vec![Shape::Tuple(PositionedItem { + )) + ); + assert_type_success!( + r#"select (foo) => { true = { }, false = { } };"#, + Shape::Narrowed(NarrowedShape::new_with_pos( + vec![Shape::Tuple(PositionedItem { pos: Position { file: None, line: 1, @@ -343,18 +341,18 @@ fn infer_select_shape() { }, val: vec![], }),], - }) - ); - assert_type_success!( - r#"select (foo) => { true = { foo = 1, }, false = { bar = "foo" } };"#, - Shape::Narrowed(NarrowedShape { - pos: Position { + Position { file: None, line: 1, column: 1, offset: 0 }, - types: vec![ + )) + ); + assert_type_success!( + r#"select (foo) => { true = { foo = 1, }, false = { bar = "foo" } };"#, + Shape::Narrowed(NarrowedShape::new_with_pos( + vec![ Shape::Tuple(PositionedItem { pos: Position { file: None, @@ -405,19 +403,19 @@ fn infer_select_shape() { }) )] }) - ] - }) - ); - assert_type_success!( - r#"select (foo) => { true = { foo = 1, bar = "quux" }, false = { bar = "foo" } };"#, - Shape::Narrowed(NarrowedShape { - pos: Position { + ], + Position { file: None, line: 1, column: 1, offset: 0 }, - types: vec![ + )) + ); + assert_type_success!( + r#"select (foo) => { true = { foo = 1, bar = "quux" }, false = { bar = "foo" } };"#, + Shape::Narrowed(NarrowedShape::new_with_pos( + vec![ Shape::Tuple(PositionedItem { pos: Position { file: None, @@ -487,49 +485,55 @@ fn infer_select_shape() { }) )] }) - ] - }) - ); - assert_type_success!( - r#"select (foo) => { true = [ "quux" ], false = [ 1 ] };"#, - Shape::Narrowed(NarrowedShape { - pos: Position { + ], + Position { file: None, line: 1, column: 1, offset: 0 }, - types: vec![ - Shape::List(NarrowedShape { - pos: Position { + )) + ); + assert_type_success!( + r#"select (foo) => { true = [ "quux" ], false = [ 1 ] };"#, + Shape::Narrowed(NarrowedShape::new_with_pos( + vec![ + Shape::List(NarrowedShape::new_with_pos( + vec![Shape::Str(Position { + file: None, + line: 1, + column: 28, + offset: 27 + })], + Position { file: None, line: 1, column: 26, offset: 25 }, - types: vec![Shape::Str(Position { + )), + Shape::List(NarrowedShape::new_with_pos( + vec![Shape::Int(Position { file: None, line: 1, - column: 28, - offset: 27 - })] - }), - Shape::List(NarrowedShape { - pos: Position { + column: 48, + offset: 47 + })], + Position { file: None, line: 1, column: 46, offset: 45 }, - types: vec![Shape::Int(Position { - file: None, - line: 1, - column: 48, - offset: 47 - })] - }) - ] - }) + )) + ], + Position { + file: None, + line: 1, + column: 1, + offset: 0 + }, + )) ); } @@ -589,5 +593,66 @@ fn test_module_inference() { offset: 14 })) }) - ) + ); + let complex_mod_stmt = include_str!("complex_module.ucg"); + assert_type_success!( + complex_mod_stmt, + Shape::Module(ModuleShape { + items: vec![( + PositionedItem { + pos: Position { + file: None, + line: 2, + column: 3, + offset: 10 + }, + val: "arg".into() + }, + Shape::Int(Position { + file: None, + line: 2, + column: 7, + offset: 14 + }) + )], + ret: Box::new(Shape::Int(Position { + file: None, + line: 2, + column: 7, + offset: 14 + })) + }) + ); + let infer_mod_arg_stmt = include_str!("infer_module_arg.ucg"); + assert_type_success!( + infer_mod_arg_stmt, + Shape::Module(ModuleShape { + items: vec![( + PositionedItem { + pos: Position { + file: None, + line: 2, + column: 3, + offset: 10 + }, + val: "arg".into() + }, + Shape::Narrowed(NarrowedShape::new_with_pos( + vec![], + Position { + file: None, + line: 2, + column: 3, + offset: 10 + }, + )) + )], + ret: Box::new(Shape::Int(Position { + file: None, + line: 2, + column: 7, + offset: 14 + })) + }) + ); }