Skip to main content

luaur_analysis/functions/
generalize.rs

1use crate::functions::follow_type::follow_type_id;
2use crate::functions::follow_type_utils::follow_optional_ty;
3use crate::functions::generalize_type::generalize_type;
4use crate::functions::generalize_type_pack::generalize_type_pack;
5use crate::functions::seal_table::seal_table;
6use crate::methods::free_type_searcher_visit_generalization_alt_c::searcher_traverse_type_id;
7use crate::methods::type_cacher_visit_generalization_alt_i::cacher_traverse_type_id;
8use crate::records::builtin_types::BuiltinTypes;
9use crate::records::free_type_searcher::FreeTypeSearcher;
10use crate::records::function_type::FunctionType;
11use crate::records::generalization_params::GeneralizationParams;
12use crate::records::generalization_result::GeneralizationResult;
13use crate::records::scope::Scope;
14use crate::records::type_arena::TypeArena;
15use crate::records::type_cacher::TypeCacher;
16use crate::type_aliases::type_id::TypeId;
17use crate::type_aliases::type_pack_id::TypePackId;
18use luaur_common::records::dense_hash_set::DenseHashSet;
19
20pub fn generalize(
21    arena: *mut TypeArena,
22    builtin_types: *mut BuiltinTypes,
23    scope: *mut Scope,
24    cached_types: *mut DenseHashSet<TypeId>,
25    ty: TypeId,
26    generalization_target: Option<TypeId>,
27) -> Option<TypeId> {
28    let ty = unsafe { follow_type_id(ty) };
29
30    let ty_ptr = unsafe { &*ty };
31    if ty_ptr.owning_arena != arena || ty_ptr.persistent {
32        return Some(ty);
33    }
34
35    let mut fts = FreeTypeSearcher::free_type_searcher(scope, cached_types);
36    searcher_traverse_type_id(&mut fts, ty);
37
38    let function_ty =
39        unsafe { crate::functions::get_mutable_type::get_mutable_type_id::<FunctionType>(ty) };
40    let mut push_generic = |t: TypeId| {
41        if !function_ty.is_null() {
42            unsafe { (*function_ty).generics.push(t) };
43        }
44    };
45
46    let mut push_generic_pack = |tp: TypePackId| {
47        if !function_ty.is_null() {
48            unsafe { (*function_ty).generic_packs.push(tp) };
49        }
50    };
51
52    for (free_ty, params) in &fts.types {
53        if !generalization_target.is_some() || *free_ty == generalization_target.unwrap() {
54            let res: GeneralizationResult =
55                generalize_type(arena, builtin_types, scope, *free_ty, &params);
56
57            if res.resource_limits_exceeded {
58                return None;
59            }
60
61            if res.was_replaced_by_generic && res.result.is_some() {
62                push_generic(res.result.unwrap());
63            }
64        }
65    }
66
67    let unsealed_tables_iter = fts.unsealed_tables.iter();
68    for unsealed_table_ty in unsealed_tables_iter {
69        if !generalization_target.is_some() || *unsealed_table_ty == generalization_target.unwrap()
70        {
71            seal_table(scope, *unsealed_table_ty);
72        }
73    }
74
75    for (free_pack_id, params) in &fts.type_packs {
76        let free_pack =
77            unsafe { crate::functions::follow_type_pack::follow_type_pack_id(*free_pack_id) };
78        if generalization_target.is_none() {
79            let generalized_tp: GeneralizationResult =
80                generalize_type_pack(arena, builtin_types, scope, free_pack, &params);
81
82            if generalized_tp.resource_limits_exceeded {
83                return None;
84            }
85
86            if generalized_tp.was_replaced_by_generic && generalized_tp.result.is_some() {
87                push_generic_pack(free_pack);
88            }
89        }
90    }
91
92    let mut cacher = TypeCacher::type_cacher(cached_types);
93    cacher_traverse_type_id(&mut cacher, ty);
94
95    Some(ty)
96}