feat: modules: Handle module args in let.

This commit is contained in:
Jeremy Wall 2023-12-09 11:10:46 -05:00 committed by Jeremy Wall
parent ce928b7bd2
commit 337b7c7203
5 changed files with 477 additions and 173 deletions

View File

@ -259,10 +259,16 @@ pub enum ImportShape {
Unresolved(PositionedItem<Rc<str>>),
}
#[derive(PartialEq, Debug, Clone)]
pub enum NarrowingShape {
Narrowed(Vec<Shape>),
Any,
}
#[derive(PartialEq, Debug, Clone)]
pub struct NarrowedShape {
pub pos: Position,
pub types: Vec<Shape>,
pub types: NarrowingShape,
}
impl NarrowedShape {
@ -271,7 +277,10 @@ impl NarrowedShape {
}
pub fn new_with_pos(types: Vec<Shape>, 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<Rc<str>, 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<Rc<str>, 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<Rc<str>, 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

View File

@ -0,0 +1,5 @@
module{
arg=0,
} => (foo) {
let foo = mod.arg;
};

View File

@ -0,0 +1,5 @@
module{
arg={},
} => (foo) {
let foo = mod.arg.bar + 1;
};

View File

@ -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<Rc<str>, Shape>) -> Shape {
let sym_table: BTreeMap<Rc<str>, Shape> = self
.arg_set
.iter()
.map(|(tok, expr)| (tok.fragment.clone(), expr.derive_shape(symbol_table)))
.collect();
let sym_positions: BTreeSet<PositionedItem<Rc<str>>> =
self.arg_set.iter().map(|(tok, _)| tok.into()).collect();
let mut sym_table: BTreeMap<Rc<str>, Shape> = BTreeMap::new();
let mod_key: Rc<str> = "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<Rc<str>, 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<Rc<str>, Shape>) -> Shape {
@ -187,19 +195,40 @@ fn derive_copy_shape(def: &CopyDef, symbol_table: &mut BTreeMap<Rc<str>, 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<Rc<str>, 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<Rc<str>, Shape>) -> Shape {
match self {

View File

@ -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
}))
})
);
}