Skip to main content

luaur_analysis/methods/
constraint_generator_check_function_signature.rs

1use crate::records::class_decl_record::ClassDeclRecord;
2use crate::records::constraint_generator::ConstraintGenerator;
3use crate::records::function_signature::FunctionSignature;
4use crate::records::scope::Scope;
5use crate::type_aliases::scope_ptr_constraint_generator::ScopePtr;
6use crate::type_aliases::type_id::TypeId;
7use crate::type_aliases::type_pack_id::TypePackId;
8use crate::type_aliases::type_pack_variant::TypePackVariant;
9use alloc::vec::Vec;
10use luaur_ast::records::ast_expr::AstExpr;
11use luaur_ast::records::ast_expr_function::AstExprFunction;
12use luaur_ast::records::location::Location;
13use luaur_common::macros::luau_assert::LUAU_ASSERT;
14use luaur_common::FFlag;
15
16impl ConstraintGenerator {
17    pub fn check_function_signature(
18        &mut self,
19        parent: &ScopePtr,
20        enclosing_class: *mut ClassDeclRecord,
21        fn_node: *mut AstExprFunction,
22        expected_type: Option<TypeId>,
23        original_name: Option<Location>,
24    ) -> FunctionSignature {
25        let fn_ref = unsafe { &*fn_node };
26        LUAU_ASSERT!(
27            FFlag::DebugLuauUserDefinedClasses.get() || enclosing_class.is_null(),
28            "check_function_signature: enclosing_class must be null when DebugLuauUserDefinedClasses is off"
29        );
30
31        let mut generic_types: Vec<TypeId> = Vec::new();
32        let mut generic_type_packs: Vec<TypePackId> = Vec::new();
33
34        let mut expected_type_opt = expected_type;
35        if let Some(et) = expected_type_opt {
36            expected_type_opt = Some(unsafe { crate::functions::follow_type::follow_type_id(et) });
37        }
38
39        let has_generics = fn_ref.generics.size > 0 || fn_ref.generic_packs.size > 0;
40
41        let signature_scope: ScopePtr =
42            self.child_scope(fn_node as *mut crate::records::ast_node::AstNode, parent);
43
44        // We need to assign returnType before creating bodyScope so that the
45        // return type gets propagated to bodyScope.
46        let mut return_type: TypePackId =
47            self.fresh_type_pack(&signature_scope, crate::enums::polarity::Polarity::Positive);
48        unsafe {
49            (*(signature_scope.as_ref() as *const Scope as *mut Scope)).return_type = return_type;
50        }
51
52        let body_scope: ScopePtr = self.child_scope(
53            fn_ref.body as *mut crate::records::ast_node::AstNode,
54            &signature_scope,
55        );
56
57        if has_generics {
58            let generic_definitions = self.create_generics(
59                &signature_scope,
60                crate::records::ast_array::AstArray {
61                    data: fn_ref.generics.data,
62                    size: fn_ref.generics.size,
63                },
64                // C++ `createGenerics(signatureScope, fn->generics)` uses the
65                // default `useCache = false` (ConstraintGenerator.h:481): each
66                // function signature gets fresh generics keyed to its own
67                // signature scope. Passing `true` here aliased every same-named
68                // generic (e.g. `S`) to the first one created, leaving its
69                // `scope` pointing at an unrelated sibling scope so `subsumes`
70                // failed and nested generic subtyping spuriously rejected.
71                false,
72                true,
73            );
74            let generic_pack_definitions = self.create_generic_packs(
75                &signature_scope,
76                crate::records::ast_array::AstArray {
77                    data: fn_ref.generic_packs.data,
78                    size: fn_ref.generic_packs.size,
79                },
80                // C++ `createGenericPacks(signatureScope, fn->genericPacks)` uses
81                // the default `useCache = false` (ConstraintGenerator.h:498),
82                // matching the `createGenerics` call above: fresh per-signature
83                // generic packs rather than name-aliased cached ones.
84                false,
85                true,
86            );
87
88            for (_name, g) in &generic_definitions {
89                generic_types.push(g.ty);
90            }
91
92            for (_name, g) in &generic_pack_definitions {
93                generic_type_packs.push(g.tp);
94            }
95
96            expected_type_opt = None;
97        }
98
99        let mut arg_types: Vec<TypeId> = Vec::new();
100        let mut arg_names: Vec<Option<crate::records::function_argument::FunctionArgument>> =
101            Vec::new();
102        let mut expected_arg_pack = crate::records::type_pack::TypePack {
103            head: Vec::new(),
104            tail: None,
105        };
106
107        let mut expected_function: *const crate::records::function_type::FunctionType =
108            if let Some(et) = expected_type_opt {
109                unsafe {
110                    crate::functions::get_type_alt_j::get_type_id::<
111                        crate::records::function_type::FunctionType,
112                    >(et)
113                }
114            } else {
115                core::ptr::null()
116            };
117
118        // This check ensures that expectedType is precisely optional and not any
119        // (since any is also an optional type)
120        if let Some(et) = expected_type_opt {
121            if crate::functions::is_optional::is_optional(et)
122                && unsafe {
123                    crate::functions::get_type_alt_j::get_type_id::<crate::records::any_type::AnyType>(et).is_null()
124                }
125            {
126                if let Some(ut) = unsafe {
127                    crate::functions::get_type_alt_j::get_type_id::<
128                        crate::records::union_type::UnionType,
129                    >(et)
130                    .as_ref()
131                } {
132                    for u in &ut.options {
133                        let ft = unsafe {
134                            crate::functions::get_type_alt_j::get_type_id::<
135                                crate::records::function_type::FunctionType,
136                            >(*u)
137                        };
138                        if !ft.is_null() && !crate::functions::is_nil::is_nil(*u) {
139                            expected_function = ft;
140                            break;
141                        }
142                    }
143                }
144            }
145        }
146
147        if !expected_function.is_null() {
148            let ef = unsafe { &*expected_function };
149            expected_arg_pack = crate::functions::extend_type_pack::extend_type_pack(
150                unsafe { &mut *self.arena },
151                self.builtin_types,
152                ef.arg_types,
153                fn_ref.args.size as usize,
154                Vec::new(),
155            );
156
157            generic_types = ef.generics.clone();
158            generic_type_packs = ef.generic_packs.clone();
159        }
160
161        let mut has_explicit_self = false;
162        let mut has_self = false;
163
164        if FFlag::DebugLuauUserDefinedClasses.get() {
165            if !enclosing_class.is_null() && fn_ref.args.size > 0 {
166                let first_arg = unsafe { &**fn_ref.args.data };
167                let arg_name =
168                    unsafe { core::ffi::CStr::from_ptr(first_arg.name.value).to_string_lossy() };
169                has_explicit_self = arg_name == "self";
170            }
171            has_self = has_explicit_self || !fn_ref.self_.is_null();
172
173            if has_self {
174                let mut self_type: TypeId = core::ptr::null();
175                if !enclosing_class.is_null() {
176                    self_type = unsafe { (*enclosing_class).ty };
177                } else {
178                    self_type = self
179                        .fresh_type(&signature_scope, crate::enums::polarity::Polarity::Negative);
180                }
181
182                let mut self_local: *mut luaur_ast::records::ast_local::AstLocal =
183                    std::ptr::null_mut();
184                if !fn_ref.self_.is_null() {
185                    self_local = fn_ref.self_;
186                } else if has_explicit_self {
187                    self_local = unsafe { *fn_ref.args.data };
188                }
189
190                LUAU_ASSERT!(!self_local.is_null());
191
192                arg_types.push(self_type);
193                let arg_name = unsafe {
194                    core::ffi::CStr::from_ptr((*self_local).name.value).to_string_lossy()
195                };
196                arg_names.push(Some(crate::records::function_argument::FunctionArgument {
197                    name: arg_name.into_owned(),
198                    location: unsafe { (*self_local).location },
199                }));
200
201                unsafe {
202                    (*(signature_scope.as_ref() as *const Scope as *mut Scope))
203                        .bindings
204                        .insert(
205                            crate::records::symbol::Symbol::from_local(self_local),
206                            crate::records::binding::Binding {
207                                type_id: self_type,
208                                location: (*self_local).location,
209                                deprecated: false,
210                                deprecated_suggestion: alloc::string::String::new(),
211                                documentation_symbol: None,
212                            },
213                        );
214                }
215
216                let def = unsafe { (*self.dfg).get_def_local(self_local) };
217                unsafe {
218                    *(*(signature_scope.as_ref() as *const Scope as *mut Scope))
219                        .lvalue_types
220                        .get_or_insert(def) = self_type;
221                }
222                self.update_r_value_refinements_scope_ptr_def_id_type_id(
223                    &signature_scope,
224                    def,
225                    self_type,
226                );
227            }
228        } else {
229            if !fn_ref.self_.is_null() {
230                let self_type =
231                    self.fresh_type(&signature_scope, crate::enums::polarity::Polarity::Negative);
232                arg_types.push(self_type);
233                let arg_name = unsafe {
234                    core::ffi::CStr::from_ptr((*fn_ref.self_).name.value).to_string_lossy()
235                };
236                arg_names.push(Some(crate::records::function_argument::FunctionArgument {
237                    name: arg_name.into_owned(),
238                    location: unsafe { (*fn_ref.self_).location },
239                }));
240
241                unsafe {
242                    (*(signature_scope.as_ref() as *const Scope as *mut Scope))
243                        .bindings
244                        .insert(
245                            crate::records::symbol::Symbol::from_local(fn_ref.self_),
246                            crate::records::binding::Binding {
247                                type_id: self_type,
248                                location: (*fn_ref.self_).location,
249                                deprecated: false,
250                                deprecated_suggestion: alloc::string::String::new(),
251                                documentation_symbol: None,
252                            },
253                        );
254                }
255
256                let def = unsafe { (*self.dfg).get_def_local(fn_ref.self_) };
257                unsafe {
258                    *(*(signature_scope.as_ref() as *const Scope as *mut Scope))
259                        .lvalue_types
260                        .get_or_insert(def) = self_type;
261                }
262                self.update_r_value_refinements_scope_ptr_def_id_type_id(
263                    &signature_scope,
264                    def,
265                    self_type,
266                );
267            }
268        }
269
270        for i in 0..fn_ref.args.size as usize {
271            if FFlag::DebugLuauUserDefinedClasses.get() && has_explicit_self && i == 0 {
272                continue;
273            }
274
275            let local_ptr = unsafe { *fn_ref.args.data.add(i) };
276            let local = unsafe { &*local_ptr };
277
278            let mut arg_ty: TypeId = core::ptr::null();
279            if !local.annotation.is_null() {
280                arg_ty = self.resolve_type(
281                    (signature_scope.as_ref() as *const Scope as *mut Scope),
282                    local.annotation,
283                    false,
284                    true,
285                    crate::enums::polarity::Polarity::Negative,
286                );
287            } else {
288                if i < expected_arg_pack.head.len() {
289                    arg_ty = expected_arg_pack.head[i];
290                } else {
291                    arg_ty = self
292                        .fresh_type(&signature_scope, crate::enums::polarity::Polarity::Negative);
293                }
294            }
295
296            arg_types.push(arg_ty);
297            let arg_name = unsafe { core::ffi::CStr::from_ptr(local.name.value).to_string_lossy() };
298            arg_names.push(Some(crate::records::function_argument::FunctionArgument {
299                name: arg_name.into_owned(),
300                location: local.location,
301            }));
302
303            let def = unsafe { (*self.dfg).get_def_local(local_ptr) };
304            unsafe {
305                (*(signature_scope.as_ref() as *const Scope as *mut Scope))
306                    .bindings
307                    .insert(
308                        crate::records::symbol::Symbol::from_local(local_ptr),
309                        crate::records::binding::Binding {
310                            type_id: arg_ty,
311                            location: local.location,
312                            deprecated: false,
313                            deprecated_suggestion: alloc::string::String::new(),
314                            documentation_symbol: None,
315                        },
316                    );
317                *(*(signature_scope.as_ref() as *const Scope as *mut Scope))
318                    .lvalue_types
319                    .get_or_insert(def) = arg_ty;
320            }
321            self.update_r_value_refinements_scope_ptr_def_id_type_id(&signature_scope, def, arg_ty);
322        }
323
324        let mut vararg_pack: TypePackId = core::ptr::null();
325
326        if fn_ref.vararg {
327            if !fn_ref.vararg_annotation.is_null() {
328                vararg_pack = self.resolve_type_pack_scope_ptr_ast_type_pack_bool_bool_polarity(
329                    (signature_scope.as_ref() as *const Scope as *mut Scope),
330                    fn_ref.vararg_annotation,
331                    false,
332                    true,
333                    crate::enums::polarity::Polarity::Negative,
334                );
335            } else if expected_arg_pack.tail.is_some()
336                && !unsafe {
337                    crate::functions::get_type_pack::get_type_pack_id::<
338                        crate::records::variadic_type_pack::VariadicTypePack,
339                    >(expected_arg_pack.tail.unwrap())
340                }
341                .is_null()
342            {
343                vararg_pack = expected_arg_pack.tail.unwrap();
344            } else {
345                vararg_pack = unsafe { (*self.builtin_types).anyTypePack };
346            }
347
348            unsafe {
349                (*(signature_scope.as_ref() as *const Scope as *mut Scope)).vararg_pack =
350                    Some(vararg_pack);
351            }
352            unsafe {
353                (*(body_scope.as_ref() as *const Scope as *mut Scope)).vararg_pack =
354                    Some(vararg_pack);
355            }
356        } else {
357            vararg_pack = unsafe {
358                (*self.arena).add_type_pack_t(
359                    crate::records::variadic_type_pack::VariadicTypePack {
360                        ty: unsafe { (*self.builtin_types).anyType },
361                        hidden: true,
362                    },
363                )
364            };
365
366            unsafe {
367                (*(signature_scope.as_ref() as *const Scope as *mut Scope)).vararg_pack = None;
368            }
369            unsafe {
370                (*(body_scope.as_ref() as *const Scope as *mut Scope)).vararg_pack = None;
371            }
372        }
373
374        LUAU_ASSERT!(!vararg_pack.is_null());
375
376        if !fn_ref.self_.is_null() {
377            generic_types.push(arg_types[0]);
378        }
379
380        let mut type_index: usize = if !fn_ref.self_.is_null() { 1 } else { 0 };
381        for i in 0..fn_ref.args.size as usize {
382            let ast_arg = unsafe { *fn_ref.args.data.add(i) };
383            let arg_ty = arg_types[type_index];
384            if unsafe { (*ast_arg).annotation.is_null() } {
385                generic_types.push(arg_ty);
386            }
387            type_index += 1;
388        }
389
390        vararg_pack =
391            unsafe { crate::functions::follow_type_pack::follow_type_pack_id(vararg_pack) };
392        return_type =
393            unsafe { crate::functions::follow_type_pack::follow_type_pack_id(return_type) };
394        if fn_ref.vararg_annotation.is_null() {
395            generic_type_packs.push(vararg_pack);
396        }
397        if fn_ref.return_annotation.is_null() {
398            generic_type_packs.push(return_type);
399        }
400
401        if !fn_ref.return_annotation.is_null() {
402            let annotated_ret_type = self
403                .resolve_type_pack_scope_ptr_ast_type_pack_bool_bool_polarity(
404                    (signature_scope.as_ref() as *const Scope as *mut Scope),
405                    fn_ref.return_annotation,
406                    false,
407                    true,
408                    crate::enums::polarity::Polarity::Negative,
409                );
410            unsafe {
411                crate::functions::emplace_type_pack::emplace_type_pack(
412                    crate::functions::as_mutable_type_pack::as_mutable_type_pack_id(return_type),
413                    TypePackVariant::Bound(annotated_ret_type),
414                );
415            }
416        } else if !expected_function.is_null() {
417            unsafe {
418                crate::functions::emplace_type_pack::emplace_type_pack(
419                    crate::functions::as_mutable_type_pack::as_mutable_type_pack_id(return_type),
420                    TypePackVariant::Bound((*expected_function).ret_types),
421                );
422            }
423        }
424
425        let mut actual_function = crate::records::function_type::FunctionType::function_type_new(
426            unsafe {
427                (*self.arena).add_type_pack_vector_type_id_optional_type_pack_id(
428                    core::mem::take(&mut arg_types),
429                    Some(vararg_pack),
430                )
431            },
432            return_type,
433            None,
434            if FFlag::DebugLuauUserDefinedClasses.get() {
435                has_self
436            } else {
437                !fn_ref.self_.is_null()
438            },
439        );
440        actual_function.generics = generic_types;
441        actual_function.generic_packs = generic_type_packs;
442        actual_function.arg_names = arg_names;
443
444        let defn = crate::records::function_definition::FunctionDefinition {
445            definition_module_name: Some(self.module.as_ref().unwrap().name.clone()),
446            definition_location: fn_ref.base.base.location,
447            vararg_location: if fn_ref.vararg {
448                Some(fn_ref.vararg_location)
449            } else {
450                None
451            },
452            original_name_location: original_name
453                .unwrap_or(Location::with_length(fn_ref.base.base.location.begin, 0)),
454        };
455        actual_function.definition = Some(defn);
456
457        let actual_function_type = unsafe { (*self.arena).add_type(actual_function) };
458        LUAU_ASSERT!(!actual_function_type.is_null());
459        unsafe {
460            let module_ptr = alloc::sync::Arc::as_ptr(self.module.as_ref().unwrap())
461                as *mut crate::records::module::Module;
462            *(*module_ptr)
463                .ast_types
464                .get_or_insert(fn_node as *const AstExpr) = actual_function_type;
465        }
466
467        if let Some(et) = expected_type_opt {
468            if !unsafe { crate::functions::get_type_alt_j::get_type_id::<crate::records::free_type::FreeType>(et) }.is_null() {
469                crate::functions::bind_free_type::bind_free_type(et, actual_function_type);
470            }
471        }
472
473        *self
474            .scope_to_function
475            .get_or_insert(signature_scope.as_ref() as *const Scope as *mut Scope) =
476            actual_function_type;
477
478        FunctionSignature {
479            signature: actual_function_type,
480            signature_scope,
481            body_scope,
482        }
483    }
484}