Skip to main content

luaur_analysis/methods/
subtyping_unifier_dispatch_one_constraint.rs

1//! Source: `Analysis/src/SubtypingUnifier.cpp:83-183` — `SubtypingUnifier::dispatchOneConstraint`.
2
3use crate::enums::occurs_check_result::OccursCheckResult;
4use crate::enums::unify_result::UnifyResult;
5use crate::functions::as_mutable_type_pack::as_mutable_type_pack_id;
6use crate::functions::emplace_type_pack::emplace_type_pack;
7use crate::functions::follow_type::follow_type_id;
8use crate::functions::follow_type_pack::follow_type_pack_id;
9use crate::functions::get_2::get2;
10use crate::functions::get_mutable_type::get_mutable_type_id;
11use crate::functions::get_type_pack::get_type_pack_id;
12use crate::functions::is_blocked_type_utils::is_blocked;
13use crate::functions::occurs_check_type_utils_alt_b::occurs_check_type_pack_id_type_pack_id;
14use crate::functions::simplify_intersection_simplify::simplify_intersection;
15use crate::functions::simplify_union::simplify_union;
16use crate::records::constraint::Constraint;
17use crate::records::free_type::FreeType;
18use crate::records::free_type_pack::FreeTypePack;
19use crate::records::pack_subtype_constraint::PackSubtypeConstraint;
20use crate::records::subtype_constraint::SubtypeConstraint;
21use crate::records::subtyping_unifier::SubtypingUnifier;
22use crate::records::table_indexer::TableIndexer;
23use crate::records::table_type::TableType;
24use crate::type_aliases::constraint_v::{ConstraintV, ConstraintVMember};
25use crate::type_aliases::type_pack_variant::TypePackVariant;
26use crate::type_aliases::upper_bounds::UpperBounds;
27use luaur_common::macros::luau_assert::LUAU_ASSERT;
28use luaur_common::FFlag;
29
30impl SubtypingUnifier {
31    pub fn dispatch_one_constraint(
32        &self,
33        constraint: *const Constraint,
34        cv: &ConstraintV,
35        upper_bound_contributors: &mut UpperBounds,
36    ) -> (UnifyResult, bool) {
37        let builtin_types = self.builtin_types;
38        let arena = self.arena;
39
40        if let Some(sc) = SubtypeConstraint::get_if(cv) {
41            let sub_ty = unsafe { follow_type_id(sc.sub_type) };
42            let super_ty = unsafe { follow_type_id(sc.super_type) };
43
44            LUAU_ASSERT!(self.can_be_unified(sub_ty) || self.can_be_unified(super_ty));
45
46            if is_blocked(sub_ty) || is_blocked(super_ty) {
47                return (UnifyResult::Ok, false);
48            }
49
50            let super_free_ty = unsafe { get_mutable_type_id::<FreeType>(super_ty) };
51            if !super_free_ty.is_null() {
52                let lower = unsafe { (*super_free_ty).lower_bound };
53                let result = simplify_union(builtin_types, arena, sub_ty, lower).result;
54                unsafe {
55                    (*super_free_ty).lower_bound = result;
56                }
57            }
58
59            let sub_free_type = unsafe { get_mutable_type_id::<FreeType>(sub_ty) };
60            if !sub_free_type.is_null() {
61                let upper = unsafe { (*sub_free_type).upper_bound };
62                let result = simplify_intersection(builtin_types, arena, upper, super_ty).result;
63                unsafe {
64                    (*sub_free_type).upper_bound = result;
65                }
66                let location = unsafe { (*constraint).location };
67                upper_bound_contributors
68                    .get_or_insert(sub_ty)
69                    .push((location, super_ty));
70            }
71
72            // FIXME CLI-182960: Unification shouldn't be the mechanism for adding table indexers
73            let pair = get2::<TableType, TableType, _>(sub_ty, super_ty);
74            if !pair.first.is_null() && unsafe { (*pair.first).indexer }.is_none() {
75                let super_indexer = unsafe { (*pair.second).indexer.clone() }.unwrap();
76                let sub_table = unsafe { get_mutable_type_id::<TableType>(sub_ty) };
77                unsafe {
78                    (*sub_table).indexer = Some(TableIndexer {
79                        index_type: super_indexer.index_type,
80                        index_result_type: super_indexer.index_result_type,
81                        is_read_only: false,
82                    });
83                }
84            }
85        } else if let Some(psc) = PackSubtypeConstraint::get_if(cv) {
86            let sub_tp = unsafe { follow_type_pack_id(psc.sub_pack) };
87            let super_tp = unsafe { follow_type_pack_id(psc.super_pack) };
88            // There *should* be an assertion here that either part of the constraint is
89            // free, but because free type packs are replaced on-the-spot, we *may*
90            // encounter conflicting constraints. One example is for:
91            //
92            //  local callbacks: { () -> () } = {
93            //      function () end,
94            //      function () end
95            //  }
96            //
97            // We'll end up minting two sets of constraints for each lambda (as we need
98            // them to be _exactly_ `()` as per the table type).
99            let error_pack = unsafe { (*builtin_types).errorTypePack };
100
101            if !unsafe { get_type_pack_id::<FreeTypePack>(sub_tp) }.is_null() {
102                if FFlag::LuauOccursCheckForAllBindings.get() {
103                    if OccursCheckResult::Fail
104                        == occurs_check_type_pack_id_type_pack_id(sub_tp, super_tp)
105                    {
106                        emplace_type_pack(
107                            unsafe { as_mutable_type_pack_id(sub_tp) },
108                            TypePackVariant::Bound(error_pack),
109                        );
110                        return (UnifyResult::OccursCheckFailed, true);
111                    }
112                } else {
113                    if OccursCheckResult::Fail == self.occurs_check_DEPRECATED(sub_tp, super_tp) {
114                        emplace_type_pack(
115                            unsafe { as_mutable_type_pack_id(sub_tp) },
116                            TypePackVariant::Bound(error_pack),
117                        );
118                        return (UnifyResult::OccursCheckFailed, true);
119                    }
120                }
121                emplace_type_pack(
122                    unsafe { as_mutable_type_pack_id(sub_tp) },
123                    TypePackVariant::Bound(super_tp),
124                );
125                return (UnifyResult::Ok, true);
126            }
127
128            if !unsafe { get_type_pack_id::<FreeTypePack>(super_tp) }.is_null() {
129                if FFlag::LuauOccursCheckForAllBindings.get() {
130                    if OccursCheckResult::Fail
131                        == occurs_check_type_pack_id_type_pack_id(super_tp, sub_tp)
132                    {
133                        emplace_type_pack(
134                            unsafe { as_mutable_type_pack_id(super_tp) },
135                            TypePackVariant::Bound(error_pack),
136                        );
137                        return (UnifyResult::OccursCheckFailed, true);
138                    }
139                } else {
140                    if OccursCheckResult::Fail == self.occurs_check_DEPRECATED(super_tp, sub_tp) {
141                        emplace_type_pack(
142                            unsafe { as_mutable_type_pack_id(super_tp) },
143                            TypePackVariant::Bound(error_pack),
144                        );
145                        return (UnifyResult::OccursCheckFailed, true);
146                    }
147                }
148
149                emplace_type_pack(
150                    unsafe { as_mutable_type_pack_id(super_tp) },
151                    TypePackVariant::Bound(sub_tp),
152                );
153                return (UnifyResult::Ok, true);
154            }
155        } else {
156            LUAU_ASSERT!(false); // Unreachable, unexpected constraint in subtyping unifier.
157            return (UnifyResult::Ok, false);
158        }
159
160        (UnifyResult::Ok, true)
161    }
162}