Skip to main content

luaur_analysis/functions/
solve_function_call.rs

1//! C++ `static std::optional<TypePackId> solveFunctionCall(NotNull<TypeFunctionContext>
2//! ctx, const Location& location, TypeId fnTy, TypePackId argsPack)`
3//! (BuiltinTypeFunctions.cpp:121-192). Resolves a (meta)method overload, unifies a
4//! prospective function shape against it, and returns the resulting return pack
5//! (instantiating generic substitutions where the overload was generic).
6use crate::enums::polarity::Polarity;
7use crate::enums::unify_result::UnifyResult;
8use crate::functions::get_approximate_return_type_for_function_call_type_utils::get_approximate_return_type_for_function_call_type_id_dense_hash_set_type_id as get_approximate_return_type_for_function_call;
9use crate::functions::instantiate_2_instantiation_2_alt_b::instantiate_2;
10use crate::functions::track_interior_free_type::track_interior_free_type;
11use crate::functions::track_interior_free_type_pack::track_interior_free_type_pack;
12use crate::records::function_type::FunctionType;
13use crate::records::overload_resolution::OverloadResolution;
14use crate::records::overload_resolver::OverloadResolver;
15use crate::records::subtyping::Subtyping;
16use crate::records::type_function_context::TypeFunctionContext;
17use crate::records::unifier_2::Unifier2;
18use crate::type_aliases::type_id::TypeId;
19use crate::type_aliases::type_pack_id::TypePackId;
20use luaur_ast::records::location::Location;
21use luaur_common::records::dense_hash_set::DenseHashSet;
22
23pub fn solve_function_call(
24    ctx: *mut TypeFunctionContext,
25    location: Location,
26    fn_ty: TypeId,
27    args_pack: TypePackId,
28) -> Option<TypePackId> {
29    let ctx_ref = unsafe { &*ctx };
30
31    // auto resolver = std::make_unique<OverloadResolver>(
32    //     ctx->builtins, ctx->arena, ctx->normalizer, ctx->typeFunctionRuntime,
33    //     ctx->scope, ctx->ice, ctx->limits, location);
34    let subtyping = Subtyping::subtyping_owned(
35        ctx_ref.builtins.as_ptr(),
36        ctx_ref.arena.as_ptr(),
37        ctx_ref.normalizer.as_ptr(),
38        ctx_ref.type_function_runtime.as_ptr(),
39        ctx_ref.ice.as_ptr(),
40    );
41    let mut resolver = OverloadResolver {
42        builtin_types: ctx_ref.builtins.as_ptr(),
43        arena: ctx_ref.arena.as_ptr(),
44        normalizer: ctx_ref.normalizer.as_ptr(),
45        type_function_runtime: ctx_ref.type_function_runtime.as_ptr(),
46        scope: ctx_ref.scope.as_ptr(),
47        ice: ctx_ref.ice.as_ptr(),
48        limits: unsafe { core::ptr::read(ctx_ref.limits.as_ptr() as *const _) },
49        subtyping,
50        call_loc: location,
51    };
52
53    let mut unique_types: DenseHashSet<TypeId> = DenseHashSet::new(core::ptr::null());
54    let resolution: OverloadResolution = resolver.resolve_overload(
55        fn_ty,
56        args_pack,
57        location,
58        &mut unique_types as *mut DenseHashSet<TypeId>,
59        /* useFreeTypeBounds */ false,
60    );
61
62    if resolution.ok.is_empty() && resolution.potential_overloads.is_empty() {
63        return None;
64    }
65
66    let selected = resolution.get_unambiguous_overload();
67
68    let selected_overload = selected.overload?;
69
70    let mut ret_pack = unsafe {
71        (*ctx_ref.arena.as_ptr()).fresh_type_pack(ctx_ref.scope.as_ptr(), Polarity::Positive)
72    };
73    let prospective_function = unsafe {
74        (*ctx_ref.arena.as_ptr()).add_type(FunctionType::function_type_new(
75            args_pack, ret_pack, None, false,
76        ))
77    };
78
79    // FIXME (mirroring C++): we have to bust out the Unifier here.
80    let mut unifier = Unifier2::unifier_2_not_null_type_arena_not_null_builtin_types_not_null_scope_not_null_internal_error_reporter(
81        ctx_ref.arena,
82        ctx_ref.builtins,
83        ctx_ref.scope,
84        ctx_ref.ice,
85    );
86
87    let unify_result = unifier.unify(selected_overload, prospective_function);
88
89    match unify_result {
90        UnifyResult::Ok => {}
91        UnifyResult::OccursCheckFailed => return None,
92        UnifyResult::TooComplex => return None,
93    }
94
95    if !unifier.generic_substitutions.empty() || !unifier.generic_pack_substitutions.empty() {
96        let mut subtyping2 = Subtyping::subtyping_owned(
97            ctx_ref.builtins.as_ptr(),
98            ctx_ref.arena.as_ptr(),
99            ctx_ref.normalizer.as_ptr(),
100            ctx_ref.type_function_runtime.as_ptr(),
101            ctx_ref.ice.as_ptr(),
102        );
103
104        let mut seen: DenseHashSet<TypeId> = DenseHashSet::new(core::ptr::null());
105        let new_ret_tp =
106            get_approximate_return_type_for_function_call(selected_overload, &mut seen)
107                .unwrap_or(unsafe { ctx_ref.builtins.as_ref().errorTypePack });
108
109        // C++ `std::move`s the substitution maps into instantiate2; a clone is the
110        // faithful behavioral equivalent (the unifier is dropped at scope end).
111        let subst = instantiate_2(
112            ctx_ref.arena.as_ptr(),
113            unifier.generic_substitutions.clone(),
114            unifier.generic_pack_substitutions.clone(),
115            &mut subtyping2 as *mut Subtyping,
116            ctx_ref.scope.as_ptr(),
117            new_ret_tp,
118        );
119
120        let subst = subst?;
121        ret_pack = subst;
122    }
123
124    // After we solve for the instantiated function type of this metamethod, we
125    // may have new free types if the metamethod was generic. We capture these so
126    // that they can be generalized later and we don't end up with free types in
127    // type checking.
128    for &ty in &unifier.new_fresh_types {
129        track_interior_free_type(ctx_ref.scope.as_ptr(), ty);
130    }
131    for &tp in &unifier.new_fresh_type_packs {
132        track_interior_free_type_pack(ctx_ref.scope.as_ptr(), tp);
133    }
134
135    Some(ret_pack)
136}