1use std::collections::{HashMap, HashSet};
12use std::path::Path;
13
14use super::{Type, parse_type_str_strict};
15use crate::ast::{BinOp, Expr, FnBody, FnDef, Literal, Module, Pattern, Stmt, TopLevel, TypeDef};
16use crate::source::{
17 canonicalize_path, find_module_file, parse_source, require_module_declaration,
18};
19
20mod builtins;
21mod exhaustiveness;
22mod flow;
23mod infer;
24mod memo;
25mod modules;
26
27#[cfg(test)]
28mod tests;
29
30#[derive(Debug, Clone)]
35pub struct TypeError {
36 pub message: String,
37 pub line: usize,
38 pub col: usize,
39}
40
41#[derive(Debug)]
43pub struct TypeCheckResult {
44 pub errors: Vec<TypeError>,
45 pub fn_sigs: HashMap<String, (Vec<Type>, Type, Vec<String>)>,
48 pub memo_safe_types: HashSet<String>,
50}
51
52pub fn run_type_check(items: &[TopLevel]) -> Vec<TypeError> {
53 run_type_check_with_base(items, None)
54}
55
56pub fn run_type_check_with_base(items: &[TopLevel], base_dir: Option<&str>) -> Vec<TypeError> {
57 run_type_check_full(items, base_dir).errors
58}
59
60pub fn run_type_check_full(items: &[TopLevel], base_dir: Option<&str>) -> TypeCheckResult {
61 let mut checker = TypeChecker::new();
62 checker.check(items, base_dir);
63
64 let fn_sigs: HashMap<String, (Vec<Type>, Type, Vec<String>)> = checker
66 .fn_sigs
67 .iter()
68 .map(|(k, v)| {
69 (
70 k.clone(),
71 (v.params.clone(), v.ret.clone(), v.effects.clone()),
72 )
73 })
74 .collect();
75
76 let memo_safe_types = checker.compute_memo_safe_types(items);
78
79 TypeCheckResult {
80 errors: checker.errors,
81 fn_sigs,
82 memo_safe_types,
83 }
84}
85
86#[derive(Debug, Clone)]
91struct FnSig {
92 params: Vec<Type>,
93 ret: Type,
94 effects: Vec<String>,
95}
96
97#[derive(Debug, Clone)]
98struct ModuleSigCache {
99 fn_entries: Vec<(String, FnSig)>,
100 value_entries: Vec<(String, Type)>,
101 record_field_entries: Vec<(String, Type)>,
102 type_variants: Vec<(String, Vec<String>)>,
103}
104
105struct TypeChecker {
106 fn_sigs: HashMap<String, FnSig>,
107 module_sig_cache: HashMap<String, ModuleSigCache>,
108 value_members: HashMap<String, Type>,
109 record_field_types: HashMap<String, Type>,
113 type_variants: HashMap<String, Vec<String>>,
116 effect_aliases: HashMap<String, Vec<String>>,
118 globals: HashMap<String, Type>,
120 locals: HashMap<String, Type>,
122 errors: Vec<TypeError>,
123 current_fn_ret: Option<Type>,
125 current_fn_line: Option<usize>,
127}
128
129impl TypeChecker {
130 fn new() -> Self {
131 let mut type_variants = HashMap::new();
132 type_variants.insert(
133 "Result".to_string(),
134 vec!["Ok".to_string(), "Err".to_string()],
135 );
136 type_variants.insert(
137 "Option".to_string(),
138 vec!["Some".to_string(), "None".to_string()],
139 );
140
141 let mut tc = TypeChecker {
142 fn_sigs: HashMap::new(),
143 module_sig_cache: HashMap::new(),
144 value_members: HashMap::new(),
145 record_field_types: HashMap::new(),
146 type_variants,
147 effect_aliases: HashMap::new(),
148 globals: HashMap::new(),
149 locals: HashMap::new(),
150 errors: Vec::new(),
151 current_fn_ret: None,
152 current_fn_line: None,
153 };
154 tc.register_builtins();
155 tc
156 }
157
158 fn expand_effects(&self, effects: &[String]) -> Vec<String> {
160 let mut result = Vec::new();
161 for e in effects {
162 if let Some(expanded) = self.effect_aliases.get(e) {
163 result.extend(expanded.iter().cloned());
164 } else {
165 result.push(e.clone());
166 }
167 }
168 result
169 }
170
171 fn caller_has_effect(&self, caller_effects: &[String], required_effect: &str) -> bool {
173 let expanded_caller = self.expand_effects(caller_effects);
174 let expanded_required = self.expand_effects(&[required_effect.to_string()]);
176 expanded_required
177 .iter()
178 .all(|e| expanded_caller.contains(e))
179 }
180
181 fn error(&mut self, msg: impl Into<String>) {
182 let line = self.current_fn_line.unwrap_or(1);
183 self.errors.push(TypeError {
184 message: msg.into(),
185 line,
186 col: 0,
187 });
188 }
189
190 fn error_at_line(&mut self, line: usize, msg: impl Into<String>) {
191 self.errors.push(TypeError {
192 message: msg.into(),
193 line,
194 col: 0,
195 });
196 }
197
198 fn insert_sig(&mut self, name: &str, params: &[Type], ret: Type, effects: &[&str]) {
199 self.fn_sigs.insert(
200 name.to_string(),
201 FnSig {
202 params: params.to_vec(),
203 ret,
204 effects: effects.iter().map(|s| s.to_string()).collect(),
205 },
206 );
207 }
208
209 fn fn_type_from_sig(sig: &FnSig) -> Type {
210 Type::Fn(
211 sig.params.clone(),
212 Box::new(sig.ret.clone()),
213 sig.effects.clone(),
214 )
215 }
216
217 fn sig_from_callable_type(ty: &Type) -> Option<FnSig> {
218 match ty {
219 Type::Fn(params, ret, effects) => Some(FnSig {
220 params: params.clone(),
221 ret: *ret.clone(),
222 effects: effects.clone(),
223 }),
224 _ => None,
225 }
226 }
227
228 fn binding_type(&self, name: &str) -> Option<Type> {
229 self.locals
230 .get(name)
231 .or_else(|| self.globals.get(name))
232 .cloned()
233 }
234
235 pub(super) fn constraint_compatible(actual: &Type, expected: &Type) -> bool {
242 if matches!(actual, Type::Unknown) && !matches!(expected, Type::Unknown) {
243 return false;
244 }
245 actual.compatible(expected)
246 }
247}