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 self.is_dynamic_numeric(&left_type) || self.is_dynamic_numeric(&right_type) {
48 return Ok(Type::new(TypeKind::Unknown, span));
49 }
50 if matches!(left_type.kind, TypeKind::Int | TypeKind::Float)
51 && matches!(right_type.kind, TypeKind::Int | TypeKind::Float)
52 {
53 if matches!(left_type.kind, TypeKind::Float)
54 || matches!(right_type.kind, TypeKind::Float)
55 {
56 Ok(Type::new(TypeKind::Float, span))
57 } else {
58 Ok(Type::new(TypeKind::Int, span))
59 }
60 } else {
61 Err(self.type_error_at(
62 format!(
63 "Arithmetic operator {} requires numeric types, got '{}' and '{}'",
64 op, left_type, right_type
65 ),
66 left.span,
67 ))
68 }
69 }
70
71 BinaryOp::Eq
72 | BinaryOp::Ne
73 | BinaryOp::Lt
74 | BinaryOp::Le
75 | BinaryOp::Gt
76 | BinaryOp::Ge => {
77 if matches!(left_type.kind, TypeKind::Unknown)
78 || matches!(right_type.kind, TypeKind::Unknown)
79 || matches!(&left_type.kind, TypeKind::Named(name) if name == "LuaValue")
80 || matches!(&right_type.kind, TypeKind::Named(name) if name == "LuaValue")
81 {
82 return Ok(Type::new(TypeKind::Bool, span));
83 }
84
85 if !self.types_equal(&left_type, &right_type) {
86 return Err(self.type_error(format!(
87 "Comparison requires compatible types, got '{}' and '{}'",
88 left_type, right_type
89 )));
90 }
91
92 Ok(Type::new(TypeKind::Bool, span))
93 }
94
95 BinaryOp::Concat => {
96 if self.concat_operand_is_dynamic(&left_type)
97 || self.concat_operand_is_dynamic(&right_type)
98 {
99 return Ok(Type::new(TypeKind::String, span));
100 }
101
102 if !self.concat_operand_implements_to_string(&left_type) {
103 return Err(self.type_error_at(
104 format!(
105 "Left operand of `..` must implement ToString trait, got '{}'",
106 left_type
107 ),
108 left.span,
109 ));
110 }
111
112 if !self.concat_operand_implements_to_string(&right_type) {
113 return Err(self.type_error_at(
114 format!(
115 "Right operand of `..` must implement ToString trait, got '{}'",
116 right_type
117 ),
118 right.span,
119 ));
120 }
121
122 Ok(Type::new(TypeKind::String, span))
123 }
124
125 BinaryOp::Range => {
126 return Err(self.type_error(
127 "Range operator is not supported; use numeric for-loops".to_string(),
128 ));
129 }
130
131 BinaryOp::And | BinaryOp::Or => {
132 unreachable!("short-circuit operators handled earlier in check_binary_expr")
133 }
134 }
135 }
136
137 fn concat_operand_is_dynamic(&self, ty: &Type) -> bool {
138 match &ty.kind {
139 TypeKind::Unknown => true,
140 TypeKind::Named(name) if name == "LuaValue" => true,
141 TypeKind::Union(types) => types.iter().any(|t| self.concat_operand_is_dynamic(t)),
142 _ => false,
143 }
144 }
145
146 fn is_dynamic_numeric(&self, ty: &Type) -> bool {
147 match &ty.kind {
148 TypeKind::Unknown => true,
149 TypeKind::Named(name) if name == "LuaValue" => true,
150 TypeKind::Union(types) => types.iter().any(|t| self.is_dynamic_numeric(t)),
151 _ => false,
152 }
153 }
154
155 fn concat_operand_implements_to_string(&self, ty: &Type) -> bool {
156 match &ty.kind {
157 TypeKind::Union(types) => types
158 .iter()
159 .all(|t| self.concat_operand_implements_to_string(t)),
160 _ => self.env.type_implements_trait(ty, "ToString"),
161 }
162 }
163
164 fn check_and_expr(&mut self, span: Span, left: &Expr, right: &Expr) -> Result<Type> {
165 let bool_type = Type::new(TypeKind::Bool, Self::dummy_span());
166 let left_bindings = self.extract_all_pattern_bindings_from_expr(left);
167 let left_narrowings = self.extract_type_narrowings_from_expr(left);
168 let left_type = self.check_expr(left)?;
169 if !left_bindings.is_empty() {
170 self.unify(&bool_type, &left_type)?;
171 }
172
173 let left_info = self.short_circuit_profile(left, &left_type);
174
175 self.env.push_scope();
176 if !left_bindings.is_empty() {
177 for (scrutinee, pattern) in left_bindings {
178 if let Ok(scrutinee_type) = self.check_expr(scrutinee) {
179 let _ = self.bind_pattern(&pattern, &scrutinee_type);
180 }
181 }
182 }
183
184 for (var_name, narrowed_type) in left_narrowings {
185 self.env.refine_variable_type(var_name, narrowed_type);
186 }
187
188 let right_type = self.check_expr(right)?;
189 let right_bindings = self.extract_all_pattern_bindings_from_expr(right);
190 if !right_bindings.is_empty() {
191 self.unify(&bool_type, &right_type)?;
192 }
193
194 let right_narrowings = self.extract_type_narrowings_from_expr(right);
195 for (var_name, narrowed_type) in right_narrowings {
196 self.env.refine_variable_type(var_name, narrowed_type);
197 }
198
199 self.env.pop_scope();
200
201 let right_info = self.short_circuit_profile(right, &right_type);
202 let mut option_inner: Option<Type> = None;
203 let should_optionize = self.should_optionize(&left_type, &right_type)
204 || self.should_optionize_narrowed_value(left, right, &right_type);
205 let (truthy, falsy, result_type) = if should_optionize {
206 let inner = self.canonicalize_type(&right_type);
207 option_inner = Some(inner.clone());
208 let option_type = Type::new(TypeKind::Option(Box::new(inner)), span);
209 (
210 Some(option_type.clone()),
211 Some(option_type.clone()),
212 option_type,
213 )
214 } else {
215 let truthy = if self.type_can_be_truthy(&left_type) {
216 right_info
217 .truthy
218 .clone()
219 .or_else(|| Some(self.canonicalize_type(&right_type)))
220 } else {
221 None
222 };
223
224 let mut falsy_parts = Vec::new();
225 if let Some(falsy) = left_info.falsy.clone() {
226 falsy_parts.push(falsy);
227 }
228
229 if self.type_can_be_truthy(&left_type) {
230 if let Some(falsy) = right_info.falsy.clone() {
231 falsy_parts.push(falsy);
232 }
233 }
234
235 let falsy = self.merge_optional_types(falsy_parts);
236 let result = self.combine_truthy_falsy(truthy.clone(), falsy.clone());
237 (truthy, falsy, result)
238 };
239
240 self.record_short_circuit_info(
241 span,
242 &ShortCircuitInfo {
243 truthy: truthy.clone(),
244 falsy: falsy.clone(),
245 option_inner: option_inner.clone(),
246 },
247 );
248 Ok(result_type)
249 }
250
251 fn should_optionize_narrowed_value(
252 &self,
253 left: &Expr,
254 right: &Expr,
255 right_type: &Type,
256 ) -> bool {
257 if self.option_inner_type(right_type).is_some() {
258 return false;
259 }
260
261 let scrutinee = match Self::extract_short_circuit_scrutinee(left) {
262 Some(expr) => expr,
263 None => return false,
264 };
265
266 let left_ident = Self::identifier_from_expr(scrutinee);
267 let right_ident = Self::identifier_from_expr(right);
268 match (left_ident, right_ident) {
269 (Some(lhs), Some(rhs)) => lhs == rhs,
270 _ => false,
271 }
272 }
273
274 fn extract_short_circuit_scrutinee<'a>(expr: &'a Expr) -> Option<&'a Expr> {
275 match &expr.kind {
276 ExprKind::TypeCheck {
277 expr: scrutinee, ..
278 } => Some(scrutinee),
279 ExprKind::IsPattern {
280 expr: scrutinee, ..
281 } => Some(scrutinee),
282 ExprKind::Paren(inner) => Self::extract_short_circuit_scrutinee(inner),
283 _ => None,
284 }
285 }
286
287 fn identifier_from_expr<'a>(expr: &'a Expr) -> Option<&'a str> {
288 match &expr.kind {
289 ExprKind::Identifier(name) => Some(name.as_str()),
290 ExprKind::Paren(inner) => Self::identifier_from_expr(inner),
291 _ => None,
292 }
293 }
294
295 fn check_or_expr(&mut self, span: Span, left: &Expr, right: &Expr) -> Result<Type> {
296 let left_type = self.check_expr(left)?;
297 let left_info = self.short_circuit_profile(left, &left_type);
298
299 let right_type = self.check_expr(right)?;
300 let right_info = self.short_circuit_profile(right, &right_type);
301
302 let mut option_candidates: Vec<Type> = Vec::new();
303 let mut option_spans: Vec<Span> = Vec::new();
304 if let Some(inner) = left_info.option_inner.clone() {
305 option_candidates.push(inner);
306 option_spans.push(left.span);
307 } else if let Some(inner) = self
308 .option_inner_type(&left_type)
309 .map(|ty| self.canonicalize_type(ty))
310 {
311 option_candidates.push(inner);
312 }
313
314 if let Some(inner) = right_info.option_inner.clone() {
315 option_candidates.push(inner);
316 option_spans.push(right.span);
317 } else if let Some(inner) = self
318 .option_inner_type(&right_type)
319 .map(|ty| self.canonicalize_type(ty))
320 {
321 option_candidates.push(inner);
322 }
323
324 if option_candidates.len() >= 2 {
325 let resolved_inner = option_candidates[0].clone();
326 let mut all_compatible = true;
327 for candidate in option_candidates.iter().skip(1) {
328 if self
329 .unify(&resolved_inner, candidate)
330 .and_then(|_| self.unify(candidate, &resolved_inner))
331 .is_err()
332 {
333 all_compatible = false;
334 break;
335 }
336 }
337
338 let resolved_inner = if all_compatible {
339 self.canonicalize_type(&resolved_inner)
340 } else {
341 self.canonicalize_type(&self.make_union_from_types(option_candidates))
342 };
343 let option_type = Type::new(TypeKind::Option(Box::new(resolved_inner.clone())), span);
344 self.record_short_circuit_info(
345 span,
346 &ShortCircuitInfo {
347 truthy: Some(option_type.clone()),
348 falsy: Some(option_type.clone()),
349 option_inner: Some(resolved_inner),
350 },
351 );
352 return Ok(option_type);
353 }
354
355 for span_to_clear in option_spans {
356 self.clear_option_for_span(span_to_clear);
357 }
358
359 let mut truthy_parts: Vec<Type> = Vec::new();
360 if self.type_can_be_truthy(&left_type) {
361 if let Some(inner) = left_info.option_inner.clone() {
362 truthy_parts.push(inner);
363 } else if let Some(truthy) = left_info.truthy.clone() {
364 truthy_parts.push(truthy);
365 } else {
366 truthy_parts.push(self.canonicalize_type(&left_type));
367 }
368 }
369
370 if self.type_can_be_falsy(&left_type) && self.type_can_be_truthy(&right_type) {
371 if let Some(inner) = right_info.option_inner.clone() {
372 truthy_parts.push(inner);
373 } else if let Some(truthy) = right_info.truthy.clone() {
374 truthy_parts.push(truthy);
375 } else {
376 truthy_parts.push(self.canonicalize_type(&right_type));
377 }
378 }
379
380 let truthy = self.merge_optional_types(truthy_parts);
381 let falsy = if self.type_can_be_falsy(&left_type) {
382 right_info
383 .falsy
384 .clone()
385 .or_else(|| self.extract_falsy_type(&right_type))
386 } else {
387 None
388 };
389
390 let result = self.combine_truthy_falsy(truthy.clone(), falsy.clone());
391 self.record_short_circuit_info(
392 span,
393 &ShortCircuitInfo {
394 truthy,
395 falsy,
396 option_inner: None,
397 },
398 );
399 Ok(result)
400 }
401
402 pub fn check_unary_expr(&mut self, op: &UnaryOp, operand: &Expr) -> Result<Type> {
403 let operand_type = self.check_expr(operand)?;
404 let span = Self::dummy_span();
405 match op {
406 UnaryOp::Neg => {
407 if matches!(operand_type.kind, TypeKind::Int | TypeKind::Float) {
408 Ok(operand_type)
409 } else {
410 Err(self.type_error(format!(
411 "Negation requires numeric type, got '{}'",
412 operand_type
413 )))
414 }
415 }
416
417 UnaryOp::Not => {
418 self.unify(&Type::new(TypeKind::Bool, span), &operand_type)?;
419 Ok(Type::new(TypeKind::Bool, span))
420 }
421 }
422 }
423
424 pub fn check_call_expr(&mut self, span: Span, callee: &Expr, args: &[Expr]) -> Result<Type> {
425 if let ExprKind::FieldAccess { object, field } = &callee.kind {
426 if let ExprKind::Identifier(type_name) = &object.kind {
427 let mut candidate_names: Vec<String> = Vec::new();
428 if let Some(real_mod) = self.resolve_module_alias(type_name) {
429 candidate_names.push(format!("{}.{}", real_mod, field));
430 }
431
432 candidate_names.push(format!("{}.{}", type_name, field));
433 let resolved_type = self.resolve_type_key(type_name);
434 if resolved_type != *type_name {
435 candidate_names.push(format!("{}.{}", resolved_type, field));
436 }
437
438 let mut static_candidate: Option<(String, type_env::FunctionSignature)> = None;
439 for name in candidate_names {
440 if let Some(sig) = self.env.lookup_function(&name) {
441 static_candidate = Some((name, sig.clone()));
442 break;
443 }
444 }
445
446 if let Some((resolved_name, sig)) = static_candidate {
447 let allow_varargs = sig.params.len() == 1
448 && matches!(sig.params[0].kind, TypeKind::Unknown)
449 && !args.is_empty();
450 let allow_optional = args.len() < sig.params.len()
451 && !args.is_empty()
452 && sig.params[args.len()..]
453 .iter()
454 .all(|param| matches!(param.kind, TypeKind::Unknown));
455 if args.len() > sig.params.len() && !allow_varargs
456 || (args.len() < sig.params.len() && !allow_optional && !allow_varargs)
457 {
458 return Err(self.type_error_at(
459 format!(
460 "Static method '{}' expects {} arguments, got {}",
461 resolved_name,
462 sig.params.len(),
463 args.len()
464 ),
465 span,
466 ));
467 }
468
469 for (i, (arg, expected_type)) in args
470 .iter()
471 .zip(sig.params.iter())
472 .take(sig.params.len().min(args.len()))
473 .enumerate()
474 {
475 let arg_type = self.check_expr(arg)?;
476 self.unify(expected_type, &arg_type).map_err(|_| {
477 self.type_error_at(
478 format!(
479 "Argument {} to static method '{}': expected '{}', got '{}'",
480 i + 1,
481 resolved_name,
482 expected_type,
483 arg_type
484 ),
485 arg.span,
486 )
487 })?;
488 }
489
490 if allow_varargs || allow_optional {
491 return Ok(sig.return_type);
492 }
493
494 return Ok(sig.return_type);
495 }
496
497 let enum_lookup = {
498 let key = self.resolve_type_key(type_name);
499 self.env
500 .lookup_enum(&key)
501 .or_else(|| self.env.lookup_enum(type_name))
502 };
503 if let Some(enum_def) = enum_lookup {
504 let enum_def = enum_def.clone();
505 let variant = field;
506 let variant_def = enum_def
507 .variants
508 .iter()
509 .find(|v| &v.name == variant)
510 .ok_or_else(|| {
511 self.type_error_at(
512 format!("Enum '{}' has no variant '{}'", type_name, variant),
513 span,
514 )
515 })?;
516 if let Some(expected_fields) = &variant_def.fields {
517 if args.len() != expected_fields.len() {
518 return Err(self.type_error_at(
519 format!(
520 "Variant '{}::{}' expects {} arguments, got {}",
521 type_name,
522 variant,
523 expected_fields.len(),
524 args.len()
525 ),
526 span,
527 ));
528 }
529
530 let mut type_params = HashMap::new();
531 for (arg, expected_type) in args.iter().zip(expected_fields.iter()) {
532 let arg_type = self.check_expr(arg)?;
533 if let TypeKind::Generic(type_param) = &expected_type.kind {
534 type_params.insert(type_param.clone(), arg_type.clone());
535 } else {
536 self.unify(expected_type, &arg_type)?;
537 }
538 }
539
540 if !type_params.is_empty() {
541 self.pending_generic_instances = Some(type_params.clone());
542 }
543
544 if type_name == "Option" {
545 if let Some(inner_type) = type_params.get("T") {
546 return Ok(Type::new(
547 TypeKind::Option(Box::new(inner_type.clone())),
548 Self::dummy_span(),
549 ));
550 }
551 } else if type_name == "Result" {
552 if let (Some(ok_type), Some(err_type)) =
553 (type_params.get("T"), type_params.get("E"))
554 {
555 return Ok(Type::new(
556 TypeKind::Result(
557 Box::new(ok_type.clone()),
558 Box::new(err_type.clone()),
559 ),
560 Self::dummy_span(),
561 ));
562 }
563 }
564
565 let enum_type_name = {
566 let key = self.resolve_type_key(type_name);
567 if self.env.lookup_enum(&key).is_some() {
568 key
569 } else {
570 type_name.clone()
571 }
572 };
573 return Ok(Type::new(
574 TypeKind::Named(enum_type_name),
575 Self::dummy_span(),
576 ));
577 } else {
578 if !args.is_empty() {
579 return Err(self.type_error(format!(
580 "Variant '{}::{}' is a unit variant and takes no arguments",
581 type_name, variant
582 )));
583 }
584
585 let enum_type_name = {
586 let key = self.resolve_type_key(type_name);
587 if self.env.lookup_enum(&key).is_some() {
588 key
589 } else {
590 type_name.clone()
591 }
592 };
593 return Ok(Type::new(
594 TypeKind::Named(enum_type_name),
595 Self::dummy_span(),
596 ));
597 }
598 }
599 }
600 }
601
602 if let ExprKind::Identifier(name) = &callee.kind {
603 if let Some(var_type) = self.env.lookup_variable(name) {
604 if let TypeKind::Function {
605 params: param_types,
606 return_type,
607 } = &var_type.kind
608 {
609 let mut expected_params = param_types.clone();
610 if args.len() != expected_params.len() {
611 if args.len() > expected_params.len() {
612 if let Some(last) = expected_params.last().cloned() {
613 let last_allows_varargs = matches!(last.kind, TypeKind::Unknown)
614 || matches!(&last.kind, TypeKind::Named(name) if name == "LuaValue");
615 if last_allows_varargs {
616 while expected_params.len() < args.len() {
617 expected_params.push(last.clone());
618 }
619 } else {
620 return Err(self.type_error_at(
621 format!(
622 "Lambda '{}' expects {} arguments, got {}",
623 name,
624 param_types.len(),
625 args.len()
626 ),
627 span,
628 ));
629 }
630 }
631 } else {
632 let missing = &expected_params[args.len()..];
633 let missing_optional = missing.iter().all(|p| {
634 matches!(p.kind, TypeKind::Unknown)
635 || matches!(&p.kind, TypeKind::Named(name) if name == "LuaValue")
636 });
637 if missing_optional {
638 expected_params.truncate(args.len());
639 } else {
640 return Err(self.type_error_at(
641 format!(
642 "Lambda '{}' expects {} arguments, got {}",
643 name,
644 param_types.len(),
645 args.len()
646 ),
647 span,
648 ));
649 }
650 }
651 }
652
653 for (i, (arg, expected_type)) in args.iter().zip(expected_params.iter()).enumerate()
654 {
655 let arg_type = self.check_expr(arg)?;
656 self.unify(expected_type, &arg_type).map_err(|_| {
657 self.type_error_at(
658 format!(
659 "Argument {} to lambda '{}': expected '{}', got '{}'",
660 i + 1,
661 name,
662 expected_type,
663 arg_type
664 ),
665 arg.span,
666 )
667 })?;
668 }
669
670 return Ok((**return_type).clone());
671 }
672 }
673
674 let resolved = self.resolve_function_key(name);
675 let sig_opt = self.env.lookup_function(&resolved).cloned();
676 if sig_opt.is_none() {
677 for arg in args {
678 self.check_expr(arg)?;
679 }
680 return Ok(Type::new(TypeKind::Unknown, span));
681 }
682 let sig = sig_opt.unwrap();
683 let mut expected_params = sig.params.clone();
684 if args.len() != expected_params.len() {
685 if args.len() > expected_params.len() {
686 if let Some(last) = expected_params.last().cloned() {
687 let last_allows_varargs = matches!(last.kind, TypeKind::Unknown)
688 || matches!(&last.kind, TypeKind::Named(name) if name == "LuaValue");
689 if last_allows_varargs {
690 while expected_params.len() < args.len() {
691 expected_params.push(last.clone());
692 }
693 } else {
694 return Err(self.type_error_at(
695 format!(
696 "Function '{}' expects {} arguments, got {}",
697 name,
698 sig.params.len(),
699 args.len()
700 ),
701 callee.span,
702 ));
703 }
704 }
705 } else {
706 let missing = &expected_params[args.len()..];
707 let missing_optional = missing.iter().all(|p| {
708 matches!(p.kind, TypeKind::Unknown)
709 || matches!(&p.kind, TypeKind::Named(name) if name == "LuaValue")
710 });
711 if missing_optional {
712 expected_params.truncate(args.len());
713 } else {
714 return Err(self.type_error_at(
715 format!(
716 "Function '{}' expects {} arguments, got {}",
717 name,
718 sig.params.len(),
719 args.len()
720 ),
721 callee.span,
722 ));
723 }
724 }
725 }
726
727 for (i, (arg, expected_type)) in args.iter().zip(expected_params.iter()).enumerate() {
728 let arg_type = self.check_expr(arg)?;
729 self.unify_with_bounds(expected_type, &arg_type)
730 .map_err(|_| {
731 self.type_error_at(
732 format!(
733 "Argument {} to function '{}': expected '{}', got '{}'",
734 i + 1,
735 name,
736 expected_type,
737 arg_type
738 ),
739 arg.span,
740 )
741 })?;
742 }
743
744 Ok(sig.return_type)
745 } else {
746 let callee_type = self.check_expr(callee)?;
747 match &callee_type.kind {
748 TypeKind::Function { params, return_type } => {
749 let expected_params = params.clone();
750 if args.len() != expected_params.len() {
751 return Err(self.type_error_at(
752 format!(
753 "Function expects {} arguments, got {}",
754 expected_params.len(),
755 args.len()
756 ),
757 span,
758 ));
759 }
760
761 for (i, (arg, expected_type)) in args.iter().zip(expected_params.iter()).enumerate()
762 {
763 let arg_type = self.check_expr(arg)?;
764 self.unify_with_bounds(expected_type, &arg_type)
765 .map_err(|_| {
766 self.type_error_at(
767 format!(
768 "Argument {}: expected '{}', got '{}'",
769 i + 1,
770 expected_type,
771 arg_type
772 ),
773 arg.span,
774 )
775 })?;
776 }
777
778 Ok((**return_type).clone())
779 }
780
781 TypeKind::Unknown => {
782 for arg in args {
783 self.check_expr(arg)?;
784 }
785 Ok(Type::new(TypeKind::Unknown, Self::dummy_span()))
786 }
787
788 TypeKind::Named(name) if name == "LuaValue" => {
789 for arg in args {
790 self.check_expr(arg)?;
791 }
792 Ok(Type::new(TypeKind::Unknown, Self::dummy_span()))
793 }
794
795 _ => Err(self.type_error_at(
796 format!("Cannot call expression of type '{}'", callee_type),
797 span,
798 )),
799 }
800 }
801 }
802
803 pub fn check_method_call(
804 &mut self,
805 receiver: &Expr,
806 method: &str,
807 args: &[Expr],
808 ) -> Result<Type> {
809 let receiver_type = self.check_expr(receiver)?;
810 let span = Self::dummy_span();
811 if matches!(receiver_type.kind, TypeKind::Unknown)
812 || matches!(&receiver_type.kind, TypeKind::Named(name) if name == "LuaValue")
813 {
814 for arg in args {
815 self.check_expr(arg)?;
816 }
817 return Ok(Type::new(TypeKind::Unknown, span));
818 }
819 match &receiver_type.kind {
820 TypeKind::String => match method {
821 "len" => {
822 if !args.is_empty() {
823 return Err(self.type_error("len() takes no arguments".to_string()));
824 }
825
826 return Ok(Type::new(TypeKind::Int, span));
827 }
828
829 "substring" => {
830 if args.len() != 2 {
831 return Err(self.type_error("substring() requires 2 arguments".to_string()));
832 }
833
834 self.check_expr(&args[0])?;
835 self.check_expr(&args[1])?;
836 return Ok(Type::new(TypeKind::String, span));
837 }
838
839 "find" => {
840 if args.len() != 1 {
841 return Err(self.type_error("find() requires 1 argument".to_string()));
842 }
843
844 self.check_expr(&args[0])?;
845 return Ok(Type::new(TypeKind::Named("Option".to_string()), span));
846 }
847
848 "starts_with" | "ends_with" | "contains" => {
849 if args.len() != 1 {
850 return Err(self.type_error(format!("{}() requires 1 argument", method)));
851 }
852
853 self.check_expr(&args[0])?;
854 return Ok(Type::new(TypeKind::Bool, span));
855 }
856
857 "split" => {
858 if args.len() != 1 {
859 return Err(self.type_error("split() requires 1 argument".to_string()));
860 }
861
862 self.check_expr(&args[0])?;
863 return Ok(Type::new(
864 TypeKind::Array(Box::new(Type::new(TypeKind::String, span))),
865 span,
866 ));
867 }
868
869 "trim" | "trim_start" | "trim_end" | "to_upper" | "to_lower" => {
870 if !args.is_empty() {
871 return Err(self.type_error(format!("{}() takes no arguments", method)));
872 }
873
874 return Ok(Type::new(TypeKind::String, span));
875 }
876
877 "replace" => {
878 if args.len() != 2 {
879 return Err(self.type_error("replace() requires 2 arguments".to_string()));
880 }
881
882 self.check_expr(&args[0])?;
883 self.check_expr(&args[1])?;
884 return Ok(Type::new(TypeKind::String, span));
885 }
886
887 "is_empty" => {
888 if !args.is_empty() {
889 return Err(self.type_error("is_empty() takes no arguments".to_string()));
890 }
891
892 return Ok(Type::new(TypeKind::Bool, span));
893 }
894
895 "chars" | "lines" => {
896 if !args.is_empty() {
897 return Err(self.type_error(format!("{}() takes no arguments", method)));
898 }
899
900 return Ok(Type::new(
901 TypeKind::Array(Box::new(Type::new(TypeKind::String, span))),
902 span,
903 ));
904 }
905
906 _ => {}
907 },
908 TypeKind::Array(elem_type) => match method {
909 "len" => {
910 if !args.is_empty() {
911 return Err(self.type_error("len() takes no arguments".to_string()));
912 }
913
914 return Ok(Type::new(TypeKind::Int, span));
915 }
916
917 "get" => {
918 if args.len() != 1 {
919 return Err(self.type_error("get() requires 1 argument".to_string()));
920 }
921
922 self.check_expr(&args[0])?;
923 return Ok(Type::new(TypeKind::Option(elem_type.clone()), span));
924 }
925
926 "first" | "last" => {
927 if !args.is_empty() {
928 return Err(self.type_error(format!("{}() takes no arguments", method)));
929 }
930
931 return Ok(Type::new(TypeKind::Option(elem_type.clone()), span));
932 }
933
934 "push" => {
935 if args.len() != 1 {
936 return Err(self.type_error("push() requires 1 argument".to_string()));
937 }
938
939 self.check_expr(&args[0])?;
940 return Ok(Type::new(TypeKind::Unit, span));
941 }
942
943 "pop" => {
944 if !args.is_empty() {
945 return Err(self.type_error("pop() takes no arguments".to_string()));
946 }
947
948 return Ok(Type::new(TypeKind::Option(elem_type.clone()), span));
949 }
950
951 "iter" => {
952 if !args.is_empty() {
953 return Err(self.type_error("iter() takes no arguments".to_string()));
954 }
955
956 return Ok(Type::new(TypeKind::Named("Iterator".to_string()), span));
957 }
958
959 "slice" => {
960 if args.len() != 2 {
961 return Err(self.type_error("slice() requires 2 arguments".to_string()));
962 }
963
964 self.check_expr(&args[0])?;
965 self.check_expr(&args[1])?;
966 return Ok(Type::new(TypeKind::Array(elem_type.clone()), span));
967 }
968
969 "clear" => {
970 if !args.is_empty() {
971 return Err(self.type_error("clear() takes no arguments".to_string()));
972 }
973
974 return Ok(Type::new(TypeKind::Unit, span));
975 }
976
977 "is_empty" => {
978 if !args.is_empty() {
979 return Err(self.type_error("is_empty() takes no arguments".to_string()));
980 }
981
982 return Ok(Type::new(TypeKind::Bool, span));
983 }
984
985 "map" => {
986 if args.len() != 1 {
987 return Err(
988 self.type_error("map() requires 1 argument (function)".to_string())
989 );
990 }
991
992 self.expected_lambda_signature = Some((vec![(**elem_type).clone()], None));
993 let func_type = self.check_expr(&args[0])?;
994 match &func_type.kind {
995 TypeKind::Function {
996 params: _,
997 return_type,
998 } => {
999 return Ok(Type::new(TypeKind::Array(return_type.clone()), span));
1000 }
1001
1002 _ => {
1003 return Err(
1004 self.type_error("map() requires a function argument".to_string())
1005 );
1006 }
1007 }
1008 }
1009
1010 "filter" => {
1011 if args.len() != 1 {
1012 return Err(
1013 self.type_error("filter() requires 1 argument (function)".to_string())
1014 );
1015 }
1016
1017 self.expected_lambda_signature = Some((
1018 vec![(**elem_type).clone()],
1019 Some(Type::new(TypeKind::Bool, span)),
1020 ));
1021 let func_type = self.check_expr(&args[0])?;
1022 match &func_type.kind {
1023 TypeKind::Function {
1024 params: _,
1025 return_type,
1026 } => {
1027 self.unify(&Type::new(TypeKind::Bool, span), return_type)?;
1028 return Ok(Type::new(TypeKind::Array(elem_type.clone()), span));
1029 }
1030
1031 _ => {
1032 return Err(self
1033 .type_error("filter() requires a function argument".to_string()));
1034 }
1035 }
1036 }
1037
1038 "reduce" => {
1039 if args.len() != 2 {
1040 return Err(self.type_error(
1041 "reduce() requires 2 arguments (initial value and function)"
1042 .to_string(),
1043 ));
1044 }
1045
1046 let init_type = self.check_expr(&args[0])?;
1047 self.expected_lambda_signature = Some((
1048 vec![init_type.clone(), (**elem_type).clone()],
1049 Some(init_type.clone()),
1050 ));
1051 let func_type = self.check_expr(&args[1])?;
1052 match &func_type.kind {
1053 TypeKind::Function {
1054 params: _,
1055 return_type,
1056 } => {
1057 self.unify(&init_type, return_type)?;
1058 return Ok(init_type);
1059 }
1060
1061 _ => {
1062 return Err(self.type_error(
1063 "reduce() requires a function as second argument".to_string(),
1064 ));
1065 }
1066 }
1067 }
1068
1069 _ => {}
1070 },
1071 TypeKind::Map(key_type, value_type) => match method {
1072 "iter" => {
1073 if !args.is_empty() {
1074 return Err(self.type_error("iter() takes no arguments".to_string()));
1075 }
1076
1077 return Ok(Type::new(TypeKind::Named("Iterator".to_string()), span));
1078 }
1079
1080 "len" => {
1081 if !args.is_empty() {
1082 return Err(self.type_error("len() takes no arguments".to_string()));
1083 }
1084
1085 return Ok(Type::new(TypeKind::Int, span));
1086 }
1087
1088 "get" => {
1089 if args.len() != 1 {
1090 return Err(self.type_error("get() requires 1 argument (key)".to_string()));
1091 }
1092
1093 let arg_type = self.check_expr(&args[0])?;
1094 self.unify(key_type, &arg_type)?;
1095 return Ok(Type::new(TypeKind::Option(value_type.clone()), span));
1096 }
1097
1098 "set" => {
1099 if args.len() != 2 {
1100 return Err(
1101 self.type_error("set() requires 2 arguments (key, value)".to_string())
1102 );
1103 }
1104
1105 let key_arg_type = self.check_expr(&args[0])?;
1106 let value_arg_type = self.check_expr(&args[1])?;
1107 self.unify(key_type, &key_arg_type)?;
1108 self.unify(value_type, &value_arg_type)?;
1109 return Ok(Type::new(TypeKind::Unit, span));
1110 }
1111
1112 "has" => {
1113 if args.len() != 1 {
1114 return Err(self.type_error("has() requires 1 argument (key)".to_string()));
1115 }
1116
1117 let arg_type = self.check_expr(&args[0])?;
1118 self.unify(key_type, &arg_type)?;
1119 return Ok(Type::new(TypeKind::Bool, span));
1120 }
1121
1122 "delete" => {
1123 if args.len() != 1 {
1124 return Err(
1125 self.type_error("delete() requires 1 argument (key)".to_string())
1126 );
1127 }
1128
1129 let arg_type = self.check_expr(&args[0])?;
1130 self.unify(key_type, &arg_type)?;
1131 return Ok(Type::new(TypeKind::Option(value_type.clone()), span));
1132 }
1133
1134 "keys" => {
1135 if !args.is_empty() {
1136 return Err(self.type_error("keys() takes no arguments".to_string()));
1137 }
1138
1139 return Ok(Type::new(TypeKind::Array(key_type.clone()), span));
1140 }
1141
1142 "values" => {
1143 if !args.is_empty() {
1144 return Err(self.type_error("values() takes no arguments".to_string()));
1145 }
1146
1147 return Ok(Type::new(TypeKind::Array(value_type.clone()), span));
1148 }
1149
1150 _ => {}
1151 },
1152 TypeKind::Named(type_name) if type_name == "Array" => match method {
1153 "len" => {
1154 if !args.is_empty() {
1155 return Err(self.type_error("len() takes no arguments".to_string()));
1156 }
1157
1158 return Ok(Type::new(TypeKind::Int, span));
1159 }
1160
1161 "get" => {
1162 if args.len() != 1 {
1163 return Err(self.type_error("get() requires 1 argument".to_string()));
1164 }
1165
1166 self.check_expr(&args[0])?;
1167 return Ok(Type::new(TypeKind::Named("Option".to_string()), span));
1168 }
1169
1170 "first" | "last" => {
1171 if !args.is_empty() {
1172 return Err(self.type_error(format!("{}() takes no arguments", method)));
1173 }
1174
1175 return Ok(Type::new(TypeKind::Named("Option".to_string()), span));
1176 }
1177
1178 "push" => {
1179 if args.len() != 1 {
1180 return Err(self.type_error("push() requires 1 argument".to_string()));
1181 }
1182
1183 self.check_expr(&args[0])?;
1184 return Ok(Type::new(TypeKind::Unit, span));
1185 }
1186
1187 "pop" => {
1188 if !args.is_empty() {
1189 return Err(self.type_error("pop() takes no arguments".to_string()));
1190 }
1191
1192 return Ok(Type::new(TypeKind::Named("Option".to_string()), span));
1193 }
1194
1195 "slice" => {
1196 if args.len() != 2 {
1197 return Err(self.type_error("slice() requires 2 arguments".to_string()));
1198 }
1199
1200 self.check_expr(&args[0])?;
1201 self.check_expr(&args[1])?;
1202 return Ok(receiver_type.clone());
1203 }
1204
1205 "clear" => {
1206 if !args.is_empty() {
1207 return Err(self.type_error("clear() takes no arguments".to_string()));
1208 }
1209
1210 return Ok(Type::new(TypeKind::Unit, span));
1211 }
1212
1213 "is_empty" => {
1214 if !args.is_empty() {
1215 return Err(self.type_error("is_empty() takes no arguments".to_string()));
1216 }
1217
1218 return Ok(Type::new(TypeKind::Bool, span));
1219 }
1220
1221 _ => {}
1222 },
1223 TypeKind::Option(inner_type) => match method {
1224 "is_some" | "is_none" => {
1225 if !args.is_empty() {
1226 return Err(self.type_error(format!("{}() takes no arguments", method)));
1227 }
1228
1229 return Ok(Type::new(TypeKind::Bool, span));
1230 }
1231
1232 "unwrap" => {
1233 if !args.is_empty() {
1234 return Err(self.type_error("unwrap() takes no arguments".to_string()));
1235 }
1236
1237 return Ok((**inner_type).clone());
1238 }
1239
1240 "unwrap_or" => {
1241 if args.len() != 1 {
1242 return Err(self.type_error("unwrap_or() requires 1 argument".to_string()));
1243 }
1244
1245 let default_type = self.check_expr(&args[0])?;
1246 return Ok(default_type);
1247 }
1248
1249 _ => {}
1250 },
1251 TypeKind::Result(ok_type, _err_type) => match method {
1252 "is_ok" | "is_err" => {
1253 if !args.is_empty() {
1254 return Err(self.type_error(format!("{}() takes no arguments", method)));
1255 }
1256
1257 return Ok(Type::new(TypeKind::Bool, span));
1258 }
1259
1260 "unwrap" => {
1261 if !args.is_empty() {
1262 return Err(self.type_error("unwrap() takes no arguments".to_string()));
1263 }
1264
1265 return Ok((**ok_type).clone());
1266 }
1267
1268 "unwrap_or" => {
1269 if args.len() != 1 {
1270 return Err(self.type_error("unwrap_or() requires 1 argument".to_string()));
1271 }
1272
1273 let default_type = self.check_expr(&args[0])?;
1274 return Ok(default_type);
1275 }
1276
1277 _ => {}
1278 },
1279 TypeKind::Named(type_name) if type_name == "Option" || type_name == "Result" => {
1280 match method {
1281 "is_some" | "is_none" | "is_ok" | "is_err" => {
1282 if !args.is_empty() {
1283 return Err(self.type_error(format!("{}() takes no arguments", method)));
1284 }
1285
1286 return Ok(Type::new(TypeKind::Bool, span));
1287 }
1288
1289 "unwrap" => {
1290 if !args.is_empty() {
1291 return Err(self.type_error("unwrap() takes no arguments".to_string()));
1292 }
1293
1294 if let ExprKind::Identifier(var_name) = &receiver.kind {
1295 if let Some(concrete_type) =
1296 self.env.lookup_generic_param(var_name, "T")
1297 {
1298 return Ok(concrete_type);
1299 }
1300 }
1301
1302 return Ok(Type::new(TypeKind::Unknown, span));
1303 }
1304
1305 "unwrap_or" => {
1306 if args.len() != 1 {
1307 return Err(
1308 self.type_error("unwrap_or() requires 1 argument".to_string())
1309 );
1310 }
1311
1312 let default_type = self.check_expr(&args[0])?;
1313 return Ok(default_type);
1314 }
1315
1316 _ => {}
1317 }
1318 }
1319
1320 TypeKind::Float => match method {
1321 "to_int" => {
1322 if !args.is_empty() {
1323 return Err(self.type_error("to_int() takes no arguments".to_string()));
1324 }
1325
1326 return Ok(Type::new(TypeKind::Int, span));
1327 }
1328
1329 "floor" | "ceil" | "round" | "sqrt" | "abs" | "sin" | "cos" | "tan" | "asin"
1330 | "acos" | "atan" => {
1331 if !args.is_empty() {
1332 return Err(self.type_error(format!("{}() takes no arguments", method)));
1333 }
1334
1335 return Ok(Type::new(TypeKind::Float, span));
1336 }
1337
1338 "atan2" => {
1339 if args.len() != 1 {
1340 return Err(self.type_error("atan2() requires 1 argument".to_string()));
1341 }
1342
1343 let other_type = self.check_expr(&args[0])?;
1344 self.unify(&Type::new(TypeKind::Float, span), &other_type)?;
1345 return Ok(Type::new(TypeKind::Float, span));
1346 }
1347
1348 "min" | "max" => {
1349 if args.len() != 1 {
1350 return Err(self.type_error(format!("{}() requires 1 argument", method)));
1351 }
1352
1353 self.check_expr(&args[0])?;
1354 return Ok(Type::new(TypeKind::Float, span));
1355 }
1356
1357 "clamp" => {
1358 if args.len() != 2 {
1359 return Err(
1360 self.type_error("clamp() requires 2 arguments (min, max)".to_string())
1361 );
1362 }
1363
1364 let min_type = self.check_expr(&args[0])?;
1365 let max_type = self.check_expr(&args[1])?;
1366 self.unify(&Type::new(TypeKind::Float, span), &min_type)?;
1367 self.unify(&Type::new(TypeKind::Float, span), &max_type)?;
1368 return Ok(Type::new(TypeKind::Float, span));
1369 }
1370
1371 _ => {}
1372 },
1373 TypeKind::Int => match method {
1374 "to_float" => {
1375 if !args.is_empty() {
1376 return Err(self.type_error("to_float() takes no arguments".to_string()));
1377 }
1378
1379 return Ok(Type::new(TypeKind::Float, span));
1380 }
1381
1382 "abs" => {
1383 if !args.is_empty() {
1384 return Err(self.type_error("abs() takes no arguments".to_string()));
1385 }
1386
1387 return Ok(Type::new(TypeKind::Int, span));
1388 }
1389
1390 "min" | "max" => {
1391 if args.len() != 1 {
1392 return Err(self.type_error(format!("{}() requires 1 argument", method)));
1393 }
1394
1395 self.check_expr(&args[0])?;
1396 return Ok(Type::new(TypeKind::Int, span));
1397 }
1398
1399 "clamp" => {
1400 if args.len() != 2 {
1401 return Err(
1402 self.type_error("clamp() requires 2 arguments (min, max)".to_string())
1403 );
1404 }
1405
1406 let min_type = self.check_expr(&args[0])?;
1407 let max_type = self.check_expr(&args[1])?;
1408 self.unify(&Type::new(TypeKind::Int, span), &min_type)?;
1409 self.unify(&Type::new(TypeKind::Int, span), &max_type)?;
1410 return Ok(Type::new(TypeKind::Int, span));
1411 }
1412
1413 _ => {}
1414 },
1415 TypeKind::Named(type_name) if type_name == "LuaTable" => match method {
1416 "len" | "maxn" => {
1417 if !args.is_empty() {
1418 return Err(self.type_error(format!("{}() takes no arguments", method)));
1419 }
1420
1421 return Ok(Type::new(TypeKind::Int, span));
1422 }
1423 "push" => {
1424 if args.len() != 1 {
1425 return Err(self.type_error("push() requires 1 argument".to_string()));
1426 }
1427
1428 self.check_expr(&args[0])?;
1429 return Ok(Type::new(TypeKind::Unit, span));
1430 }
1431 "insert" => {
1432 if args.len() != 2 {
1433 return Err(self.type_error("insert() requires 2 arguments".to_string()));
1434 }
1435
1436 self.check_expr(&args[0])?;
1437 self.check_expr(&args[1])?;
1438 return Ok(Type::new(TypeKind::Unit, span));
1439 }
1440 "remove" => {
1441 if args.len() != 1 {
1442 return Err(self.type_error("remove() requires 1 argument".to_string()));
1443 }
1444
1445 self.check_expr(&args[0])?;
1446 return Ok(Type::new(TypeKind::Unknown, span));
1447 }
1448 "concat" => {
1449 if args.len() != 3 {
1450 return Err(self.type_error("concat() requires 3 arguments".to_string()));
1451 }
1452
1453 self.check_expr(&args[0])?;
1454 self.check_expr(&args[1])?;
1455 self.check_expr(&args[2])?;
1456 return Ok(Type::new(TypeKind::String, span));
1457 }
1458 "unpack" => {
1459 if args.len() != 2 {
1460 return Err(self.type_error("unpack() requires 2 arguments".to_string()));
1461 }
1462
1463 self.check_expr(&args[0])?;
1464 self.check_expr(&args[1])?;
1465 return Ok(Type::new(TypeKind::Unknown, span));
1466 }
1467 "sort" => {
1468 if args.len() != 1 {
1469 return Err(self.type_error("sort() requires 1 argument".to_string()));
1470 }
1471
1472 self.check_expr(&args[0])?;
1473 return Ok(Type::new(TypeKind::Unit, span));
1474 }
1475 _ => {
1476 return Err(self.type_error(format!(
1477 "LuaTable has no method '{}'",
1478 method
1479 )));
1480 }
1481 },
1482 TypeKind::Named(type_name) => {
1483 if let Some(method_def) = self.env.lookup_method(type_name.as_str(), method) {
1484 let method_def = method_def.clone();
1485 let expected_args = method_def.params.len().saturating_sub(1);
1486 if args.len() != expected_args {
1487 return Err(self.type_error(format!(
1488 "Method '{}' expects {} arguments, got {}",
1489 method,
1490 expected_args,
1491 args.len()
1492 )));
1493 }
1494
1495 for (i, (arg, param)) in args
1496 .iter()
1497 .zip(method_def.params.iter().skip(1))
1498 .enumerate()
1499 {
1500 let arg_type = self.check_expr(arg)?;
1501 if !self.types_equal(&arg_type, ¶m.ty) {
1502 return Err(self.type_error(format!(
1503 "Argument {} to method '{}': expected '{}', got '{}'",
1504 i + 1,
1505 method,
1506 param.ty,
1507 arg_type
1508 )));
1509 }
1510 }
1511
1512 return Ok(method_def
1513 .return_type
1514 .clone()
1515 .unwrap_or(Type::new(TypeKind::Unit, span)));
1516 }
1517 }
1518
1519 TypeKind::Generic(type_param) => {
1520 if let Some(trait_names) = self.current_trait_bounds.get(type_param.as_str()) {
1521 for trait_name in trait_names {
1522 if let Some(trait_def) = {
1523 let key = self.resolve_type_key(trait_name.as_str());
1524 self.env
1525 .lookup_trait(&key)
1526 .or_else(|| self.env.lookup_trait(trait_name.as_str()))
1527 } {
1528 if let Some(trait_method) =
1529 trait_def.methods.iter().find(|m| m.name == method)
1530 {
1531 let expected_args =
1532 trait_method.params.iter().filter(|p| !p.is_self).count();
1533 if args.len() != expected_args {
1534 return Err(self.type_error(format!(
1535 "Method '{}' expects {} arguments, got {}",
1536 method,
1537 expected_args,
1538 args.len()
1539 )));
1540 }
1541
1542 return Ok(trait_method
1543 .return_type
1544 .clone()
1545 .unwrap_or(Type::new(TypeKind::Unit, span)));
1546 }
1547 }
1548 }
1549 }
1550 }
1551
1552 TypeKind::Trait(trait_name) => {
1553 if let Some(trait_def) = {
1554 let key = self.resolve_type_key(trait_name);
1555 self.env
1556 .lookup_trait(&key)
1557 .or_else(|| self.env.lookup_trait(trait_name.as_str()))
1558 } {
1559 if let Some(trait_method) = trait_def.methods.iter().find(|m| m.name == method)
1560 {
1561 let expected_args =
1562 trait_method.params.iter().filter(|p| !p.is_self).count();
1563 if args.len() != expected_args {
1564 return Err(self.type_error(format!(
1565 "Method '{}' expects {} arguments, got {}",
1566 method,
1567 expected_args,
1568 args.len()
1569 )));
1570 }
1571
1572 return Ok(trait_method
1573 .return_type
1574 .clone()
1575 .unwrap_or(Type::new(TypeKind::Unit, span)));
1576 }
1577 }
1578 }
1579
1580 _ => {}
1581 }
1582
1583 Err(self.type_error(format!(
1584 "Type '{}' has no method '{}'",
1585 receiver_type, method
1586 )))
1587 }
1588
1589 pub fn check_field_access_with_hint(
1590 &mut self,
1591 span: Span,
1592 object: &Expr,
1593 field: &str,
1594 expected_type: Option<&Type>,
1595 ) -> Result<Type> {
1596 if let ExprKind::Identifier(enum_name) = &object.kind {
1597 if let Some(enum_def) = {
1598 let key = self.resolve_type_key(enum_name);
1599 self.env
1600 .lookup_enum(&key)
1601 .or_else(|| self.env.lookup_enum(enum_name))
1602 } {
1603 let enum_def = enum_def.clone();
1604 let variant_def = enum_def
1605 .variants
1606 .iter()
1607 .find(|v| &v.name == field)
1608 .ok_or_else(|| {
1609 self.type_error_at(
1610 format!("Enum '{}' has no variant '{}'", enum_name, field),
1611 span,
1612 )
1613 })?;
1614 if variant_def.fields.is_some() {
1615 return Err(self.type_error_at(
1616 format!(
1617 "Variant '{}::{}' has fields and must be called with arguments",
1618 enum_name, field
1619 ),
1620 span,
1621 ));
1622 }
1623
1624 if let Some(expected) = expected_type {
1625 match &expected.kind {
1626 TypeKind::Option(_) if enum_name == "Option" => {
1627 return Ok(expected.clone());
1628 }
1629
1630 TypeKind::Result(_, _) if enum_name == "Result" => {
1631 return Ok(expected.clone());
1632 }
1633
1634 _ => {}
1635 }
1636 }
1637
1638 return Ok(Type::new(TypeKind::Named(enum_name.clone()), span));
1639 }
1640 }
1641
1642 let object_type = self.check_expr(object)?;
1643 if matches!(object_type.kind, TypeKind::Unknown)
1644 || matches!(&object_type.kind, TypeKind::Named(name) if name == "LuaValue" || name == "LuaTable")
1645 {
1646 return Ok(Type::new(TypeKind::Unknown, span));
1647 }
1648 if let TypeKind::Union(types) = &object_type.kind {
1649 if types.iter().any(|t| {
1650 matches!(t.kind, TypeKind::Unknown)
1651 || matches!(&t.kind, TypeKind::Named(name) if name == "LuaValue" || name == "LuaTable")
1652 }) {
1653 return Ok(Type::new(TypeKind::Unknown, span));
1654 }
1655 }
1656 if let TypeKind::Map(_, value_type) = &object_type.kind {
1657 return Ok(value_type.as_ref().clone());
1658 }
1659 let type_name = match &object_type.kind {
1660 TypeKind::Named(name) => name.clone(),
1661 _ => {
1662 return Err(self.type_error_at(
1663 format!("Cannot access field on type '{}'", object_type),
1664 object.span,
1665 ))
1666 }
1667 };
1668 self.env
1669 .lookup_struct_field(&type_name, field)
1670 .ok_or_else(|| {
1671 self.type_error_at(
1672 format!("Type '{}' has no field '{}'", type_name, field),
1673 span,
1674 )
1675 })
1676 }
1677
1678 pub fn check_index_expr(&mut self, object: &Expr, index: &Expr) -> Result<Type> {
1679 let object_type = self.check_expr(object)?;
1680 let index_type = self.check_expr(index)?;
1681 if matches!(object_type.kind, TypeKind::Unknown)
1682 || matches!(&object_type.kind, TypeKind::Named(name) if name == "LuaValue" || name == "LuaTable")
1683 {
1684 return Ok(Type::new(TypeKind::Unknown, Self::dummy_span()));
1685 }
1686 if let TypeKind::Union(types) = &object_type.kind {
1687 if types.iter().any(|t| {
1688 matches!(t.kind, TypeKind::Unknown)
1689 || matches!(&t.kind, TypeKind::Named(name) if name == "LuaValue" || name == "LuaTable")
1690 }) {
1691 return Ok(Type::new(TypeKind::Unknown, Self::dummy_span()));
1692 }
1693 }
1694 match &object_type.kind {
1695 TypeKind::Array(elem_type) => {
1696 self.unify(&Type::new(TypeKind::Int, Self::dummy_span()), &index_type)?;
1697 Ok(elem_type.as_ref().clone())
1698 }
1699
1700 TypeKind::Map(key_type, value_type) => {
1701 self.unify(key_type.as_ref(), &index_type)?;
1702 Ok(value_type.as_ref().clone())
1703 }
1704
1705 _ => Err(self.type_error(format!("Cannot index type '{}'", object_type))),
1706 }
1707 }
1708}