Skip to main content

luaur_analysis/methods/
normalizer_intersect_normal_with_ty.rs

1//! Source: `Analysis/src/Normalize.cpp:3319-3564` (hand-ported)
2use crate::enums::normalization_result::NormalizationResult;
3use crate::functions::follow_type::follow_type_id;
4use crate::functions::get_singleton_type::get_singleton_type;
5use crate::functions::get_type_alt_j::get_type_id;
6use crate::functions::should_early_exit::should_early_exit;
7use crate::records::any_type::AnyType;
8use crate::records::blocked_type::BlockedType;
9use crate::records::boolean_singleton::BooleanSingleton;
10use crate::records::extern_type::ExternType;
11use crate::records::free_type::FreeType;
12use crate::records::function_type::FunctionType;
13use crate::records::generic_type::GenericType;
14use crate::records::intersection_type::IntersectionType;
15use crate::records::metatable_type::MetatableType;
16use crate::records::negation_type::NegationType;
17use crate::records::never_type::NeverType;
18use crate::records::no_refine_type::NoRefineType;
19use crate::records::normalized_extern_type::NormalizedExternType;
20use crate::records::normalized_function_type::NormalizedFunctionType;
21use crate::records::normalized_string_type::NormalizedStringType;
22use crate::records::normalized_type::NormalizedType;
23use crate::records::normalizer::Normalizer;
24use crate::records::pending_expansion_type::PendingExpansionType;
25use crate::records::primitive_type::{PrimitiveType, Type as PrimType};
26use crate::records::singleton_type::SingletonType;
27use crate::records::string_singleton::StringSingleton;
28use crate::records::table_type::TableType;
29use crate::records::type_function_instance_type::TypeFunctionInstanceType;
30use crate::records::type_ids::TypeIds;
31use crate::records::union_type::UnionType;
32use crate::records::unknown_type::UnknownType;
33use crate::type_aliases::error_type::ErrorType;
34use crate::type_aliases::normalized_tyvars::NormalizedTyvars;
35use crate::type_aliases::seen_table_prop_pairs::SeenTablePropPairs;
36use crate::type_aliases::type_id::TypeId;
37use alloc::boxed::Box;
38use alloc::collections::BTreeMap;
39use core::mem;
40use luaur_common::macros::luau_assert::LUAU_ASSERT;
41use luaur_common::records::dense_hash_set::DenseHashSet;
42use luaur_common::FFlag;
43
44/// RAII guard mirroring C++ `RecursionCounter _rc(&sharedState->counters.recursionCount)`.
45struct RcGuard {
46    count: *mut i32,
47}
48
49impl RcGuard {
50    fn new(count: *mut i32) -> Self {
51        unsafe {
52            *count += 1;
53        }
54        RcGuard { count }
55    }
56}
57
58impl Drop for RcGuard {
59    fn drop(&mut self) {
60        unsafe {
61            *self.count -= 1;
62        }
63    }
64}
65
66fn fresh_normalized_type(
67    builtin_types: *mut crate::records::builtin_types::BuiltinTypes,
68) -> NormalizedType {
69    let never_type = unsafe { (*builtin_types).neverType };
70    NormalizedType {
71        builtin_types,
72        tops: never_type,
73        booleans: never_type,
74        extern_types: NormalizedExternType {
75            extern_types: BTreeMap::new(),
76            shape_extensions: TypeIds::type_ids(),
77            ordering: Vec::new(),
78        },
79        errors: never_type,
80        nils: never_type,
81        numbers: never_type,
82        integers: never_type,
83        strings: NormalizedStringType::never,
84        threads: never_type,
85        buffers: never_type,
86        tables: TypeIds::type_ids(),
87        functions: NormalizedFunctionType {
88            is_top: false,
89            parts: TypeIds::type_ids(),
90        },
91        tyvars: BTreeMap::new(),
92        is_cacheable: true,
93    }
94}
95
96impl Normalizer {
97    pub fn intersect_normal_with_ty(
98        &mut self,
99        here: &mut NormalizedType,
100        there: TypeId,
101        seen_table_prop_pairs: &mut SeenTablePropPairs,
102        seen_set_types: &mut DenseHashSet<TypeId>,
103    ) -> NormalizationResult {
104        let _rc = RcGuard::new(unsafe { &mut (*self.shared_state).counters.recursion_count });
105        if !self.within_resource_limits() {
106            return NormalizationResult::HitLimits;
107        }
108
109        self.consume_fuel();
110
111        let there = unsafe { follow_type_id(there) };
112
113        if !unsafe { get_type_id::<AnyType>(there).is_null() }
114            || !unsafe { get_type_id::<UnknownType>(there).is_null() }
115        {
116            here.tops = self.intersection_of_tops(here.tops, there);
117            return NormalizationResult::True;
118        } else if unsafe { get_type_id::<NeverType>(here.tops).is_null() } {
119            self.clear_normal(here);
120            return self.union_normal_with_ty(
121                here,
122                there,
123                seen_table_prop_pairs,
124                seen_set_types,
125                -1,
126            );
127        } else if !unsafe { get_type_id::<UnionType>(there).is_null() } {
128            let mut norm = fresh_normalized_type(self.builtin_types);
129            let options = unsafe { (*get_type_id::<UnionType>(there)).options.clone() };
130            for opt in options {
131                let res = self.union_normal_with_ty(
132                    &mut norm,
133                    opt,
134                    seen_table_prop_pairs,
135                    seen_set_types,
136                    -1,
137                );
138                if res != NormalizationResult::True {
139                    return res;
140                }
141            }
142            return self.intersect_normals(here, &norm, -1);
143        } else if !unsafe { get_type_id::<IntersectionType>(there).is_null() } {
144            let parts = unsafe { (*get_type_id::<IntersectionType>(there)).parts.clone() };
145            for part in parts {
146                let res = self.intersect_normal_with_ty(
147                    here,
148                    part,
149                    seen_table_prop_pairs,
150                    seen_set_types,
151                );
152                if res != NormalizationResult::True {
153                    return res;
154                }
155            }
156            return NormalizationResult::True;
157        } else if !unsafe { get_type_id::<GenericType>(there).is_null() }
158            || !unsafe { get_type_id::<FreeType>(there).is_null() }
159            || !unsafe { get_type_id::<BlockedType>(there).is_null() }
160            || !unsafe { get_type_id::<PendingExpansionType>(there).is_null() }
161            || !unsafe { get_type_id::<TypeFunctionInstanceType>(there).is_null() }
162        {
163            let mut there_norm = fresh_normalized_type(self.builtin_types);
164            let mut top_norm = fresh_normalized_type(self.builtin_types);
165            top_norm.tops = unsafe { (*self.builtin_types).unknownType };
166            there_norm.tyvars.insert(there, Box::new(top_norm));
167            here.is_cacheable = false;
168            return self.intersect_normals(here, &there_norm, -1);
169        }
170
171        let mut tyvars: NormalizedTyvars = mem::take(&mut here.tyvars);
172
173        if !unsafe { get_type_id::<FunctionType>(there).is_null() } {
174            let mut functions = mem::replace(
175                &mut here.functions,
176                NormalizedFunctionType {
177                    is_top: false,
178                    parts: TypeIds::type_ids(),
179                },
180            );
181            self.clear_normal(here);
182            self.intersect_functions_with_function(&mut functions, there);
183            here.functions = functions;
184        } else if !unsafe { get_type_id::<TableType>(there).is_null() }
185            || !unsafe { get_type_id::<MetatableType>(there).is_null() }
186        {
187            if self.use_new_luau_solver() {
188                let mut extern_types = mem::replace(
189                    &mut here.extern_types,
190                    NormalizedExternType {
191                        extern_types: BTreeMap::new(),
192                        shape_extensions: TypeIds::type_ids(),
193                        ordering: Vec::new(),
194                    },
195                );
196                let mut tables = mem::replace(&mut here.tables, TypeIds::type_ids());
197                self.clear_normal(here);
198
199                if FFlag::LuauExternTypesNormalizeWithShapes.get() {
200                    if extern_types.is_never() {
201                        self.intersect_tables_with_table(
202                            &mut tables,
203                            there,
204                            seen_table_prop_pairs,
205                            seen_set_types,
206                        );
207                    } else {
208                        self.intersect_extern_types_with_shape(&mut extern_types, there);
209                    }
210                } else {
211                    self.intersect_tables_with_table(
212                        &mut tables,
213                        there,
214                        seen_table_prop_pairs,
215                        seen_set_types,
216                    );
217                }
218
219                here.tables = tables;
220                here.extern_types = extern_types;
221            } else {
222                let mut tables = mem::replace(&mut here.tables, TypeIds::type_ids());
223                self.clear_normal(here);
224                self.intersect_tables_with_table(
225                    &mut tables,
226                    there,
227                    seen_table_prop_pairs,
228                    seen_set_types,
229                );
230                here.tables = tables;
231            }
232        } else if !unsafe { get_type_id::<ExternType>(there).is_null() } {
233            let mut nct = mem::replace(
234                &mut here.extern_types,
235                NormalizedExternType {
236                    extern_types: BTreeMap::new(),
237                    shape_extensions: TypeIds::type_ids(),
238                    ordering: Vec::new(),
239                },
240            );
241            self.clear_normal(here);
242            self.intersect_extern_types_with_extern_type(&mut nct, there);
243            here.extern_types = nct;
244        } else if !unsafe { get_type_id::<ErrorType>(there).is_null() } {
245            let errors = here.errors;
246            self.clear_normal(here);
247            here.errors = if !unsafe { get_type_id::<ErrorType>(errors).is_null() } {
248                errors
249            } else {
250                there
251            };
252        } else if !unsafe { get_type_id::<PrimitiveType>(there).is_null() } {
253            let booleans = here.booleans;
254            let nils = here.nils;
255            let numbers = here.numbers;
256            let integers = here.integers;
257            let strings = mem::replace(&mut here.strings, NormalizedStringType::never);
258            let functions = mem::replace(
259                &mut here.functions,
260                NormalizedFunctionType {
261                    is_top: false,
262                    parts: TypeIds::type_ids(),
263                },
264            );
265            let threads = here.threads;
266            let buffers = here.buffers;
267            let tables = mem::replace(&mut here.tables, TypeIds::type_ids());
268
269            self.clear_normal(here);
270
271            let ptv = unsafe { &*get_type_id::<PrimitiveType>(there) };
272            match ptv.r#type {
273                PrimType::Boolean => here.booleans = booleans,
274                PrimType::NilType => here.nils = nils,
275                PrimType::Number => here.numbers = numbers,
276                PrimType::Integer if FFlag::LuauIntegerType2.get() => here.integers = integers,
277                PrimType::String => here.strings = strings,
278                PrimType::Thread => here.threads = threads,
279                PrimType::Buffer => here.buffers = buffers,
280                PrimType::Function => here.functions = functions,
281                PrimType::Table => here.tables = tables,
282                _ => LUAU_ASSERT!(false),
283            }
284        } else if !unsafe { get_type_id::<SingletonType>(there).is_null() } {
285            let booleans = here.booleans;
286            let strings = mem::replace(&mut here.strings, NormalizedStringType::never);
287
288            self.clear_normal(here);
289
290            let stv = unsafe { get_type_id::<SingletonType>(there) };
291            if !get_singleton_type::<BooleanSingleton>(stv).is_null() {
292                here.booleans = self.intersection_of_bools(booleans, there);
293            } else if !get_singleton_type::<StringSingleton>(stv).is_null() {
294                let sstv = unsafe { &*get_singleton_type::<StringSingleton>(stv) };
295                if strings.includes(&sstv.value) {
296                    here.strings.singletons.insert(sstv.value.clone(), there);
297                }
298            } else {
299                LUAU_ASSERT!(false);
300            }
301        } else if !unsafe { get_type_id::<NegationType>(there).is_null() } {
302            let ntv_ty = unsafe { (*get_type_id::<NegationType>(there)).ty };
303            let t = unsafe { follow_type_id(ntv_ty) };
304            if !unsafe { get_type_id::<PrimitiveType>(t).is_null() } {
305                self.subtract_primitive(here, ntv_ty);
306            } else if !unsafe { get_type_id::<SingletonType>(t).is_null() } {
307                self.subtract_singleton(here, unsafe { follow_type_id(ntv_ty) });
308            } else if !unsafe { get_type_id::<ExternType>(t).is_null() } {
309                let res = self.intersect_normal_with_negation_ty(t, here);
310                if should_early_exit(res) {
311                    here.tyvars = tyvars;
312                    return res;
313                }
314            } else if !unsafe { get_type_id::<UnionType>(t).is_null() } {
315                let options = unsafe { (*get_type_id::<UnionType>(t)).options.clone() };
316                for part in options {
317                    let res = self.intersect_normal_with_negation_ty(part, here);
318                    if should_early_exit(res) {
319                        here.tyvars = tyvars;
320                        return res;
321                    }
322                }
323            } else if !unsafe { get_type_id::<AnyType>(t).is_null() } {
324                // HACK: Refinements sometimes intersect with ~any under the
325                // assumption that it is the same as any.
326                here.tyvars = tyvars;
327                return NormalizationResult::True;
328            } else if !unsafe { get_type_id::<NoRefineType>(t).is_null() } {
329                // `*no-refine*` means we will never do anything to affect the intersection.
330                here.tyvars = tyvars;
331                return NormalizationResult::True;
332            } else if !unsafe { get_type_id::<NeverType>(t).is_null() } {
333                // intersecting with `~never` is equivalent to intersecting with `unknown` (a noop).
334                here.tyvars = tyvars;
335                return NormalizationResult::True;
336            } else if !unsafe { get_type_id::<UnknownType>(t).is_null() } {
337                // intersecting with `~unknown` is equivalent to intersecting with `never`.
338                self.clear_normal(here);
339                here.tyvars = tyvars;
340                return NormalizationResult::True;
341            } else if !unsafe { get_type_id::<ErrorType>(t).is_null() } {
342                // ~error is still an error.
343                let errors = here.errors;
344                self.clear_normal(here);
345                here.errors = if !unsafe { get_type_id::<ErrorType>(errors).is_null() } {
346                    errors
347                } else {
348                    t
349                };
350            } else if !unsafe { get_type_id::<NegationType>(t).is_null() } {
351                let nt_ty = unsafe { (*get_type_id::<NegationType>(t)).ty };
352                here.tyvars = tyvars;
353                return self.intersect_normal_with_ty(
354                    here,
355                    nt_ty,
356                    seen_table_prop_pairs,
357                    seen_set_types,
358                );
359            } else {
360                // TODO negated unions, intersections, table, and function.
361                // Report a TypeError for other types.
362                LUAU_ASSERT!(false);
363            }
364        } else if !unsafe { get_type_id::<NeverType>(there).is_null() } {
365            here.extern_types.reset_to_never();
366        } else if !unsafe { get_type_id::<NoRefineType>(there).is_null() } {
367            // `*no-refine*` means we will never do anything to affect the intersection.
368            here.tyvars = tyvars;
369            return NormalizationResult::True;
370        } else {
371            LUAU_ASSERT!(false);
372        }
373
374        let res = self.intersect_tyvars_with_ty(
375            &mut tyvars,
376            there,
377            seen_table_prop_pairs,
378            seen_set_types,
379        );
380        if res != NormalizationResult::True {
381            here.tyvars = tyvars;
382            return res;
383        }
384        here.tyvars = tyvars;
385
386        NormalizationResult::True
387    }
388}