Skip to main content

luaur_analysis/methods/
unifier_deeply_optional.rs

1use crate::functions::follow_type::follow_type_id;
2use crate::functions::get_mutable_type::get_mutable_type_id;
3use crate::functions::get_type_alt_j::get_type_id;
4use crate::functions::is_optional::is_optional;
5use crate::records::table_type::TableType;
6use crate::records::unifier::Unifier;
7use crate::records::union_type::UnionType;
8use crate::type_aliases::type_id::TypeId;
9use luaur_common::macros::luau_assert::LUAU_ASSERT;
10use std::collections::HashMap;
11
12impl Unifier {
13    pub fn unifier_deeply_optional(
14        &mut self,
15        mut ty: TypeId,
16        seen: &mut HashMap<TypeId, TypeId>,
17    ) -> TypeId {
18        ty = unsafe { follow_type_id(ty) };
19
20        if is_optional(ty) {
21            return ty;
22        }
23
24        // SAFETY: get_type_id returns a raw pointer that must be dereferenced
25        let ttv_ptr = unsafe { get_type_id::<TableType>(ty) };
26
27        if !ttv_ptr.is_null() {
28            let ttv = unsafe { &*ttv_ptr };
29
30            if let Some(&result) = seen.get(&ty) {
31                return result;
32            }
33
34            // Add the table type to the arena first to get a result TypeId
35            let result = unsafe { (*self.types).add_type(ttv.clone()) };
36
37            // Store the result in seen map to handle cycles
38            seen.insert(ty, result);
39
40            // SAFETY: get_mutable_type_id returns a raw mutable pointer
41            let result_ttv_ptr = unsafe { get_mutable_type_id::<TableType>(result) };
42            LUAU_ASSERT!(!result_ttv_ptr.is_null());
43            let result_ttv = unsafe { &mut *result_ttv_ptr };
44
45            // Recursively make each property deeply optional
46            for (_name, prop) in &mut result_ttv.props {
47                let prop_ty = prop.read_ty.or(prop.write_ty).unwrap_or(core::ptr::null());
48                let new_prop_ty = self.unifier_deeply_optional(prop_ty, seen);
49                prop.read_ty = Some(new_prop_ty);
50                prop.write_ty = Some(new_prop_ty);
51            }
52
53            // Return nil | result
54            let builtin_types = unsafe { &*self.builtin_types };
55            let union_types = alloc::vec![builtin_types.nilType, result];
56            let union_type = UnionType {
57                options: union_types,
58            };
59            unsafe { (*self.types).add_type(union_type) }
60        } else {
61            // Return nil | ty
62            let builtin_types = unsafe { &*self.builtin_types };
63            let union_types = alloc::vec![builtin_types.nilType, ty];
64            let union_type = UnionType {
65                options: union_types,
66            };
67            unsafe { (*self.types).add_type(union_type) }
68        }
69    }
70}