plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
d1ea2ff
— Peter John
2026-07-20T21:46:46+05:30
feat(plum-checker): infer closure literal types; type-check fn(...)-typed params
- plum-checker/src/lib.rs +14 -3
- plum-checker/tests/checker_tests.rs +39 -0
plum-checker/src/lib.rs
CHANGED
|
@@ -429,9 +429,20 @@ pub fn infer_expr(expr: &ast::Expr, env: &TypeEnv, ctx: &CheckCtx) -> Result<Plu
|
|
|
429
429
|
},
|
|
430
430
|
},
|
|
431
431
|
ast::Expr::Paren(inner) => infer_expr(inner, env, ctx),
|
|
432
|
+
ast::Expr::Closure(cl) => {
|
|
432
|
-
|
|
433
|
+
let mut closure_env = env.clone();
|
|
433
|
-
|
|
434
|
+
let param_types: Vec<PlumType> = cl.params.iter().map(|p| {
|
|
434
|
-
|
|
435
|
+
let t = PlumType::TVar(format!("_closure_{}", p));
|
|
436
|
+
closure_env.insert(p.clone(), TypeScheme::mono(t.clone()));
|
|
437
|
+
t
|
|
438
|
+
}).collect();
|
|
439
|
+
let body_ty = match &cl.body.stmts.last() {
|
|
440
|
+
Some(ast::Stmt::Expr(e)) => infer_expr(e, &closure_env, ctx)?,
|
|
441
|
+
Some(ast::Stmt::Return(Some(e))) => infer_expr(e, &closure_env, ctx)?,
|
|
442
|
+
_ => PlumType::TUnit,
|
|
443
|
+
};
|
|
444
|
+
Ok(PlumType::TFun(param_types, Box::new(body_ty)))
|
|
445
|
+
}
|
|
435
446
|
ast::Expr::Not(inner) => {
|
|
436
447
|
let t = infer_expr(inner, env, ctx)?;
|
|
437
448
|
unify(&PlumType::TBool, &t)?;
|
plum-checker/tests/checker_tests.rs
CHANGED
|
@@ -572,3 +572,42 @@ use() -> Int =
|
|
|
572
572
|
let result = check_source(&source);
|
|
573
573
|
assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
|
|
574
574
|
}
|
|
575
|
+
|
|
576
|
+
#[test]
|
|
577
|
+
fn closure_literal_infers_as_a_function_type() {
|
|
578
|
+
let src = "\
|
|
579
|
+
useClosure() -> Bool =
|
|
580
|
+
cb = |v|
|
|
581
|
+
True
|
|
582
|
+
cb(5)
|
|
583
|
+
";
|
|
584
|
+
let source = parse(src);
|
|
585
|
+
let result = check_source(&source);
|
|
586
|
+
assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
#[test]
|
|
590
|
+
fn fn_value_typed_param_can_be_called() {
|
|
591
|
+
let src = "\
|
|
592
|
+
each(cb: fn(Int) -> Bool) -> Bool =
|
|
593
|
+
cb(5)
|
|
594
|
+
";
|
|
595
|
+
let source = parse(src);
|
|
596
|
+
let result = check_source(&source);
|
|
597
|
+
assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
#[test]
|
|
601
|
+
fn closure_passed_to_fn_value_typed_param_type_checks() {
|
|
602
|
+
let src = "\
|
|
603
|
+
each(cb: fn(Int) -> Bool) -> Bool =
|
|
604
|
+
cb(5)
|
|
605
|
+
|
|
606
|
+
use() -> Bool =
|
|
607
|
+
each(|v|
|
|
608
|
+
True)
|
|
609
|
+
";
|
|
610
|
+
let source = parse(src);
|
|
611
|
+
let result = check_source(&source);
|
|
612
|
+
assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
|
|
613
|
+
}
|