luaur_analysis/methods/
constraint_solver_try_dispatch_constraint_solver_alt_n.rs1use crate::enums::table_state::TableState;
2use crate::records::assign_index_constraint::AssignIndexConstraint;
3use crate::records::constraint::Constraint;
4use crate::records::constraint_solver::ConstraintSolver;
5use crate::records::extern_type::ExternType;
6use crate::records::free_type::FreeType;
7use crate::records::intersection_type::IntersectionType;
8use crate::records::table_indexer::TableIndexer;
9use crate::records::table_type as TableTypeRec;
10use crate::records::table_type::TableType;
11use crate::records::type_ids::TypeIds;
12use crate::records::type_level::TypeLevel;
13use crate::records::union_type::UnionType;
14use crate::type_aliases::type_id::TypeId;
15
16use crate::functions::add_union::add_union;
17use crate::functions::follow_type::follow_type_id;
18use crate::functions::follow_type_utils::follow_optional_ty;
19use crate::functions::simplify_intersection_simplify::simplify_intersection;
20
21use crate::records::builtin_types::BuiltinTypes;
22
23use luaur_common::macros::luau_assert::LUAU_ASSERT;
24
25impl ConstraintSolver {
26 pub fn try_dispatch_assign_index_constraint_not_null_constraint(
27 &mut self,
28 c: &AssignIndexConstraint,
29 constraint: *const Constraint,
30 ) -> bool {
31 let lhs_type: TypeId = unsafe { follow_type_id(c.lhs_type) };
32 let index_type: TypeId = unsafe { follow_type_id(c.index_type) };
33 let rhs_type: TypeId = unsafe { follow_type_id(c.rhs_type) };
34
35 if self.is_blocked_type_id(lhs_type) {
36 return self.block_type_id_not_null_constraint(lhs_type, constraint);
37 }
38
39 let mut table_stuff = |lhs_table: &mut TableType| -> Option<bool> {
42 if lhs_table.indexer.is_some() {
43 let indexer: &TableIndexer = lhs_table.indexer.as_ref().unwrap();
44
45 self.constraint_solver_unify(constraint, index_type, indexer.index_type);
46 self.constraint_solver_unify(constraint, rhs_type, indexer.index_result_type);
47
48 let prop_bound = self.bind_not_null_constraint_type_id_type_id(
49 constraint,
50 c.prop_type,
51 add_union(
52 self.arena,
53 self.builtin_types,
54 &[indexer.index_result_type, unsafe {
55 (*self.builtin_types).nilType
56 }],
57 ),
58 );
59
60 let _ = prop_bound;
63
64 return Some(true);
65 }
66
67 if lhs_table.state == TableState::Unsealed || lhs_table.state == TableState::Free {
68 lhs_table.indexer = Some(TableIndexer {
69 index_type,
70 index_result_type: rhs_type,
71 is_read_only: false,
72 });
73
74 self.bind_not_null_constraint_type_id_type_id(constraint, c.prop_type, rhs_type);
75 return Some(true);
76 }
77
78 None
79 };
80
81 let lhs_free: *mut FreeType = unsafe {
82 crate::functions::get_mutable_type::get_mutable_type_id::<FreeType>(lhs_type)
83 };
84 if !lhs_free.is_null() {
85 let lhs_upper = unsafe { follow_type_id((*lhs_free).upper_bound) };
86 let lhs_table: *mut TableType = unsafe {
87 crate::functions::get_mutable_type::get_mutable_type_id::<TableType>(lhs_upper)
88 };
89 if !lhs_table.is_null() {
90 let res = unsafe { table_stuff(&mut *lhs_table) };
91 if let Some(v) = res {
92 return v;
93 }
94 }
95
96 let new_upper_bound = unsafe {
97 (*self.arena).add_type(
98 TableType::table_type_props_optional_table_indexer_type_level_scope_table_state(
99 &Default::default(),
100 Some(TableIndexer {
101 index_type,
102 index_result_type: rhs_type,
103 is_read_only: false,
104 }),
105 TypeLevel::default(),
106 (*constraint).scope,
107 TableState::Free,
108 ),
109 )
110 };
111
112 let new_table: *const TableType = unsafe {
113 crate::functions::get_type_alt_j::get_type_id::<TableType>(new_upper_bound)
114 };
115 LUAU_ASSERT!(!new_table.is_null());
116
117 self.constraint_solver_unify(constraint, lhs_type, new_upper_bound);
118
119 let new_table_mut: *mut TableType = unsafe {
120 crate::functions::get_mutable_type::get_mutable_type_id::<TableType>(
121 new_upper_bound,
122 )
123 };
124 LUAU_ASSERT!(!new_table_mut.is_null());
125 LUAU_ASSERT!(unsafe { (*new_table_mut).indexer.is_some() });
126
127 let idx_res = unsafe { (*new_table_mut).indexer.as_ref().unwrap().index_result_type };
128 self.bind_not_null_constraint_type_id_type_id(constraint, c.prop_type, idx_res);
129 return true;
130 }
131
132 let lhs_table: *mut TableType = unsafe {
133 crate::functions::get_mutable_type::get_mutable_type_id::<TableType>(lhs_type)
134 };
135 if !lhs_table.is_null() {
136 let res = unsafe { table_stuff(&mut *lhs_table) };
137 if let Some(v) = res {
138 return v;
139 }
140 }
141
142 let mut lhs_extern_type: *mut ExternType = unsafe {
143 crate::functions::get_type_alt_j::get_type_id::<ExternType>(lhs_type) as *mut ExternType
144 };
145 if !lhs_extern_type.is_null() {
146 loop {
147 if unsafe { (*lhs_extern_type).indexer.is_some() } {
148 let indexer = unsafe { (*lhs_extern_type).indexer.as_ref().unwrap() };
149 self.constraint_solver_unify(constraint, index_type, indexer.index_type);
150 self.constraint_solver_unify(constraint, rhs_type, indexer.index_result_type);
151
152 let res_ty = add_union(
153 self.arena,
154 self.builtin_types,
155 &[indexer.index_result_type, unsafe {
156 (*self.builtin_types).nilType
157 }],
158 );
159 self.bind_not_null_constraint_type_id_type_id(constraint, c.prop_type, res_ty);
160 return true;
161 }
162
163 if let Some(parent) = unsafe { (*lhs_extern_type).parent } {
164 lhs_extern_type = unsafe {
165 crate::functions::get_type_alt_j::get_type_id::<ExternType>(parent)
166 as *mut ExternType
167 };
168 continue;
169 }
170
171 break;
172 }
173 return true;
174 }
175
176 let lhs_intersection: *mut IntersectionType = unsafe {
177 crate::functions::get_mutable_type::get_mutable_type_id::<IntersectionType>(lhs_type)
178 };
179 if !lhs_intersection.is_null() {
180 let mut parts = TypeIds::type_ids();
181
182 let intersection_parts: &alloc::vec::Vec<TypeId> =
187 unsafe { &(*lhs_intersection).parts };
188 for &t in intersection_parts.iter() {
189 let followed = unsafe { follow_type_id(t) };
190
191 let tbl_ptr: *mut TableType = unsafe {
192 crate::functions::get_mutable_type::get_mutable_type_id::<TableType>(followed)
193 };
194 if !tbl_ptr.is_null() {
195 if let Some(indexer) = unsafe { &(*tbl_ptr).indexer } {
196 self.constraint_solver_unify(constraint, index_type, indexer.index_type);
197 parts.insert_type_id(indexer.index_result_type);
198 }
199
200 if unsafe {
201 (*tbl_ptr).state == TableState::Unsealed
202 || (*tbl_ptr).state == TableState::Free
203 } {
204 unsafe {
205 (*tbl_ptr).indexer = Some(TableIndexer {
206 index_type,
207 index_result_type: rhs_type,
208 is_read_only: false,
209 });
210 }
211 parts.insert_type_id(rhs_type);
212 }
213
214 continue;
215 }
216
217 let cls_ptr: *const ExternType = unsafe {
218 crate::functions::get_type_alt_j::get_type_id::<ExternType>(followed)
219 };
220 if !cls_ptr.is_null() {
221 let mut cls_mut = cls_ptr as *mut ExternType;
222 loop {
223 if unsafe { (*cls_mut).indexer.is_some() } {
224 let indexer = unsafe { (*cls_mut).indexer.as_ref().unwrap() };
225 self.constraint_solver_unify(
226 constraint,
227 index_type,
228 indexer.index_type,
229 );
230 parts.insert_type_id(indexer.index_result_type);
231 break;
232 }
233
234 if let Some(parent) = unsafe { (*cls_mut).parent } {
235 cls_mut = unsafe {
236 crate::functions::get_type_alt_j::get_type_id::<ExternType>(parent)
237 as *mut ExternType
238 };
239 continue;
240 }
241
242 break;
243 }
244 }
245 }
246
247 let scope = unsafe { (*constraint).scope };
248 let location = unsafe { (*constraint).location };
249
250 let res =
251 self.simplify_intersection_not_null_scope_location_type_ids(scope, location, parts);
252 self.constraint_solver_unify(constraint, rhs_type, res);
253 }
254
255 self.bind_not_null_constraint_type_id_type_id(constraint, c.prop_type, unsafe {
257 (*self.builtin_types).errorType
258 });
259
260 true
261 }
262}