1use crate::enums::polarity::Polarity;
2use crate::enums::table_state::TableState;
3use crate::functions::as_mutable_type::as_mutable_type_id;
4use crate::functions::follow_type::follow_type_id;
5use crate::functions::fresh_type::fresh_type;
6use crate::functions::get_mutable_type::get_mutable_type_id;
7use crate::functions::get_type_alt_j::get_type_id;
8use crate::functions::track_interior_free_type::track_interior_free_type;
9use crate::records::any_type::AnyType;
10use crate::records::blocked_type::BlockedType;
11use crate::records::constraint::Constraint;
12use crate::records::constraint_solver::ConstraintSolver;
13use crate::records::extern_type::ExternType;
14use crate::records::free_type::FreeType;
15use crate::records::intersection_builder::IntersectionBuilder;
16use crate::records::intersection_type::IntersectionType;
17use crate::records::metatable_type::MetatableType;
18use crate::records::never_type::NeverType;
19use crate::records::set::Set;
20use crate::records::table_indexer::TableIndexer;
21use crate::records::table_type::TableType;
22use crate::records::type_level::TypeLevel;
23use crate::records::union_builder::UnionBuilder;
24use crate::records::union_type::UnionType;
25use crate::type_aliases::error_type::ErrorType;
26use crate::type_aliases::type_id::TypeId;
27use crate::type_aliases::type_variant::TypeVariant;
28use luaur_common::records::dense_hash_set::DenseHashSet;
29use luaur_common::FFlag;
30
31impl ConstraintSolver {
32 pub fn constraint_solver_try_dispatch_has_indexer(
33 &mut self,
34 _recursion_depth: &mut i32,
35 constraint: *const Constraint,
36 subject_type: TypeId,
37 index_type: TypeId,
38 mut result_type: TypeId,
39 _seen: &mut DenseHashSet<TypeId>,
40 ) -> bool {
41 let subject_type = unsafe { follow_type_id(subject_type) };
42 let index_type = unsafe { follow_type_id(index_type) };
43
44 if _seen.contains(&subject_type) {
45 return false;
46 }
47 _seen.insert(subject_type);
48
49 if unsafe { !get_type_id::<AnyType>(subject_type).is_null() } {
50 self.bind_not_null_constraint_type_id_type_id(constraint, result_type, unsafe {
51 (*self.builtin_types).anyType
52 });
53 return true;
54 }
55
56 let free_type = unsafe { get_mutable_type_id::<FreeType>(subject_type) };
57 if !free_type.is_null() {
58 let upper_bound = unsafe { follow_type_id((*free_type).upper_bound) };
59
60 if let Some(table) = unsafe { get_type_id::<TableType>(upper_bound).as_ref() } {
61 if let Some(indexer) = &table.indexer {
62 self.constraint_solver_unify(constraint, index_type, indexer.index_type);
63 self.bind_not_null_constraint_type_id_type_id(
64 constraint,
65 result_type,
66 indexer.index_result_type,
67 );
68 return true;
69 }
70 } else if let Some(metatable) =
71 unsafe { get_type_id::<MetatableType>(upper_bound).as_ref() }
72 {
73 return self.constraint_solver_try_dispatch_has_indexer(
74 _recursion_depth,
75 constraint,
76 metatable.table(),
77 index_type,
78 result_type,
79 _seen,
80 );
81 }
82
83 let scope = unsafe { (*free_type).scope };
84 let free_result = unsafe {
85 fresh_type(
86 &mut *self.arena,
87 &*self.builtin_types,
88 scope,
89 Polarity::Mixed,
90 )
91 };
92 track_interior_free_type(scope, free_result);
93 self.bind_not_null_constraint_type_id_type_id(constraint, result_type, free_result);
94 result_type = free_result;
95
96 let mut table = TableType::table_type_table_state_type_level_scope(
97 TableState::Unsealed,
98 TypeLevel::default(),
99 scope,
100 );
101 table.indexer = Some(TableIndexer {
102 index_type,
103 index_result_type: free_result,
104 is_read_only: false,
105 });
106
107 let upper_bound = unsafe { (*self.arena).add_type(table) };
108 let simplified = self.simplify_intersection_not_null_scope_location_type_id_type_id(
109 unsafe { (*constraint).scope },
110 unsafe { (*constraint).location },
111 unsafe { (*free_type).upper_bound },
112 upper_bound,
113 );
114
115 if unsafe { !get_type_id::<NeverType>(simplified).is_null() } {
116 self.bind_not_null_constraint_type_id_type_id(constraint, result_type, unsafe {
117 (*self.builtin_types).errorType
118 });
119 } else {
120 unsafe {
121 (*free_type).upper_bound = simplified;
122 }
123 }
124
125 return true;
126 }
127
128 if let Some(table) = unsafe { get_mutable_type_id::<TableType>(subject_type).as_mut() } {
129 if let Some(indexer) = &table.indexer {
130 self.constraint_solver_unify(constraint, index_type, indexer.index_type);
131 self.bind_not_null_constraint_type_id_type_id(
132 constraint,
133 result_type,
134 indexer.index_result_type,
135 );
136 return true;
137 }
138
139 if table.state == TableState::Unsealed {
140 let scope = table.scope;
141 let free_result = unsafe {
142 fresh_type(
143 &mut *self.arena,
144 &*self.builtin_types,
145 scope,
146 Polarity::Mixed,
147 )
148 };
149 track_interior_free_type(scope, free_result);
150 self.bind_not_null_constraint_type_id_type_id(constraint, result_type, free_result);
151 table.indexer = Some(TableIndexer {
152 index_type,
153 index_result_type: result_type,
154 is_read_only: false,
155 });
156 return true;
157 }
158 }
159
160 if let Some(metatable) = unsafe { get_type_id::<MetatableType>(subject_type).as_ref() } {
161 return self.constraint_solver_try_dispatch_has_indexer(
162 _recursion_depth,
163 constraint,
164 metatable.table(),
165 index_type,
166 result_type,
167 _seen,
168 );
169 }
170
171 let mut extern_type = unsafe { get_type_id::<ExternType>(subject_type) as *mut ExternType };
172 while !extern_type.is_null() {
173 if let Some(indexer) = unsafe { &(*extern_type).indexer } {
174 self.constraint_solver_unify(constraint, index_type, indexer.index_type);
175 self.bind_not_null_constraint_type_id_type_id(
176 constraint,
177 result_type,
178 indexer.index_result_type,
179 );
180 return true;
181 }
182
183 extern_type = if let Some(parent) = unsafe { (*extern_type).parent } {
184 unsafe { get_type_id::<ExternType>(parent) as *mut ExternType }
185 } else {
186 core::ptr::null_mut()
187 };
188 }
189
190 if let Some(it) = unsafe { get_type_id::<IntersectionType>(subject_type).as_ref() } {
191 if FFlag::LuauRemoveConstraintSolverEmplace.get() {
196 let mut ib =
197 IntersectionBuilder::intersection_builder(self.arena, self.builtin_types);
198 let mut success = false;
199
200 let parts: alloc::vec::Vec<TypeId> = it.parts.clone();
201 for part in parts {
202 let r = unsafe { (*self.arena).add_type(BlockedType::default()) };
203 unsafe {
204 (*(get_mutable_type_id::<BlockedType>(r))).set_owner(constraint);
205 }
206
207 let ok = self.constraint_solver_try_dispatch_has_indexer(
208 _recursion_depth,
209 constraint,
210 part,
211 index_type,
212 r,
213 _seen,
214 );
215 if !ok {
217 continue;
218 }
219
220 let r = unsafe { follow_type_id(r) };
221 if unsafe { get_type_id::<ErrorType>(r).is_null() } {
222 success = true;
223 ib.add(r);
224 }
225 }
226
227 if success {
233 let built = ib.build();
234 self.bind_not_null_constraint_type_id_type_id(constraint, result_type, built);
235 } else {
236 self.bind_not_null_constraint_type_id_type_id(
237 constraint,
238 result_type,
239 unsafe { (*self.builtin_types).errorType },
240 );
241 }
242 } else {
243 let mut parts: Set<TypeId> = Set::new(core::ptr::null());
244 let part_list: alloc::vec::Vec<TypeId> = it.parts.clone();
245 for part in part_list {
246 parts.insert(&unsafe { follow_type_id(part) });
247 }
248
249 let mut results: Set<TypeId> = Set::new(core::ptr::null());
250
251 let parts_iter: alloc::vec::Vec<TypeId> = parts.iter().copied().collect();
252 for part in parts_iter {
253 let r = unsafe { (*self.arena).add_type(BlockedType::default()) };
254 unsafe {
255 (*(get_mutable_type_id::<BlockedType>(r))).set_owner(constraint);
256 }
257
258 let ok = self.constraint_solver_try_dispatch_has_indexer(
259 _recursion_depth,
260 constraint,
261 part,
262 index_type,
263 r,
264 _seen,
265 );
266 if !ok {
268 continue;
269 }
270
271 let r = unsafe { follow_type_id(r) };
272 if unsafe { get_type_id::<ErrorType>(r).is_null() } {
273 results.insert(&r);
274 }
275 }
276
277 if results.size() == 0 {
278 self.bind_not_null_constraint_type_id_type_id(
279 constraint,
280 result_type,
281 unsafe { (*self.builtin_types).errorType },
282 );
283 } else if results.size() == 1 {
284 let first = *results.iter().next().unwrap();
285 self.bind_not_null_constraint_type_id_type_id(constraint, result_type, first);
286 } else {
287 let parts_vec: alloc::vec::Vec<TypeId> = results.iter().copied().collect();
288 let mutable_ty = unsafe { as_mutable_type_id(result_type) };
289 unsafe {
290 (*mutable_ty).ty =
291 TypeVariant::Intersection(IntersectionType { parts: parts_vec });
292 }
293 let location = unsafe { (*constraint).location };
294 self.unblock_type_id_location(result_type, location);
295 }
296 }
297
298 return true;
299 }
300
301 if let Some(ut) = unsafe { get_type_id::<UnionType>(subject_type).as_ref() } {
302 if FFlag::LuauRemoveConstraintSolverEmplace.get() {
305 let mut ub = UnionBuilder::union_builder(self.arena, self.builtin_types);
306 let mut success = false;
307
308 let options: alloc::vec::Vec<TypeId> = ut.options.clone();
309 for option in options {
310 let r = unsafe { (*self.arena).add_type(BlockedType::default()) };
311 unsafe {
312 (*(get_mutable_type_id::<BlockedType>(r))).set_owner(constraint);
313 }
314
315 let ok = self.constraint_solver_try_dispatch_has_indexer(
316 _recursion_depth,
317 constraint,
318 option,
319 index_type,
320 r,
321 _seen,
322 );
323 if !ok {
325 continue;
326 }
327
328 let r = unsafe { follow_type_id(r) };
329 success = true;
330 ub.add(r);
331 }
332
333 if success {
339 let built = ub.build();
340 self.bind_not_null_constraint_type_id_type_id(constraint, result_type, built);
341 } else {
342 self.bind_not_null_constraint_type_id_type_id(
343 constraint,
344 result_type,
345 unsafe { (*self.builtin_types).errorType },
346 );
347 }
348 } else {
349 let mut parts: Set<TypeId> = Set::new(core::ptr::null());
350 let option_list: alloc::vec::Vec<TypeId> = ut.options.clone();
351 for part in option_list {
352 parts.insert(&unsafe { follow_type_id(part) });
353 }
354
355 let mut results: Set<TypeId> = Set::new(core::ptr::null());
356
357 let parts_iter: alloc::vec::Vec<TypeId> = parts.iter().copied().collect();
358 for part in parts_iter {
359 let r = unsafe { (*self.arena).add_type(BlockedType::default()) };
360 unsafe {
361 (*(get_mutable_type_id::<BlockedType>(r))).set_owner(constraint);
362 }
363
364 let ok = self.constraint_solver_try_dispatch_has_indexer(
365 _recursion_depth,
366 constraint,
367 part,
368 index_type,
369 r,
370 _seen,
371 );
372 if !ok {
374 continue;
375 }
376
377 let r = unsafe { follow_type_id(r) };
378 results.insert(&r);
379 }
380
381 if results.size() == 0 {
382 self.bind_not_null_constraint_type_id_type_id(
383 constraint,
384 result_type,
385 unsafe { (*self.builtin_types).errorType },
386 );
387 } else if results.size() == 1 {
388 let first_result = *results.iter().next().unwrap();
389 if !FFlag::LuauConstraintGraph.get() {
390 self.deprecate_d_shift_references(result_type, first_result);
392 }
393 self.bind_not_null_constraint_type_id_type_id(
394 constraint,
395 result_type,
396 first_result,
397 );
398 } else {
399 let options_vec: alloc::vec::Vec<TypeId> = results.iter().copied().collect();
400 let mutable_ty = unsafe { as_mutable_type_id(result_type) };
401 unsafe {
402 (*mutable_ty).ty = TypeVariant::Union(UnionType {
403 options: options_vec,
404 });
405 }
406 let location = unsafe { (*constraint).location };
407 self.unblock_type_id_location(result_type, location);
408 }
409 }
410
411 return true;
412 }
413
414 self.bind_not_null_constraint_type_id_type_id(constraint, result_type, unsafe {
415 (*self.builtin_types).errorType
416 });
417 true
418 }
419}