feat: Properly handle closure logic in function inference.

This commit is contained in:
Jeremy Wall 2023-09-28 19:29:18 -04:00 committed by Jeremy Wall
parent c7c26222fc
commit d1e5d4129f
2 changed files with 66 additions and 10 deletions

View File

@ -35,14 +35,21 @@ pub trait DeriveShape {
impl DeriveShape for FuncDef {
fn derive_shape(&self, symbol_table: &mut BTreeMap<Rc<str>, Shape>) -> Shape {
// 1. First set up our symbols.
let mut table = self
let mut sym_table = self
.argdefs
.iter()
.map(|sym| (sym.val.clone(), Shape::Hole(sym.clone())))
.collect::<BTreeMap<Rc<str>, Shape>>();
sym_table.append(&mut symbol_table.clone());
// 2.Then determine the shapes of those symbols in our expression.
let shape = self.fields.derive_shape(&mut table);
let shape = self.fields.derive_shape(&mut sym_table);
// 3. Finally determine what the return shape can be.
// only include the closed over shapes.
let table = self
.argdefs
.iter()
.map(|sym| (sym.val.clone(), sym_table.get(&sym.val).unwrap().clone()))
.collect::<BTreeMap<Rc<str>, Shape>>();
Shape::Func(FuncShapeDef {
args: table,
ret: shape.into(),
@ -278,6 +285,11 @@ impl Checker {
};
}
pub fn with_symbol_table(mut self, symbol_table: BTreeMap<Rc<str>, Shape>) -> Self {
self.symbol_table = symbol_table;
self
}
pub fn pop_shape(&mut self) -> Option<Shape> {
self.shape_stack.pop()
}

View File

@ -24,19 +24,18 @@ macro_rules! assert_type_fail {
}
macro_rules! assert_type_success {
($e:expr, $shap:expr) => {{
let mut checker = Checker::new();
($e:expr, $shape:expr) => {{
assert_type_success!($e, $shape, BTreeMap::new());
}};
($e:expr, $shape:expr, $sym_table:expr) => {{
let mut checker = Checker::new().with_symbol_table($sym_table);
let mut expr = parse($e.into(), None).unwrap();
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.unwrap().is_empty(),
"We don't expect a symbol table entry."
);
assert!(maybe_shape.is_some(), "We got a shape out of it");
assert_eq!(maybe_shape.unwrap(), $shap);
assert_eq!(maybe_shape.unwrap(), $shape);
}};
}
@ -224,8 +223,10 @@ fn infer_symbol_type_test() {
#[test]
fn infer_func_type_test() {
let mut args = BTreeMap::new();
let foo = Into::<Rc<str>>::into("foo");
let bar = Into::<Rc<str>>::into("bar");
args.insert(
Into::<Rc<str>>::into("foo"),
foo.clone(),
Shape::Int(PositionedItem {
pos: Position {
file: None,
@ -250,4 +251,47 @@ fn infer_func_type_test() {
"func(foo) => foo + 1;",
Shape::Func(FuncShapeDef { args, ret })
);
let mut symbol_table = BTreeMap::new();
symbol_table.insert(
bar.clone(),
Shape::Int(PositionedItem {
pos: Position {
file: None,
line: 1,
column: 20,
offset: 19,
},
val: 1,
}),
);
let mut args = BTreeMap::new();
args.insert(
foo.clone(),
Shape::Int(PositionedItem {
pos: Position {
file: None,
line: 1,
column: 6,
offset: 5,
},
val: 1,
}),
);
assert_type_success!(
"func(foo) => foo + bar;",
Shape::Func(FuncShapeDef {
args: args,
ret: Shape::Int(PositionedItem {
pos: Position {
file: None,
line: 1,
column: 20,
offset: 19,
},
val: 1,
})
.into()
}),
symbol_table
);
}