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