1mod expr_checker;
2mod item_checker;
3mod stmt_checker;
4mod type_env;
5use crate::modules::{LoadedModule, ModuleImports};
6use crate::{
7 ast::*,
8 config::LustConfig,
9 error::{LustError, Result},
10};
11pub(super) use alloc::{
12 boxed::Box,
13 format,
14 string::{String, ToString},
15 vec,
16 vec::Vec,
17};
18use core::mem;
19use hashbrown::{HashMap, HashSet};
20pub use type_env::FunctionSignature;
21pub use type_env::TypeEnv;
22pub struct TypeChecker {
23 env: TypeEnv,
24 current_function_return_type: Option<Type>,
25 in_loop: bool,
26 pending_generic_instances: Option<HashMap<String, Type>>,
27 expected_lambda_signature: Option<(Vec<Type>, Option<Type>)>,
28 current_trait_bounds: HashMap<String, Vec<String>>,
29 current_module: Option<String>,
30 imports_by_module: HashMap<String, ModuleImports>,
31 expr_types_by_module: HashMap<String, HashMap<Span, Type>>,
32 variable_types_by_module: HashMap<String, HashMap<Span, Type>>,
33 short_circuit_info: HashMap<String, HashMap<Span, ShortCircuitInfo>>,
34}
35
36pub struct TypeCollection {
37 pub expr_types: HashMap<String, HashMap<Span, Type>>,
38 pub variable_types: HashMap<String, HashMap<Span, Type>>,
39}
40
41#[derive(Clone, Debug)]
42struct ShortCircuitInfo {
43 truthy: Option<Type>,
44 falsy: Option<Type>,
45 option_inner: Option<Type>,
46}
47
48impl TypeChecker {
49 pub fn new() -> Self {
50 Self::with_config(&LustConfig::default())
51 }
52
53 pub fn with_config(config: &LustConfig) -> Self {
54 Self {
55 env: TypeEnv::with_config(config),
56 current_function_return_type: None,
57 in_loop: false,
58 pending_generic_instances: None,
59 expected_lambda_signature: None,
60 current_trait_bounds: HashMap::new(),
61 current_module: None,
62 imports_by_module: HashMap::new(),
63 expr_types_by_module: HashMap::new(),
64 variable_types_by_module: HashMap::new(),
65 short_circuit_info: HashMap::new(),
66 }
67 }
68
69 fn dummy_span() -> Span {
70 Span::new(0, 0, 0, 0)
71 }
72
73 pub fn check_module(&mut self, items: &[Item]) -> Result<()> {
74 for item in items {
75 self.register_type_definition(item)?;
76 }
77
78 self.validate_struct_cycles()?;
79 self.env.push_scope();
80 self.register_module_init_locals(items)?;
81 for item in items {
82 self.check_item(item)?;
83 }
84
85 self.env.pop_scope();
86 Ok(())
87 }
88
89 pub fn check_program(&mut self, modules: &[LoadedModule]) -> Result<()> {
90 for m in modules {
91 self.current_module = Some(m.path.clone());
92 for item in &m.items {
93 self.register_type_definition(item)?;
94 }
95 }
96
97 self.validate_struct_cycles()?;
98 for m in modules {
99 self.current_module = Some(m.path.clone());
100 self.env.push_scope();
101 self.register_module_init_locals(&m.items)?;
102 for item in &m.items {
103 self.check_item(item)?;
104 }
105
106 self.env.pop_scope();
107 }
108
109 self.current_module = None;
110 Ok(())
111 }
112
113 fn validate_struct_cycles(&self) -> Result<()> {
114 use hashbrown::{HashMap, HashSet};
115 let struct_defs = self.env.struct_definitions();
116 if struct_defs.is_empty() {
117 return Ok(());
118 }
119
120 let mut simple_to_full: HashMap<String, Vec<String>> = HashMap::new();
121 for name in struct_defs.keys() {
122 let simple = name.rsplit('.').next().unwrap_or(name).to_string();
123 simple_to_full.entry(simple).or_default().push(name.clone());
124 }
125
126 let mut struct_has_weak: HashMap<String, bool> = HashMap::new();
127 for (name, def) in &struct_defs {
128 let has_weak = def
129 .fields
130 .iter()
131 .any(|field| matches!(field.ownership, FieldOwnership::Weak));
132 struct_has_weak.insert(name.clone(), has_weak);
133 }
134
135 let mut graph: HashMap<String, Vec<String>> = HashMap::new();
136 for (name, def) in &struct_defs {
137 let module_prefix = name.rsplit_once('.').map(|(module, _)| module.to_string());
138 let mut edges: HashSet<String> = HashSet::new();
139 for field in &def.fields {
140 if matches!(field.ownership, FieldOwnership::Weak) {
141 let target = field.weak_target.as_ref().ok_or_else(|| {
142 self.type_error(format!(
143 "Field '{}.{}' is marked as 'ref' but has no target type",
144 name, field.name
145 ))
146 })?;
147 let target_name = if let TypeKind::Named(inner) = &target.kind {
148 inner
149 } else {
150 return Err(self.type_error(format!(
151 "Field '{}.{}' uses 'ref' but only struct types are supported",
152 name, field.name
153 )));
154 };
155 let resolved = self.resolve_struct_name_for_cycle(
156 target_name.as_str(),
157 module_prefix.as_deref(),
158 &struct_defs,
159 &simple_to_full,
160 );
161 if resolved.is_none() {
162 return Err(self.type_error(format!(
163 "Field '{}.{}' uses 'ref' but '{}' is not a known struct type",
164 name, field.name, target_name
165 )));
166 }
167
168 continue;
169 }
170
171 self.collect_strong_struct_targets(
172 &field.ty,
173 module_prefix.as_deref(),
174 &struct_defs,
175 &simple_to_full,
176 &mut edges,
177 );
178 }
179
180 graph.insert(name.clone(), edges.into_iter().collect());
181 }
182
183 fn dfs(
184 node: &str,
185 graph: &HashMap<String, Vec<String>>,
186 visited: &mut HashSet<String>,
187 on_stack: &mut HashSet<String>,
188 stack: &mut Vec<String>,
189 ) -> Option<Vec<String>> {
190 visited.insert(node.to_string());
191 on_stack.insert(node.to_string());
192 stack.push(node.to_string());
193 if let Some(neighbors) = graph.get(node) {
194 for neighbor in neighbors {
195 if !visited.contains(neighbor) {
196 if let Some(cycle) = dfs(neighbor, graph, visited, on_stack, stack) {
197 return Some(cycle);
198 }
199 } else if on_stack.contains(neighbor) {
200 if let Some(pos) = stack.iter().position(|n| n == neighbor) {
201 let mut cycle = stack[pos..].to_vec();
202 cycle.push(neighbor.clone());
203 return Some(cycle);
204 }
205 }
206 }
207 }
208
209 stack.pop();
210 on_stack.remove(node);
211 None
212 }
213
214 let mut visited: HashSet<String> = HashSet::new();
215 let mut on_stack: HashSet<String> = HashSet::new();
216 let mut stack: Vec<String> = Vec::new();
217 for name in struct_defs.keys() {
218 if !visited.contains(name) {
219 if let Some(cycle) = dfs(name, &graph, &mut visited, &mut on_stack, &mut stack) {
220 let contains_weak = cycle
221 .iter()
222 .any(|node| struct_has_weak.get(node).copied().unwrap_or(false));
223 if contains_weak {
224 continue;
225 }
226
227 let description = cycle.join(" -> ");
228 return Err(self.type_error(format!(
229 "Strong ownership cycle detected: {}. Mark at least one field as 'ref' to break the cycle.",
230 description
231 )));
232 }
233 }
234 }
235
236 Ok(())
237 }
238
239 fn collect_strong_struct_targets(
240 &self,
241 ty: &Type,
242 parent_module: Option<&str>,
243 struct_defs: &HashMap<String, StructDef>,
244 simple_to_full: &HashMap<String, Vec<String>>,
245 out: &mut HashSet<String>,
246 ) {
247 match &ty.kind {
248 TypeKind::Named(name) => {
249 if let Some(resolved) = self.resolve_struct_name_for_cycle(
250 name,
251 parent_module,
252 struct_defs,
253 simple_to_full,
254 ) {
255 out.insert(resolved);
256 }
257 }
258
259 TypeKind::Array(inner)
260 | TypeKind::Ref(inner)
261 | TypeKind::MutRef(inner)
262 | TypeKind::Option(inner) => {
263 self.collect_strong_struct_targets(
264 inner,
265 parent_module,
266 struct_defs,
267 simple_to_full,
268 out,
269 );
270 }
271
272 TypeKind::Map(key, value) => {
273 self.collect_strong_struct_targets(
274 key,
275 parent_module,
276 struct_defs,
277 simple_to_full,
278 out,
279 );
280 self.collect_strong_struct_targets(
281 value,
282 parent_module,
283 struct_defs,
284 simple_to_full,
285 out,
286 );
287 }
288
289 TypeKind::Tuple(elements) | TypeKind::Union(elements) => {
290 for element in elements {
291 self.collect_strong_struct_targets(
292 element,
293 parent_module,
294 struct_defs,
295 simple_to_full,
296 out,
297 );
298 }
299 }
300
301 TypeKind::Result(ok, err) => {
302 self.collect_strong_struct_targets(
303 ok,
304 parent_module,
305 struct_defs,
306 simple_to_full,
307 out,
308 );
309 self.collect_strong_struct_targets(
310 err,
311 parent_module,
312 struct_defs,
313 simple_to_full,
314 out,
315 );
316 }
317
318 TypeKind::GenericInstance { type_args, .. } => {
319 for arg in type_args {
320 self.collect_strong_struct_targets(
321 arg,
322 parent_module,
323 struct_defs,
324 simple_to_full,
325 out,
326 );
327 }
328 }
329
330 _ => {}
331 }
332 }
333
334 fn resolve_struct_name_for_cycle(
335 &self,
336 name: &str,
337 parent_module: Option<&str>,
338 struct_defs: &HashMap<String, StructDef>,
339 simple_to_full: &HashMap<String, Vec<String>>,
340 ) -> Option<String> {
341 if struct_defs.contains_key(name) {
342 return Some(name.to_string());
343 }
344
345 if name.contains('.') {
346 return None;
347 }
348
349 if let Some(candidates) = simple_to_full.get(name) {
350 if candidates.len() == 1 {
351 return Some(candidates[0].clone());
352 }
353
354 if let Some(module) = parent_module {
355 for candidate in candidates {
356 if let Some((candidate_module, _)) = candidate.rsplit_once('.') {
357 if candidate_module == module {
358 return Some(candidate.clone());
359 }
360 }
361 }
362 }
363 }
364
365 None
366 }
367
368 pub fn set_imports_by_module(&mut self, map: HashMap<String, ModuleImports>) {
369 self.imports_by_module = map;
370 }
371
372 pub fn take_type_info(&mut self) -> TypeCollection {
373 TypeCollection {
374 expr_types: mem::take(&mut self.expr_types_by_module),
375 variable_types: mem::take(&mut self.variable_types_by_module),
376 }
377 }
378
379 pub fn take_option_coercions(&mut self) -> HashMap<String, HashSet<Span>> {
380 let mut result: HashMap<String, HashSet<Span>> = HashMap::new();
381 let info = mem::take(&mut self.short_circuit_info);
382 for (module, entries) in info {
383 let mut spans: HashSet<Span> = HashSet::new();
384 for (span, entry) in entries {
385 if entry.option_inner.is_some() {
386 spans.insert(span);
387 }
388 }
389 if !spans.is_empty() {
390 result.insert(module, spans);
391 }
392 }
393
394 result
395 }
396
397 pub fn function_signatures(&self) -> HashMap<String, type_env::FunctionSignature> {
398 self.env.function_signatures()
399 }
400
401 pub fn struct_definitions(&self) -> HashMap<String, StructDef> {
402 self.env.struct_definitions()
403 }
404
405 pub fn enum_definitions(&self) -> HashMap<String, EnumDef> {
406 self.env.enum_definitions()
407 }
408
409 fn register_module_init_locals(&mut self, items: &[Item]) -> Result<()> {
410 let module = match &self.current_module {
411 Some(m) => m.clone(),
412 None => return Ok(()),
413 };
414 let init_name = format!("__init@{}", module);
415 for item in items {
416 if let ItemKind::Function(func) = &item.kind {
417 if func.name == init_name {
418 for stmt in &func.body {
419 if let StmtKind::Local {
420 bindings,
421 ref mutable,
422 initializer,
423 } = &stmt.kind
424 {
425 self.check_local_stmt(
426 bindings.as_slice(),
427 *mutable,
428 initializer.as_ref().map(|values| values.as_slice()),
429 )?;
430 }
431 }
432 }
433 }
434 }
435
436 Ok(())
437 }
438
439 pub fn resolve_function_key(&self, name: &str) -> String {
440 if name.contains('.') || name.contains(':') {
441 return name.to_string();
442 }
443
444 if let Some(module) = &self.current_module {
445 if let Some(imports) = self.imports_by_module.get(module) {
446 if let Some(fq) = imports.function_aliases.get(name) {
447 return fq.clone();
448 }
449 }
450
451 let qualified = format!("{}.{}", module, name);
452 if self.env.lookup_function(&qualified).is_some() {
453 return qualified;
454 }
455
456 if self.env.lookup_function(name).is_some() {
457 return name.to_string();
458 }
459
460 return qualified;
461 }
462
463 name.to_string()
464 }
465
466 pub fn resolve_module_alias(&self, alias: &str) -> Option<String> {
467 if let Some(module) = &self.current_module {
468 if let Some(imports) = self.imports_by_module.get(module) {
469 if let Some(m) = imports.module_aliases.get(alias) {
470 return Some(m.clone());
471 }
472 }
473 }
474
475 None
476 }
477
478 pub fn resolve_type_key(&self, name: &str) -> String {
479 if let Some((head, tail)) = name.split_once('.') {
480 if let Some(module) = &self.current_module {
481 if let Some(imports) = self.imports_by_module.get(module) {
482 if let Some(real_module) = imports.module_aliases.get(head) {
483 if tail.is_empty() {
484 return real_module.clone();
485 } else {
486 return format!("{}.{}", real_module, tail);
487 }
488 }
489 }
490 }
491
492 return name.to_string();
493 }
494
495 if self.env.lookup_struct(name).is_some()
496 || self.env.lookup_enum(name).is_some()
497 || self.env.lookup_trait(name).is_some()
498 {
499 return name.to_string();
500 }
501
502 if self.env.is_builtin_type(name) {
503 return name.to_string();
504 }
505
506 if let Some(module) = &self.current_module {
507 if let Some(imports) = self.imports_by_module.get(module) {
508 if let Some(fq) = imports.type_aliases.get(name) {
509 return fq.clone();
510 }
511 }
512
513 return format!("{}.{}", module, name);
514 }
515
516 name.to_string()
517 }
518
519 fn register_type_definition(&mut self, item: &Item) -> Result<()> {
520 match &item.kind {
521 ItemKind::Struct(s) => {
522 let mut s2 = s.clone();
523 if let Some(module) = &self.current_module {
524 if !s2.name.contains('.') {
525 s2.name = format!("{}.{}", module, s2.name);
526 }
527 }
528
529 self.env.register_struct(&s2)?;
530 }
531
532 ItemKind::Enum(e) => {
533 let mut e2 = e.clone();
534 if let Some(module) = &self.current_module {
535 if !e2.name.contains('.') {
536 e2.name = format!("{}.{}", module, e2.name);
537 }
538 }
539
540 self.env.register_enum(&e2)?;
541 }
542
543 ItemKind::Trait(t) => {
544 let mut t2 = t.clone();
545 if let Some(module) = &self.current_module {
546 if !t2.name.contains('.') {
547 t2.name = format!("{}.{}", module, t2.name);
548 }
549 }
550
551 self.env.register_trait(&t2)?;
552 }
553
554 ItemKind::TypeAlias {
555 name,
556 type_params,
557 target,
558 } => {
559 let qname = if let Some(module) = &self.current_module {
560 if name.contains('.') {
561 name.clone()
562 } else {
563 format!("{}.{}", module, name)
564 }
565 } else {
566 name.clone()
567 };
568 self.env
569 .register_type_alias(qname, type_params.clone(), target.clone())?;
570 }
571
572 _ => {}
573 }
574
575 Ok(())
576 }
577
578 fn type_error(&self, message: String) -> LustError {
579 LustError::TypeError { message }
580 }
581
582 fn type_error_at(&self, message: String, span: Span) -> LustError {
583 if span.start_line > 0 {
584 LustError::TypeErrorWithSpan {
585 message,
586 line: span.start_line,
587 column: span.start_col,
588 module: self.current_module.clone(),
589 }
590 } else {
591 LustError::TypeError { message }
592 }
593 }
594
595 fn types_equal(&self, t1: &Type, t2: &Type) -> bool {
596 t1.kind == t2.kind
597 }
598
599 pub fn canonicalize_type(&self, ty: &Type) -> Type {
600 use crate::ast::TypeKind as TK;
601 match &ty.kind {
602 TK::Named(name) => Type::new(TK::Named(self.resolve_type_key(name)), ty.span),
603 TK::Array(inner) => {
604 Type::new(TK::Array(Box::new(self.canonicalize_type(inner))), ty.span)
605 }
606
607 TK::Tuple(elements) => Type::new(
608 TK::Tuple(elements.iter().map(|t| self.canonicalize_type(t)).collect()),
609 ty.span,
610 ),
611 TK::Option(inner) => {
612 Type::new(TK::Option(Box::new(self.canonicalize_type(inner))), ty.span)
613 }
614
615 TK::Result(ok, err) => Type::new(
616 TK::Result(
617 Box::new(self.canonicalize_type(ok)),
618 Box::new(self.canonicalize_type(err)),
619 ),
620 ty.span,
621 ),
622 TK::Map(k, v) => Type::new(
623 TK::Map(
624 Box::new(self.canonicalize_type(k)),
625 Box::new(self.canonicalize_type(v)),
626 ),
627 ty.span,
628 ),
629 TK::Ref(inner) => Type::new(TK::Ref(Box::new(self.canonicalize_type(inner))), ty.span),
630 TK::MutRef(inner) => {
631 Type::new(TK::MutRef(Box::new(self.canonicalize_type(inner))), ty.span)
632 }
633
634 TK::Pointer { mutable, pointee } => Type::new(
635 TK::Pointer {
636 mutable: *mutable,
637 pointee: Box::new(self.canonicalize_type(pointee)),
638 },
639 ty.span,
640 ),
641 _ => ty.clone(),
642 }
643 }
644
645 fn unify(&self, expected: &Type, actual: &Type) -> Result<()> {
646 let span = if actual.span.start_line > 0 {
647 Some(actual.span)
648 } else if expected.span.start_line > 0 {
649 Some(expected.span)
650 } else {
651 None
652 };
653 self.unify_at(expected, actual, span)
654 }
655
656 fn unify_at(&self, expected: &Type, actual: &Type, span: Option<Span>) -> Result<()> {
657 if matches!(expected.kind, TypeKind::Unknown) || matches!(actual.kind, TypeKind::Unknown) {
658 return Ok(());
659 }
660
661 if matches!(expected.kind, TypeKind::Infer) || matches!(actual.kind, TypeKind::Infer) {
662 return Ok(());
663 }
664
665 match (&expected.kind, &actual.kind) {
666 (TypeKind::Union(expected_types), TypeKind::Union(actual_types)) => {
667 if expected_types.len() != actual_types.len() {
668 return Err(self.type_error(format!(
669 "Union types have different number of members: expected {}, got {}",
670 expected_types.len(),
671 actual_types.len()
672 )));
673 }
674
675 for exp_type in expected_types {
676 let mut found = false;
677 for act_type in actual_types {
678 if self.types_equal(exp_type, act_type) {
679 found = true;
680 break;
681 }
682 }
683
684 if !found {
685 return Err(match span {
686 Some(s) => self.type_error_at(
687 format!(
688 "Union type member '{}' not found in actual union",
689 exp_type
690 ),
691 s,
692 ),
693 None => self.type_error(format!(
694 "Union type member '{}' not found in actual union",
695 exp_type
696 )),
697 });
698 }
699 }
700
701 return Ok(());
702 }
703
704 (TypeKind::Union(expected_types), _) => {
705 for union_member in expected_types {
706 if self.unify(union_member, actual).is_ok() {
707 return Ok(());
708 }
709 }
710
711 return Err(match span {
712 Some(s) => self.type_error_at(
713 format!("Type '{}' is not compatible with union type", actual),
714 s,
715 ),
716 None => self.type_error(format!(
717 "Type '{}' is not compatible with union type",
718 actual
719 )),
720 });
721 }
722
723 (_, TypeKind::Union(actual_types)) => {
724 for union_member in actual_types {
725 self.unify(expected, union_member)?;
726 }
727
728 return Ok(());
729 }
730
731 _ => {}
732 }
733
734 match (&expected.kind, &actual.kind) {
735 (TypeKind::Tuple(expected_elems), TypeKind::Tuple(actual_elems)) => {
736 if expected_elems.len() != actual_elems.len() {
737 return Err(match span {
738 Some(s) => self.type_error_at(
739 format!(
740 "Tuple length mismatch: expected {} element(s), got {}",
741 expected_elems.len(),
742 actual_elems.len()
743 ),
744 s,
745 ),
746 None => self.type_error(format!(
747 "Tuple length mismatch: expected {} element(s), got {}",
748 expected_elems.len(),
749 actual_elems.len()
750 )),
751 });
752 }
753
754 for (exp_elem, act_elem) in expected_elems.iter().zip(actual_elems.iter()) {
755 self.unify(exp_elem, act_elem)?;
756 }
757
758 return Ok(());
759 }
760
761 (TypeKind::Tuple(_), _) | (_, TypeKind::Tuple(_)) => {
762 return Err(match span {
763 Some(s) => self.type_error_at(
764 format!("Tuple type is not compatible with type '{}'", actual),
765 s,
766 ),
767 None => self.type_error(format!(
768 "Tuple type is not compatible with type '{}'",
769 actual
770 )),
771 })
772 }
773
774 (TypeKind::Named(name), TypeKind::Array(_))
775 | (TypeKind::Array(_), TypeKind::Named(name))
776 if name == "Array" =>
777 {
778 return Ok(());
779 }
780
781 (TypeKind::Array(exp_el), TypeKind::Array(act_el)) => {
782 if matches!(exp_el.kind, TypeKind::Unknown | TypeKind::Infer)
783 || matches!(act_el.kind, TypeKind::Unknown | TypeKind::Infer)
784 {
785 return Ok(());
786 } else {
787 return self.unify(exp_el, act_el);
788 }
789 }
790
791 (TypeKind::Map(exp_key, exp_value), TypeKind::Map(act_key, act_value)) => {
792 self.unify(exp_key, act_key)?;
793 return self.unify(exp_value, act_value);
794 }
795
796 (TypeKind::Named(name), TypeKind::Option(_))
797 | (TypeKind::Option(_), TypeKind::Named(name))
798 if name == "Option" =>
799 {
800 return Ok(());
801 }
802
803 (TypeKind::Option(exp_inner), TypeKind::Option(act_inner)) => {
804 if matches!(exp_inner.kind, TypeKind::Unknown | TypeKind::Infer)
805 || matches!(act_inner.kind, TypeKind::Unknown | TypeKind::Infer)
806 {
807 return Ok(());
808 } else {
809 return self.unify(exp_inner, act_inner);
810 }
811 }
812
813 (TypeKind::Named(name), TypeKind::Result(_, _))
814 | (TypeKind::Result(_, _), TypeKind::Named(name))
815 if name == "Result" =>
816 {
817 return Ok(());
818 }
819
820 (TypeKind::Result(exp_ok, exp_err), TypeKind::Result(act_ok, act_err)) => {
821 if matches!(exp_ok.kind, TypeKind::Unknown | TypeKind::Infer)
822 || matches!(act_ok.kind, TypeKind::Unknown | TypeKind::Infer)
823 {
824 if matches!(exp_err.kind, TypeKind::Unknown | TypeKind::Infer)
825 || matches!(act_err.kind, TypeKind::Unknown | TypeKind::Infer)
826 {
827 return Ok(());
828 } else {
829 return self.unify(exp_err, act_err);
830 }
831 } else {
832 self.unify(exp_ok, act_ok)?;
833 return self.unify(exp_err, act_err);
834 }
835 }
836
837 _ => {}
838 }
839
840 if self.types_equal(expected, actual) {
841 Ok(())
842 } else {
843 Err(match span {
844 Some(s) => self.type_error_at(
845 format!("Type mismatch: expected '{}', got '{}'", expected, actual),
846 s,
847 ),
848 None => self.type_error(format!(
849 "Type mismatch: expected '{}', got '{}'",
850 expected, actual
851 )),
852 })
853 }
854 }
855
856 fn types_compatible(&self, expected: &Type, actual: &Type) -> bool {
857 if matches!(expected.kind, TypeKind::Unknown) || matches!(actual.kind, TypeKind::Unknown) {
858 return true;
859 }
860
861 if matches!(expected.kind, TypeKind::Infer) || matches!(actual.kind, TypeKind::Infer) {
862 return true;
863 }
864
865 match (&expected.kind, &actual.kind) {
866 (TypeKind::Generic(_), TypeKind::Generic(_)) => return true,
867 (TypeKind::Generic(_), _) | (_, TypeKind::Generic(_)) => return true,
868 _ => {}
869 }
870
871 match (&expected.kind, &actual.kind) {
872 (TypeKind::Array(e1), TypeKind::Array(e2)) => {
873 return self.types_compatible(e1, e2);
874 }
875
876 (TypeKind::Named(name), TypeKind::Array(_))
877 | (TypeKind::Array(_), TypeKind::Named(name))
878 if name == "Array" =>
879 {
880 return true;
881 }
882
883 _ => {}
884 }
885
886 match (&expected.kind, &actual.kind) {
887 (TypeKind::Map(k1, v1), TypeKind::Map(k2, v2)) => {
888 return self.types_compatible(k1, k2) && self.types_compatible(v1, v2);
889 }
890
891 _ => {}
892 }
893
894 match (&expected.kind, &actual.kind) {
895 (TypeKind::Option(t1), TypeKind::Option(t2)) => {
896 return self.types_compatible(t1, t2);
897 }
898
899 (TypeKind::Named(name), TypeKind::Option(_))
900 | (TypeKind::Option(_), TypeKind::Named(name))
901 if name == "Option" =>
902 {
903 return true;
904 }
905
906 _ => {}
907 }
908
909 match (&expected.kind, &actual.kind) {
910 (TypeKind::Result(ok1, err1), TypeKind::Result(ok2, err2)) => {
911 return self.types_compatible(ok1, ok2) && self.types_compatible(err1, err2);
912 }
913
914 (TypeKind::Named(name), TypeKind::Result(_, _))
915 | (TypeKind::Result(_, _), TypeKind::Named(name))
916 if name == "Result" =>
917 {
918 return true;
919 }
920
921 _ => {}
922 }
923
924 match (&expected.kind, &actual.kind) {
925 (
926 TypeKind::Function {
927 params: p1,
928 return_type: r1,
929 },
930 TypeKind::Function {
931 params: p2,
932 return_type: r2,
933 },
934 ) => {
935 if p1.len() != p2.len() {
936 return false;
937 }
938
939 for (t1, t2) in p1.iter().zip(p2.iter()) {
940 if !self.types_compatible(t1, t2) {
941 return false;
942 }
943 }
944
945 return self.types_compatible(r1, r2);
946 }
947
948 _ => {}
949 }
950
951 self.types_equal(expected, actual)
952 }
953
954 fn unify_with_bounds(&self, expected: &Type, actual: &Type) -> Result<()> {
955 if let TypeKind::Generic(type_param) = &expected.kind {
956 if let Some(trait_names) = self.current_trait_bounds.get(type_param) {
957 for trait_name in trait_names {
958 if !self.env.type_implements_trait(actual, trait_name) {
959 return Err(self.type_error(format!(
960 "Type '{}' does not implement required trait '{}'",
961 actual, trait_name
962 )));
963 }
964 }
965
966 return Ok(());
967 }
968
969 return Ok(());
970 }
971
972 self.unify(expected, actual)
973 }
974
975 fn record_short_circuit_info(&mut self, span: Span, info: &ShortCircuitInfo) {
976 let truthy = info.truthy.as_ref().map(|ty| self.canonicalize_type(ty));
977 let falsy = info.falsy.as_ref().map(|ty| self.canonicalize_type(ty));
978 let option_inner = info
979 .option_inner
980 .as_ref()
981 .map(|ty| self.canonicalize_type(ty));
982 let module_key = self.current_module_key();
983 self.short_circuit_info
984 .entry(module_key)
985 .or_default()
986 .insert(
987 span,
988 ShortCircuitInfo {
989 truthy,
990 falsy,
991 option_inner,
992 },
993 );
994 }
995
996 fn short_circuit_profile(&self, expr: &Expr, ty: &Type) -> ShortCircuitInfo {
997 let module_key = self
998 .current_module
999 .as_ref()
1000 .map(String::as_str)
1001 .unwrap_or("");
1002 if let Some(module_map) = self.short_circuit_info.get(module_key) {
1003 if let Some(info) = module_map.get(&expr.span) {
1004 return info.clone();
1005 }
1006 }
1007
1008 ShortCircuitInfo {
1009 truthy: if self.type_can_be_truthy(ty) {
1010 Some(self.canonicalize_type(ty))
1011 } else {
1012 None
1013 },
1014 falsy: self.extract_falsy_type(ty),
1015 option_inner: None,
1016 }
1017 }
1018
1019 fn current_module_key(&self) -> String {
1020 self.current_module
1021 .as_ref()
1022 .cloned()
1023 .unwrap_or_else(|| "".to_string())
1024 }
1025
1026 fn clear_option_for_span(&mut self, span: Span) {
1027 let module_key = self.current_module_key();
1028 if let Some(module_map) = self.short_circuit_info.get_mut(&module_key) {
1029 if let Some(info) = module_map.get_mut(&span) {
1030 info.option_inner = None;
1031 }
1032 }
1033 }
1034
1035 fn type_can_be_truthy(&self, ty: &Type) -> bool {
1036 match &ty.kind {
1037 TypeKind::Union(members) => {
1038 members.iter().any(|member| self.type_can_be_truthy(member))
1039 }
1040 TypeKind::Bool => true,
1041 TypeKind::Unknown => true,
1042 _ => true,
1043 }
1044 }
1045
1046 fn type_can_be_falsy(&self, ty: &Type) -> bool {
1047 match &ty.kind {
1048 TypeKind::Union(members) => members.iter().any(|member| self.type_can_be_falsy(member)),
1049 TypeKind::Bool => true,
1050 TypeKind::Unknown => true,
1051 TypeKind::Option(_) => true,
1052 _ => false,
1053 }
1054 }
1055
1056 fn extract_falsy_type(&self, ty: &Type) -> Option<Type> {
1057 match &ty.kind {
1058 TypeKind::Bool => Some(Type::new(TypeKind::Bool, ty.span)),
1059 TypeKind::Unknown => Some(Type::new(TypeKind::Unknown, ty.span)),
1060 TypeKind::Option(inner) => Some(Type::new(
1061 TypeKind::Option(Box::new(self.canonicalize_type(inner))),
1062 ty.span,
1063 )),
1064 TypeKind::Union(members) => {
1065 let mut parts = Vec::new();
1066 for member in members {
1067 if let Some(part) = self.extract_falsy_type(member) {
1068 parts.push(part);
1069 }
1070 }
1071 self.merge_optional_types(parts)
1072 }
1073 _ => None,
1074 }
1075 }
1076
1077 fn merge_optional_types(&self, types: Vec<Type>) -> Option<Type> {
1078 if types.is_empty() {
1079 return None;
1080 }
1081
1082 Some(self.make_union_from_types(types))
1083 }
1084
1085 fn make_union_from_types(&self, types: Vec<Type>) -> Type {
1086 let mut flat: Vec<Type> = Vec::new();
1087 for ty in types {
1088 let canonical = self.canonicalize_type(&ty);
1089 match &canonical.kind {
1090 TypeKind::Union(members) => {
1091 for member in members {
1092 self.push_unique_type(&mut flat, member.clone());
1093 }
1094 }
1095 _ => self.push_unique_type(&mut flat, canonical),
1096 }
1097 }
1098
1099 match flat.len() {
1100 0 => Type::new(TypeKind::Unknown, Self::dummy_span()),
1101 1 => flat.into_iter().next().unwrap(),
1102 _ => Type::new(TypeKind::Union(flat), Self::dummy_span()),
1103 }
1104 }
1105
1106 fn push_unique_type(&self, list: &mut Vec<Type>, candidate: Type) {
1107 if !list
1108 .iter()
1109 .any(|existing| self.types_equal(existing, &candidate))
1110 {
1111 list.push(candidate);
1112 }
1113 }
1114
1115 fn combine_truthy_falsy(&self, truthy: Option<Type>, falsy: Option<Type>) -> Type {
1116 match (truthy, falsy) {
1117 (Some(t), Some(f)) => self.make_union_from_types(vec![t, f]),
1118 (Some(t), None) => t,
1119 (None, Some(f)) => f,
1120 (None, None) => Type::new(TypeKind::Unknown, Self::dummy_span()),
1121 }
1122 }
1123
1124 fn is_bool_like(&self, ty: &Type) -> bool {
1125 match &ty.kind {
1126 TypeKind::Bool => true,
1127 TypeKind::Union(members) => members.iter().all(|member| self.is_bool_like(member)),
1128 _ => false,
1129 }
1130 }
1131
1132 fn option_inner_type<'a>(&self, ty: &'a Type) -> Option<&'a Type> {
1133 match &ty.kind {
1134 TypeKind::Option(inner) => Some(inner.as_ref()),
1135 TypeKind::Union(members) => {
1136 for member in members {
1137 if let Some(inner) = self.option_inner_type(member) {
1138 return Some(inner);
1139 }
1140 }
1141 None
1142 }
1143 _ => None,
1144 }
1145 }
1146
1147 fn should_optionize(&self, left: &Type, right: &Type) -> bool {
1148 self.is_bool_like(left)
1149 && !self.is_bool_like(right)
1150 && self.option_inner_type(right).is_none()
1151 }
1152}