Skip to main content

luaur_analysis/methods/
constraint_solver_try_dispatch_constraint_solver_alt_m.rs

1use crate::enums::table_state::TableState;
2use crate::enums::value_context::ValueContext;
3use crate::functions::follow_type::follow_type_id;
4use crate::functions::get_mutable_type::getMutable;
5use crate::functions::get_type_alt_j::get_type_id;
6use crate::functions::lookup_extern_type_prop::lookup_extern_type_prop;
7use crate::functions::maybe_string::maybe_string;
8use crate::records::assign_prop_constraint::AssignPropConstraint;
9use crate::records::blocked_type::BlockedType;
10use crate::records::constraint::Constraint;
11use crate::records::constraint_solver::ConstraintSolver;
12use crate::records::extern_type::ExternType;
13use crate::records::metatable_type::MetatableType;
14use crate::records::property_type::Property;
15use crate::records::table_type::TableType;
16use crate::records::union_type::UnionType;
17use luaur_common::macros::luau_assert::LUAU_ASSERT;
18use luaur_common::FFlag;
19
20impl ConstraintSolver {
21    pub fn try_dispatch_assign_prop_constraint_not_null_constraint(
22        &mut self,
23        c: &AssignPropConstraint,
24        constraint: *const Constraint,
25    ) -> bool {
26        let mut lhs_type = unsafe { follow_type_id(c.lhs_type) };
27        let rhs_type = unsafe { follow_type_id(c.rhs_type) };
28
29        if self.is_blocked_type_id(lhs_type) {
30            return self.block_type_id_not_null_constraint(lhs_type, constraint);
31        }
32
33        let lhs_extern_type = unsafe { get_type_id::<ExternType>(lhs_type) };
34        if !lhs_extern_type.is_null() {
35            let prop = unsafe { lookup_extern_type_prop(&*lhs_extern_type, &c.prop_name) };
36            if prop.is_null() {
37                self.bind_not_null_constraint_type_id_type_id(constraint, c.prop_type, unsafe {
38                    (*self.builtin_types).anyType
39                });
40                return true;
41            }
42
43            if let Some(write_ty) = unsafe { (*prop).write_ty } {
44                self.bind_not_null_constraint_type_id_type_id(constraint, c.prop_type, write_ty);
45                self.constraint_solver_unify(constraint, rhs_type, write_ty);
46            } else {
47                self.bind_not_null_constraint_type_id_type_id(constraint, c.prop_type, unsafe {
48                    (*self.builtin_types).anyType
49                });
50            }
51
52            return true;
53        }
54
55        let lookup = self
56            .lookup_table_prop_not_null_constraint_type_id_string_value_context_bool_bool(
57                constraint,
58                lhs_type,
59                &c.prop_name,
60                ValueContext::LValue,
61                false,
62                false,
63            );
64        if !lookup.blocked_types.is_empty() {
65            for blocked in lookup.blocked_types {
66                self.block_type_id_not_null_constraint(blocked, constraint);
67            }
68            return false;
69        }
70
71        if let Some(prop_ty) = lookup.prop_type {
72            let bound_prop_ty = if lookup.is_index {
73                unsafe {
74                    (*self.arena).add_type(UnionType {
75                        options: alloc::vec![prop_ty, (*self.builtin_types).nilType],
76                    })
77                }
78            } else {
79                prop_ty
80            };
81
82            self.bind_not_null_constraint_type_id_type_id(constraint, c.prop_type, bound_prop_ty);
83            self.constraint_solver_unify(constraint, rhs_type, prop_ty);
84            return true;
85        }
86
87        let mut lhs_table = unsafe { getMutable::<TableType>(lhs_type) };
88        if lhs_table.is_null() {
89            let lhs_meta = unsafe { get_type_id::<MetatableType>(lhs_type) };
90            if !lhs_meta.is_null() {
91                lhs_type = unsafe { follow_type_id((*lhs_meta).table) };
92                lhs_table = unsafe { getMutable::<TableType>(lhs_type) };
93            }
94        }
95
96        if !lhs_table.is_null() {
97            let table = unsafe { &mut *lhs_table };
98
99            if let Some(prop) = table.props.get_mut(&c.prop_name) {
100                if let Some(write_ty) = prop.write_ty {
101                    self.bind_not_null_constraint_type_id_type_id(
102                        constraint,
103                        c.prop_type,
104                        write_ty,
105                    );
106                    self.constraint_solver_unify(constraint, rhs_type, write_ty);
107                    return true;
108                }
109
110                if (table.state == TableState::Unsealed || table.state == TableState::Free)
111                    && prop.read_ty.is_some()
112                {
113                    prop.write_ty = prop.read_ty;
114                    let write_ty = prop.write_ty.unwrap();
115                    self.bind_not_null_constraint_type_id_type_id(
116                        constraint,
117                        c.prop_type,
118                        write_ty,
119                    );
120                    self.constraint_solver_unify(constraint, rhs_type, write_ty);
121                    return true;
122                }
123
124                self.bind_not_null_constraint_type_id_type_id(constraint, c.prop_type, unsafe {
125                    (*self.builtin_types).errorType
126                });
127                return true;
128            }
129
130            if let Some(indexer) = &table.indexer {
131                if maybe_string(indexer.index_type) {
132                    let prop_ty = indexer.index_result_type;
133                    let union = unsafe {
134                        (*self.arena).add_type(UnionType {
135                            options: alloc::vec![prop_ty, (*self.builtin_types).nilType],
136                        })
137                    };
138                    self.bind_not_null_constraint_type_id_type_id(constraint, c.prop_type, union);
139                    self.constraint_solver_unify(constraint, rhs_type, prop_ty);
140                    return true;
141                }
142            }
143
144            if table.state == TableState::Unsealed || table.state == TableState::Free {
145                if FFlag::LuauConstraintGraph.get() {
146                    LUAU_ASSERT!(!self.cgraph.is_null());
147                    unsafe { (*self.cgraph).copy_dependencies_of_type_id(lhs_type, rhs_type) };
148                } else {
149                    self.deprecate_d_shift_references(lhs_type, rhs_type);
150                }
151
152                self.bind_not_null_constraint_type_id_type_id(constraint, c.prop_type, rhs_type);
153
154                let mut prop = Property::rw_type_id(rhs_type);
155                prop.location = c.prop_location;
156                table.props.insert(c.prop_name.clone(), prop);
157
158                if table.state == TableState::Unsealed && c.decrement_prop_count {
159                    LUAU_ASSERT!(table.remaining_props > 0);
160                    table.remaining_props -= 1;
161                    self.unblock_type_id_location(lhs_type, unsafe { (*constraint).location });
162                }
163
164                return true;
165            }
166        }
167
168        let prop_type_is_blocked = unsafe { !get_type_id::<BlockedType>(c.prop_type).is_null() };
169        if prop_type_is_blocked {
170            self.bind_not_null_constraint_type_id_type_id(constraint, c.prop_type, unsafe {
171                (*self.builtin_types).errorType
172            });
173        }
174
175        true
176    }
177}