Skip to main content

luaur_analysis/methods/
unifier_2_unify_unifier_2_alt_c.rs

1//! Source: `Analysis/src/Unifier2.cpp:149-302` — `Unifier2::unify_(TypeId, TypeId)`,
2//! the core of new-solver type unification.
3
4use crate::enums::unify_result::UnifyResult;
5use crate::functions::follow_type::follow_type_id;
6use crate::functions::get_mutable_type::get_mutable_type_id;
7use crate::functions::get_type_alt_j::get_type_id;
8use crate::functions::is_irresolvable_unifier_2::is_irresolvable;
9use crate::records::any_type::AnyType;
10use crate::records::free_type::FreeType;
11use crate::records::function_type::FunctionType;
12use crate::records::intersection_type::IntersectionType;
13use crate::records::metatable_type::MetatableType;
14use crate::records::negation_type::NegationType;
15use crate::records::never_type::NeverType;
16use crate::records::non_exceptional_recursion_limiter::NonExceptionalRecursionLimiter;
17use crate::records::subtype_constraint::SubtypeConstraint;
18use crate::records::table_type::TableType;
19use crate::records::unifier_2::Unifier2;
20use crate::records::union_type::UnionType;
21use crate::records::unknown_type::UnknownType;
22use crate::type_aliases::constraint_v::ConstraintV;
23use crate::type_aliases::type_id::TypeId;
24use luaur_common::{FFlag, FInt};
25
26impl Unifier2 {
27    pub fn unify_type_id_type_id(
28        &mut self,
29        mut sub_ty: TypeId,
30        mut super_ty: TypeId,
31    ) -> UnifyResult {
32        if FInt::LuauTypeInferIterationLimit.get() > 0
33            && self.iteration_count >= FInt::LuauTypeInferIterationLimit.get() as i32
34        {
35            return UnifyResult::TooComplex;
36        }
37
38        self.iteration_count += 1;
39
40        // NOTE: It's a little odd that we are doing something non-exceptional for
41        // the core of unification but not for occurs check, which may throw an
42        // exception. It would be nice if, in the future, this were unified.
43        if FFlag::LuauLimitUnificationRecursion.get() {
44            // ++(*count) — mirror the C++ NonExceptionalRecursionLimiter (RecursionCounter ctor).
45            self.recursion_count += 1;
46            let mut nerl = NonExceptionalRecursionLimiter {
47                base: unsafe { core::mem::zeroed() },
48                native_stack_guard: unsafe { core::mem::zeroed() },
49            };
50            nerl.non_exceptional_recursion_limiter_non_exceptional_recursion_limiter(
51                &mut self.recursion_count as *mut i32 as *mut core::ffi::c_int,
52            );
53            if !nerl.is_ok(self.recursion_limit as core::ffi::c_int) {
54                return UnifyResult::TooComplex;
55            }
56        }
57
58        sub_ty = unsafe { follow_type_id(sub_ty) };
59        super_ty = unsafe { follow_type_id(super_ty) };
60
61        if let Some(sub_gen) = self.generic_substitutions.find(&sub_ty) {
62            let sub_gen = *sub_gen;
63            return self.unify_type_id_type_id(sub_gen, super_ty);
64        }
65
66        if let Some(super_gen) = self.generic_substitutions.find(&super_ty) {
67            let super_gen = *super_gen;
68            return self.unify_type_id_type_id(sub_ty, super_gen);
69        }
70
71        if self.seen_type_pairings.contains(&(sub_ty, super_ty)) {
72            return UnifyResult::Ok;
73        }
74        self.seen_type_pairings.insert((sub_ty, super_ty));
75
76        if sub_ty == super_ty {
77            return UnifyResult::Ok;
78        }
79
80        // We have potentially done some unifications while dispatching either `SubtypeConstraint` or `PackSubtypeConstraint`,
81        // so rather than implementing backtracking or traversing the entire type graph multiple times, we could push
82        // additional constraints as we discover blocked types along with their proper bounds.
83        //
84        // But we exclude these two subtyping patterns, they are tautological:
85        //   - never <: *blocked*
86        //   - *blocked* <: unknown
87        if (is_irresolvable(sub_ty) || is_irresolvable(super_ty))
88            && unsafe { get_type_id::<NeverType>(sub_ty) }.is_null()
89            && unsafe { get_type_id::<UnknownType>(super_ty) }.is_null()
90        {
91            if !self.uninhabited_type_functions.is_null()
92                && unsafe {
93                    (*self.uninhabited_type_functions)
94                        .contains(&(sub_ty as *const core::ffi::c_void))
95                        || (*self.uninhabited_type_functions)
96                            .contains(&(super_ty as *const core::ffi::c_void))
97                }
98            {
99                return UnifyResult::Ok;
100            }
101
102            self.incomplete_subtypes
103                .push(ConstraintV::Subtype(SubtypeConstraint {
104                    sub_type: sub_ty,
105                    super_type: super_ty,
106                }));
107            return UnifyResult::Ok;
108        }
109
110        let sub_free = unsafe { get_mutable_type_id::<FreeType>(sub_ty) };
111        let super_free = unsafe { get_mutable_type_id::<FreeType>(super_ty) };
112
113        if !super_free.is_null() {
114            let instantiated = self.instantiate_with_bound_types(sub_ty);
115            let new_lower = self.mk_union(unsafe { (*super_free).lower_bound }, instantiated);
116            unsafe {
117                (*super_free).lower_bound = new_lower;
118            }
119        }
120
121        if !sub_free.is_null() {
122            return self.unify_free_with_type(sub_ty, super_ty);
123        }
124
125        if !sub_free.is_null() || !super_free.is_null() {
126            return UnifyResult::Ok;
127        }
128
129        let sub_fn = unsafe { get_type_id::<FunctionType>(sub_ty) };
130        let super_fn = unsafe { get_type_id::<FunctionType>(super_ty) };
131        if !sub_fn.is_null() && !super_fn.is_null() {
132            return self.unify_type_id_function_type(sub_ty, unsafe { &*super_fn });
133        }
134
135        let sub_union = unsafe { get_type_id::<UnionType>(sub_ty) };
136        let super_union = unsafe { get_type_id::<UnionType>(super_ty) };
137        if !sub_union.is_null() {
138            return self.unify_union_type_type_id(unsafe { &*sub_union }, super_ty);
139        } else if !super_union.is_null() {
140            return self.unify_type_id_union_type(sub_ty, unsafe { &*super_union });
141        }
142
143        let sub_intersection = unsafe { get_type_id::<IntersectionType>(sub_ty) };
144        let super_intersection = unsafe { get_type_id::<IntersectionType>(super_ty) };
145        if !sub_intersection.is_null() {
146            return self.unify_intersection_type_type_id(unsafe { &*sub_intersection }, super_ty);
147        } else if !super_intersection.is_null() {
148            return self.unify_type_id_intersection_type(sub_ty, unsafe { &*super_intersection });
149        }
150
151        let sub_never = unsafe { get_type_id::<NeverType>(sub_ty) };
152        let super_never = unsafe { get_type_id::<NeverType>(super_ty) };
153        if !sub_never.is_null() && !super_never.is_null() {
154            return UnifyResult::Ok;
155        } else if !sub_never.is_null() && !super_fn.is_null() {
156            // If `never` is the subtype, then we can propagate that inward.
157            let builtin_types = unsafe { &*self.builtin_types.as_ptr() };
158            let never_pack = builtin_types.neverTypePack;
159            let super_fn_ref = unsafe { &*super_fn };
160            let arg_result =
161                self.unify_type_pack_id_type_pack_id(super_fn_ref.arg_types, never_pack);
162            let ret_result =
163                self.unify_type_pack_id_type_pack_id(never_pack, super_fn_ref.ret_types);
164            return arg_result & ret_result;
165        } else if !sub_fn.is_null() && !super_never.is_null() {
166            // If `never` is the supertype, then we can propagate that inward.
167            let builtin_types = unsafe { &*self.builtin_types.as_ptr() };
168            let never_pack = builtin_types.neverTypePack;
169            let sub_fn_ref = unsafe { &*sub_fn };
170            let arg_result = self.unify_type_pack_id_type_pack_id(never_pack, sub_fn_ref.arg_types);
171            let ret_result = self.unify_type_pack_id_type_pack_id(sub_fn_ref.ret_types, never_pack);
172            return arg_result & ret_result;
173        }
174
175        let sub_any = unsafe { get_type_id::<AnyType>(sub_ty) };
176        let super_any = unsafe { get_type_id::<AnyType>(super_ty) };
177
178        let sub_table = unsafe { get_mutable_type_id::<TableType>(sub_ty) };
179        let super_table = unsafe { get_type_id::<TableType>(super_ty) };
180
181        if !sub_any.is_null() && !super_any.is_null() {
182            return UnifyResult::Ok;
183        } else if !sub_any.is_null() && !super_fn.is_null() {
184            return self.unify_any_type_function_type(unsafe { &*sub_any }, unsafe { &*super_fn });
185        } else if !sub_fn.is_null() && !super_any.is_null() {
186            return self.unify_function_type_any_type(unsafe { &*sub_fn }, unsafe { &*super_any });
187        } else if !sub_any.is_null() && !super_table.is_null() {
188            return self.unify_any_type_table_type(unsafe { &*sub_any }, unsafe { &*super_table });
189        } else if !sub_table.is_null() && !super_any.is_null() {
190            return self.unify_table_type_any_type(unsafe { &*sub_table }, unsafe { &*super_any });
191        }
192
193        if !sub_table.is_null() && !super_table.is_null() {
194            // `boundTo` works like a bound type, and therefore we'd replace it
195            // with the `boundTo` and try unification again.
196            //
197            // However, these pointers should have been chased already by follow().
198            luaur_common::macros::luau_assert::LUAU_ASSERT!(
199                unsafe { (*sub_table).bound_to }.is_none()
200            );
201            luaur_common::macros::luau_assert::LUAU_ASSERT!(
202                unsafe { (*super_table).bound_to }.is_none()
203            );
204
205            return self
206                .unify_table_type_table_type(unsafe { &mut *sub_table }, unsafe { &*super_table });
207        }
208
209        let sub_metatable = unsafe { get_type_id::<MetatableType>(sub_ty) };
210        let super_metatable = unsafe { get_type_id::<MetatableType>(super_ty) };
211        if !sub_metatable.is_null() && !super_metatable.is_null() {
212            return self.unify_metatable_type_metatable_type(unsafe { &*sub_metatable }, unsafe {
213                &*super_metatable
214            });
215        } else if !sub_metatable.is_null() && !super_any.is_null() {
216            return self
217                .unify_metatable_type_any_type(unsafe { &*sub_metatable }, unsafe { &*super_any });
218        } else if !sub_any.is_null() && !super_metatable.is_null() {
219            return self
220                .unify_any_type_metatable_type(unsafe { &*sub_any }, unsafe { &*super_metatable });
221        } else if !sub_metatable.is_null() {
222            // if we only have one metatable, unify with the inner table
223            let inner = unsafe { (*sub_metatable).table() };
224            return self.unify_type_id_type_id(inner, super_ty);
225        } else if !super_metatable.is_null() {
226            // if we only have one metatable, unify with the inner table
227            let inner = unsafe { (*super_metatable).table() };
228            return self.unify_type_id_type_id(sub_ty, inner);
229        }
230
231        let sub_negation = unsafe { get_type_id::<NegationType>(sub_ty) };
232        let super_negation = unsafe { get_type_id::<NegationType>(super_ty) };
233        if !sub_negation.is_null() && !super_negation.is_null() {
234            return self.unify_type_id_type_id(unsafe { (*sub_negation).ty }, unsafe {
235                (*super_negation).ty
236            });
237        }
238
239        // The unification failed, but we're not doing type checking.
240        UnifyResult::Ok
241    }
242}