Skip to main content

luaur_analysis/methods/
unifier_try_unify_unifier_alt_d.rs

1//! Source: `Analysis/src/Unifier.cpp` (Unifier::tryUnify_(TypePackId,...), L1405-1634)
2use crate::enums::polarity::Polarity;
3use crate::functions::flatten_type_pack_alt_b::flatten;
4use crate::functions::fresh_type::fresh_type;
5use crate::functions::is_blocked_unifier_alt_c::is_blocked_txn_log_type_pack_id;
6use crate::functions::is_optional::is_optional;
7use crate::functions::size_type_pack::size;
8use crate::records::count_mismatch::{CountMismatch, CountMismatchContext};
9use crate::records::free_type_pack::FreeTypePack;
10use crate::records::type_pack::TypePack;
11use crate::records::type_pack_mismatch::TypePackMismatch;
12use crate::records::type_pack_var::TypePackVar;
13use crate::records::unifier::Unifier;
14use crate::records::variadic_type_pack::VariadicTypePack;
15use crate::records::weird_iter::WeirdIter;
16use crate::records::widen::Widen;
17use crate::type_aliases::error_type_pack::ErrorTypePack;
18use crate::type_aliases::type_error_data::TypeErrorData;
19use crate::type_aliases::type_pack_id::TypePackId;
20use crate::type_aliases::type_pack_variant::TypePackVariant;
21use alloc::string::String;
22
23impl Unifier {
24    /// `void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCall)`
25    pub fn try_unify_type_pack_id_type_pack_id_bool(
26        &mut self,
27        mut sub_tp: TypePackId,
28        mut super_tp: TypePackId,
29        is_function_call: bool,
30    ) {
31        unsafe {
32            (*self.shared_state).counters.iteration_count += 1;
33            if (*self.shared_state).counters.iteration_limit > 0
34                && (*self.shared_state).counters.iteration_limit
35                    < (*self.shared_state).counters.iteration_count
36            {
37                self.report_error_location_type_error_data(
38                    self.location,
39                    TypeErrorData::UnificationTooComplex(
40                        crate::records::unification_too_complex::UnificationTooComplex::default(),
41                    ),
42                );
43                return;
44            }
45        }
46
47        super_tp = self.log.follow_type_pack_id(super_tp);
48        sub_tp = self.log.follow_type_pack_id(sub_tp);
49
50        // Reflexive structural-equality fast-path (see unifier_reflexive_equal):
51        // identical curried-function arg/return packs (e.g. `(Color)` vs
52        // `(Color)`) recur element-by-element on every use without this, which
53        // is what tips np_hard over the iteration limit.
54        if self.reflexive_equal_type_pack_id(super_tp, sub_tp, 32) {
55            return;
56        }
57
58        loop {
59            let tp = self.log.txn_log_get_mutable::<TypePack, TypePackId>(sub_tp);
60            if tp.is_null() {
61                break;
62            }
63            if unsafe { (*tp).head.is_empty() && (*tp).tail.is_some() } {
64                sub_tp = self.log.follow_type_pack_id(unsafe { (*tp).tail.unwrap() });
65            } else {
66                break;
67            }
68        }
69
70        loop {
71            let tp = self
72                .log
73                .txn_log_get_mutable::<TypePack, TypePackId>(super_tp);
74            if tp.is_null() {
75                break;
76            }
77            if unsafe { (*tp).head.is_empty() && (*tp).tail.is_some() } {
78                super_tp = self.log.follow_type_pack_id(unsafe { (*tp).tail.unwrap() });
79            } else {
80                break;
81            }
82        }
83
84        if super_tp == sub_tp {
85            return;
86        }
87
88        if self
89            .log
90            .have_seen_type_pack_id_type_pack_id(super_tp, sub_tp)
91        {
92            return;
93        }
94
95        let sub_blocked = is_blocked_txn_log_type_pack_id(&self.log, sub_tp);
96        let super_blocked = is_blocked_txn_log_type_pack_id(&self.log, super_tp);
97        if sub_blocked && super_blocked {
98            self.blocked_type_packs.push(sub_tp);
99            self.blocked_type_packs.push(super_tp);
100        } else if sub_blocked {
101            self.blocked_type_packs.push(sub_tp);
102        } else if super_blocked {
103            self.blocked_type_packs.push(super_tp);
104        }
105
106        if !self
107            .log
108            .txn_log_get_mutable::<FreeTypePack, TypePackId>(super_tp)
109            .is_null()
110        {
111            if !self.occurs_check_type_pack_id_type_pack_id_bool(super_tp, sub_tp, true) {
112                let mut widen = Widen::widen_widen(self.types, self.builtin_types);
113                let widened = widen.operator_call(sub_tp);
114                let bound = TypePackVar {
115                    ty: TypePackVariant::Bound(widened),
116                    persistent: false,
117                    owningArena: core::ptr::null_mut(),
118                };
119                self.log.replace_type_pack_id_type_pack_var(super_tp, bound);
120            }
121        } else if !self
122            .log
123            .txn_log_get_mutable::<FreeTypePack, TypePackId>(sub_tp)
124            .is_null()
125        {
126            if !self.occurs_check_type_pack_id_type_pack_id_bool(sub_tp, super_tp, false) {
127                let bound = TypePackVar {
128                    ty: TypePackVariant::Bound(super_tp),
129                    persistent: false,
130                    owningArena: core::ptr::null_mut(),
131                };
132                self.log.replace_type_pack_id_type_pack_var(sub_tp, bound);
133            }
134        } else if !self
135            .log
136            .txn_log_get_mutable::<ErrorTypePack, TypePackId>(super_tp)
137            .is_null()
138        {
139            self.try_unify_with_any_type_pack_id_type_pack_id(sub_tp, super_tp);
140        } else if !self
141            .log
142            .txn_log_get_mutable::<ErrorTypePack, TypePackId>(sub_tp)
143            .is_null()
144        {
145            self.try_unify_with_any_type_pack_id_type_pack_id(super_tp, sub_tp);
146        } else if !self
147            .log
148            .txn_log_get_mutable::<VariadicTypePack, TypePackId>(super_tp)
149            .is_null()
150        {
151            self.unifier_try_unify_variadics(sub_tp, super_tp, false, 0);
152        } else if !self
153            .log
154            .txn_log_get_mutable::<VariadicTypePack, TypePackId>(sub_tp)
155            .is_null()
156        {
157            self.unifier_try_unify_variadics(super_tp, sub_tp, true, 0);
158        } else if !self
159            .log
160            .txn_log_get_mutable::<TypePack, TypePackId>(super_tp)
161            .is_null()
162            && !self
163                .log
164                .txn_log_get_mutable::<TypePack, TypePackId>(sub_tp)
165                .is_null()
166        {
167            let super_tpv = self
168                .log
169                .txn_log_get_mutable::<TypePack, TypePackId>(super_tp);
170            let sub_tpv = self.log.txn_log_get_mutable::<TypePack, TypePackId>(sub_tp);
171
172            // If the size of two heads does not match, but both packs have free tail
173            // we set the sentinel to avoid growing forever.
174            let (super_types, super_tail) = flatten(super_tp, &self.log);
175            let (sub_types, sub_tail) = flatten(sub_tp, &self.log);
176
177            let no_infinite_growth = (super_types.len() != sub_types.len())
178                && super_tail.map_or(false, |t| {
179                    !self
180                        .log
181                        .txn_log_get_mutable::<FreeTypePack, TypePackId>(t)
182                        .is_null()
183                })
184                && sub_tail.map_or(false, |t| {
185                    !self
186                        .log
187                        .txn_log_get_mutable::<FreeTypePack, TypePackId>(t)
188                        .is_null()
189                });
190
191            let mut super_iter = WeirdIter {
192                pack_id: super_tp,
193                log: &mut self.log as *mut _,
194                pack: core::ptr::null_mut(),
195                index: 0,
196                growing: false,
197                level: crate::records::type_level::TypeLevel::default(),
198                scope: core::ptr::null_mut(),
199            };
200            super_iter.weird_iter_type_pack_id_txn_log(super_tp, unsafe { &mut *(self.log_ptr()) });
201
202            let mut sub_iter = WeirdIter {
203                pack_id: sub_tp,
204                log: &mut self.log as *mut _,
205                pack: core::ptr::null_mut(),
206                index: 0,
207                growing: false,
208                level: crate::records::type_level::TypeLevel::default(),
209                scope: core::ptr::null_mut(),
210            };
211            sub_iter.weird_iter_type_pack_id_txn_log(sub_tp, unsafe { &mut *(self.log_ptr()) });
212
213            super_iter.scope = self.scope;
214            sub_iter.scope = self.scope;
215
216            let empty_tp = unsafe {
217                (*self.types).add_type_pack_t(TypePack {
218                    head: alloc::vec::Vec::new(),
219                    tail: None,
220                })
221            };
222
223            let mut loop_count = 0;
224
225            loop {
226                if luaur_common::FInt::LuauTypeInferTypePackLoopLimit.get() > 0
227                    && loop_count >= luaur_common::FInt::LuauTypeInferTypePackLoopLimit.get()
228                {
229                    self.ice_string("Detected possibly infinite TypePack growth");
230                }
231
232                loop_count += 1;
233
234                if super_iter.weird_iter_good() && sub_iter.growing {
235                    let ft = self.mk_fresh_for_iter(sub_iter.scope);
236                    sub_iter.weird_iter_push_type(ft);
237                }
238
239                if sub_iter.weird_iter_good() && super_iter.growing {
240                    let ft = self.mk_fresh_for_iter(super_iter.scope);
241                    super_iter.weird_iter_push_type(ft);
242                }
243
244                if super_iter.weird_iter_good() && sub_iter.weird_iter_good() {
245                    let s = *sub_iter.weird_iter_operator_deref();
246                    let sup = *super_iter.weird_iter_operator_deref();
247                    self.try_unify_type_id_type_id_bool_bool_literal_properties(
248                        s, sup, false, false, None,
249                    );
250
251                    if !self.errors.is_empty() && self.first_pack_error_pos.is_none() {
252                        self.first_pack_error_pos = Some(loop_count);
253                    }
254
255                    super_iter.weird_iter_advance();
256                    sub_iter.weird_iter_advance();
257                    continue;
258                }
259
260                // If both are at the end, we're done
261                if !super_iter.weird_iter_good() && !sub_iter.weird_iter_good() {
262                    let l_free_tail = unsafe {
263                        (*super_tpv).tail.map_or(false, |t| {
264                            !self
265                                .log
266                                .txn_log_get_mutable::<FreeTypePack, TypePackId>(
267                                    self.log.follow_type_pack_id(t),
268                                )
269                                .is_null()
270                        })
271                    };
272                    let r_free_tail = unsafe {
273                        (*sub_tpv).tail.map_or(false, |t| {
274                            !self
275                                .log
276                                .txn_log_get_mutable::<FreeTypePack, TypePackId>(
277                                    self.log.follow_type_pack_id(t),
278                                )
279                                .is_null()
280                        })
281                    };
282                    if l_free_tail && r_free_tail {
283                        self.try_unify_type_pack_id_type_pack_id_bool(
284                            unsafe { (*sub_tpv).tail.unwrap() },
285                            unsafe { (*super_tpv).tail.unwrap() },
286                            false,
287                        );
288                    } else if l_free_tail {
289                        self.try_unify_type_pack_id_type_pack_id_bool(
290                            empty_tp,
291                            unsafe { (*super_tpv).tail.unwrap() },
292                            false,
293                        );
294                    } else if r_free_tail {
295                        self.try_unify_type_pack_id_type_pack_id_bool(
296                            empty_tp,
297                            unsafe { (*sub_tpv).tail.unwrap() },
298                            false,
299                        );
300                    } else if unsafe { (*sub_tpv).tail.is_some() && (*super_tpv).tail.is_some() } {
301                        if !self
302                            .log
303                            .txn_log_get_mutable::<VariadicTypePack, TypePackId>(super_iter.pack_id)
304                            .is_null()
305                        {
306                            self.unifier_try_unify_variadics(
307                                sub_iter.pack_id,
308                                super_iter.pack_id,
309                                false,
310                                sub_iter.index as i32,
311                            );
312                        } else if !self
313                            .log
314                            .txn_log_get_mutable::<VariadicTypePack, TypePackId>(sub_iter.pack_id)
315                            .is_null()
316                        {
317                            self.unifier_try_unify_variadics(
318                                super_iter.pack_id,
319                                sub_iter.pack_id,
320                                true,
321                                super_iter.index as i32,
322                            );
323                        } else {
324                            self.try_unify_type_pack_id_type_pack_id_bool(
325                                unsafe { (*sub_tpv).tail.unwrap() },
326                                unsafe { (*super_tpv).tail.unwrap() },
327                                false,
328                            );
329                        }
330                    }
331
332                    break;
333                }
334
335                // If both tails are free, bind one to the other and call it a day
336                if super_iter.weird_iter_can_grow() && sub_iter.weird_iter_can_grow() {
337                    let s = unsafe { (*sub_iter.pack).tail.unwrap() };
338                    let sup = unsafe { (*super_iter.pack).tail.unwrap() };
339                    return self.try_unify_type_pack_id_type_pack_id_bool(s, sup, false);
340                }
341
342                // If just one side is free on its tail, grow it to fit the other side.
343                if super_iter.weird_iter_can_grow() {
344                    let new_tail = unsafe {
345                        (*self.types).add_type_pack_type_pack_var(TypePackVar {
346                            ty: TypePackVariant::TypePack(TypePack {
347                                head: alloc::vec::Vec::new(),
348                                tail: None,
349                            }),
350                            persistent: false,
351                            owningArena: core::ptr::null_mut(),
352                        })
353                    };
354                    super_iter.weird_iter_grow(new_tail);
355                } else if sub_iter.weird_iter_can_grow() {
356                    let new_tail = unsafe {
357                        (*self.types).add_type_pack_type_pack_var(TypePackVar {
358                            ty: TypePackVariant::TypePack(TypePack {
359                                head: alloc::vec::Vec::new(),
360                                tail: None,
361                            }),
362                            persistent: false,
363                            owningArena: core::ptr::null_mut(),
364                        })
365                    };
366                    sub_iter.weird_iter_grow(new_tail);
367                } else {
368                    // A union type including nil marks an optional argument
369                    if super_iter.weird_iter_good()
370                        && is_optional(*super_iter.weird_iter_operator_deref())
371                    {
372                        super_iter.weird_iter_advance();
373                        continue;
374                    } else if sub_iter.weird_iter_good()
375                        && is_optional(*sub_iter.weird_iter_operator_deref())
376                    {
377                        sub_iter.weird_iter_advance();
378                        continue;
379                    }
380
381                    if !self
382                        .log
383                        .txn_log_get_mutable::<VariadicTypePack, TypePackId>(super_iter.pack_id)
384                        .is_null()
385                    {
386                        self.unifier_try_unify_variadics(
387                            sub_iter.pack_id,
388                            super_iter.pack_id,
389                            false,
390                            sub_iter.index as i32,
391                        );
392                        return;
393                    }
394
395                    if !self
396                        .log
397                        .txn_log_get_mutable::<VariadicTypePack, TypePackId>(sub_iter.pack_id)
398                        .is_null()
399                    {
400                        self.unifier_try_unify_variadics(
401                            super_iter.pack_id,
402                            sub_iter.pack_id,
403                            true,
404                            super_iter.index as i32,
405                        );
406                        return;
407                    }
408
409                    if !is_function_call && sub_iter.weird_iter_good() {
410                        // Sometimes it is ok to pass too many arguments
411                        return;
412                    }
413
414                    // This is a bit weird because we don't actually know expected vs actual.
415                    let log_ptr = self.log_ptr();
416                    let mut expected_size = size(super_tp, log_ptr);
417                    let mut actual_size = size(sub_tp, log_ptr);
418                    if self.ctx == CountMismatchContext::FunctionResult
419                        || self.ctx == CountMismatchContext::ExprListResult
420                    {
421                        core::mem::swap(&mut expected_size, &mut actual_size);
422                    }
423                    let ctx = self.ctx;
424                    self.report_error_location_type_error_data(
425                        self.location,
426                        TypeErrorData::CountMismatch(CountMismatch {
427                            expected: expected_size,
428                            maximum: None,
429                            actual: actual_size,
430                            context: ctx,
431                            is_variadic: false,
432                            function: String::new(),
433                        }),
434                    );
435
436                    let error_type = unsafe { (*self.builtin_types).errorType };
437                    while super_iter.weird_iter_good() {
438                        let cur = *super_iter.weird_iter_operator_deref();
439                        self.try_unify_type_id_type_id_bool_bool_literal_properties(
440                            cur, error_type, false, false, None,
441                        );
442                        super_iter.weird_iter_advance();
443                    }
444
445                    while sub_iter.weird_iter_good() {
446                        let cur = *sub_iter.weird_iter_operator_deref();
447                        self.try_unify_type_id_type_id_bool_bool_literal_properties(
448                            cur, error_type, false, false, None,
449                        );
450                        sub_iter.weird_iter_advance();
451                    }
452
453                    return;
454                }
455
456                if no_infinite_growth {
457                    break;
458                }
459            }
460        } else {
461            self.report_error_location_type_error_data(
462                self.location,
463                TypeErrorData::TypePackMismatch(TypePackMismatch {
464                    wanted_tp: super_tp,
465                    given_tp: sub_tp,
466                    reason: String::new(),
467                }),
468            );
469        }
470    }
471
472    /// `mkFreshType` lambda in tryUnify_: `freshType(NotNull{types}, builtinTypes, scope)`.
473    fn mk_fresh_for_iter(
474        &mut self,
475        scope: *mut crate::records::scope::Scope,
476    ) -> crate::type_aliases::type_id::TypeId {
477        fresh_type(
478            unsafe { &mut *self.types },
479            unsafe { &*self.builtin_types },
480            scope,
481            Polarity::Positive,
482        )
483    }
484
485    #[inline]
486    fn log_ptr(&mut self) -> *mut crate::records::txn_log::TxnLog {
487        &mut self.log as *mut _
488    }
489}