Skip to main content

luaur_analysis/methods/
constraint_solver_bind_constraint_solver.rs

1//! `void ConstraintSolver::bind(NotNull<const Constraint> constraint, TypeId ty, TypeId boundTo)`
2//! (`Analysis/src/ConstraintSolver.cpp:938-980`, hand-ported faithfully).
3
4use crate::enums::polarity::Polarity;
5use crate::functions::as_mutable_type::as_mutable_type_id;
6use crate::functions::follow_type::follow_type_id;
7use crate::functions::fresh_type::fresh_type;
8use crate::functions::get_type_alt_j::get_type_id;
9use crate::functions::track_interior_free_type::track_interior_free_type;
10use crate::records::blocked_type::BlockedType;
11use crate::records::constraint::Constraint;
12use crate::records::constraint_solver::ConstraintSolver;
13use crate::records::free_type::FreeType;
14use crate::records::pending_expansion_type::PendingExpansionType;
15use crate::type_aliases::type_id::TypeId;
16use crate::type_aliases::type_variant::TypeVariant;
17use luaur_common::macros::luau_assert::LUAU_ASSERT;
18use luaur_common::FFlag;
19
20impl ConstraintSolver {
21    pub fn bind_not_null_constraint_type_id_type_id(
22        &mut self,
23        constraint: *const Constraint,
24        ty: TypeId,
25        bound_to: TypeId,
26    ) {
27        LUAU_ASSERT!(unsafe {
28            !get_type_id::<BlockedType>(ty).is_null()
29                || !get_type_id::<FreeType>(ty).is_null()
30                || !get_type_id::<PendingExpansionType>(ty).is_null()
31        });
32        LUAU_ASSERT!(can_mutate_type_id(ty, constraint));
33
34        let bound_to = unsafe { follow_type_id(bound_to) };
35        let scope = unsafe { (*constraint).scope };
36        let location = unsafe { (*constraint).location };
37
38        if FFlag::LuauOccursCheckForAllBindings.get() {
39            // This follow shouldn't be needed, but if for some reason we end up
40            // with a bound type, we want to also follow it when doing this
41            // occurence check.
42            if unsafe { follow_type_id(ty) } == bound_to {
43                let fresh_ty = fresh_type(
44                    unsafe { &mut *self.arena },
45                    unsafe { &*self.builtin_types },
46                    scope,
47                    Polarity::Mixed,
48                );
49                let mutable_ty = unsafe { as_mutable_type_id(ty) };
50                let mut fresh_arg = fresh_ty;
51                crate::methods::unifiable_bound_type_id_emplace_type_bound_type::unifiable_bound_type_id_emplace_type_bound_type(
52                    unsafe { &mut *mutable_ty },
53                    &mut fresh_arg,
54                );
55                track_interior_free_type(scope, fresh_ty);
56                self.unblock_type_id_location(ty, location);
57                return;
58            }
59        } else if unsafe { !get_type_id::<BlockedType>(ty).is_null() } && ty == bound_to {
60            // DEPRECATED_emplace<FreeType>(constraint, ty, scope, neverType, unknownType, Polarity::Mixed)
61            // FIXME?  Is this the right polarity?
62            let free_ty = FreeType::free_type_scope_type_id_type_id_polarity(
63                scope,
64                unsafe { (*self.builtin_types).neverType },
65                unsafe { (*self.builtin_types).unknownType },
66                Polarity::Mixed,
67            );
68            let mutable_ty = unsafe { as_mutable_type_id(ty) };
69            unsafe {
70                (*mutable_ty).ty = TypeVariant::Free(free_ty);
71            }
72            self.unblock_type_id_location(ty, location);
73            track_interior_free_type(scope, ty);
74            return;
75        }
76
77        let mutable_ty = unsafe { as_mutable_type_id(ty) };
78        let mut bound_arg = bound_to;
79        crate::methods::unifiable_bound_type_id_emplace_type_bound_type::unifiable_bound_type_id_emplace_type_bound_type(
80            unsafe { &mut *mutable_ty },
81            &mut bound_arg,
82        );
83
84        if !FFlag::LuauConstraintGraph.get() {
85            // `unblock` will "shift references" under the hood.
86            self.deprecate_d_shift_references(ty, bound_to);
87        }
88
89        self.unblock_type_id_location(ty, location);
90    }
91}
92
93// C++ `[[maybe_unused]] static bool canMutate(TypeId ty, NotNull<const Constraint> constraint)`
94// (`Analysis/src/ConstraintSolver.cpp:88-98`), used only in asserts.
95fn can_mutate_type_id(ty: TypeId, constraint: *const Constraint) -> bool {
96    let blocked = unsafe { get_type_id::<BlockedType>(ty) };
97    if !blocked.is_null() {
98        let owner = unsafe { (*blocked).getOwner() };
99        LUAU_ASSERT!(!owner.is_null());
100        return owner == constraint;
101    }
102    true
103}