1use crate::builtins::{module_for_import, module_scope};
5use crate::env::{TypeDefKind, TypeEnv, ty_from_canon_env};
6use crate::error::{PositionedError, TypeError};
7use crate::position::Position;
8use crate::types::*;
9use crate::unifier::{UnifyError, Unifier};
10use indexmap::IndexMap;
11use lex_ast as a;
12use std::collections::{BTreeMap, HashMap};
13
14mod exhaustive;
15mod parse_strict;
16
17pub use parse_strict::{rewrite_parse_calls, ParseSite};
18use parse_strict::*;
19
20pub struct ProgramTypes {
22 pub fn_signatures: IndexMap<String, Scheme>,
23 pub type_env: TypeEnv,
24 pub parse_required_fields: HashMap<ParseSite, Vec<String>>,
35 pub parse_type_schemas: HashMap<ParseSite, Vec<(String, String)>>,
40}
41
42pub fn check_program_with_positions(
56 stages: &[a::Stage],
57 positions: &BTreeMap<String, Position>,
58) -> Result<ProgramTypes, Vec<PositionedError>> {
59 check_program_inner(stages, Some(positions))
60 .map_err(|errs| errs.into_iter().map(|(e, fn_name)| {
61 let pos = fn_name.as_deref().and_then(|n| positions.get(n)).cloned();
62 PositionedError::new(e, pos)
63 }).collect())
64}
65
66pub fn check_program(stages: &[a::Stage]) -> Result<ProgramTypes, Vec<TypeError>> {
67 check_program_inner(stages, None)
68 .map_err(|errs| errs.into_iter().map(|(e, _)| e).collect())
69}
70
71fn check_program_inner(
72 stages: &[a::Stage],
73 _positions: Option<&BTreeMap<String, Position>>,
74) -> Result<ProgramTypes, Vec<(TypeError, Option<String>)>> {
75 let mut tcx = Checker::new();
76 let mut errors: Vec<(TypeError, Option<String>)> = Vec::new();
79
80 for stage in stages {
82 if let a::Stage::Import(i) = stage {
83 if let Some(mod_name) = module_for_import(&i.reference) {
84 if let Some(ty) = module_scope(mod_name, &tcx.type_env) {
85 tcx.globals.insert(i.alias.clone(), Scheme {
86 vars: collect_vars(&ty),
90 eff_vars: collect_eff_vars(&ty),
91 ty,
92 });
93 tcx.module_aliases.insert(i.alias.clone(), mod_name.to_string());
94 }
95 }
96 }
97 }
98
99 for stage in stages {
101 if let a::Stage::TypeDecl(td) = stage {
102 if let Err(e) = tcx.type_env.add_user_type(&td.name, td.clone()) {
103 errors.push((TypeError::RecursiveTypeWithoutConstructor {
104 at_node: "n_0".into(),
105 name: e,
106 }, None));
107 }
108 }
109 }
110
111 for stage in stages {
113 if let a::Stage::FnDecl(fd) = stage {
114 let scheme = function_scheme(fd, &tcx.type_env);
115 tcx.globals.insert(fd.name.clone(), scheme);
116 tcx.fn_params.insert(fd.name.clone(), fd.params.clone());
120 }
121 }
122
123 let mut signatures = IndexMap::new();
128 let wants_parse_sites = tcx.has_parse_capable_imports();
133 for (stage_idx, stage) in stages.iter().enumerate() {
134 if let a::Stage::FnDecl(fd) = stage {
135 tcx.stage_ids = if wants_parse_sites {
136 Some((stage_idx, a::expr_ids(stage)))
137 } else {
138 None
139 };
140 match tcx.check_fn(fd) {
141 Ok(scheme) => { signatures.insert(fd.name.clone(), scheme); }
142 Err(es) => {
143 errors.extend(es.into_iter().map(|e| (e, Some(fd.name.clone()))));
144 }
145 }
146 }
147 }
148 tcx.stage_ids = None;
149
150 if errors.is_empty() {
151 let mut parse_required_fields = HashMap::new();
157 let mut parse_type_schemas = HashMap::new();
158 for (site, ret_ty) in &tcx.pending_parse_calls {
159 if let Some((fields, schema)) = extract_record_fields_and_schema(&tcx.u, &tcx.type_env, ret_ty) {
160 parse_required_fields.insert(site.clone(), fields);
161 parse_type_schemas.insert(site.clone(), schema);
162 }
163 }
164 Ok(ProgramTypes {
165 fn_signatures: signatures,
166 type_env: tcx.type_env,
167 parse_required_fields,
168 parse_type_schemas,
169 })
170 } else {
171 Err(errors)
172 }
173}
174
175pub fn check_and_rewrite_program(
181 stages: &mut [a::Stage],
182) -> Result<ProgramTypes, Vec<TypeError>> {
183 let pt = check_program(&*stages)?;
184 rewrite_parse_calls(stages, &pt);
185 Ok(pt)
186}
187
188fn synthesize_decode_typed_field(field: &str, fields: &IndexMap<String, Ty>) -> Option<Ty> {
204 let base_field = match field {
205 "parse_strict_typed" => "parse",
206 "json_body_typed" => "json_body",
207 _ => return None,
208 };
209 let Ty::Function { params, effects, ret } = fields.get(base_field)? else {
210 return None;
211 };
212 let mut synthesized_params = params.clone();
213 synthesized_params.push(Ty::List(Box::new(Ty::Prim(Prim::Str))));
214 synthesized_params.push(Ty::List(Box::new(Ty::Tuple(vec![
215 Ty::Prim(Prim::Str),
216 Ty::Prim(Prim::Str),
217 ]))));
218 Some(Ty::Function {
219 params: synthesized_params,
220 effects: effects.clone(),
221 ret: ret.clone(),
222 })
223}
224
225fn collect_vars(t: &Ty) -> Vec<TyVarId> {
226 let mut out = Vec::new();
227 fn walk(t: &Ty, out: &mut Vec<TyVarId>) {
228 match t {
229 Ty::Var(v) => { if !out.contains(v) { out.push(*v); } }
230 Ty::Prim(_) | Ty::Unit | Ty::Never => {}
231 Ty::List(inner) => walk(inner, out),
232 Ty::Tuple(items) => for it in items { walk(it, out); },
233 Ty::Record(fs) => for v in fs.values() { walk(v, out); },
234 Ty::Con(_, args) => for a in args { walk(a, out); },
235 Ty::Function { params, ret, .. } => {
236 for p in params { walk(p, out); }
237 walk(ret, out);
238 }
239 }
240 }
241 walk(t, &mut out);
242 out
243}
244
245fn collect_eff_vars(t: &Ty) -> Vec<u32> {
249 let mut out = Vec::new();
250 fn walk(t: &Ty, out: &mut Vec<u32>) {
251 match t {
252 Ty::Var(_) | Ty::Prim(_) | Ty::Unit | Ty::Never => {}
253 Ty::List(inner) => walk(inner, out),
254 Ty::Tuple(items) => for it in items { walk(it, out); },
255 Ty::Record(fs) => for v in fs.values() { walk(v, out); },
256 Ty::Con(_, args) => for a in args { walk(a, out); },
257 Ty::Function { params, effects, ret } => {
258 if let Some(v) = effects.var {
259 if !out.contains(&v) { out.push(v); }
260 }
261 for p in params { walk(p, out); }
262 walk(ret, out);
263 }
264 }
265 }
266 walk(t, &mut out);
267 out
268}
269
270fn function_scheme(fd: &a::FnDecl, env: &TypeEnv) -> Scheme {
271 let params: Vec<Ty> = fd.params.iter().map(|p| ty_from_canon_env(&p.ty, &fd.type_params, env)).collect();
273 let ret = ty_from_canon_env(&fd.return_type, &fd.type_params, env);
274 let effects = EffectSet {
278 concrete: {
279 let mut s = std::collections::BTreeSet::new();
280 for e in &fd.effects {
281 let arg = e.arg.as_ref().map(|a| match a {
282 a::EffectArg::Str { value } => crate::types::EffectArg::Str(value.clone()),
283 a::EffectArg::Int { value } => crate::types::EffectArg::Int(*value),
284 a::EffectArg::Ident { value } => crate::types::EffectArg::Ident(value.clone()),
285 });
286 s.insert(crate::types::EffectKind { name: e.name.clone(), arg });
287 }
288 s
289 },
290 var: fd.effect_row_var
295 .as_ref()
296 .and_then(|n| fd.type_params.iter().position(|p| p == n))
297 .map(|i| i as u32),
298 };
299 let ty = Ty::Function { params, effects, ret: Box::new(ret) };
300 let vars: Vec<TyVarId> = (0..fd.type_params.len() as u32).collect();
301 let eff_vars = collect_eff_vars(&ty);
308 Scheme { vars, eff_vars, ty }
309}
310
311struct Checker {
312 u: Unifier,
313 type_env: TypeEnv,
314 globals: IndexMap<String, Scheme>,
315 module_aliases: IndexMap<String, String>,
319 pending_parse_calls: Vec<(ParseSite, Ty)>,
328 stage_ids: Option<(usize, HashMap<*const a::CExpr, a::NodeId>)>,
334 fn_params: IndexMap<String, Vec<a::Param>>,
340 recovered_errors: Vec<TypeError>,
346 eff_row_scope: IndexMap<String, u32>,
354}
355
356impl Checker {
357 fn new() -> Self {
358 Self {
359 u: Unifier::new(),
360 type_env: TypeEnv::new_with_builtins(),
361 globals: IndexMap::new(),
362 module_aliases: IndexMap::new(),
363 pending_parse_calls: Vec::new(),
364 stage_ids: None,
365 fn_params: IndexMap::new(),
366 recovered_errors: Vec::new(),
367 eff_row_scope: IndexMap::new(),
368 }
369 }
370
371 fn check_expr_recover(
379 &mut self,
380 e: &a::CExpr,
381 node_id: &str,
382 locals: &mut IndexMap<String, Ty>,
383 effs: &mut EffectSet,
384 ) -> Ty {
385 match self.check_expr(e, node_id, locals, effs) {
386 Ok(ty) => ty,
387 Err(err) => {
388 self.recovered_errors.push(err);
389 self.u.fresh()
390 }
391 }
392 }
393
394 fn unfold_record_alias(&self, ty: Ty) -> Ty {
404 if let Ty::Con(ref n, ref args) = ty {
405 if let Some(td) = self.type_env.types.get(n) {
406 if let TypeDefKind::Alias(inner) = &td.kind {
407 if td.params.len() != args.len() {
408 return ty;
409 }
410 if td.params.is_empty() {
411 return inner.clone();
412 }
413 let mut subst = IndexMap::new();
414 for (i, a) in args.iter().enumerate() {
415 subst.insert(i as u32, a.clone());
416 }
417 return subst_vars(inner, &subst, &IndexMap::new());
418 }
419 }
420 }
421 ty
422 }
423
424 fn is_alias_con(&self, ty: &Ty) -> bool {
431 if let Ty::Con(name, args) = ty {
432 if let Some(td) = self.type_env.types.get(name) {
433 if matches!(td.kind, TypeDefKind::Alias(_))
434 && td.params.len() == args.len()
435 {
436 return true;
437 }
438 }
439 }
440 false
441 }
442
443 fn unify_with_record_coercion(&mut self, a: &Ty, b: &Ty) -> Result<(), UnifyError> {
455 let a = self.u.resolve(a);
456 let b = self.u.resolve(b);
457 self.unify_coerce_inner(a, b)
458 }
459
460 fn unify_coerce_inner(&mut self, a: Ty, b: Ty) -> Result<(), UnifyError> {
461 let (a, b) = match (&a, &b) {
487 (Ty::Con(n1, _), Ty::Con(n2, _)) if n1 == n2 => (a, b),
488 (Ty::Var(_), _) | (_, Ty::Var(_)) => (a, b),
489 (Ty::Con(_, _), Ty::Con(_, _))
490 if self.is_alias_con(&a) && self.is_alias_con(&b) =>
491 {
492 (a, b)
493 }
494 _ => {
495 let a_u = if let Ty::Con(_, _) = &a {
496 self.unfold_record_alias(a.clone())
497 } else {
498 a
499 };
500 let b_u = if let Ty::Con(_, _) = &b {
501 self.unfold_record_alias(b.clone())
502 } else {
503 b
504 };
505 (a_u, b_u)
506 }
507 };
508
509 match (&a, &b) {
510 (Ty::Record(fa), Ty::Record(fb)) => {
511 if fa.len() != fb.len() {
512 return Err(UnifyError::Mismatch { a: a.clone(), b: b.clone() });
513 }
514 for (k, va) in fa.clone() {
515 match fb.get(&k) {
516 Some(vb) => self.unify_coerce_inner(va, vb.clone())?,
517 None => return Err(UnifyError::Mismatch { a: a.clone(), b: b.clone() }),
518 }
519 }
520 Ok(())
521 }
522 (Ty::List(ta), Ty::List(tb)) => {
523 self.unify_coerce_inner((**ta).clone(), (**tb).clone())
524 }
525 (Ty::Tuple(xs), Ty::Tuple(ys)) if xs.len() == ys.len() => {
526 for (x, y) in xs.clone().into_iter().zip(ys.clone()) {
527 self.unify_coerce_inner(x, y)?;
528 }
529 Ok(())
530 }
531 (Ty::Con(n1, a1), Ty::Con(n2, a2)) if n1 == n2 && a1.len() == a2.len() => {
534 for (x, y) in a1.clone().into_iter().zip(a2.clone()) {
535 self.unify_coerce_inner(x, y)?;
536 }
537 Ok(())
538 }
539 (Ty::Function { params: pa, effects: ea, ret: ra },
544 Ty::Function { params: pb, effects: eb, ret: rb })
545 if pa.len() == pb.len() => {
546 for (x, y) in pa.clone().into_iter().zip(pb.clone()) {
547 self.unify_coerce_inner(x, y)?;
548 }
549 self.u.unify_effects(ea, eb)?;
554 self.unify_coerce_inner((**ra).clone(), (**rb).clone())
555 }
556 _ => self.u.unify(&a, &b),
557 }
558 }
559
560 fn check_fn(&mut self, fd: &a::FnDecl) -> Result<Scheme, Vec<TypeError>> {
561 let scheme = function_scheme(fd, &self.type_env);
563 let (inst_ty, eff_subst) = instantiate_with_eff(&scheme, &mut self.u);
564 let (param_tys, declared_effects, ret_ty) = match inst_ty {
565 Ty::Function { params, effects, ret } => (params, effects, *ret),
566 _ => unreachable!(),
567 };
568
569 let saved_scope = std::mem::take(&mut self.eff_row_scope);
575 for (i, name) in fd.type_params.iter().enumerate() {
576 if let Some(fresh) = eff_subst.get(&(i as u32)) {
577 self.eff_row_scope.insert(name.clone(), *fresh);
578 }
579 }
580
581 let mut locals: IndexMap<String, Ty> = IndexMap::new();
582 for (p, t) in fd.params.iter().zip(param_tys.iter()) {
583 locals.insert(p.name.clone(), t.clone());
584 }
585
586 let mut errors: Vec<TypeError> = Vec::new();
590 let mut inferred_effects = EffectSet::empty();
591
592 let body_ok = match self.check_expr(&fd.body, "n_0", &mut locals, &mut inferred_effects) {
594 Ok(body_ty) => {
595 if let Err(e) = self.unify_with_record_coercion(&body_ty, &ret_ty) {
601 errors.push(mismatch_err("n_0", e, &self.u, vec![format!("in function `{}`", fd.name)]));
602 false
603 } else {
604 true
605 }
606 }
607 Err(e) => { errors.push(e); false }
608 };
609
610 let body_had_recovered = !self.recovered_errors.is_empty();
614 errors.append(&mut self.recovered_errors);
615
616 if body_ok && !body_had_recovered && !inferred_effects.is_subset(&declared_effects) {
621 for e in inferred_effects.concrete.iter() {
622 if !declared_effects.concrete.iter().any(|d| d.subsumes(e)) {
623 errors.push(TypeError::EffectNotDeclared {
624 at_node: "n_0".into(),
625 effect: e.pretty(),
626 });
627 break;
628 }
629 }
630 }
631
632 if !fd.examples.is_empty() {
637 if !declared_effects.concrete.is_empty() {
638 errors.push(TypeError::ExamplesOnEffectfulFn {
639 at_node: "n_0".into(),
640 fn_name: fd.name.clone(),
641 });
642 } else {
643 for (case_index, ex) in fd.examples.iter().enumerate() {
644 if ex.args.len() != param_tys.len() {
645 errors.push(TypeError::ExampleArityMismatch {
646 at_node: "n_0".into(),
647 fn_name: fd.name.clone(),
648 case_index,
649 expected: param_tys.len(),
650 got: ex.args.len(),
651 });
652 continue;
653 }
654 let mut example_locals: IndexMap<String, Ty> = IndexMap::new();
655 let mut example_effects = EffectSet::empty();
656 let mut args_ok = true;
657 for (i, (arg, expected_ty)) in
658 ex.args.iter().zip(param_tys.iter()).enumerate()
659 {
660 match self.check_expr(arg, "n_0", &mut example_locals, &mut example_effects) {
661 Ok(arg_ty) => {
662 if let Err(e) = self.unify_with_record_coercion(&arg_ty, expected_ty) {
663 errors.push(mismatch_err(
664 "n_0", e, &self.u,
665 vec![format!("in example #{} for `{}`, argument {}", case_index + 1, fd.name, i + 1)],
666 ));
667 args_ok = false;
668 }
669 }
670 Err(e) => { errors.push(e); args_ok = false; }
671 }
672 }
673 if args_ok {
674 match self.check_expr(&ex.expected, "n_0", &mut example_locals, &mut example_effects) {
675 Ok(expected_ty) => {
676 if let Err(e) = self.unify_with_record_coercion(&expected_ty, &ret_ty) {
677 errors.push(mismatch_err(
678 "n_0", e, &self.u,
679 vec![format!("in example #{} for `{}`, expected value", case_index + 1, fd.name)],
680 ));
681 }
682 }
683 Err(e) => errors.push(e),
684 }
685 }
686 if let Some(e) = example_effects.concrete.iter().next() {
691 errors.push(TypeError::EffectNotDeclared {
692 at_node: "n_0".into(),
693 effect: e.pretty(),
694 });
695 }
696 }
697 }
698 }
699
700 errors.append(&mut self.recovered_errors);
702 self.eff_row_scope = saved_scope;
705 if errors.is_empty() { Ok(scheme) } else { Err(errors) }
706 }
707
708 fn check_expr(
709 &mut self,
710 e: &a::CExpr,
711 node_id: &str,
712 locals: &mut IndexMap<String, Ty>,
713 effs: &mut EffectSet,
714 ) -> Result<Ty, TypeError> {
715 match e {
716 a::CExpr::Literal { value } => Ok(lit_type(value)),
717 a::CExpr::Var { name } => {
718 if let Some(t) = locals.get(name) {
719 return Ok(t.clone());
720 }
721 if let Some(scheme) = self.globals.get(name).cloned() {
722 return Ok(instantiate(&scheme, &mut self.u));
723 }
724 Err(TypeError::UnknownIdentifier { at_node: node_id.into(), name: name.clone() })
725 }
726 a::CExpr::Constructor { name, args } => self.check_constructor(name, args, node_id, locals, effs),
727 a::CExpr::Call { callee, args } => self.check_call(e, callee, args, node_id, locals, effs),
728 a::CExpr::Let { name, ty, value, body } => {
729 let v_ty = self.check_expr_recover(value, node_id, locals, effs);
733 if let Some(declared) = ty {
734 let d = ty_from_canon_env(declared, &[], &self.type_env);
735 if let Err(err) = self.unify_with_record_coercion(&v_ty, &d) {
736 return Err(mismatch_err(node_id, err, &self.u, vec![format!("in let `{}`", name)]));
737 }
738 }
739 let prev = locals.insert(name.clone(), v_ty);
740 let body_ty = self.check_expr(body, node_id, locals, effs)?;
741 match prev {
742 Some(p) => { locals.insert(name.clone(), p); }
743 None => { locals.shift_remove(name); }
744 }
745 Ok(body_ty)
746 }
747 a::CExpr::Match { scrutinee, arms } => {
748 let scrut_ty = self.check_expr(scrutinee, node_id, locals, effs)?;
749 if arms.is_empty() {
750 return Err(TypeError::NonExhaustiveMatch {
751 at_node: node_id.into(), missing: vec!["_".into()]
752 });
753 }
754 let result_ty = self.u.fresh();
755 for arm in arms {
756 let mut arm_locals = locals.clone();
757 self.bind_pattern(&arm.pattern, &scrut_ty, &mut arm_locals, node_id)?;
758 let arm_ty = self.check_expr(&arm.body, node_id, &mut arm_locals, effs)?;
759 if let Err(err) = self.unify_with_record_coercion(&arm_ty, &result_ty) {
760 return Err(mismatch_err(node_id, err, &self.u, vec!["in match arm".into()]));
761 }
762 }
763 let rows: Vec<Vec<a::Pattern>> = arms.iter().map(|arm| vec![arm.pattern.clone()]).collect();
768 if let Some(witnesses) = self.missing_patterns(&rows, std::slice::from_ref(&scrut_ty)) {
769 return Err(TypeError::NonExhaustiveMatch {
770 at_node: node_id.into(),
771 missing: witnesses.into_iter().map(|w| w.join(", ")).collect(),
772 });
773 }
774 Ok(result_ty)
775 }
776 a::CExpr::Block { statements, result } => {
777 for s in statements {
781 let _ = self.check_expr_recover(s, node_id, locals, effs);
782 }
783 self.check_expr(result, node_id, locals, effs)
784 }
785 a::CExpr::RecordLit { fields } => {
786 let mut tys = IndexMap::new();
787 for f in fields {
788 if tys.contains_key(&f.name) {
789 return Err(TypeError::DuplicateField {
790 at_node: node_id.into(), field: f.name.clone()
791 });
792 }
793 let ft = self.check_expr(&f.value, node_id, locals, effs)?;
794 tys.insert(f.name.clone(), ft);
795 }
796 Ok(Ty::Record(tys))
797 }
798 a::CExpr::TupleLit { items } => {
799 let mut ts = Vec::new();
800 for it in items { ts.push(self.check_expr(it, node_id, locals, effs)?); }
801 Ok(Ty::Tuple(ts))
802 }
803 a::CExpr::ListLit { items } => {
804 let elem = self.u.fresh();
805 for it in items {
806 let t = self.check_expr(it, node_id, locals, effs)?;
807 if let Err(err) = self.unify_with_record_coercion(&t, &elem) {
808 return Err(mismatch_err(node_id, err, &self.u, vec!["in list literal".into()]));
809 }
810 }
811 Ok(Ty::List(Box::new(elem)))
812 }
813 a::CExpr::FieldAccess { value, field } => {
814 let vt = self.check_expr(value, node_id, locals, effs)?;
815 let resolved = self.u.resolve(&vt);
816 let resolved = if let Ty::Con(_, _) = &resolved {
824 let unfolded = self.unfold_record_alias(resolved.clone());
825 if matches!(unfolded, Ty::Record(_)) {
826 unfolded
827 } else {
828 resolved
829 }
830 } else {
831 resolved
832 };
833 match resolved {
834 Ty::Record(fields) => fields.get(field).cloned()
835 .or_else(|| synthesize_decode_typed_field(field, &fields))
836 .ok_or_else(|| TypeError::UnknownField {
837 at_node: node_id.into(),
838 record_type: Ty::Record(fields.clone()).pretty(),
839 field: field.clone(),
840 }),
841 other => Err(TypeError::TypeMismatch {
842 at_node: node_id.into(),
843 expected: "record".into(),
844 got: other.pretty(),
845 context: vec![format!("field access `.{}`", field)],
846 }),
847 }
848 }
849 a::CExpr::Lambda { params, return_type, effects: l_effects, effect_row_var: l_row_var, body } => {
850 let param_tys: Vec<Ty> = params.iter().map(|p| ty_from_canon_env(&p.ty, &[], &self.type_env)).collect();
851 let ret_ty = ty_from_canon_env(return_type, &[], &self.type_env);
852 let row_var = match l_row_var {
859 Some(name) => match self.eff_row_scope.get(name) {
860 Some(id) => Some(*id),
861 None => {
862 return Err(TypeError::EffectNotDeclared {
863 at_node: node_id.into(),
864 effect: format!("unbound effect-row variable `{}`", name),
865 });
866 }
867 },
868 None => None,
869 };
870 let declared = EffectSet {
871 concrete: {
872 let mut s = std::collections::BTreeSet::new();
873 for e in l_effects {
874 let arg = e.arg.as_ref().map(|a| match a {
875 a::EffectArg::Str { value } => crate::types::EffectArg::Str(value.clone()),
876 a::EffectArg::Int { value } => crate::types::EffectArg::Int(*value),
877 a::EffectArg::Ident { value } => crate::types::EffectArg::Ident(value.clone()),
878 });
879 s.insert(crate::types::EffectKind { name: e.name.clone(), arg });
880 }
881 s
882 },
883 var: row_var,
884 };
885 let mut inner_locals = locals.clone();
886 for (p, t) in params.iter().zip(param_tys.iter()) {
887 inner_locals.insert(p.name.clone(), t.clone());
888 }
889 let mut inner_effs = EffectSet::empty();
890 let body_ty = self.check_expr(body, node_id, &mut inner_locals, &mut inner_effs)?;
891 if let Err(err) = self.unify_with_record_coercion(&body_ty, &ret_ty) {
892 return Err(mismatch_err(node_id, err, &self.u, vec!["in lambda body".into()]));
893 }
894 if !inner_effs.is_subset(&declared) {
895 for e in inner_effs.concrete.iter() {
896 if !declared.concrete.iter().any(|d| d.subsumes(e)) {
897 return Err(TypeError::EffectNotDeclared {
898 at_node: node_id.into(),
899 effect: e.pretty(),
900 });
901 }
902 }
903 }
904 if let Some(iv) = inner_effs.var {
910 if declared.var != Some(iv) {
911 return Err(TypeError::EffectNotDeclared {
912 at_node: node_id.into(),
913 effect: "open effect row (annotate the lambda's effects with `| <row-var>`)".into(),
914 });
915 }
916 }
917 Ok(Ty::function(param_tys, declared, ret_ty))
918 }
919 a::CExpr::BinOp { op, lhs, rhs } => self.check_binop(op, lhs, rhs, node_id, locals, effs),
920 a::CExpr::UnaryOp { op, expr } => {
921 let t = self.check_expr(expr, node_id, locals, effs)?;
922 match op.as_str() {
923 "-" => {
924 let r = self.u.resolve(&t);
926 match r {
927 Ty::Prim(Prim::Int) | Ty::Prim(Prim::Float) => Ok(t),
928 Ty::Var(_) => {
929 self.u.unify(&t, &Ty::int()).map_err(|e| mismatch_err(node_id, e, &self.u, vec![]))?;
931 Ok(Ty::int())
932 }
933 other => Err(TypeError::TypeMismatch {
934 at_node: node_id.into(),
935 expected: "Int or Float".into(),
936 got: other.pretty(),
937 context: vec!["unary `-`".into()],
938 }),
939 }
940 }
941 "not" => {
942 self.u.unify(&t, &Ty::bool()).map_err(|e| mismatch_err(node_id, e, &self.u, vec!["unary `not`".into()]))?;
943 Ok(Ty::bool())
944 }
945 other => panic!("unknown unary op: {other}"),
946 }
947 }
948 a::CExpr::Return { value } => {
949 self.check_expr(value, node_id, locals, effs)?;
952 Ok(Ty::Never)
953 }
954 }
955 }
956
957 fn check_binop(
958 &mut self,
959 op: &str,
960 lhs: &a::CExpr,
961 rhs: &a::CExpr,
962 node_id: &str,
963 locals: &mut IndexMap<String, Ty>,
964 effs: &mut EffectSet,
965 ) -> Result<Ty, TypeError> {
966 let lt = self.check_expr(lhs, node_id, locals, effs)?;
967 let rt = self.check_expr(rhs, node_id, locals, effs)?;
968 match op {
969 "+" => {
970 self.u.unify(<, &rt).map_err(|e| mismatch_err(node_id, e, &self.u, vec![format!("operator `{op}`")]))?;
978 let r = self.unfold_record_alias(self.u.resolve(<));
979 match r {
980 Ty::Prim(Prim::Int) | Ty::Prim(Prim::Float) | Ty::Prim(Prim::Str) => Ok(lt),
981 Ty::Var(_) => {
982 self.u.unify(<, &Ty::int()).map_err(|e| mismatch_err(node_id, e, &self.u, vec![format!("operator `{op}`")]))?;
983 Ok(Ty::int())
984 }
985 other => Err(TypeError::TypeMismatch {
986 at_node: node_id.into(),
987 expected: "Int, Float, or Str".into(),
988 got: other.pretty(),
989 context: vec![format!("operator `{op}`")],
990 }),
991 }
992 }
993 "-" | "*" | "/" | "%" => {
994 self.u.unify(<, &rt).map_err(|e| mismatch_err(node_id, e, &self.u, vec![format!("operator `{op}`")]))?;
995 let r = self.unfold_record_alias(self.u.resolve(<));
996 match r {
997 Ty::Prim(Prim::Int) | Ty::Prim(Prim::Float) => Ok(lt),
998 Ty::Var(_) => {
999 self.u.unify(<, &Ty::int()).map_err(|e| mismatch_err(node_id, e, &self.u, vec![format!("operator `{op}`")]))?;
1000 Ok(Ty::int())
1001 }
1002 other => Err(TypeError::TypeMismatch {
1003 at_node: node_id.into(),
1004 expected: "Int or Float".into(),
1005 got: other.pretty(),
1006 context: vec![format!("operator `{op}`")],
1007 }),
1008 }
1009 }
1010 "==" | "!=" => {
1011 self.u.unify(<, &rt).map_err(|e| mismatch_err(node_id, e, &self.u, vec![format!("operator `{op}`")]))?;
1012 Ok(Ty::bool())
1013 }
1014 "<" | "<=" | ">" | ">=" => {
1015 self.u.unify(<, &rt).map_err(|e| mismatch_err(node_id, e, &self.u, vec![format!("operator `{op}`")]))?;
1016 let r = self.unfold_record_alias(self.u.resolve(<));
1017 match r {
1018 Ty::Prim(Prim::Int) | Ty::Prim(Prim::Float) | Ty::Prim(Prim::Str) => Ok(Ty::bool()),
1019 Ty::Var(_) => {
1020 self.u.unify(<, &Ty::int()).map_err(|e| mismatch_err(node_id, e, &self.u, vec![format!("operator `{op}`")]))?;
1021 Ok(Ty::bool())
1022 }
1023 other => Err(TypeError::TypeMismatch {
1024 at_node: node_id.into(),
1025 expected: "Int, Float, or Str".into(),
1026 got: other.pretty(),
1027 context: vec![format!("operator `{op}`")],
1028 }),
1029 }
1030 }
1031 "and" | "or" => {
1032 self.u.unify(<, &Ty::bool()).map_err(|e| mismatch_err(node_id, e, &self.u, vec![format!("operator `{op}`")]))?;
1033 self.u.unify(&rt, &Ty::bool()).map_err(|e| mismatch_err(node_id, e, &self.u, vec![format!("operator `{op}`")]))?;
1034 Ok(Ty::bool())
1035 }
1036 other => panic!("unknown binop: {other}"),
1037 }
1038 }
1039
1040 fn check_call(
1041 &mut self,
1042 call_expr: &a::CExpr,
1043 callee: &a::CExpr,
1044 args: &[a::CExpr],
1045 node_id: &str,
1046 locals: &mut IndexMap<String, Ty>,
1047 effs: &mut EffectSet,
1048 ) -> Result<Ty, TypeError> {
1049 let parse_site = if self.is_module_parse_call(callee) {
1057 self.parse_site_of(call_expr)
1058 } else {
1059 None
1060 };
1061 let callee_ty = self.check_expr(callee, node_id, locals, effs)?;
1062 let resolved = self.u.resolve(&callee_ty);
1063 match resolved {
1064 Ty::Function { params, effects, ret } => {
1065 if params.len() != args.len() {
1066 return Err(TypeError::ArityMismatch {
1067 at_node: node_id.into(),
1068 expected: params.len(),
1069 got: args.len(),
1070 });
1071 }
1072 for (i, (a, p)) in args.iter().zip(params.iter()).enumerate() {
1073 let at = self.check_expr(a, node_id, locals, effs)?;
1074 if let Err(err) = self.unify_with_record_coercion(&at, p) {
1075 return Err(mismatch_err(node_id, err, &self.u, vec![format!("argument {} of call", i + 1)]));
1076 }
1077 }
1078 if let a::CExpr::Var { name: callee_name } = callee {
1085 if let Some(callee_params) = self.fn_params.get(callee_name).cloned() {
1086 for (i, (param, arg)) in callee_params.iter().zip(args.iter()).enumerate() {
1087 if let a::TypeExpr::Refined { binding, predicate, .. } = ¶m.ty {
1088 let outcome = crate::discharge::try_discharge(
1089 predicate, binding, arg);
1090 if let crate::discharge::DischargeOutcome::Refuted { reason } = outcome {
1091 return Err(TypeError::RefinementViolation {
1092 at_node: node_id.into(),
1093 fn_name: callee_name.clone(),
1094 param_index: i,
1095 binding: binding.clone(),
1096 reason,
1097 });
1098 }
1099 }
1100 }
1101 }
1102 }
1103 let resolved_effects = self.u.resolve_effects(&effects);
1108 effs.extend(&resolved_effects);
1109 if let Some(site) = parse_site {
1117 self.pending_parse_calls.push((site, (*ret).clone()));
1118 }
1119 Ok(*ret)
1120 }
1121 Ty::Var(_) => {
1122 let mut p_tys = Vec::new();
1124 for a in args { p_tys.push(self.check_expr(a, node_id, locals, effs)?); }
1125 let r = self.u.fresh();
1126 let f = Ty::function(p_tys, EffectSet::empty(), r.clone());
1127 self.u.unify(&callee_ty, &f).map_err(|e| mismatch_err(node_id, e, &self.u, vec!["in call".into()]))?;
1128 Ok(r)
1129 }
1130 other => Err(TypeError::TypeMismatch {
1131 at_node: node_id.into(),
1132 expected: "function".into(),
1133 got: other.pretty(),
1134 context: vec!["in call".into()],
1135 }),
1136 }
1137 }
1138
1139 fn check_constructor(
1140 &mut self,
1141 name: &str,
1142 args: &[a::CExpr],
1143 node_id: &str,
1144 locals: &mut IndexMap<String, Ty>,
1145 effs: &mut EffectSet,
1146 ) -> Result<Ty, TypeError> {
1147 let owning = self.type_env.ctor_to_type.get(name).cloned()
1148 .ok_or_else(|| TypeError::UnknownVariant {
1149 at_node: node_id.into(),
1150 constructor: name.to_string(),
1151 })?;
1152 let def = self.type_env.types.get(&owning).cloned()
1153 .expect("ctor_to_type points to a real type");
1154 let variants = match &def.kind {
1155 TypeDefKind::Union(v) => v.clone(),
1156 _ => return Err(TypeError::UnknownVariant {
1157 at_node: node_id.into(),
1158 constructor: name.to_string(),
1159 }),
1160 };
1161 let mut subst = IndexMap::new();
1164 let mut con_args = Vec::with_capacity(def.params.len());
1165 for (i, _p) in def.params.iter().enumerate() {
1166 let fresh = self.u.fresh();
1167 subst.insert(i as u32, fresh.clone());
1168 con_args.push(fresh);
1169 }
1170 let payload = variants.get(name).cloned().flatten();
1171 match (payload, args) {
1172 (None, []) => Ok(Ty::Con(owning, con_args)),
1173 (Some(payload), args) => {
1174 let inst_payload = subst_vars(&payload, &subst, &IndexMap::new());
1175 let arg_count = match &inst_payload {
1176 Ty::Tuple(items) => items.len(),
1177 _ => 1,
1178 };
1179 if arg_count != args.len() {
1180 return Err(TypeError::ArityMismatch {
1181 at_node: node_id.into(),
1182 expected: arg_count,
1183 got: args.len(),
1184 });
1185 }
1186 if args.len() == 1 {
1187 let at = self.check_expr(&args[0], node_id, locals, effs)?;
1188 self.unify_with_record_coercion(&at, &inst_payload).map_err(|e| mismatch_err(node_id, e, &self.u, vec![format!("constructor `{}`", name)]))?;
1189 } else if let Ty::Tuple(items) = inst_payload {
1190 for (i, (a, t)) in args.iter().zip(items.iter()).enumerate() {
1191 let at = self.check_expr(a, node_id, locals, effs)?;
1192 self.unify_with_record_coercion(&at, t).map_err(|e| mismatch_err(node_id, e, &self.u, vec![format!("constructor `{}` arg {}", name, i + 1)]))?;
1193 }
1194 }
1195 Ok(Ty::Con(owning, con_args))
1196 }
1197 (None, _) => Err(TypeError::ArityMismatch {
1198 at_node: node_id.into(), expected: 0, got: args.len(),
1199 }),
1200 }
1201 }
1202
1203 fn bind_pattern(
1204 &mut self,
1205 pat: &a::Pattern,
1206 ty: &Ty,
1207 locals: &mut IndexMap<String, Ty>,
1208 node_id: &str,
1209 ) -> Result<(), TypeError> {
1210 match pat {
1211 a::Pattern::PWild => Ok(()),
1212 a::Pattern::PVar { name } => {
1213 locals.insert(name.clone(), ty.clone());
1214 Ok(())
1215 }
1216 a::Pattern::PLiteral { value } => {
1217 let lt = lit_type(value);
1218 self.unify_with_record_coercion(<, ty).map_err(|e| mismatch_err(node_id, e, &self.u, vec!["in pattern".into()]))?;
1219 Ok(())
1220 }
1221 a::Pattern::PConstructor { name, args } => {
1222 let owning = self.type_env.ctor_to_type.get(name).cloned()
1224 .ok_or_else(|| TypeError::UnknownVariant {
1225 at_node: node_id.into(), constructor: name.clone(),
1226 })?;
1227 let def = self.type_env.types.get(&owning).cloned().unwrap();
1228 let mut subst = IndexMap::new();
1229 let mut con_args = Vec::new();
1230 for (i, _) in def.params.iter().enumerate() {
1231 let fresh = self.u.fresh();
1232 subst.insert(i as u32, fresh.clone());
1233 con_args.push(fresh);
1234 }
1235 let con_ty = Ty::Con(owning.clone(), con_args);
1236 self.unify_with_record_coercion(&con_ty, ty).map_err(|e| mismatch_err(node_id, e, &self.u, vec![format!("constructor pattern `{}`", name)]))?;
1237 let payload = match &def.kind {
1238 TypeDefKind::Union(v) => v.get(name).cloned().flatten(),
1239 _ => None,
1240 };
1241 match (payload, args.as_slice()) {
1242 (None, []) => Ok(()),
1243 (Some(payload), args) => {
1244 let inst = subst_vars(&payload, &subst, &IndexMap::new());
1245 if args.len() == 1 {
1246 self.bind_pattern(&args[0], &inst, locals, node_id)?;
1247 } else if let Ty::Tuple(items) = inst {
1248 for (a, t) in args.iter().zip(items.iter()) {
1249 self.bind_pattern(a, t, locals, node_id)?;
1250 }
1251 }
1252 Ok(())
1253 }
1254 (None, _) => Err(TypeError::ArityMismatch {
1255 at_node: node_id.into(), expected: 0, got: args.len(),
1256 }),
1257 }
1258 }
1259 a::Pattern::PRecord { fields } => {
1260 let resolved = self.unfold_record_alias(self.u.resolve(ty));
1265 let rec = match resolved {
1266 Ty::Record(r) => r,
1267 _ => return Err(TypeError::TypeMismatch {
1268 at_node: node_id.into(),
1269 expected: "record".into(),
1270 got: ty.pretty(),
1271 context: vec!["in record pattern".into()],
1272 }),
1273 };
1274 for f in fields {
1275 let ft = rec.get(&f.name).cloned()
1276 .ok_or_else(|| TypeError::UnknownField {
1277 at_node: node_id.into(),
1278 record_type: Ty::Record(rec.clone()).pretty(),
1279 field: f.name.clone(),
1280 })?;
1281 self.bind_pattern(&f.pattern, &ft, locals, node_id)?;
1282 }
1283 Ok(())
1284 }
1285 a::Pattern::PTuple { items } => {
1286 if items.is_empty() {
1288 return self.unify_with_record_coercion(&Ty::Unit, ty)
1289 .map_err(|e| mismatch_err(node_id, e, &self.u, vec!["in unit pattern".into()]));
1290 }
1291 let resolved = self.u.resolve(ty);
1292 let tup = match resolved {
1293 Ty::Tuple(t) => t,
1294 Ty::Var(_) => {
1295 let fresh: Vec<Ty> = items.iter().map(|_| self.u.fresh()).collect();
1296 let tup_ty = Ty::Tuple(fresh.clone());
1297 self.unify_with_record_coercion(&tup_ty, ty).map_err(|e| mismatch_err(node_id, e, &self.u, vec!["in tuple pattern".into()]))?;
1298 fresh
1299 }
1300 other => {
1301 return Err(TypeError::TypeMismatch {
1302 at_node: node_id.into(),
1303 expected: "tuple".into(),
1304 got: other.pretty(),
1305 context: vec!["in tuple pattern".into()],
1306 });
1307 }
1308 };
1309 if tup.len() != items.len() {
1310 return Err(TypeError::ArityMismatch {
1311 at_node: node_id.into(), expected: tup.len(), got: items.len(),
1312 });
1313 }
1314 for (p, t) in items.iter().zip(tup.iter()) {
1315 self.bind_pattern(p, t, locals, node_id)?;
1316 }
1317 Ok(())
1318 }
1319 }
1320 }
1321}
1322
1323fn lit_type(l: &a::CLit) -> Ty {
1324 match l {
1325 a::CLit::Int { .. } => Ty::int(),
1326 a::CLit::Float { .. } => Ty::float(),
1327 a::CLit::Str { .. } => Ty::str(),
1328 a::CLit::Bytes { .. } => Ty::bytes(),
1329 a::CLit::Bool { .. } => Ty::bool(),
1330 a::CLit::Unit => Ty::Unit,
1331 }
1332}
1333
1334fn instantiate(s: &Scheme, u: &mut Unifier) -> Ty {
1335 instantiate_with_eff(s, u).0
1336}
1337
1338fn instantiate_with_eff(s: &Scheme, u: &mut Unifier) -> (Ty, IndexMap<u32, u32>) {
1343 let mut ty_subst = IndexMap::new();
1344 for v in &s.vars { ty_subst.insert(*v, u.fresh()); }
1345 let mut eff_subst = IndexMap::new();
1346 for v in &s.eff_vars { eff_subst.insert(*v, u.fresh_eff_id()); }
1347 let ty = subst_vars(&s.ty, &ty_subst, &eff_subst);
1348 (ty, eff_subst)
1349}
1350
1351fn subst_vars(
1352 t: &Ty,
1353 subst: &IndexMap<TyVarId, Ty>,
1354 eff_subst: &IndexMap<u32, u32>,
1355) -> Ty {
1356 match t {
1357 Ty::Var(v) => subst.get(v).cloned().unwrap_or_else(|| Ty::Var(*v)),
1358 Ty::Prim(_) | Ty::Unit | Ty::Never => t.clone(),
1359 Ty::List(inner) => Ty::List(Box::new(subst_vars(inner, subst, eff_subst))),
1360 Ty::Tuple(items) => Ty::Tuple(items.iter().map(|t| subst_vars(t, subst, eff_subst)).collect()),
1361 Ty::Record(fs) => {
1362 let mut out = IndexMap::new();
1363 for (k, v) in fs { out.insert(k.clone(), subst_vars(v, subst, eff_subst)); }
1364 Ty::Record(out)
1365 }
1366 Ty::Con(n, args) => Ty::Con(n.clone(),
1367 args.iter().map(|t| subst_vars(t, subst, eff_subst)).collect()),
1368 Ty::Function { params, effects, ret } => {
1369 let new_effects = EffectSet {
1372 concrete: effects.concrete.clone(),
1373 var: effects.var.and_then(|v| eff_subst.get(&v).copied()).or(effects.var),
1374 };
1375 Ty::Function {
1376 params: params.iter().map(|t| subst_vars(t, subst, eff_subst)).collect(),
1377 effects: new_effects,
1378 ret: Box::new(subst_vars(ret, subst, eff_subst)),
1379 }
1380 }
1381 }
1382}
1383
1384fn mismatch_err(node_id: &str, e: UnifyError, u: &Unifier, context: Vec<String>) -> TypeError {
1385 match e {
1386 UnifyError::Mismatch { a, b } => TypeError::TypeMismatch {
1387 at_node: node_id.into(),
1388 expected: u.resolve(&b).pretty(),
1389 got: u.resolve(&a).pretty(),
1390 context,
1391 },
1392 UnifyError::Infinite { .. } => TypeError::InfiniteType { at_node: node_id.into() },
1393 UnifyError::EffectMismatch { a, b } => {
1394 let render = |e: &EffectSet| -> String {
1399 let mut parts: Vec<String> = e.concrete.iter()
1400 .map(crate::types::EffectKind::pretty).collect();
1401 if let Some(v) = e.var { parts.push(format!("?e{}", v)); }
1402 if parts.is_empty() { "[]".into() } else { format!("[{}]", parts.join(", ")) }
1403 };
1404 TypeError::EffectRowMismatch {
1405 at_node: node_id.into(),
1406 expected: render(&b),
1407 got: render(&a),
1408 context,
1409 }
1410 }
1411 }
1412}