plum

#treesitter#compiler#wasm

git clone https://git.pyrossh.dev/plum

A statically typed, imperative programming language inspired by rust, python


d1ea2ffPeter 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 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
- // TODO: closures are not yet type-checked; treat as an unconstrained type
433
+ let mut closure_env = env.clone();
433
- // for now so downstream code can compile against `Expr::Closure`.
434
+ let param_types: Vec<PlumType> = cl.params.iter().map(|p| {
434
- ast::Expr::Closure(_) => Ok(PlumType::TVar("_".to_string())),
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
+ }