1use std::collections::{HashMap, HashSet};
12
13use super::{Type, parse_type_str_strict};
14use crate::ast::{
15 BinOp, Expr, FnDef, Literal, Module, Pattern, Spanned, Stmt, TailCallData, TopLevel, TypeDef,
16};
17
18mod builtins;
19pub mod effect_classification;
20pub mod effect_lifting;
21mod exhaustiveness;
22mod flow;
23mod infer;
24mod memo;
25mod modules;
26pub mod proof_trust_header;
27
28#[cfg(test)]
29mod tests;
30
31#[derive(Debug, Clone)]
36pub struct TypeError {
37 pub message: String,
38 pub line: usize,
39 pub col: usize,
40 pub secondary: Option<TypeErrorSpan>,
42}
43
44#[derive(Debug, Clone)]
45pub struct TypeErrorSpan {
46 pub line: usize,
47 pub col: usize,
48 pub label: String,
49}
50
51#[derive(Debug)]
53pub struct TypeCheckResult {
54 pub errors: Vec<TypeError>,
55 pub fn_sigs: HashMap<String, (Vec<Type>, Type, Vec<String>)>,
58 pub memo_safe_types: HashSet<String>,
60 pub unused_bindings: Vec<(String, String, usize)>,
62}
63
64pub fn run_type_check(items: &[TopLevel]) -> Vec<TypeError> {
65 run_type_check_with_base(items, None)
66}
67
68pub fn run_type_check_with_base(items: &[TopLevel], base_dir: Option<&str>) -> Vec<TypeError> {
69 run_type_check_full(items, base_dir).errors
70}
71
72pub fn run_type_check_full(items: &[TopLevel], base_dir: Option<&str>) -> TypeCheckResult {
73 let mut checker = TypeChecker::new();
74 checker.check(items, base_dir);
75 finalize_check_result(checker, items)
76}
77
78pub fn run_type_check_with_loaded(
83 items: &[TopLevel],
84 loaded: &[crate::source::LoadedModule],
85) -> TypeCheckResult {
86 let mut checker = TypeChecker::new();
87 checker.check_with_loaded(items, loaded);
88 finalize_check_result(checker, items)
89}
90
91fn finalize_check_result(checker: TypeChecker, items: &[TopLevel]) -> TypeCheckResult {
92 let fn_sigs: HashMap<String, (Vec<Type>, Type, Vec<String>)> = checker
93 .fn_sigs
94 .iter()
95 .map(|(k, v)| {
96 (
97 k.clone(),
98 (v.params.clone(), v.ret.clone(), v.effects.clone()),
99 )
100 })
101 .collect();
102
103 let memo_safe_types = checker.compute_memo_safe_types(items);
104
105 TypeCheckResult {
106 errors: checker.errors,
107 fn_sigs,
108 memo_safe_types,
109 unused_bindings: checker.unused_warnings,
110 }
111}
112
113#[derive(Debug, Clone)]
118struct FnSig {
119 params: Vec<Type>,
120 ret: Type,
121 effects: Vec<String>,
122}
123
124struct TypeChecker {
125 fn_sigs: HashMap<String, FnSig>,
126 value_members: HashMap<String, Type>,
127 record_field_types: HashMap<String, Type>,
131 sig_aliases: HashMap<String, String>,
134 type_variants: HashMap<String, Vec<String>>,
137 globals: HashMap<String, Type>,
139 locals: HashMap<String, Type>,
141 errors: Vec<TypeError>,
142 current_fn_ret: Option<Type>,
144 current_fn_line: Option<usize>,
146 opaque_types: HashSet<String>,
148 used_names: HashSet<String>,
150 fn_bindings: Vec<(String, usize)>,
152 unused_warnings: Vec<(String, String, usize)>,
154 in_verify_trace_context: bool,
161}
162
163impl TypeChecker {
164 fn new() -> Self {
165 let mut type_variants = HashMap::new();
166 type_variants.insert(
167 "Result".to_string(),
168 vec!["Ok".to_string(), "Err".to_string()],
169 );
170 type_variants.insert(
171 "Option".to_string(),
172 vec!["Some".to_string(), "None".to_string()],
173 );
174
175 let mut tc = TypeChecker {
176 fn_sigs: HashMap::new(),
177 value_members: HashMap::new(),
178 record_field_types: HashMap::new(),
179 sig_aliases: HashMap::new(),
180 type_variants,
181 globals: HashMap::new(),
182 locals: HashMap::new(),
183 errors: Vec::new(),
184 current_fn_ret: None,
185 current_fn_line: None,
186 opaque_types: HashSet::new(),
187 used_names: HashSet::new(),
188 fn_bindings: Vec::new(),
189 unused_warnings: Vec::new(),
190 in_verify_trace_context: false,
191 };
192 tc.register_builtins();
193 tc
194 }
195
196 fn find_fn_sig(&self, key: &str) -> Option<&FnSig> {
199 self.fn_sigs
200 .get(key)
201 .or_else(|| self.sig_aliases.get(key).and_then(|c| self.fn_sigs.get(c)))
202 }
203
204 fn find_value_member(&self, key: &str) -> Option<&Type> {
205 self.value_members.get(key).or_else(|| {
206 self.sig_aliases
207 .get(key)
208 .and_then(|c| self.value_members.get(c))
209 })
210 }
211
212 fn find_record_field_type(&self, key: &str) -> Option<&Type> {
213 self.record_field_types.get(key).or_else(|| {
214 self.sig_aliases
215 .get(key)
216 .and_then(|c| self.record_field_types.get(c))
217 })
218 }
219
220 fn caller_has_effect(&self, caller_effects: &[String], required_effect: &str) -> bool {
224 caller_effects
225 .iter()
226 .any(|declared| crate::effects::effect_satisfies(declared, required_effect))
227 }
228
229 fn error(&mut self, msg: impl Into<String>) {
230 let line = self.current_fn_line.unwrap_or(1);
231 self.errors.push(TypeError {
232 message: msg.into(),
233 line,
234 col: 0,
235 secondary: None,
236 });
237 }
238
239 fn error_at_line(&mut self, line: usize, msg: impl Into<String>) {
240 self.errors.push(TypeError {
241 message: msg.into(),
242 line,
243 col: 0,
244 secondary: None,
245 });
246 }
247
248 fn insert_sig(&mut self, name: &str, params: &[Type], ret: Type, effects: &[&str]) {
249 self.fn_sigs.insert(
250 name.to_string(),
251 FnSig {
252 params: params.to_vec(),
253 ret,
254 effects: effects.iter().map(|s| s.to_string()).collect(),
255 },
256 );
257 }
258
259 fn fn_type_from_sig(sig: &FnSig) -> Type {
260 Type::Fn(
261 sig.params.clone(),
262 Box::new(sig.ret.clone()),
263 sig.effects.clone(),
264 )
265 }
266
267 fn sig_from_callable_type(ty: &Type) -> Option<FnSig> {
268 match ty {
269 Type::Fn(params, ret, effects) => Some(FnSig {
270 params: params.clone(),
271 ret: *ret.clone(),
272 effects: effects.clone(),
273 }),
274 _ => None,
275 }
276 }
277
278 fn binding_type(&self, name: &str) -> Option<Type> {
279 self.locals
280 .get(name)
281 .or_else(|| self.globals.get(name))
282 .cloned()
283 }
284
285 pub(super) fn constraint_compatible(actual: &Type, expected: &Type) -> bool {
292 if matches!(actual, Type::Unknown) && !matches!(expected, Type::Unknown) {
293 return false;
294 }
295 actual.compatible(expected)
296 }
297}