Skip to main content

luaur_analysis/methods/
overload_resolver_test_function_or_call_metamethod.rs

1//! Source: `Analysis/src/OverloadResolver.cpp:604-643` (hand-ported)
2//!
3//! A utility function for ::resolveOverload. If a particular overload is a table
4//! with a __call metamethod, unwrap that and test it.
5//!
6//! Note: The __call metamethod can itself be overloaded, but it cannot be a
7//! table that overloads __call.  It must be an actual function.
8use crate::functions::find_metatable_entry::find_metatable_entry;
9use crate::functions::follow_type::follow_type_id;
10use crate::functions::get_type_alt_j::get_type_id;
11use crate::records::function_type::FunctionType;
12use crate::records::intersection_type::IntersectionType;
13use crate::records::overload_resolution::OverloadResolution;
14use crate::records::overload_resolver::OverloadResolver;
15use crate::type_aliases::error_vec::ErrorVec;
16use crate::type_aliases::type_id::TypeId;
17use crate::type_aliases::type_pack_id::TypePackId;
18use luaur_ast::records::location::Location;
19use luaur_common::records::dense_hash_set::DenseHashSet;
20
21impl OverloadResolver {
22    pub fn test_function_or_call_metamethod(
23        &mut self,
24        result: &mut OverloadResolution,
25        fn_ty: TypeId,
26        args_pack: TypePackId,
27        fn_location: Location,
28        unique_types: *mut DenseHashSet<TypeId>,
29    ) {
30        let mut fn_ty = unsafe { follow_type_id(fn_ty) };
31        let mut args_pack = args_pack;
32
33        let mut dummy_errors: ErrorVec = alloc::vec::Vec::new();
34        if let Some(call_metamethod) = find_metatable_entry(
35            self.builtin_types,
36            &mut dummy_errors,
37            fn_ty,
38            "__call",
39            self.call_loc,
40        ) {
41            // Calling a metamethod forwards `fnTy` as self.
42            args_pack = unsafe {
43                (*self.arena).add_type_pack_vector_type_id_optional_type_pack_id(
44                    alloc::vec![fn_ty],
45                    Some(args_pack),
46                )
47            };
48            fn_ty = unsafe { follow_type_id(call_metamethod) };
49
50            // Handle an overloaded __call metamethod.
51            let it = unsafe { get_type_id::<IntersectionType>(fn_ty) };
52            if !it.is_null() {
53                let it = unsafe { &*it };
54                let parts = it.parts.clone();
55                for component in parts {
56                    let component = unsafe { follow_type_id(component) };
57                    result.metamethods.insert(component);
58                    let fn_ptr = unsafe { get_type_id::<FunctionType>(component) };
59
60                    if !fn_ptr.is_null()
61                        && !self.is_arity_compatible(
62                            args_pack,
63                            unsafe { (*fn_ptr).arg_types },
64                            self.builtin_types,
65                        )
66                    {
67                        result.arity_mismatches.push(component);
68                    } else {
69                        self.test_function(result, component, args_pack, fn_location, unique_types);
70                    }
71                }
72                return;
73            }
74
75            result.metamethods.insert(fn_ty);
76        }
77
78        // Handle non-metamethods and metamethods which aren't overloaded.
79        self.test_function(result, fn_ty, args_pack, fn_location, unique_types);
80    }
81}