1use super::super::ShortCircuitInfo;
2use super::*;
3use alloc::{
4 boxed::Box,
5 format,
6 string::{String, ToString},
7 vec,
8 vec::Vec,
9};
10use hashbrown::HashMap;
11impl TypeChecker {
12 pub fn check_literal(&self, lit: &Literal) -> Result<Type> {
13 let span = Self::dummy_span();
14 Ok(match lit {
15 Literal::Integer(_) => Type::new(TypeKind::Int, span),
16 Literal::Float(_) => Type::new(TypeKind::Float, span),
17 Literal::String(_) => Type::new(TypeKind::String, span),
18 Literal::Bool(_) => Type::new(TypeKind::Bool, span),
19 })
20 }
21
22 pub fn check_binary_expr(
23 &mut self,
24 span: Span,
25 left: &Expr,
26 op: &BinaryOp,
27 right: &Expr,
28 ) -> Result<Type> {
29 if matches!(op, BinaryOp::And) {
30 return self.check_and_expr(span, left, right);
31 }
32
33 if matches!(op, BinaryOp::Or) {
34 return self.check_or_expr(span, left, right);
35 }
36
37 let span = Self::dummy_span();
38 let left_type = self.check_expr(left)?;
39 let right_type = self.check_expr(right)?;
40 match op {
41 BinaryOp::Add
42 | BinaryOp::Sub
43 | BinaryOp::Mul
44 | BinaryOp::Div
45 | BinaryOp::Mod
46 | BinaryOp::Pow => {
47 if matches!(left_type.kind, TypeKind::Int | TypeKind::Float)
48 && matches!(right_type.kind, TypeKind::Int | TypeKind::Float)
49 {
50 if matches!(left_type.kind, TypeKind::Float)
51 || matches!(right_type.kind, TypeKind::Float)
52 {
53 Ok(Type::new(TypeKind::Float, span))
54 } else {
55 Ok(Type::new(TypeKind::Int, span))
56 }
57 } else {
58 Err(self.type_error_at(
59 format!(
60 "Arithmetic operator {} requires numeric types, got '{}' and '{}'",
61 op, left_type, right_type
62 ),
63 left.span,
64 ))
65 }
66 }
67
68 BinaryOp::Eq
69 | BinaryOp::Ne
70 | BinaryOp::Lt
71 | BinaryOp::Le
72 | BinaryOp::Gt
73 | BinaryOp::Ge => {
74 if matches!(left_type.kind, TypeKind::Unknown)
75 || matches!(right_type.kind, TypeKind::Unknown)
76 {
77 return Ok(Type::new(TypeKind::Bool, span));
78 }
79
80 if !self.types_equal(&left_type, &right_type) {
81 return Err(self.type_error(format!(
82 "Comparison requires compatible types, got '{}' and '{}'",
83 left_type, right_type
84 )));
85 }
86
87 Ok(Type::new(TypeKind::Bool, span))
88 }
89
90 BinaryOp::Concat => {
91 if !self.env.type_implements_trait(&left_type, "ToString") {
92 return Err(self.type_error_at(
93 format!(
94 "Left operand of `..` must implement ToString trait, got '{}'",
95 left_type
96 ),
97 left.span,
98 ));
99 }
100
101 if !self.env.type_implements_trait(&right_type, "ToString") {
102 return Err(self.type_error_at(
103 format!(
104 "Right operand of `..` must implement ToString trait, got '{}'",
105 right_type
106 ),
107 right.span,
108 ));
109 }
110
111 Ok(Type::new(TypeKind::String, span))
112 }
113
114 BinaryOp::Range => {
115 return Err(self.type_error(
116 "Range operator is not supported; use numeric for-loops".to_string(),
117 ));
118 }
119
120 BinaryOp::And | BinaryOp::Or => {
121 unreachable!("short-circuit operators handled earlier in check_binary_expr")
122 }
123 }
124 }
125
126 fn check_and_expr(&mut self, span: Span, left: &Expr, right: &Expr) -> Result<Type> {
127 let bool_type = Type::new(TypeKind::Bool, Self::dummy_span());
128 let left_bindings = self.extract_all_pattern_bindings_from_expr(left);
129 let left_narrowings = self.extract_type_narrowings_from_expr(left);
130 let left_type = self.check_expr(left)?;
131 if !left_bindings.is_empty() {
132 self.unify(&bool_type, &left_type)?;
133 }
134
135 let left_info = self.short_circuit_profile(left, &left_type);
136
137 self.env.push_scope();
138 if !left_bindings.is_empty() {
139 for (scrutinee, pattern) in left_bindings {
140 if let Ok(scrutinee_type) = self.check_expr(scrutinee) {
141 let _ = self.bind_pattern(&pattern, &scrutinee_type);
142 }
143 }
144 }
145
146 for (var_name, narrowed_type) in left_narrowings {
147 self.env.refine_variable_type(var_name, narrowed_type);
148 }
149
150 let right_type = self.check_expr(right)?;
151 let right_bindings = self.extract_all_pattern_bindings_from_expr(right);
152 if !right_bindings.is_empty() {
153 self.unify(&bool_type, &right_type)?;
154 }
155
156 let right_narrowings = self.extract_type_narrowings_from_expr(right);
157 for (var_name, narrowed_type) in right_narrowings {
158 self.env.refine_variable_type(var_name, narrowed_type);
159 }
160
161 self.env.pop_scope();
162
163 let right_info = self.short_circuit_profile(right, &right_type);
164 let mut option_inner: Option<Type> = None;
165 let should_optionize =
166 self.should_optionize(&left_type, &right_type)
167 || self.should_optionize_narrowed_value(left, right, &right_type);
168 let (truthy, falsy, result_type) = if should_optionize {
169 let inner = self.canonicalize_type(&right_type);
170 option_inner = Some(inner.clone());
171 let option_type = Type::new(TypeKind::Option(Box::new(inner)), span);
172 (
173 Some(option_type.clone()),
174 Some(option_type.clone()),
175 option_type,
176 )
177 } else {
178 let truthy = if self.type_can_be_truthy(&left_type) {
179 right_info
180 .truthy
181 .clone()
182 .or_else(|| Some(self.canonicalize_type(&right_type)))
183 } else {
184 None
185 };
186
187 let mut falsy_parts = Vec::new();
188 if let Some(falsy) = left_info.falsy.clone() {
189 falsy_parts.push(falsy);
190 }
191
192 if self.type_can_be_truthy(&left_type) {
193 if let Some(falsy) = right_info.falsy.clone() {
194 falsy_parts.push(falsy);
195 }
196 }
197
198 let falsy = self.merge_optional_types(falsy_parts);
199 let result = self.combine_truthy_falsy(truthy.clone(), falsy.clone());
200 (truthy, falsy, result)
201 };
202
203 self.record_short_circuit_info(
204 span,
205 &ShortCircuitInfo {
206 truthy: truthy.clone(),
207 falsy: falsy.clone(),
208 option_inner: option_inner.clone(),
209 },
210 );
211 Ok(result_type)
212 }
213
214 fn should_optionize_narrowed_value(
215 &self,
216 left: &Expr,
217 right: &Expr,
218 right_type: &Type,
219 ) -> bool {
220 if self.option_inner_type(right_type).is_some() {
221 return false;
222 }
223
224 let scrutinee = match Self::extract_short_circuit_scrutinee(left) {
225 Some(expr) => expr,
226 None => return false,
227 };
228
229 let left_ident = Self::identifier_from_expr(scrutinee);
230 let right_ident = Self::identifier_from_expr(right);
231 match (left_ident, right_ident) {
232 (Some(lhs), Some(rhs)) => lhs == rhs,
233 _ => false,
234 }
235 }
236
237 fn extract_short_circuit_scrutinee<'a>(expr: &'a Expr) -> Option<&'a Expr> {
238 match &expr.kind {
239 ExprKind::TypeCheck { expr: scrutinee, .. } => Some(scrutinee),
240 ExprKind::IsPattern { expr: scrutinee, .. } => Some(scrutinee),
241 ExprKind::Paren(inner) => Self::extract_short_circuit_scrutinee(inner),
242 _ => None,
243 }
244 }
245
246 fn identifier_from_expr<'a>(expr: &'a Expr) -> Option<&'a str> {
247 match &expr.kind {
248 ExprKind::Identifier(name) => Some(name.as_str()),
249 ExprKind::Paren(inner) => Self::identifier_from_expr(inner),
250 _ => None,
251 }
252 }
253
254 fn check_or_expr(&mut self, span: Span, left: &Expr, right: &Expr) -> Result<Type> {
255 let left_type = self.check_expr(left)?;
256 let left_info = self.short_circuit_profile(left, &left_type);
257
258 let right_type = self.check_expr(right)?;
259 let right_info = self.short_circuit_profile(right, &right_type);
260
261 let mut option_candidates: Vec<Type> = Vec::new();
262 let mut option_spans: Vec<Span> = Vec::new();
263 if let Some(inner) = left_info.option_inner.clone() {
264 option_candidates.push(inner);
265 option_spans.push(left.span);
266 } else if let Some(inner) = self
267 .option_inner_type(&left_type)
268 .map(|ty| self.canonicalize_type(ty))
269 {
270 option_candidates.push(inner);
271 }
272
273 if let Some(inner) = right_info.option_inner.clone() {
274 option_candidates.push(inner);
275 option_spans.push(right.span);
276 } else if let Some(inner) = self
277 .option_inner_type(&right_type)
278 .map(|ty| self.canonicalize_type(ty))
279 {
280 option_candidates.push(inner);
281 }
282
283 if option_candidates.len() >= 2 {
284 let mut resolved_inner = option_candidates[0].clone();
285 for candidate in option_candidates.iter().skip(1) {
286 self.unify(&resolved_inner, candidate)?;
287 }
288 resolved_inner = self.canonicalize_type(&resolved_inner);
289 let option_type = Type::new(TypeKind::Option(Box::new(resolved_inner.clone())), span);
290 self.record_short_circuit_info(
291 span,
292 &ShortCircuitInfo {
293 truthy: Some(option_type.clone()),
294 falsy: Some(option_type.clone()),
295 option_inner: Some(resolved_inner),
296 },
297 );
298 return Ok(option_type);
299 }
300
301 for span_to_clear in option_spans {
302 self.clear_option_for_span(span_to_clear);
303 }
304
305 let mut truthy_parts: Vec<Type> = Vec::new();
306 if self.type_can_be_truthy(&left_type) {
307 if let Some(inner) = left_info.option_inner.clone() {
308 truthy_parts.push(inner);
309 } else if let Some(truthy) = left_info.truthy.clone() {
310 truthy_parts.push(truthy);
311 } else {
312 truthy_parts.push(self.canonicalize_type(&left_type));
313 }
314 }
315
316 if self.type_can_be_falsy(&left_type) && self.type_can_be_truthy(&right_type) {
317 if let Some(inner) = right_info.option_inner.clone() {
318 truthy_parts.push(inner);
319 } else if let Some(truthy) = right_info.truthy.clone() {
320 truthy_parts.push(truthy);
321 } else {
322 truthy_parts.push(self.canonicalize_type(&right_type));
323 }
324 }
325
326 let truthy = self.merge_optional_types(truthy_parts);
327 let falsy = if self.type_can_be_falsy(&left_type) {
328 right_info
329 .falsy
330 .clone()
331 .or_else(|| self.extract_falsy_type(&right_type))
332 } else {
333 None
334 };
335
336 let result = self.combine_truthy_falsy(truthy.clone(), falsy.clone());
337 self.record_short_circuit_info(
338 span,
339 &ShortCircuitInfo {
340 truthy,
341 falsy,
342 option_inner: None,
343 },
344 );
345 Ok(result)
346 }
347
348 pub fn check_unary_expr(&mut self, op: &UnaryOp, operand: &Expr) -> Result<Type> {
349 let operand_type = self.check_expr(operand)?;
350 let span = Self::dummy_span();
351 match op {
352 UnaryOp::Neg => {
353 if matches!(operand_type.kind, TypeKind::Int | TypeKind::Float) {
354 Ok(operand_type)
355 } else {
356 Err(self.type_error(format!(
357 "Negation requires numeric type, got '{}'",
358 operand_type
359 )))
360 }
361 }
362
363 UnaryOp::Not => {
364 self.unify(&Type::new(TypeKind::Bool, span), &operand_type)?;
365 Ok(Type::new(TypeKind::Bool, span))
366 }
367 }
368 }
369
370 pub fn check_call_expr(&mut self, span: Span, callee: &Expr, args: &[Expr]) -> Result<Type> {
371 if let ExprKind::FieldAccess { object, field } = &callee.kind {
372 if let ExprKind::Identifier(type_name) = &object.kind {
373 let mut candidate_names: Vec<String> = Vec::new();
374 if let Some(real_mod) = self.resolve_module_alias(type_name) {
375 candidate_names.push(format!("{}.{}", real_mod, field));
376 }
377
378 candidate_names.push(format!("{}.{}", type_name, field));
379 let resolved_type = self.resolve_type_key(type_name);
380 if resolved_type != *type_name {
381 candidate_names.push(format!("{}.{}", resolved_type, field));
382 }
383
384 let mut static_candidate: Option<(String, type_env::FunctionSignature)> = None;
385 for name in candidate_names {
386 if let Some(sig) = self.env.lookup_function(&name) {
387 static_candidate = Some((name, sig.clone()));
388 break;
389 }
390 }
391
392 if let Some((resolved_name, sig)) = static_candidate {
393 if args.len() != sig.params.len() {
394 return Err(self.type_error_at(
395 format!(
396 "Static method '{}' expects {} arguments, got {}",
397 resolved_name,
398 sig.params.len(),
399 args.len()
400 ),
401 span,
402 ));
403 }
404
405 for (i, (arg, expected_type)) in args.iter().zip(sig.params.iter()).enumerate()
406 {
407 let arg_type = self.check_expr(arg)?;
408 self.unify(expected_type, &arg_type).map_err(|_| {
409 self.type_error_at(
410 format!(
411 "Argument {} to static method '{}': expected '{}', got '{}'",
412 i + 1,
413 resolved_name,
414 expected_type,
415 arg_type
416 ),
417 arg.span,
418 )
419 })?;
420 }
421
422 return Ok(sig.return_type);
423 }
424
425 let enum_lookup = {
426 let key = self.resolve_type_key(type_name);
427 self.env
428 .lookup_enum(&key)
429 .or_else(|| self.env.lookup_enum(type_name))
430 };
431 if let Some(enum_def) = enum_lookup {
432 let enum_def = enum_def.clone();
433 let variant = field;
434 let variant_def = enum_def
435 .variants
436 .iter()
437 .find(|v| &v.name == variant)
438 .ok_or_else(|| {
439 self.type_error_at(
440 format!("Enum '{}' has no variant '{}'", type_name, variant),
441 span,
442 )
443 })?;
444 if let Some(expected_fields) = &variant_def.fields {
445 if args.len() != expected_fields.len() {
446 return Err(self.type_error_at(
447 format!(
448 "Variant '{}::{}' expects {} arguments, got {}",
449 type_name,
450 variant,
451 expected_fields.len(),
452 args.len()
453 ),
454 span,
455 ));
456 }
457
458 let mut type_params = HashMap::new();
459 for (arg, expected_type) in args.iter().zip(expected_fields.iter()) {
460 let arg_type = self.check_expr(arg)?;
461 if let TypeKind::Generic(type_param) = &expected_type.kind {
462 type_params.insert(type_param.clone(), arg_type.clone());
463 } else {
464 self.unify(expected_type, &arg_type)?;
465 }
466 }
467
468 if !type_params.is_empty() {
469 self.pending_generic_instances = Some(type_params.clone());
470 }
471
472 if type_name == "Option" {
473 if let Some(inner_type) = type_params.get("T") {
474 return Ok(Type::new(
475 TypeKind::Option(Box::new(inner_type.clone())),
476 Self::dummy_span(),
477 ));
478 }
479 } else if type_name == "Result" {
480 if let (Some(ok_type), Some(err_type)) =
481 (type_params.get("T"), type_params.get("E"))
482 {
483 return Ok(Type::new(
484 TypeKind::Result(
485 Box::new(ok_type.clone()),
486 Box::new(err_type.clone()),
487 ),
488 Self::dummy_span(),
489 ));
490 }
491 }
492
493 let enum_type_name = {
494 let key = self.resolve_type_key(type_name);
495 if self.env.lookup_enum(&key).is_some() {
496 key
497 } else {
498 type_name.clone()
499 }
500 };
501 return Ok(Type::new(
502 TypeKind::Named(enum_type_name),
503 Self::dummy_span(),
504 ));
505 } else {
506 if !args.is_empty() {
507 return Err(self.type_error(format!(
508 "Variant '{}::{}' is a unit variant and takes no arguments",
509 type_name, variant
510 )));
511 }
512
513 let enum_type_name = {
514 let key = self.resolve_type_key(type_name);
515 if self.env.lookup_enum(&key).is_some() {
516 key
517 } else {
518 type_name.clone()
519 }
520 };
521 return Ok(Type::new(
522 TypeKind::Named(enum_type_name),
523 Self::dummy_span(),
524 ));
525 }
526 }
527 }
528 }
529
530 if let ExprKind::Identifier(name) = &callee.kind {
531 if let Some(var_type) = self.env.lookup_variable(name) {
532 if let TypeKind::Function {
533 params: param_types,
534 return_type,
535 } = &var_type.kind
536 {
537 if args.len() != param_types.len() {
538 return Err(self.type_error_at(
539 format!(
540 "Lambda '{}' expects {} arguments, got {}",
541 name,
542 param_types.len(),
543 args.len()
544 ),
545 span,
546 ));
547 }
548
549 for (i, (arg, expected_type)) in args.iter().zip(param_types.iter()).enumerate()
550 {
551 let arg_type = self.check_expr(arg)?;
552 self.unify(expected_type, &arg_type).map_err(|_| {
553 self.type_error_at(
554 format!(
555 "Argument {} to lambda '{}': expected '{}', got '{}'",
556 i + 1,
557 name,
558 expected_type,
559 arg_type
560 ),
561 arg.span,
562 )
563 })?;
564 }
565
566 return Ok((**return_type).clone());
567 }
568 }
569
570 let resolved = self.resolve_function_key(name);
571 let sig = self
572 .env
573 .lookup_function(&resolved)
574 .ok_or_else(|| {
575 self.type_error_at(format!("Undefined function '{}'", name), callee.span)
576 })?
577 .clone();
578 if args.len() != sig.params.len() {
579 return Err(self.type_error_at(
580 format!(
581 "Function '{}' expects {} arguments, got {}",
582 name,
583 sig.params.len(),
584 args.len()
585 ),
586 callee.span,
587 ));
588 }
589
590 for (i, (arg, expected_type)) in args.iter().zip(sig.params.iter()).enumerate() {
591 let arg_type = self.check_expr(arg)?;
592 self.unify_with_bounds(expected_type, &arg_type)
593 .map_err(|_| {
594 self.type_error_at(
595 format!(
596 "Argument {} to function '{}': expected '{}', got '{}'",
597 i + 1,
598 name,
599 expected_type,
600 arg_type
601 ),
602 arg.span,
603 )
604 })?;
605 }
606
607 Ok(sig.return_type)
608 } else {
609 Err(self.type_error_at(
610 "Only direct function/lambda calls are supported".to_string(),
611 span,
612 ))
613 }
614 }
615
616 pub fn check_method_call(
617 &mut self,
618 receiver: &Expr,
619 method: &str,
620 args: &[Expr],
621 ) -> Result<Type> {
622 let receiver_type = self.check_expr(receiver)?;
623 let span = Self::dummy_span();
624 match &receiver_type.kind {
625 TypeKind::String => match method {
626 "len" => {
627 if !args.is_empty() {
628 return Err(self.type_error("len() takes no arguments".to_string()));
629 }
630
631 return Ok(Type::new(TypeKind::Int, span));
632 }
633
634 "substring" => {
635 if args.len() != 2 {
636 return Err(self.type_error("substring() requires 2 arguments".to_string()));
637 }
638
639 self.check_expr(&args[0])?;
640 self.check_expr(&args[1])?;
641 return Ok(Type::new(TypeKind::String, span));
642 }
643
644 "find" => {
645 if args.len() != 1 {
646 return Err(self.type_error("find() requires 1 argument".to_string()));
647 }
648
649 self.check_expr(&args[0])?;
650 return Ok(Type::new(TypeKind::Named("Option".to_string()), span));
651 }
652
653 "starts_with" | "ends_with" | "contains" => {
654 if args.len() != 1 {
655 return Err(self.type_error(format!("{}() requires 1 argument", method)));
656 }
657
658 self.check_expr(&args[0])?;
659 return Ok(Type::new(TypeKind::Bool, span));
660 }
661
662 "split" => {
663 if args.len() != 1 {
664 return Err(self.type_error("split() requires 1 argument".to_string()));
665 }
666
667 self.check_expr(&args[0])?;
668 return Ok(Type::new(
669 TypeKind::Array(Box::new(Type::new(TypeKind::String, span))),
670 span,
671 ));
672 }
673
674 "trim" | "trim_start" | "trim_end" | "to_upper" | "to_lower" => {
675 if !args.is_empty() {
676 return Err(self.type_error(format!("{}() takes no arguments", method)));
677 }
678
679 return Ok(Type::new(TypeKind::String, span));
680 }
681
682 "replace" => {
683 if args.len() != 2 {
684 return Err(self.type_error("replace() requires 2 arguments".to_string()));
685 }
686
687 self.check_expr(&args[0])?;
688 self.check_expr(&args[1])?;
689 return Ok(Type::new(TypeKind::String, span));
690 }
691
692 "is_empty" => {
693 if !args.is_empty() {
694 return Err(self.type_error("is_empty() takes no arguments".to_string()));
695 }
696
697 return Ok(Type::new(TypeKind::Bool, span));
698 }
699
700 "chars" | "lines" => {
701 if !args.is_empty() {
702 return Err(self.type_error(format!("{}() takes no arguments", method)));
703 }
704
705 return Ok(Type::new(
706 TypeKind::Array(Box::new(Type::new(TypeKind::String, span))),
707 span,
708 ));
709 }
710
711 _ => {}
712 },
713 TypeKind::Array(elem_type) => match method {
714 "len" => {
715 if !args.is_empty() {
716 return Err(self.type_error("len() takes no arguments".to_string()));
717 }
718
719 return Ok(Type::new(TypeKind::Int, span));
720 }
721
722 "get" => {
723 if args.len() != 1 {
724 return Err(self.type_error("get() requires 1 argument".to_string()));
725 }
726
727 self.check_expr(&args[0])?;
728 return Ok(Type::new(TypeKind::Option(elem_type.clone()), span));
729 }
730
731 "first" | "last" => {
732 if !args.is_empty() {
733 return Err(self.type_error(format!("{}() takes no arguments", method)));
734 }
735
736 return Ok(Type::new(TypeKind::Option(elem_type.clone()), span));
737 }
738
739 "push" => {
740 if args.len() != 1 {
741 return Err(self.type_error("push() requires 1 argument".to_string()));
742 }
743
744 self.check_expr(&args[0])?;
745 return Ok(Type::new(TypeKind::Unit, span));
746 }
747
748 "pop" => {
749 if !args.is_empty() {
750 return Err(self.type_error("pop() takes no arguments".to_string()));
751 }
752
753 return Ok(Type::new(TypeKind::Option(elem_type.clone()), span));
754 }
755
756 "slice" => {
757 if args.len() != 2 {
758 return Err(self.type_error("slice() requires 2 arguments".to_string()));
759 }
760
761 self.check_expr(&args[0])?;
762 self.check_expr(&args[1])?;
763 return Ok(Type::new(TypeKind::Array(elem_type.clone()), span));
764 }
765
766 "clear" => {
767 if !args.is_empty() {
768 return Err(self.type_error("clear() takes no arguments".to_string()));
769 }
770
771 return Ok(Type::new(TypeKind::Unit, span));
772 }
773
774 "is_empty" => {
775 if !args.is_empty() {
776 return Err(self.type_error("is_empty() takes no arguments".to_string()));
777 }
778
779 return Ok(Type::new(TypeKind::Bool, span));
780 }
781
782 "map" => {
783 if args.len() != 1 {
784 return Err(
785 self.type_error("map() requires 1 argument (function)".to_string())
786 );
787 }
788
789 self.expected_lambda_signature = Some((vec![(**elem_type).clone()], None));
790 let func_type = self.check_expr(&args[0])?;
791 match &func_type.kind {
792 TypeKind::Function {
793 params: _,
794 return_type,
795 } => {
796 return Ok(Type::new(TypeKind::Array(return_type.clone()), span));
797 }
798
799 _ => {
800 return Err(
801 self.type_error("map() requires a function argument".to_string())
802 );
803 }
804 }
805 }
806
807 "filter" => {
808 if args.len() != 1 {
809 return Err(
810 self.type_error("filter() requires 1 argument (function)".to_string())
811 );
812 }
813
814 self.expected_lambda_signature = Some((
815 vec![(**elem_type).clone()],
816 Some(Type::new(TypeKind::Bool, span)),
817 ));
818 let func_type = self.check_expr(&args[0])?;
819 match &func_type.kind {
820 TypeKind::Function {
821 params: _,
822 return_type,
823 } => {
824 self.unify(&Type::new(TypeKind::Bool, span), return_type)?;
825 return Ok(Type::new(TypeKind::Array(elem_type.clone()), span));
826 }
827
828 _ => {
829 return Err(self
830 .type_error("filter() requires a function argument".to_string()));
831 }
832 }
833 }
834
835 "reduce" => {
836 if args.len() != 2 {
837 return Err(self.type_error(
838 "reduce() requires 2 arguments (initial value and function)"
839 .to_string(),
840 ));
841 }
842
843 let init_type = self.check_expr(&args[0])?;
844 self.expected_lambda_signature = Some((
845 vec![init_type.clone(), (**elem_type).clone()],
846 Some(init_type.clone()),
847 ));
848 let func_type = self.check_expr(&args[1])?;
849 match &func_type.kind {
850 TypeKind::Function {
851 params: _,
852 return_type,
853 } => {
854 self.unify(&init_type, return_type)?;
855 return Ok(init_type);
856 }
857
858 _ => {
859 return Err(self.type_error(
860 "reduce() requires a function as second argument".to_string(),
861 ));
862 }
863 }
864 }
865
866 _ => {}
867 },
868 TypeKind::Map(key_type, value_type) => match method {
869 "iter" => {
870 if !args.is_empty() {
871 return Err(self.type_error("iter() takes no arguments".to_string()));
872 }
873
874 return Ok(Type::new(TypeKind::Named("Iterator".to_string()), span));
875 }
876
877 "len" => {
878 if !args.is_empty() {
879 return Err(self.type_error("len() takes no arguments".to_string()));
880 }
881
882 return Ok(Type::new(TypeKind::Int, span));
883 }
884
885 "get" => {
886 if args.len() != 1 {
887 return Err(self.type_error("get() requires 1 argument (key)".to_string()));
888 }
889
890 let arg_type = self.check_expr(&args[0])?;
891 self.unify(key_type, &arg_type)?;
892 return Ok(Type::new(TypeKind::Option(value_type.clone()), span));
893 }
894
895 "set" => {
896 if args.len() != 2 {
897 return Err(
898 self.type_error("set() requires 2 arguments (key, value)".to_string())
899 );
900 }
901
902 let key_arg_type = self.check_expr(&args[0])?;
903 let value_arg_type = self.check_expr(&args[1])?;
904 self.unify(key_type, &key_arg_type)?;
905 self.unify(value_type, &value_arg_type)?;
906 return Ok(Type::new(TypeKind::Unit, span));
907 }
908
909 "has" => {
910 if args.len() != 1 {
911 return Err(self.type_error("has() requires 1 argument (key)".to_string()));
912 }
913
914 let arg_type = self.check_expr(&args[0])?;
915 self.unify(key_type, &arg_type)?;
916 return Ok(Type::new(TypeKind::Bool, span));
917 }
918
919 "delete" => {
920 if args.len() != 1 {
921 return Err(
922 self.type_error("delete() requires 1 argument (key)".to_string())
923 );
924 }
925
926 let arg_type = self.check_expr(&args[0])?;
927 self.unify(key_type, &arg_type)?;
928 return Ok(Type::new(TypeKind::Option(value_type.clone()), span));
929 }
930
931 "keys" => {
932 if !args.is_empty() {
933 return Err(self.type_error("keys() takes no arguments".to_string()));
934 }
935
936 return Ok(Type::new(TypeKind::Array(key_type.clone()), span));
937 }
938
939 "values" => {
940 if !args.is_empty() {
941 return Err(self.type_error("values() takes no arguments".to_string()));
942 }
943
944 return Ok(Type::new(TypeKind::Array(value_type.clone()), span));
945 }
946
947 _ => {}
948 },
949 TypeKind::Named(type_name) if type_name == "Array" => match method {
950 "len" => {
951 if !args.is_empty() {
952 return Err(self.type_error("len() takes no arguments".to_string()));
953 }
954
955 return Ok(Type::new(TypeKind::Int, span));
956 }
957
958 "get" => {
959 if args.len() != 1 {
960 return Err(self.type_error("get() requires 1 argument".to_string()));
961 }
962
963 self.check_expr(&args[0])?;
964 return Ok(Type::new(TypeKind::Named("Option".to_string()), span));
965 }
966
967 "first" | "last" => {
968 if !args.is_empty() {
969 return Err(self.type_error(format!("{}() takes no arguments", method)));
970 }
971
972 return Ok(Type::new(TypeKind::Named("Option".to_string()), span));
973 }
974
975 "push" => {
976 if args.len() != 1 {
977 return Err(self.type_error("push() requires 1 argument".to_string()));
978 }
979
980 self.check_expr(&args[0])?;
981 return Ok(Type::new(TypeKind::Unit, span));
982 }
983
984 "pop" => {
985 if !args.is_empty() {
986 return Err(self.type_error("pop() takes no arguments".to_string()));
987 }
988
989 return Ok(Type::new(TypeKind::Named("Option".to_string()), span));
990 }
991
992 "slice" => {
993 if args.len() != 2 {
994 return Err(self.type_error("slice() requires 2 arguments".to_string()));
995 }
996
997 self.check_expr(&args[0])?;
998 self.check_expr(&args[1])?;
999 return Ok(receiver_type.clone());
1000 }
1001
1002 "clear" => {
1003 if !args.is_empty() {
1004 return Err(self.type_error("clear() takes no arguments".to_string()));
1005 }
1006
1007 return Ok(Type::new(TypeKind::Unit, span));
1008 }
1009
1010 "is_empty" => {
1011 if !args.is_empty() {
1012 return Err(self.type_error("is_empty() takes no arguments".to_string()));
1013 }
1014
1015 return Ok(Type::new(TypeKind::Bool, span));
1016 }
1017
1018 _ => {}
1019 },
1020 TypeKind::Option(inner_type) => match method {
1021 "is_some" | "is_none" => {
1022 if !args.is_empty() {
1023 return Err(self.type_error(format!("{}() takes no arguments", method)));
1024 }
1025
1026 return Ok(Type::new(TypeKind::Bool, span));
1027 }
1028
1029 "unwrap" => {
1030 if !args.is_empty() {
1031 return Err(self.type_error("unwrap() takes no arguments".to_string()));
1032 }
1033
1034 return Ok((**inner_type).clone());
1035 }
1036
1037 "unwrap_or" => {
1038 if args.len() != 1 {
1039 return Err(self.type_error("unwrap_or() requires 1 argument".to_string()));
1040 }
1041
1042 let default_type = self.check_expr(&args[0])?;
1043 return Ok(default_type);
1044 }
1045
1046 _ => {}
1047 },
1048 TypeKind::Result(ok_type, _err_type) => match method {
1049 "is_ok" | "is_err" => {
1050 if !args.is_empty() {
1051 return Err(self.type_error(format!("{}() takes no arguments", method)));
1052 }
1053
1054 return Ok(Type::new(TypeKind::Bool, span));
1055 }
1056
1057 "unwrap" => {
1058 if !args.is_empty() {
1059 return Err(self.type_error("unwrap() takes no arguments".to_string()));
1060 }
1061
1062 return Ok((**ok_type).clone());
1063 }
1064
1065 "unwrap_or" => {
1066 if args.len() != 1 {
1067 return Err(self.type_error("unwrap_or() requires 1 argument".to_string()));
1068 }
1069
1070 let default_type = self.check_expr(&args[0])?;
1071 return Ok(default_type);
1072 }
1073
1074 _ => {}
1075 },
1076 TypeKind::Named(type_name) if type_name == "Option" || type_name == "Result" => {
1077 match method {
1078 "is_some" | "is_none" | "is_ok" | "is_err" => {
1079 if !args.is_empty() {
1080 return Err(self.type_error(format!("{}() takes no arguments", method)));
1081 }
1082
1083 return Ok(Type::new(TypeKind::Bool, span));
1084 }
1085
1086 "unwrap" => {
1087 if !args.is_empty() {
1088 return Err(self.type_error("unwrap() takes no arguments".to_string()));
1089 }
1090
1091 if let ExprKind::Identifier(var_name) = &receiver.kind {
1092 if let Some(concrete_type) =
1093 self.env.lookup_generic_param(var_name, "T")
1094 {
1095 return Ok(concrete_type);
1096 }
1097 }
1098
1099 return Ok(Type::new(TypeKind::Unknown, span));
1100 }
1101
1102 "unwrap_or" => {
1103 if args.len() != 1 {
1104 return Err(
1105 self.type_error("unwrap_or() requires 1 argument".to_string())
1106 );
1107 }
1108
1109 let default_type = self.check_expr(&args[0])?;
1110 return Ok(default_type);
1111 }
1112
1113 _ => {}
1114 }
1115 }
1116
1117 TypeKind::Float => match method {
1118 "to_int" => {
1119 if !args.is_empty() {
1120 return Err(self.type_error("to_int() takes no arguments".to_string()));
1121 }
1122
1123 return Ok(Type::new(TypeKind::Int, span));
1124 }
1125
1126 "floor" | "ceil" | "round" | "sqrt" | "abs" => {
1127 if !args.is_empty() {
1128 return Err(self.type_error(format!("{}() takes no arguments", method)));
1129 }
1130
1131 return Ok(Type::new(TypeKind::Float, span));
1132 }
1133
1134 "clamp" => {
1135 if args.len() != 2 {
1136 return Err(
1137 self.type_error("clamp() requires 2 arguments (min, max)".to_string())
1138 );
1139 }
1140
1141 let min_type = self.check_expr(&args[0])?;
1142 let max_type = self.check_expr(&args[1])?;
1143 self.unify(&Type::new(TypeKind::Float, span), &min_type)?;
1144 self.unify(&Type::new(TypeKind::Float, span), &max_type)?;
1145 return Ok(Type::new(TypeKind::Float, span));
1146 }
1147
1148 _ => {}
1149 },
1150 TypeKind::Int => match method {
1151 "to_float" => {
1152 if !args.is_empty() {
1153 return Err(self.type_error("to_float() takes no arguments".to_string()));
1154 }
1155
1156 return Ok(Type::new(TypeKind::Float, span));
1157 }
1158
1159 "abs" => {
1160 if !args.is_empty() {
1161 return Err(self.type_error("abs() takes no arguments".to_string()));
1162 }
1163
1164 return Ok(Type::new(TypeKind::Int, span));
1165 }
1166
1167 "clamp" => {
1168 if args.len() != 2 {
1169 return Err(
1170 self.type_error("clamp() requires 2 arguments (min, max)".to_string())
1171 );
1172 }
1173
1174 let min_type = self.check_expr(&args[0])?;
1175 let max_type = self.check_expr(&args[1])?;
1176 self.unify(&Type::new(TypeKind::Int, span), &min_type)?;
1177 self.unify(&Type::new(TypeKind::Int, span), &max_type)?;
1178 return Ok(Type::new(TypeKind::Int, span));
1179 }
1180
1181 _ => {}
1182 },
1183 TypeKind::Named(type_name) => {
1184 if let Some(method_def) = self.env.lookup_method(type_name.as_str(), method) {
1185 let method_def = method_def.clone();
1186 let expected_args = method_def.params.len().saturating_sub(1);
1187 if args.len() != expected_args {
1188 return Err(self.type_error(format!(
1189 "Method '{}' expects {} arguments, got {}",
1190 method,
1191 expected_args,
1192 args.len()
1193 )));
1194 }
1195
1196 for (i, (arg, param)) in args
1197 .iter()
1198 .zip(method_def.params.iter().skip(1))
1199 .enumerate()
1200 {
1201 let arg_type = self.check_expr(arg)?;
1202 if !self.types_equal(&arg_type, ¶m.ty) {
1203 return Err(self.type_error(format!(
1204 "Argument {} to method '{}': expected '{}', got '{}'",
1205 i + 1,
1206 method,
1207 param.ty,
1208 arg_type
1209 )));
1210 }
1211 }
1212
1213 return Ok(method_def
1214 .return_type
1215 .clone()
1216 .unwrap_or(Type::new(TypeKind::Unit, span)));
1217 }
1218 }
1219
1220 TypeKind::Generic(type_param) => {
1221 if let Some(trait_names) = self.current_trait_bounds.get(type_param.as_str()) {
1222 for trait_name in trait_names {
1223 if let Some(trait_def) = {
1224 let key = self.resolve_type_key(trait_name.as_str());
1225 self.env
1226 .lookup_trait(&key)
1227 .or_else(|| self.env.lookup_trait(trait_name.as_str()))
1228 } {
1229 if let Some(trait_method) =
1230 trait_def.methods.iter().find(|m| m.name == method)
1231 {
1232 let expected_args =
1233 trait_method.params.iter().filter(|p| !p.is_self).count();
1234 if args.len() != expected_args {
1235 return Err(self.type_error(format!(
1236 "Method '{}' expects {} arguments, got {}",
1237 method,
1238 expected_args,
1239 args.len()
1240 )));
1241 }
1242
1243 return Ok(trait_method
1244 .return_type
1245 .clone()
1246 .unwrap_or(Type::new(TypeKind::Unit, span)));
1247 }
1248 }
1249 }
1250 }
1251 }
1252
1253 TypeKind::Trait(trait_name) => {
1254 if let Some(trait_def) = {
1255 let key = self.resolve_type_key(trait_name);
1256 self.env
1257 .lookup_trait(&key)
1258 .or_else(|| self.env.lookup_trait(trait_name.as_str()))
1259 } {
1260 if let Some(trait_method) = trait_def.methods.iter().find(|m| m.name == method)
1261 {
1262 let expected_args =
1263 trait_method.params.iter().filter(|p| !p.is_self).count();
1264 if args.len() != expected_args {
1265 return Err(self.type_error(format!(
1266 "Method '{}' expects {} arguments, got {}",
1267 method,
1268 expected_args,
1269 args.len()
1270 )));
1271 }
1272
1273 return Ok(trait_method
1274 .return_type
1275 .clone()
1276 .unwrap_or(Type::new(TypeKind::Unit, span)));
1277 }
1278 }
1279 }
1280
1281 _ => {}
1282 }
1283
1284 Err(self.type_error(format!(
1285 "Type '{}' has no method '{}'",
1286 receiver_type, method
1287 )))
1288 }
1289
1290 pub fn check_field_access_with_hint(
1291 &mut self,
1292 span: Span,
1293 object: &Expr,
1294 field: &str,
1295 expected_type: Option<&Type>,
1296 ) -> Result<Type> {
1297 if let ExprKind::Identifier(enum_name) = &object.kind {
1298 if let Some(enum_def) = {
1299 let key = self.resolve_type_key(enum_name);
1300 self.env
1301 .lookup_enum(&key)
1302 .or_else(|| self.env.lookup_enum(enum_name))
1303 } {
1304 let enum_def = enum_def.clone();
1305 let variant_def = enum_def
1306 .variants
1307 .iter()
1308 .find(|v| &v.name == field)
1309 .ok_or_else(|| {
1310 self.type_error_at(
1311 format!("Enum '{}' has no variant '{}'", enum_name, field),
1312 span,
1313 )
1314 })?;
1315 if variant_def.fields.is_some() {
1316 return Err(self.type_error_at(
1317 format!(
1318 "Variant '{}::{}' has fields and must be called with arguments",
1319 enum_name, field
1320 ),
1321 span,
1322 ));
1323 }
1324
1325 if let Some(expected) = expected_type {
1326 match &expected.kind {
1327 TypeKind::Option(_) if enum_name == "Option" => {
1328 return Ok(expected.clone());
1329 }
1330
1331 TypeKind::Result(_, _) if enum_name == "Result" => {
1332 return Ok(expected.clone());
1333 }
1334
1335 _ => {}
1336 }
1337 }
1338
1339 return Ok(Type::new(TypeKind::Named(enum_name.clone()), span));
1340 }
1341 }
1342
1343 let object_type = self.check_expr(object)?;
1344 let type_name = match &object_type.kind {
1345 TypeKind::Named(name) => name.clone(),
1346 _ => {
1347 return Err(self.type_error_at(
1348 format!("Cannot access field on type '{}'", object_type),
1349 object.span,
1350 ))
1351 }
1352 };
1353 self.env
1354 .lookup_struct_field(&type_name, field)
1355 .ok_or_else(|| {
1356 self.type_error_at(
1357 format!("Type '{}' has no field '{}'", type_name, field),
1358 span,
1359 )
1360 })
1361 }
1362
1363 pub fn check_index_expr(&mut self, object: &Expr, index: &Expr) -> Result<Type> {
1364 let object_type = self.check_expr(object)?;
1365 let index_type = self.check_expr(index)?;
1366 match &object_type.kind {
1367 TypeKind::Array(elem_type) => {
1368 self.unify(&Type::new(TypeKind::Int, Self::dummy_span()), &index_type)?;
1369 Ok(elem_type.as_ref().clone())
1370 }
1371
1372 TypeKind::Map(key_type, value_type) => {
1373 self.unify(key_type.as_ref(), &index_type)?;
1374 Ok(Type::new(
1375 TypeKind::Option(Box::new(value_type.as_ref().clone())),
1376 Self::dummy_span(),
1377 ))
1378 }
1379
1380 _ => Err(self.type_error(format!("Cannot index type '{}'", object_type))),
1381 }
1382 }
1383}