lust/typechecker/expr_checker/
operations.rs

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" => {
1330                    if !args.is_empty() {
1331                        return Err(self.type_error(format!("{}() takes no arguments", method)));
1332                    }
1333
1334                    return Ok(Type::new(TypeKind::Float, span));
1335                }
1336
1337                "min" | "max" => {
1338                    if args.len() != 1 {
1339                        return Err(self.type_error(format!("{}() requires 1 argument", method)));
1340                    }
1341
1342                    self.check_expr(&args[0])?;
1343                    return Ok(Type::new(TypeKind::Float, span));
1344                }
1345
1346                "clamp" => {
1347                    if args.len() != 2 {
1348                        return Err(
1349                            self.type_error("clamp() requires 2 arguments (min, max)".to_string())
1350                        );
1351                    }
1352
1353                    let min_type = self.check_expr(&args[0])?;
1354                    let max_type = self.check_expr(&args[1])?;
1355                    self.unify(&Type::new(TypeKind::Float, span), &min_type)?;
1356                    self.unify(&Type::new(TypeKind::Float, span), &max_type)?;
1357                    return Ok(Type::new(TypeKind::Float, span));
1358                }
1359
1360                _ => {}
1361            },
1362            TypeKind::Int => match method {
1363                "to_float" => {
1364                    if !args.is_empty() {
1365                        return Err(self.type_error("to_float() takes no arguments".to_string()));
1366                    }
1367
1368                    return Ok(Type::new(TypeKind::Float, span));
1369                }
1370
1371                "abs" => {
1372                    if !args.is_empty() {
1373                        return Err(self.type_error("abs() takes no arguments".to_string()));
1374                    }
1375
1376                    return Ok(Type::new(TypeKind::Int, span));
1377                }
1378
1379                "min" | "max" => {
1380                    if args.len() != 1 {
1381                        return Err(self.type_error(format!("{}() requires 1 argument", method)));
1382                    }
1383
1384                    self.check_expr(&args[0])?;
1385                    return Ok(Type::new(TypeKind::Int, span));
1386                }
1387
1388                "clamp" => {
1389                    if args.len() != 2 {
1390                        return Err(
1391                            self.type_error("clamp() requires 2 arguments (min, max)".to_string())
1392                        );
1393                    }
1394
1395                    let min_type = self.check_expr(&args[0])?;
1396                    let max_type = self.check_expr(&args[1])?;
1397                    self.unify(&Type::new(TypeKind::Int, span), &min_type)?;
1398                    self.unify(&Type::new(TypeKind::Int, span), &max_type)?;
1399                    return Ok(Type::new(TypeKind::Int, span));
1400                }
1401
1402                _ => {}
1403            },
1404            TypeKind::Named(type_name) if type_name == "LuaTable" => match method {
1405                "len" | "maxn" => {
1406                    if !args.is_empty() {
1407                        return Err(self.type_error(format!("{}() takes no arguments", method)));
1408                    }
1409
1410                    return Ok(Type::new(TypeKind::Int, span));
1411                }
1412                "push" => {
1413                    if args.len() != 1 {
1414                        return Err(self.type_error("push() requires 1 argument".to_string()));
1415                    }
1416
1417                    self.check_expr(&args[0])?;
1418                    return Ok(Type::new(TypeKind::Unit, span));
1419                }
1420                "insert" => {
1421                    if args.len() != 2 {
1422                        return Err(self.type_error("insert() requires 2 arguments".to_string()));
1423                    }
1424
1425                    self.check_expr(&args[0])?;
1426                    self.check_expr(&args[1])?;
1427                    return Ok(Type::new(TypeKind::Unit, span));
1428                }
1429                "remove" => {
1430                    if args.len() != 1 {
1431                        return Err(self.type_error("remove() requires 1 argument".to_string()));
1432                    }
1433
1434                    self.check_expr(&args[0])?;
1435                    return Ok(Type::new(TypeKind::Unknown, span));
1436                }
1437                "concat" => {
1438                    if args.len() != 3 {
1439                        return Err(self.type_error("concat() requires 3 arguments".to_string()));
1440                    }
1441
1442                    self.check_expr(&args[0])?;
1443                    self.check_expr(&args[1])?;
1444                    self.check_expr(&args[2])?;
1445                    return Ok(Type::new(TypeKind::String, span));
1446                }
1447                "unpack" => {
1448                    if args.len() != 2 {
1449                        return Err(self.type_error("unpack() requires 2 arguments".to_string()));
1450                    }
1451
1452                    self.check_expr(&args[0])?;
1453                    self.check_expr(&args[1])?;
1454                    return Ok(Type::new(TypeKind::Unknown, span));
1455                }
1456                "sort" => {
1457                    if args.len() != 1 {
1458                        return Err(self.type_error("sort() requires 1 argument".to_string()));
1459                    }
1460
1461                    self.check_expr(&args[0])?;
1462                    return Ok(Type::new(TypeKind::Unit, span));
1463                }
1464                _ => {
1465                    return Err(self.type_error(format!(
1466                        "LuaTable has no method '{}'",
1467                        method
1468                    )));
1469                }
1470            },
1471            TypeKind::Named(type_name) => {
1472                if let Some(method_def) = self.env.lookup_method(type_name.as_str(), method) {
1473                    let method_def = method_def.clone();
1474                    let expected_args = method_def.params.len().saturating_sub(1);
1475                    if args.len() != expected_args {
1476                        return Err(self.type_error(format!(
1477                            "Method '{}' expects {} arguments, got {}",
1478                            method,
1479                            expected_args,
1480                            args.len()
1481                        )));
1482                    }
1483
1484                    for (i, (arg, param)) in args
1485                        .iter()
1486                        .zip(method_def.params.iter().skip(1))
1487                        .enumerate()
1488                    {
1489                        let arg_type = self.check_expr(arg)?;
1490                        if !self.types_equal(&arg_type, &param.ty) {
1491                            return Err(self.type_error(format!(
1492                                "Argument {} to method '{}': expected '{}', got '{}'",
1493                                i + 1,
1494                                method,
1495                                param.ty,
1496                                arg_type
1497                            )));
1498                        }
1499                    }
1500
1501                    return Ok(method_def
1502                        .return_type
1503                        .clone()
1504                        .unwrap_or(Type::new(TypeKind::Unit, span)));
1505                }
1506            }
1507
1508            TypeKind::Generic(type_param) => {
1509                if let Some(trait_names) = self.current_trait_bounds.get(type_param.as_str()) {
1510                    for trait_name in trait_names {
1511                        if let Some(trait_def) = {
1512                            let key = self.resolve_type_key(trait_name.as_str());
1513                            self.env
1514                                .lookup_trait(&key)
1515                                .or_else(|| self.env.lookup_trait(trait_name.as_str()))
1516                        } {
1517                            if let Some(trait_method) =
1518                                trait_def.methods.iter().find(|m| m.name == method)
1519                            {
1520                                let expected_args =
1521                                    trait_method.params.iter().filter(|p| !p.is_self).count();
1522                                if args.len() != expected_args {
1523                                    return Err(self.type_error(format!(
1524                                        "Method '{}' expects {} arguments, got {}",
1525                                        method,
1526                                        expected_args,
1527                                        args.len()
1528                                    )));
1529                                }
1530
1531                                return Ok(trait_method
1532                                    .return_type
1533                                    .clone()
1534                                    .unwrap_or(Type::new(TypeKind::Unit, span)));
1535                            }
1536                        }
1537                    }
1538                }
1539            }
1540
1541            TypeKind::Trait(trait_name) => {
1542                if let Some(trait_def) = {
1543                    let key = self.resolve_type_key(trait_name);
1544                    self.env
1545                        .lookup_trait(&key)
1546                        .or_else(|| self.env.lookup_trait(trait_name.as_str()))
1547                } {
1548                    if let Some(trait_method) = trait_def.methods.iter().find(|m| m.name == method)
1549                    {
1550                        let expected_args =
1551                            trait_method.params.iter().filter(|p| !p.is_self).count();
1552                        if args.len() != expected_args {
1553                            return Err(self.type_error(format!(
1554                                "Method '{}' expects {} arguments, got {}",
1555                                method,
1556                                expected_args,
1557                                args.len()
1558                            )));
1559                        }
1560
1561                        return Ok(trait_method
1562                            .return_type
1563                            .clone()
1564                            .unwrap_or(Type::new(TypeKind::Unit, span)));
1565                    }
1566                }
1567            }
1568
1569            _ => {}
1570        }
1571
1572        Err(self.type_error(format!(
1573            "Type '{}' has no method '{}'",
1574            receiver_type, method
1575        )))
1576    }
1577
1578    pub fn check_field_access_with_hint(
1579        &mut self,
1580        span: Span,
1581        object: &Expr,
1582        field: &str,
1583        expected_type: Option<&Type>,
1584    ) -> Result<Type> {
1585        if let ExprKind::Identifier(enum_name) = &object.kind {
1586            if let Some(enum_def) = {
1587                let key = self.resolve_type_key(enum_name);
1588                self.env
1589                    .lookup_enum(&key)
1590                    .or_else(|| self.env.lookup_enum(enum_name))
1591            } {
1592                let enum_def = enum_def.clone();
1593                let variant_def = enum_def
1594                    .variants
1595                    .iter()
1596                    .find(|v| &v.name == field)
1597                    .ok_or_else(|| {
1598                        self.type_error_at(
1599                            format!("Enum '{}' has no variant '{}'", enum_name, field),
1600                            span,
1601                        )
1602                    })?;
1603                if variant_def.fields.is_some() {
1604                    return Err(self.type_error_at(
1605                        format!(
1606                            "Variant '{}::{}' has fields and must be called with arguments",
1607                            enum_name, field
1608                        ),
1609                        span,
1610                    ));
1611                }
1612
1613                if let Some(expected) = expected_type {
1614                    match &expected.kind {
1615                        TypeKind::Option(_) if enum_name == "Option" => {
1616                            return Ok(expected.clone());
1617                        }
1618
1619                        TypeKind::Result(_, _) if enum_name == "Result" => {
1620                            return Ok(expected.clone());
1621                        }
1622
1623                        _ => {}
1624                    }
1625                }
1626
1627                return Ok(Type::new(TypeKind::Named(enum_name.clone()), span));
1628            }
1629        }
1630
1631        let object_type = self.check_expr(object)?;
1632        if matches!(object_type.kind, TypeKind::Unknown)
1633            || matches!(&object_type.kind, TypeKind::Named(name) if name == "LuaValue" || name == "LuaTable")
1634        {
1635            return Ok(Type::new(TypeKind::Unknown, span));
1636        }
1637        if let TypeKind::Union(types) = &object_type.kind {
1638            if types.iter().any(|t| {
1639                matches!(t.kind, TypeKind::Unknown)
1640                    || matches!(&t.kind, TypeKind::Named(name) if name == "LuaValue" || name == "LuaTable")
1641            }) {
1642                return Ok(Type::new(TypeKind::Unknown, span));
1643            }
1644        }
1645        if let TypeKind::Map(_, value_type) = &object_type.kind {
1646            return Ok(value_type.as_ref().clone());
1647        }
1648        let type_name = match &object_type.kind {
1649            TypeKind::Named(name) => name.clone(),
1650            _ => {
1651                return Err(self.type_error_at(
1652                    format!("Cannot access field on type '{}'", object_type),
1653                    object.span,
1654                ))
1655            }
1656        };
1657        self.env
1658            .lookup_struct_field(&type_name, field)
1659            .ok_or_else(|| {
1660                self.type_error_at(
1661                    format!("Type '{}' has no field '{}'", type_name, field),
1662                    span,
1663                )
1664            })
1665    }
1666
1667    pub fn check_index_expr(&mut self, object: &Expr, index: &Expr) -> Result<Type> {
1668        let object_type = self.check_expr(object)?;
1669        let index_type = self.check_expr(index)?;
1670        if matches!(object_type.kind, TypeKind::Unknown)
1671            || matches!(&object_type.kind, TypeKind::Named(name) if name == "LuaValue" || name == "LuaTable")
1672        {
1673            return Ok(Type::new(TypeKind::Unknown, Self::dummy_span()));
1674        }
1675        if let TypeKind::Union(types) = &object_type.kind {
1676            if types.iter().any(|t| {
1677                matches!(t.kind, TypeKind::Unknown)
1678                    || matches!(&t.kind, TypeKind::Named(name) if name == "LuaValue" || name == "LuaTable")
1679            }) {
1680                return Ok(Type::new(TypeKind::Unknown, Self::dummy_span()));
1681            }
1682        }
1683        match &object_type.kind {
1684            TypeKind::Array(elem_type) => {
1685                self.unify(&Type::new(TypeKind::Int, Self::dummy_span()), &index_type)?;
1686                Ok(elem_type.as_ref().clone())
1687            }
1688
1689            TypeKind::Map(key_type, value_type) => {
1690                self.unify(key_type.as_ref(), &index_type)?;
1691                Ok(value_type.as_ref().clone())
1692            }
1693
1694            _ => Err(self.type_error(format!("Cannot index type '{}'", object_type))),
1695        }
1696    }
1697}