luaur_analysis/functions/
generalize.rs1use 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, ¶ms);
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, ¶ms);
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}