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