Skip to main content

luaur_analysis/methods/
unifier_try_unify_unifier_alt_b.rs

1//! Source: `Analysis/src/Unifier.cpp` (Unifier::tryUnify_(TypeId,...), L404-670)
2use crate::enums::normalization_result::NormalizationResult;
3use crate::enums::variance::Variance;
4use crate::functions::get_type_alt_j::get_type_id;
5use crate::functions::is_blocked_unifier::is_blocked_txn_log_type_id;
6use crate::functions::is_prim::is_prim;
7use crate::records::any_type::AnyType;
8use crate::records::extern_type::ExternType;
9use crate::records::free_type::FreeType;
10use crate::records::function_type::FunctionType;
11use crate::records::generic_error::GenericError;
12use crate::records::generic_type::GenericType;
13use crate::records::intersection_type::IntersectionType;
14use crate::records::metatable_type::MetatableType;
15use crate::records::negation_type::NegationType;
16use crate::records::never_type::NeverType;
17use crate::records::primitive_type::{PrimitiveType, Type as PrimType};
18use crate::records::r#type::Type;
19use crate::records::singleton_type::SingletonType;
20use crate::records::substitution::Substitution;
21use crate::records::table_type::TableType;
22use crate::records::txn_log::TxnLog;
23use crate::records::type_function_instance_type::TypeFunctionInstanceType;
24use crate::records::type_mismatch::TypeMismatch;
25use crate::records::unifier::Unifier;
26use crate::records::unknown_type::UnknownType;
27use crate::records::widen::Widen;
28use crate::type_aliases::error_type::ErrorType;
29use crate::type_aliases::literal_properties::LiteralProperties;
30use crate::type_aliases::type_error_data::TypeErrorData;
31use crate::type_aliases::type_id::TypeId;
32use crate::type_aliases::type_variant::TypeVariant;
33use alloc::string::String;
34
35impl Unifier {
36    /// `void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool isIntersection, const LiteralProperties* literalProperties)`
37    pub fn try_unify_type_id_type_id_bool_bool_literal_properties(
38        &mut self,
39        mut sub_ty: TypeId,
40        mut super_ty: TypeId,
41        is_function_call: bool,
42        is_intersection: bool,
43        literal_properties: Option<&LiteralProperties>,
44    ) {
45        unsafe {
46            (*self.shared_state).counters.iteration_count += 1;
47            if (*self.shared_state).counters.iteration_limit > 0
48                && (*self.shared_state).counters.iteration_limit
49                    < (*self.shared_state).counters.iteration_count
50            {
51                self.report_error_location_type_error_data(
52                    self.location,
53                    TypeErrorData::UnificationTooComplex(
54                        crate::records::unification_too_complex::UnificationTooComplex::default(),
55                    ),
56                );
57                return;
58            }
59        }
60
61        super_ty = self.log.follow_type_id(super_ty);
62        sub_ty = self.log.follow_type_id(sub_ty);
63
64        if super_ty == sub_ty {
65            return;
66        }
67
68        // Reflexive structural-equality fast-path. C++ relies on alias-shared
69        // TypeIds making the `super_ty == sub_ty` pointer check above fire
70        // pervasively; we don't pointer-share alias-derived composites, so
71        // structurally-identical unions/functions/packs (e.g. `Color <: Color`,
72        // Color = "red" | "blue") otherwise re-run the full walk on every
73        // curried use and blow the iteration limit (np_hard). Reflexive — sound.
74        if self.reflexive_equal_type_id(super_ty, sub_ty, 32) {
75            return;
76        }
77
78        let sub_blocked = is_blocked_txn_log_type_id(&self.log, sub_ty);
79        let super_blocked = is_blocked_txn_log_type_id(&self.log, super_ty);
80        if sub_blocked && super_blocked {
81            self.blocked_types.push(sub_ty);
82            self.blocked_types.push(super_ty);
83        } else if sub_blocked {
84            self.blocked_types.push(sub_ty);
85        } else if super_blocked {
86            self.blocked_types.push(super_ty);
87        }
88
89        if !self
90            .log
91            .txn_log_get::<TypeFunctionInstanceType, TypeId>(super_ty)
92            .is_null()
93        {
94            self.ice_string("Unexpected TypeFunctionInstanceType superTy");
95        }
96
97        if !self
98            .log
99            .txn_log_get::<TypeFunctionInstanceType, TypeId>(sub_ty)
100            .is_null()
101        {
102            self.ice_string("Unexpected TypeFunctionInstanceType subTy");
103        }
104
105        let super_free = self.log.txn_log_get_mutable::<FreeType, TypeId>(super_ty);
106        let sub_free = self.log.txn_log_get_mutable::<FreeType, TypeId>(sub_ty);
107
108        // C++ `subsumes(a, b)` for free/generic vars: `a->level.subsumes(b->level)`.
109        // (Caller guarantees non-null pointers at each use site.)
110        if !super_free.is_null()
111            && !sub_free.is_null()
112            && unsafe { (*super_free).level.subsumes(&(*sub_free).level) }
113        {
114            if !self.occurs_check_type_id_type_id_bool(sub_ty, super_ty, false) {
115                self.log
116                    .replace_type_id_t(sub_ty, Type::new(TypeVariant::Bound(super_ty)));
117            }
118            return;
119        } else if !super_free.is_null() && !sub_free.is_null() {
120            if !self.occurs_check_type_id_type_id_bool(super_ty, sub_ty, true) {
121                if unsafe { (*super_free).level.subsumes(&(*sub_free).level) } {
122                    self.log
123                        .change_level_type_id_type_level(sub_ty, unsafe { (*super_free).level });
124                }
125                self.log
126                    .replace_type_id_t(super_ty, Type::new(TypeVariant::Bound(sub_ty)));
127            }
128            return;
129        } else if !super_free.is_null() {
130            // Unification can't change the level of a generic.
131            let sub_generic = self.log.txn_log_get_mutable::<GenericType, TypeId>(sub_ty);
132            if !sub_generic.is_null()
133                && !unsafe { (*sub_generic).level.subsumes(&(*super_free).level) }
134            {
135                self.report_error_location_type_error_data(
136                    self.location,
137                    TypeErrorData::GenericError(GenericError::new(String::from(
138                        "Generic subtype escaping scope",
139                    ))),
140                );
141                return;
142            }
143
144            if !self.occurs_check_type_id_type_id_bool(super_ty, sub_ty, true) {
145                let super_level = unsafe { (*super_free).level };
146                crate::functions::promote_type_levels_unifier::promote_type_levels_txn_log_type_arena_type_level_type_id(
147                    &mut self.log,
148                    unsafe { &*self.types },
149                    super_level,
150                    sub_ty,
151                );
152
153                let mut widen = Widen::widen_widen(self.types, self.builtin_types);
154                let widened = widen.operator_call_mut(sub_ty);
155                self.log
156                    .replace_type_id_t(super_ty, Type::new(TypeVariant::Bound(widened)));
157            }
158            return;
159        } else if !sub_free.is_null() {
160            // Normally, if the subtype is free, it should not be bound to any, unknown, or error types.
161            // But for bug compatibility, we'll only apply this rule to unknown.
162            if !self
163                .log
164                .txn_log_get::<UnknownType, TypeId>(super_ty)
165                .is_null()
166            {
167                return;
168            }
169
170            let super_generic = self
171                .log
172                .txn_log_get_mutable::<GenericType, TypeId>(super_ty);
173            if !super_generic.is_null()
174                && !unsafe { (*super_generic).level.subsumes(&(*sub_free).level) }
175            {
176                self.report_error_location_type_error_data(
177                    self.location,
178                    TypeErrorData::GenericError(GenericError::new(String::from(
179                        "Generic supertype escaping scope",
180                    ))),
181                );
182                return;
183            }
184
185            if !self.occurs_check_type_id_type_id_bool(sub_ty, super_ty, false) {
186                let sub_level = unsafe { (*sub_free).level };
187                crate::functions::promote_type_levels_unifier::promote_type_levels_txn_log_type_arena_type_level_type_id(
188                    &mut self.log,
189                    unsafe { &*self.types },
190                    sub_level,
191                    super_ty,
192                );
193                self.log
194                    .replace_type_id_t(sub_ty, Type::new(TypeVariant::Bound(super_ty)));
195            }
196            return;
197        }
198
199        if !self.log.txn_log_get::<AnyType, TypeId>(super_ty).is_null() {
200            return self.try_unify_with_any_type_id_type_id(sub_ty, unsafe {
201                (*self.builtin_types).anyType
202            });
203        }
204
205        if !self.log.txn_log_get::<AnyType, TypeId>(sub_ty).is_null() {
206            if self.normalize {
207                // TODO: there are probably cheaper ways to check if any <: T.
208                let super_norm = unsafe { (*self.normalizer).normalize(super_ty) };
209                if !unsafe { get_type_id::<AnyType>(super_norm.tops) }.is_null() {
210                    // handled below
211                } else {
212                    self.failure = true;
213                }
214            } else {
215                self.failure = true;
216            }
217            return self.try_unify_with_any_type_id_type_id(super_ty, unsafe {
218                (*self.builtin_types).anyType
219            });
220        }
221
222        if !self.log.txn_log_get::<NeverType, TypeId>(sub_ty).is_null() {
223            return self.try_unify_with_any_type_id_type_id(super_ty, unsafe {
224                (*self.builtin_types).neverType
225            });
226        }
227
228        // What if the types are immutable and we proved their relation before
229        let cache_enabled =
230            !is_function_call && !is_intersection && self.variance == Variance::Invariant;
231
232        if cache_enabled {
233            if unsafe {
234                (*self.shared_state)
235                    .cached_unify
236                    .find(&(sub_ty, super_ty))
237                    .is_some()
238            } {
239                return;
240            }
241
242            // C++: `if (auto error = sharedState.cachedUnifyError.find({subTy, superTy})) { reportError(*error); return; }`
243            // Serve a previously-cached unification error rather than re-exploring the
244            // (potentially exponential) failing subtree. This negative-cache fast-path is
245            // what keeps pathological intersection subtyping (e.g. graph-coloring encodings)
246            // under the iteration limit.
247            if let Some(error) = unsafe {
248                (*self.shared_state)
249                    .cached_unify_error
250                    .find(&(sub_ty, super_ty))
251                    .cloned()
252            } {
253                self.report_error_location_type_error_data(self.location, error);
254                return;
255            }
256        }
257
258        // If we have seen this pair before, we are recursing into cyclic types; assume they unify.
259        if self.log.have_seen_type_id_type_id(super_ty, sub_ty) {
260            return;
261        }
262
263        self.log.push_seen_type_id_type_id(super_ty, sub_ty);
264
265        let error_count = self.errors.len();
266
267        let sub_union = self
268            .log
269            .txn_log_get_mutable::<crate::records::union_type::UnionType, TypeId>(sub_ty);
270        let super_intersection = self
271            .log
272            .txn_log_get_mutable::<IntersectionType, TypeId>(super_ty);
273        let super_union = self
274            .log
275            .txn_log_get_mutable::<crate::records::union_type::UnionType, TypeId>(super_ty);
276        let sub_intersection = self
277            .log
278            .txn_log_get_mutable::<IntersectionType, TypeId>(sub_ty);
279
280        if !sub_union.is_null() {
281            self.unifier_try_unify_union_with_type(sub_ty, sub_union, super_ty);
282        } else if !super_intersection.is_null() {
283            self.unifier_try_unify_type_with_intersection(sub_ty, super_ty, super_intersection);
284        } else if !super_union.is_null() {
285            self.unifier_try_unify_type_with_union(
286                sub_ty,
287                super_ty,
288                super_union,
289                cache_enabled,
290                is_function_call,
291            );
292        } else if !sub_intersection.is_null() {
293            self.unifier_try_unify_intersection_with_type(
294                sub_ty,
295                sub_intersection,
296                super_ty,
297                cache_enabled,
298                is_function_call,
299            );
300        } else if !self.log.txn_log_get::<AnyType, TypeId>(sub_ty).is_null() {
301            self.try_unify_with_any_type_id_type_id(super_ty, unsafe {
302                (*self.builtin_types).unknownType
303            });
304            self.failure = true;
305        } else if !self.log.txn_log_get::<ErrorType, TypeId>(sub_ty).is_null()
306            && !self
307                .log
308                .txn_log_get::<ErrorType, TypeId>(super_ty)
309                .is_null()
310        {
311            // error <: error
312        } else if !self
313            .log
314            .txn_log_get::<ErrorType, TypeId>(super_ty)
315            .is_null()
316        {
317            self.try_unify_with_any_type_id_type_id(sub_ty, unsafe {
318                (*self.builtin_types).errorType
319            });
320            self.failure = true;
321        } else if !self.log.txn_log_get::<ErrorType, TypeId>(sub_ty).is_null() {
322            self.try_unify_with_any_type_id_type_id(super_ty, unsafe {
323                (*self.builtin_types).errorType
324            });
325            self.failure = true;
326        } else if !self
327            .log
328            .txn_log_get::<UnknownType, TypeId>(super_ty)
329            .is_null()
330        {
331            // At this point, all the supertypes of `error` have been handled.
332            self.try_unify_with_any_type_id_type_id(sub_ty, unsafe {
333                (*self.builtin_types).unknownType
334            });
335        } else if !self
336            .log
337            .txn_log_get_mutable::<PrimitiveType, TypeId>(super_ty)
338            .is_null()
339            && !self
340                .log
341                .txn_log_get_mutable::<PrimitiveType, TypeId>(sub_ty)
342                .is_null()
343        {
344            self.unifier_try_unify_primitives(sub_ty, super_ty);
345        } else if (!self
346            .log
347            .txn_log_get_mutable::<PrimitiveType, TypeId>(super_ty)
348            .is_null()
349            || !self
350                .log
351                .txn_log_get_mutable::<SingletonType, TypeId>(super_ty)
352                .is_null())
353            && !self
354                .log
355                .txn_log_get_mutable::<SingletonType, TypeId>(sub_ty)
356                .is_null()
357        {
358            self.unifier_try_unify_singletons(sub_ty, super_ty);
359        } else if {
360            let ptv = unsafe { get_type_id::<PrimitiveType>(super_ty) };
361            !ptv.is_null()
362                && unsafe { (*ptv).r#type } == PrimType::Function
363                && !unsafe { get_type_id::<FunctionType>(sub_ty) }.is_null()
364        } {
365            // Ok. Do nothing. forall functions F, F <: function
366        } else if is_prim(super_ty, PrimType::Table)
367            && (!unsafe { get_type_id::<TableType>(sub_ty) }.is_null()
368                || !unsafe { get_type_id::<MetatableType>(sub_ty) }.is_null())
369        {
370            // Ok, do nothing: forall tables T, T <: table
371        } else if !self
372            .log
373            .txn_log_get_mutable::<FunctionType, TypeId>(super_ty)
374            .is_null()
375            && !self
376                .log
377                .txn_log_get_mutable::<FunctionType, TypeId>(sub_ty)
378                .is_null()
379        {
380            self.unifier_try_unify_functions(sub_ty, super_ty, is_function_call);
381        } else if {
382            let table = self.log.txn_log_get::<PrimitiveType, TypeId>(super_ty);
383            !table.is_null() && unsafe { (*table).r#type } == PrimType::Table
384        } {
385            let empty_table = unsafe { (*self.builtin_types).emptyTableType };
386            self.try_unify_type_id_type_id_bool_bool_literal_properties_entry(
387                sub_ty,
388                empty_table,
389                is_function_call,
390                is_intersection,
391                None,
392            );
393        } else if {
394            let table = self.log.txn_log_get::<PrimitiveType, TypeId>(sub_ty);
395            !table.is_null() && unsafe { (*table).r#type } == PrimType::Table
396        } {
397            let empty_table = unsafe { (*self.builtin_types).emptyTableType };
398            self.try_unify_type_id_type_id_bool_bool_literal_properties_entry(
399                empty_table,
400                super_ty,
401                is_function_call,
402                is_intersection,
403                None,
404            );
405        } else if !self
406            .log
407            .txn_log_get_mutable::<TableType, TypeId>(super_ty)
408            .is_null()
409            && !self
410                .log
411                .txn_log_get_mutable::<TableType, TypeId>(sub_ty)
412                .is_null()
413        {
414            self.unifier_try_unify_tables(
415                sub_ty,
416                super_ty,
417                is_intersection,
418                literal_properties.map_or(core::ptr::null(), |lp| lp as *const LiteralProperties),
419            );
420        } else if !self
421            .log
422            .txn_log_get::<TableType, TypeId>(super_ty)
423            .is_null()
424            && (!self
425                .log
426                .txn_log_get::<PrimitiveType, TypeId>(sub_ty)
427                .is_null()
428                || !self
429                    .log
430                    .txn_log_get::<SingletonType, TypeId>(sub_ty)
431                    .is_null())
432        {
433            self.unifier_try_unify_scalar_shape(sub_ty, super_ty, false);
434        } else if !self.log.txn_log_get::<TableType, TypeId>(sub_ty).is_null()
435            && (!self
436                .log
437                .txn_log_get::<PrimitiveType, TypeId>(super_ty)
438                .is_null()
439                || !self
440                    .log
441                    .txn_log_get::<SingletonType, TypeId>(super_ty)
442                    .is_null())
443        {
444            self.unifier_try_unify_scalar_shape(sub_ty, super_ty, true);
445        } else if !self
446            .log
447            .txn_log_get_mutable::<MetatableType, TypeId>(super_ty)
448            .is_null()
449        {
450            self.unifier_try_unify_with_metatable(sub_ty, super_ty, false);
451        } else if !self
452            .log
453            .txn_log_get_mutable::<MetatableType, TypeId>(sub_ty)
454            .is_null()
455        {
456            self.unifier_try_unify_with_metatable(super_ty, sub_ty, true);
457        } else if !self
458            .log
459            .txn_log_get_mutable::<ExternType, TypeId>(super_ty)
460            .is_null()
461        {
462            self.unifier_try_unify_with_extern_type(sub_ty, super_ty, false);
463        } else if !self
464            .log
465            .txn_log_get_mutable::<ExternType, TypeId>(sub_ty)
466            .is_null()
467        {
468            self.unifier_try_unify_with_extern_type(sub_ty, super_ty, true);
469        } else if !self
470            .log
471            .txn_log_get::<NegationType, TypeId>(super_ty)
472            .is_null()
473            || !self
474                .log
475                .txn_log_get::<NegationType, TypeId>(sub_ty)
476                .is_null()
477        {
478            self.unifier_try_unify_negations(sub_ty, super_ty);
479        } else if self.check_inhabited
480            && unsafe { (*self.normalizer).is_inhabited_type_id(sub_ty) }
481                == NormalizationResult::False
482        {
483            // uninhabited; nothing to do
484        } else {
485            let context = self.unifier_mismatch_context();
486            self.report_error_location_type_error_data(
487                self.location,
488                TypeErrorData::TypeMismatch(TypeMismatch {
489                    wanted_type: super_ty,
490                    given_type: sub_ty,
491                    reason: String::new(),
492                    error: None,
493                    context,
494                }),
495            );
496        }
497
498        if cache_enabled {
499            self.unifier_cache_result(sub_ty, super_ty, error_count);
500        }
501
502        self.log.pop_seen_type_id_type_id(super_ty, sub_ty);
503    }
504}