Skip to main content

luaur_analysis/methods/
overload_resolver_test_function.rs

1//! Source: `Analysis/src/OverloadResolver.cpp:457-548` (hand-ported)
2//!
3//! Test a single FunctionType against an argument list. Reduces type functions
4//! and does a proper arity check.
5use crate::enums::type_function_instance_state::TypeFunctionInstanceState;
6use crate::functions::are_unsatisfied_arguments_optional::are_unsatisfied_arguments_optional;
7use crate::functions::follow_type::follow_type_id;
8use crate::functions::get_type_alt_j::get_type_id;
9use crate::functions::ignore_reasoning_for_return_type::ignore_reasoning_for_return_type;
10use crate::functions::reduce_type_functions_type_function::reduce_type_functions;
11use crate::records::blocked_type::BlockedType;
12use crate::records::free_type::FreeType;
13use crate::records::function_type::FunctionType;
14use crate::records::overload_resolution::OverloadResolution;
15use crate::records::overload_resolver::OverloadResolver;
16use crate::records::pending_expansion_type::PendingExpansionType;
17use crate::records::type_error::TypeError;
18use crate::records::type_function_context::TypeFunctionContext;
19use crate::records::type_function_instance_type::TypeFunctionInstanceType;
20use crate::type_aliases::error_vec::ErrorVec;
21use crate::type_aliases::type_id::TypeId;
22use crate::type_aliases::type_pack_id::TypePackId;
23use core::ptr::NonNull;
24use luaur_ast::records::location::Location;
25use luaur_common::records::dense_hash_set::DenseHashSet;
26use luaur_common::records::variant::Variant2;
27
28impl OverloadResolver {
29    pub fn test_function(
30        &mut self,
31        result: &mut OverloadResolution,
32        fn_ty: TypeId,
33        args_pack: TypePackId,
34        fn_location: Location,
35        unique_types: *mut DenseHashSet<TypeId>,
36    ) {
37        let fn_ty = unsafe { follow_type_id(fn_ty) };
38
39        // TODO: This seems like the wrong spot to do this check.
40        if unsafe {
41            !get_type_id::<FreeType>(fn_ty).is_null()
42                || !get_type_id::<BlockedType>(fn_ty).is_null()
43                || !get_type_id::<PendingExpansionType>(fn_ty).is_null()
44        } {
45            // TODO.  Luckily, these constraints are not yet used.
46            let constraints = alloc::vec::Vec::new();
47            result.potential_overloads.push((fn_ty, constraints));
48            return;
49        }
50
51        let tfit = unsafe { get_type_id::<TypeFunctionInstanceType>(fn_ty) };
52        if !tfit.is_null() && unsafe { (*tfit).state } == TypeFunctionInstanceState::Unsolved {
53            // TODO.  Luckily, these constraints are not yet used.
54            let constraints = alloc::vec::Vec::new();
55            result.potential_overloads.push((fn_ty, constraints));
56            return;
57        }
58
59        let ftv = unsafe { get_type_id::<FunctionType>(fn_ty) };
60        if ftv.is_null() {
61            result.non_functions.push(fn_ty);
62            return;
63        }
64        let ftv = unsafe { &*ftv };
65
66        if !self.is_arity_compatible(args_pack, ftv.arg_types, self.builtin_types) {
67            result.arity_mismatches.push(fn_ty);
68            return;
69        }
70
71        let context = TypeFunctionContext {
72            arena: unsafe { NonNull::new_unchecked(self.arena) },
73            builtins: unsafe { NonNull::new_unchecked(self.builtin_types) },
74            scope: unsafe { NonNull::new_unchecked(self.scope) },
75            normalizer: unsafe { NonNull::new_unchecked(self.normalizer) },
76            type_function_runtime: unsafe { NonNull::new_unchecked(self.type_function_runtime) },
77            ice: unsafe { NonNull::new_unchecked(self.ice) },
78            limits: unsafe { NonNull::new_unchecked(&self.limits as *const _ as *mut _) },
79            subtyping: unsafe { NonNull::new_unchecked(&mut self.subtyping as *mut _) },
80            solver: core::ptr::null_mut(),
81            constraint: core::ptr::null(),
82            user_func_name: None,
83            fresh_instances: alloc::vec::Vec::new(),
84        };
85        let mut context = context;
86        let reduce_result = reduce_type_functions(
87            fn_ty,
88            self.call_loc,
89            unsafe { NonNull::new_unchecked(&mut context as *mut _) },
90            /*force=*/ true,
91        );
92        if !reduce_result.errors.is_empty() {
93            result
94                .incompatible_overloads
95                .push((fn_ty, Variant2::V1(reduce_result.errors)));
96            return;
97        }
98
99        let prospective_function = unsafe {
100            (*self.arena).add_type(FunctionType::function_type_new(
101                args_pack,
102                (*self.builtin_types).anyTypePack,
103                None,
104                false,
105            ))
106        };
107
108        self.subtyping.unique_types = unique_types as *const DenseHashSet<TypeId>;
109        let scope = self.scope;
110        let mut r = self.subtyping.is_subtype_type_id_type_id_not_null_scope(
111            fn_ty,
112            prospective_function,
113            scope,
114        );
115
116        // Frustratingly, subtyping does not know about error suppression, so this
117        // subtype test will probably fail due to the mismatched return types. Here,
118        // we'll prune any SubtypingReasons that have anything to do with the return
119        // type.
120        ignore_reasoning_for_return_type(&mut r);
121
122        if r.is_subtype {
123            if r.assumed_constraints.is_empty() {
124                result.ok.push(fn_ty);
125            } else {
126                result
127                    .potential_overloads
128                    .push((fn_ty, core::mem::take(&mut r.assumed_constraints)));
129            }
130        } else if !r.generic_bounds_mismatches.is_empty() {
131            let mut errors: ErrorVec = alloc::vec::Vec::new();
132            for gbm in r.generic_bounds_mismatches.iter() {
133                errors.push(TypeError::type_error_location_type_error_data(
134                    fn_location,
135                    crate::type_aliases::type_error_data::TypeErrorData::GenericBoundsMismatch(
136                        gbm.clone(),
137                    ),
138                ));
139            }
140            result
141                .incompatible_overloads
142                .push((fn_ty, Variant2::V1(errors)));
143        } else if are_unsatisfied_arguments_optional(&r.reasoning, args_pack, ftv.arg_types) {
144            // Important!  Subtyping doesn't know anything about
145            // optional arguments.  If the only reason subtyping
146            // failed is because optional arguments were not provided,
147            // then this overload is actually okay.
148            if r.assumed_constraints.is_empty() {
149                result.ok.push(fn_ty);
150            } else {
151                result
152                    .potential_overloads
153                    .push((fn_ty, core::mem::take(&mut r.assumed_constraints)));
154            }
155        } else {
156            result
157                .incompatible_overloads
158                .push((fn_ty, Variant2::V0(core::mem::take(&mut r.reasoning))));
159        }
160    }
161}