Skip to main content

luaur_analysis/methods/
constraint_solver_try_dispatch_constraint_solver_alt_g.rs

1use crate::functions::follow_type::follow_type_id;
2use crate::functions::get_mutable_table_type::get_mutable_table_type;
3use crate::functions::get_mutable_type::get_mutable_type_id;
4use crate::functions::get_table_type::get_table_type;
5use crate::functions::get_type_alt_j::get_type_id;
6use crate::functions::occurs_check_type_utils::occurs_check_type_id_type_id;
7use crate::functions::saturate_arguments::saturate_arguments;
8use crate::functions::shallow_clone_clone_alt_b::shallow_clone;
9use crate::records::clone_state::CloneState;
10use crate::records::constraint::Constraint;
11use crate::records::constraint_solver::ConstraintSolver;
12use crate::records::generic_type_visitor::GenericTypeVisitorTrait;
13use crate::records::infinite_type_finder::InfiniteTypeFinder;
14use crate::records::instantiation_queuer::InstantiationQueuer;
15use crate::records::instantiation_queuer_deprecated::InstantiationQueuerDeprecated;
16use crate::records::instantiation_signature::InstantiationSignature;
17use crate::records::iterative_type_visitor::IterativeTypeVisitorTrait;
18use crate::records::metatable_type::MetatableType;
19use crate::records::occurs_check_failed::OccursCheckFailed;
20use crate::records::pending_expansion_type::PendingExpansionType;
21use crate::records::reduce_constraint::ReduceConstraint;
22use crate::records::table_type::TableType;
23use crate::records::type_alias_expansion_constraint::TypeAliasExpansionConstraint;
24use crate::records::type_function_instance_type::TypeFunctionInstanceType;
25use crate::records::unknown_symbol::{Context, UnknownSymbol};
26use crate::type_aliases::constraint_v::ConstraintV;
27use crate::type_aliases::type_error_data::TypeErrorData;
28use luaur_ast::records::ast_name::AstName;
29use luaur_common::FFlag;
30
31impl ConstraintSolver {
32    pub fn try_dispatch_type_alias_expansion_constraint_not_null_constraint(
33        &mut self,
34        c: &TypeAliasExpansionConstraint,
35        constraint: *const Constraint,
36    ) -> bool {
37        let petv = unsafe { get_type_id::<PendingExpansionType>(follow_type_id(c.target)) };
38        if petv.is_null() {
39            self.unblock_type_id_location(c.target, unsafe { (*constraint).location });
40            return true;
41        }
42
43        let petv = unsafe { &*petv };
44        let alias_name = ast_name_to_string(petv.name);
45        let alias_prefix = petv.prefix.map(ast_name_to_string);
46        let raw_type_arguments = petv.type_arguments.clone();
47        let raw_pack_arguments = petv.pack_arguments.clone();
48
49        let tf = unsafe {
50            if let Some(prefix) = &alias_prefix {
51                (*(*constraint).scope).lookup_imported_type(prefix, &alias_name)
52            } else {
53                (*(*constraint).scope).lookup_type(&alias_name)
54            }
55        };
56
57        let Some(tf) = tf else {
58            self.report_error_type_error_data_location(
59                TypeErrorData::UnknownSymbol(UnknownSymbol::new(alias_name, Context::Type)),
60                unsafe { &(*constraint).location },
61            );
62            bind_alias_expansion_result(self, c, constraint, unsafe {
63                (*self.builtin_types).errorType
64            });
65            return true;
66        };
67
68        if unsafe {
69            !get_type_id::<TypeFunctionInstanceType>(follow_type_id(tf.r#type())).is_null()
70        } {
71            self.push_constraint(
72                unsafe { core::ptr::NonNull::new_unchecked((*constraint).scope) },
73                unsafe { (*constraint).location },
74                ConstraintV::Reduce(ReduceConstraint { ty: tf.r#type() }),
75            );
76        }
77
78        let lhs = unsafe { follow_type_id(c.target) };
79        let rhs = tf.r#type();
80        if occurs_check_type_id_type_id(lhs, rhs) {
81            self.report_error_type_error_data_location(
82                TypeErrorData::OccursCheckFailed(OccursCheckFailed::default()),
83                unsafe { &(*constraint).location },
84            );
85            bind_alias_expansion_result(self, c, constraint, unsafe {
86                (*self.builtin_types).errorType
87            });
88            return true;
89        }
90
91        if tf.type_params().is_empty() && tf.type_pack_params().is_empty() {
92            bind_alias_expansion_result(self, c, constraint, tf.r#type());
93            return true;
94        }
95
96        let (type_arguments, pack_arguments) = unsafe {
97            saturate_arguments(
98                &mut *self.arena,
99                &mut *self.builtin_types,
100                &tf,
101                &raw_type_arguments,
102                &raw_pack_arguments,
103            )
104        };
105
106        let same_types = type_arguments.len() == tf.type_params().len()
107            && type_arguments
108                .iter()
109                .zip(tf.type_params())
110                .all(|(arg, param)| *arg == param.ty);
111        let same_packs = pack_arguments.len() == tf.type_pack_params().len()
112            && pack_arguments
113                .iter()
114                .zip(tf.type_pack_params())
115                .all(|(arg, param)| *arg == param.tp);
116
117        if same_types && same_packs {
118            bind_alias_expansion_result(self, c, constraint, tf.r#type());
119            return true;
120        }
121
122        let signature = InstantiationSignature {
123            fn_sig: tf.clone(),
124            arguments: type_arguments.clone(),
125            pack_arguments: pack_arguments.clone(),
126        };
127
128        if let Some(cached) = self.instantiated_aliases.find(&signature).copied() {
129            bind_alias_expansion_result(self, c, constraint, cached);
130            return true;
131        }
132
133        let mut itf = InfiniteTypeFinder::infinite_type_finder_infinite_type_finder(
134            self,
135            &signature,
136            unsafe { core::ptr::NonNull::new_unchecked((*constraint).scope) },
137        );
138        itf.run_type_id(tf.r#type());
139
140        if itf.found_infinite_type {
141            bind_alias_expansion_result(self, c, constraint, unsafe {
142                (*self.builtin_types).errorType
143            });
144            unsafe {
145                (*(*constraint).scope)
146                    .invalid_type_aliases
147                    .try_insert(alias_name.clone(), (*constraint).location);
148            }
149            return true;
150        }
151
152        let mut apply_type_function =
153            crate::records::apply_type_function::ApplyTypeFunction::apply_type_function(self.arena);
154        for (i, ty) in type_arguments.iter().enumerate() {
155            *apply_type_function
156                .type_arguments
157                .get_or_insert(tf.type_params()[i].ty) = *ty;
158        }
159
160        for (i, tp) in pack_arguments.iter().enumerate() {
161            *apply_type_function
162                .type_pack_arguments
163                .get_or_insert(tf.type_pack_params()[i].tp) = *tp;
164        }
165
166        let Some(mut instantiated) = apply_type_function.substitute_type_id(tf.r#type()) else {
167            bind_alias_expansion_result(self, c, constraint, unsafe {
168                (*self.builtin_types).errorType
169            });
170            return true;
171        };
172
173        let mut target = unsafe { follow_type_id(instantiated) };
174
175        if FFlag::LuauIterativeInstantiationQueuer.get() {
176            let mut queuer = InstantiationQueuer::instantiation_queuer(
177                unsafe { core::ptr::NonNull::new_unchecked((*constraint).scope) },
178                unsafe { &(*constraint).location },
179                self as *mut ConstraintSolver,
180            );
181            queuer.run_type_id(target);
182        } else {
183            let mut queuer = InstantiationQueuerDeprecated::instantiation_queuer_deprecated_instantiation_queuer_deprecated(
184                unsafe { core::ptr::NonNull::new_unchecked((*constraint).scope) },
185                unsafe { &(*constraint).location },
186                self as *mut ConstraintSolver,
187            );
188            queuer.traverse_type_id(target);
189        }
190
191        if unsafe { (*target).persistent || (*target).owning_arena != self.arena } {
192            bind_alias_expansion_result(self, c, constraint, target);
193            return true;
194        }
195
196        let tf_table = get_table_type(tf.r#type())
197            .map(|table| table as *const TableType)
198            .unwrap_or(core::ptr::null());
199        let target_table = get_table_type(target)
200            .map(|table| table as *const TableType)
201            .unwrap_or(core::ptr::null());
202        let needs_clone = unsafe { follow_type_id(tf.r#type()) == target }
203            || (!tf_table.is_null() && tf_table == target_table)
204            || type_arguments.iter().any(|other| *other == target);
205
206        let mut table = get_mutable_table_type(target);
207        if !table.is_null() {
208            if needs_clone {
209                if unsafe { !get_type_id::<MetatableType>(target).is_null() } {
210                    let mut clone_state = unsafe { CloneState::new(&mut *self.builtin_types) };
211                    instantiated =
212                        unsafe { shallow_clone(target, &mut *self.arena, &mut clone_state, true) };
213                    let metatable = unsafe { get_mutable_type_id::<MetatableType>(instantiated) };
214                    unsafe {
215                        (*metatable).table = shallow_clone(
216                            (*metatable).table(),
217                            &mut *self.arena,
218                            &mut clone_state,
219                            true,
220                        );
221                        table = get_mutable_type_id::<TableType>((*metatable).table());
222                    }
223                } else if unsafe { !get_type_id::<TableType>(target).is_null() } {
224                    let mut clone_state = unsafe { CloneState::new(&mut *self.builtin_types) };
225                    instantiated =
226                        unsafe { shallow_clone(target, &mut *self.arena, &mut clone_state, true) };
227                    table = unsafe { get_mutable_type_id::<TableType>(instantiated) };
228                }
229
230                target = unsafe { follow_type_id(instantiated) };
231            }
232
233            unsafe {
234                (*table).instantiated_type_params = type_arguments.clone();
235                (*table).instantiated_type_pack_params = pack_arguments.clone();
236                (*table).definition_location = (*constraint).location;
237                if let Some(module) = &self.module {
238                    (*table).definition_module_name = module.name.clone();
239                }
240            }
241        }
242
243        bind_alias_expansion_result(self, c, constraint, target);
244        self.instantiated_aliases.try_insert(signature, target);
245
246        true
247    }
248}
249
250fn bind_alias_expansion_result(
251    solver: &mut ConstraintSolver,
252    c: &TypeAliasExpansionConstraint,
253    constraint: *const Constraint,
254    result: crate::type_aliases::type_id::TypeId,
255) {
256    let c_target = unsafe { follow_type_id(c.target) };
257
258    if occurs_check_type_id_type_id(c_target, result) {
259        solver.report_error_type_error_data_location(
260            TypeErrorData::OccursCheckFailed(OccursCheckFailed::default()),
261            unsafe { &(*constraint).location },
262        );
263        solver.bind_not_null_constraint_type_id_type_id(constraint, c_target, unsafe {
264            (*solver.builtin_types).errorType
265        });
266    } else {
267        solver.bind_not_null_constraint_type_id_type_id(constraint, c_target, result);
268    }
269}
270
271fn ast_name_to_string(name: AstName) -> String {
272    if name.value.is_null() {
273        String::new()
274    } else {
275        unsafe { core::ffi::CStr::from_ptr(name.value) }
276            .to_string_lossy()
277            .into_owned()
278    }
279}