Skip to main content

luaur_analysis/methods/
normalizer_intersect_normals.rs

1//! Source: `Analysis/src/Normalize.cpp:3244-3317` (hand-ported)
2use crate::enums::normalization_result::NormalizationResult;
3use crate::functions::get_type_alt_j::get_type_id;
4use crate::functions::is_shallow_inhabited::is_shallow_inhabited;
5use crate::functions::tyvar_index::tyvar_index;
6use crate::records::never_type::NeverType;
7use crate::records::normalized_type::NormalizedType;
8use crate::records::normalizer::Normalizer;
9use crate::type_aliases::type_id::TypeId;
10use alloc::boxed::Box;
11use luaur_common::macros::luau_assert::LUAU_ASSERT;
12use luaur_common::FFlag;
13
14/// RAII guard mirroring C++ `RecursionCounter _rc(&sharedState->counters.recursionCount)`.
15struct RcGuard {
16    count: *mut i32,
17}
18
19impl RcGuard {
20    fn new(count: *mut i32) -> Self {
21        unsafe {
22            *count += 1;
23        }
24        RcGuard { count }
25    }
26}
27
28impl Drop for RcGuard {
29    fn drop(&mut self) {
30        unsafe {
31            *self.count -= 1;
32        }
33    }
34}
35
36impl Normalizer {
37    // See above for an explanation of `ignoreSmallerTyvars`.
38    pub fn intersect_normals(
39        &mut self,
40        here: &mut NormalizedType,
41        there: &NormalizedType,
42        ignore_smaller_tyvars: i32,
43    ) -> NormalizationResult {
44        let _rc = RcGuard::new(unsafe { &mut (*self.shared_state).counters.recursion_count });
45        if !self.within_resource_limits() {
46            return NormalizationResult::HitLimits;
47        }
48
49        self.consume_fuel();
50
51        if unsafe { get_type_id::<NeverType>(there.tops).is_null() } {
52            here.tops = self.intersection_of_tops(here.tops, there.tops);
53            return NormalizationResult::True;
54        } else if unsafe { get_type_id::<NeverType>(here.tops).is_null() } {
55            self.clear_normal(here);
56            return self.union_normals(here, there, ignore_smaller_tyvars);
57        }
58
59        for (tyvar, _inter) in there.tyvars.iter() {
60            let index = tyvar_index(*tyvar);
61            if ignore_smaller_tyvars < index {
62                let fresh = !here.tyvars.contains_key(tyvar);
63                if fresh {
64                    let mut entry = Box::new(fresh_normalized_type(self.builtin_types));
65                    let res = self.union_normals(&mut entry, here, index);
66                    if res != NormalizationResult::True {
67                        return res;
68                    }
69                    here.tyvars.insert(*tyvar, entry);
70                }
71            }
72        }
73
74        here.booleans = self.intersection_of_bools(here.booleans, there.booleans);
75
76        self.intersect_extern_types(&mut here.extern_types, &there.extern_types);
77        here.errors = if !unsafe { get_type_id::<NeverType>(there.errors).is_null() } {
78            there.errors
79        } else {
80            here.errors
81        };
82        here.nils = if !unsafe { get_type_id::<NeverType>(there.nils).is_null() } {
83            there.nils
84        } else {
85            here.nils
86        };
87        here.numbers = if !unsafe { get_type_id::<NeverType>(there.numbers).is_null() } {
88            there.numbers
89        } else {
90            here.numbers
91        };
92        if FFlag::LuauIntegerType2.get() {
93            here.integers = if !unsafe { get_type_id::<NeverType>(there.integers).is_null() } {
94                there.integers
95            } else {
96                here.integers
97            };
98        }
99        self.intersect_strings(&mut here.strings, &there.strings);
100        here.threads = if !unsafe { get_type_id::<NeverType>(there.threads).is_null() } {
101            there.threads
102        } else {
103            here.threads
104        };
105        here.buffers = if !unsafe { get_type_id::<NeverType>(there.buffers).is_null() } {
106            there.buffers
107        } else {
108            here.buffers
109        };
110        self.intersect_functions(&mut here.functions, &there.functions);
111        self.intersect_tables(&mut here.tables, &there.tables);
112
113        let tyvar_keys: Vec<TypeId> = here.tyvars.keys().copied().collect();
114        for tyvar in tyvar_keys {
115            let mut inter = match here.tyvars.remove(&tyvar) {
116                Some(b) => b,
117                None => continue,
118            };
119            let index = tyvar_index(tyvar);
120            LUAU_ASSERT!(ignore_smaller_tyvars < index);
121            let res = match there.tyvars.get(&tyvar) {
122                None => self.intersect_normals(&mut inter, there, index),
123                Some(found) => self.intersect_normals(&mut inter, found, index),
124            };
125            if res != NormalizationResult::True {
126                here.tyvars.insert(tyvar, inter);
127                return res;
128            }
129            if is_shallow_inhabited(&inter) {
130                here.tyvars.insert(tyvar, inter);
131            }
132            // else: drop `inter`, removing the entry (C++ `here.tyvars.erase(it)`).
133        }
134        NormalizationResult::True
135    }
136}
137
138fn fresh_normalized_type(
139    builtin_types: *mut crate::records::builtin_types::BuiltinTypes,
140) -> NormalizedType {
141    let never_type = unsafe { (*builtin_types).neverType };
142    NormalizedType {
143        builtin_types,
144        tops: never_type,
145        booleans: never_type,
146        extern_types: crate::records::normalized_extern_type::NormalizedExternType {
147            extern_types: alloc::collections::BTreeMap::new(),
148            shape_extensions: crate::records::type_ids::TypeIds::type_ids(),
149            ordering: Vec::new(),
150        },
151        errors: never_type,
152        nils: never_type,
153        numbers: never_type,
154        integers: never_type,
155        strings: crate::records::normalized_string_type::NormalizedStringType::never,
156        threads: never_type,
157        buffers: never_type,
158        tables: crate::records::type_ids::TypeIds::type_ids(),
159        functions: crate::records::normalized_function_type::NormalizedFunctionType {
160            is_top: false,
161            parts: crate::records::type_ids::TypeIds::type_ids(),
162        },
163        tyvars: alloc::collections::BTreeMap::new(),
164        is_cacheable: true,
165    }
166}