luaur_analysis/functions/
generalize_type.rs1use 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_known::is_known;
5use crate::functions::is_positive::is_positive;
6use crate::functions::remove_type::remove_type;
7use crate::records::builtin_types::BuiltinTypes;
8use crate::records::free_type::FreeType;
9use crate::records::generalization_params::GeneralizationParams;
10use crate::records::generalization_result::GeneralizationResult;
11use crate::records::generic_type::GenericType;
12use crate::records::intersection_type::IntersectionType;
13use crate::records::never_type::NeverType;
14use crate::records::r#type::Type;
15use crate::records::scope::Scope;
16use crate::records::type_arena::TypeArena;
17use crate::records::unknown_type::UnknownType;
18use crate::type_aliases::type_id::TypeId;
19use crate::type_aliases::type_variant::TypeVariant;
20use luaur_common::macros::luau_assert::LUAU_ASSERT;
21
22pub fn generalize_type(
26 arena: *mut TypeArena,
27 builtin_types: *mut BuiltinTypes,
28 scope: *mut Scope,
29 free_ty: TypeId,
30 params: &GeneralizationParams,
31) -> GeneralizationResult {
32 let free_ty = unsafe { follow_type_id(free_ty) };
33
34 let ft = unsafe { get_mutable_type_id::<FreeType>(free_ty) };
35 LUAU_ASSERT!(!ft.is_null());
36
37 LUAU_ASSERT!(is_known(params.polarity));
38
39 let has_lower_bound =
40 unsafe { get_type_id::<NeverType>(follow_type_id((*ft).lower_bound)) }.is_null();
41 let has_upper_bound =
42 unsafe { get_type_id::<UnknownType>(follow_type_id((*ft).upper_bound)) }.is_null();
43
44 let is_within_function = !params.found_outside_functions;
45
46 if !has_lower_bound && !has_upper_bound {
47 if !is_within_function {
48 emplace_bound(free_ty, unsafe { (*builtin_types).unknownType });
49 } else {
50 emplace_generic(free_ty, scope, params.polarity);
51 return result_generic(free_ty);
52 }
53 }
54 else if is_positive(params.polarity) && !has_upper_bound {
58 let lb = unsafe { follow_type_id((*ft).lower_bound) };
59 let lower_free = unsafe { get_mutable_type_id::<FreeType>(lb) };
60 if !lower_free.is_null() && unsafe { (*lower_free).upper_bound } == free_ty {
61 let upper_bound = unsafe { follow_type_id((*ft).upper_bound) };
64 remove_type(arena, builtin_types, upper_bound, free_ty);
65 unsafe { (*lower_free).upper_bound = follow_type_id(upper_bound) };
66 } else {
67 remove_type(arena, builtin_types, lb, free_ty);
68 }
69
70 if unsafe { follow_type_id(lb) } != free_ty {
71 emplace_bound(free_ty, lb);
72 } else if !is_within_function {
73 emplace_bound(free_ty, unsafe { (*builtin_types).unknownType });
74 } else {
75 emplace_generic(free_ty, scope, params.polarity);
78 return result_generic(free_ty);
79 }
80 } else {
81 let ub = unsafe { follow_type_id((*ft).upper_bound) };
82 let upper_free = unsafe { get_mutable_type_id::<FreeType>(ub) };
83 if !upper_free.is_null() && unsafe { (*upper_free).lower_bound } == free_ty {
84 let lower_bound = unsafe { follow_type_id((*ft).lower_bound) };
87 remove_type(arena, builtin_types, lower_bound, free_ty);
88 unsafe { (*upper_free).lower_bound = follow_type_id(lower_bound) };
89 } else {
90 remove_type(arena, builtin_types, ub, free_ty);
91 }
92
93 if unsafe { follow_type_id(ub) } != free_ty {
94 emplace_bound(free_ty, ub);
95 } else if !is_within_function || params.use_count == 1 {
96 let lower_bound = unsafe { (*ft).lower_bound };
100 remove_type(arena, builtin_types, lower_bound, free_ty);
101 let cleaned_ty = unsafe {
102 (*arena).add_type(IntersectionType {
103 parts: alloc::vec![(*ft).lower_bound, ub],
104 })
105 };
106 remove_type(arena, builtin_types, cleaned_ty, free_ty);
107 emplace_bound(free_ty, cleaned_ty);
108 } else {
109 emplace_generic(free_ty, scope, params.polarity);
112 return result_generic(free_ty);
113 }
114 }
115
116 GeneralizationResult {
117 result: Some(free_ty),
118 was_replaced_by_generic: false,
119 resource_limits_exceeded: false,
120 }
121}
122
123fn emplace_bound(ty: TypeId, bound_to: TypeId) {
125 unsafe {
126 (*(ty as *mut Type)).ty = TypeVariant::Bound(bound_to);
127 }
128}
129
130fn emplace_generic(ty: TypeId, scope: *mut Scope, polarity: crate::enums::polarity::Polarity) {
132 unsafe {
133 (*(ty as *mut Type)).ty =
134 TypeVariant::Generic(GenericType::generic_type_scope_polarity(scope, polarity));
135 }
136}
137
138#[inline]
139fn result_generic(free_ty: TypeId) -> GeneralizationResult {
140 GeneralizationResult {
141 result: Some(free_ty),
142 was_replaced_by_generic: true,
143 resource_limits_exceeded: false,
144 }
145}