1use crate::functions::follow_type::follow_type_id;
2use crate::functions::get_type_alt_j::get_type_id;
3use crate::records::generic_bounds::GenericBounds;
4use crate::records::generic_type::GenericType;
5use crate::records::subtyping::Subtyping;
6use crate::records::subtyping_environment::SubtypingEnvironment;
7use crate::records::type_ids::TypeIds;
8use crate::type_aliases::type_id::TypeId;
9use core::marker::PhantomData;
10use luaur_common::macros::luau_assert::LUAU_ASSERT;
11use luaur_common::records::dense_hash_map::DenseHashMap;
12use luaur_common::records::dense_hash_table::{
13 DenseDefault, DenseEq, DenseHashTable, DenseHasher, ItemInterface,
14};
15
16impl Default for GenericBounds {
17 fn default() -> Self {
18 GenericBounds {
19 lower_bound: TypeIds::type_ids(),
20 upper_bound: TypeIds::type_ids(),
21 }
22 }
23}
24
25impl DenseDefault for GenericBounds {
26 fn dense_default() -> Self {
27 GenericBounds::default()
28 }
29}
30
31struct ItemInterfaceMapNoDefault<K, V>(PhantomData<(K, V)>);
32
33impl<K: Clone, V> ItemInterface<K, (K, V)> for ItemInterfaceMapNoDefault<K, V> {
34 fn get_key(item: &(K, V)) -> &K {
35 &item.0
36 }
37
38 fn set_key(item: &mut (K, V), key: K) {
39 item.0 = key;
40 }
41
42 fn make_empty(_empty_key: &K) -> (K, V) {
43 unreachable!("find does not construct empty DenseHashMap entries")
44 }
45}
46
47type DenseHashMapTable<K, V, H, E> =
48 DenseHashTable<K, (K, V), ItemInterfaceMapNoDefault<K, V>, H, E>;
49
50pub(crate) fn dense_hash_map_find_no_default<'a, K, V, H, E>(
51 map: &'a DenseHashMap<K, V, H, E>,
52 key: &K,
53) -> Option<&'a V>
54where
55 K: Clone,
56 H: DenseHasher<K> + Default,
57 E: DenseEq<K> + Default,
58{
59 let table = unsafe {
60 &*(map as *const DenseHashMap<K, V, H, E> as *const DenseHashMapTable<K, V, H, E>)
61 };
62 let item = luaur_common::methods::dense_hash_table_find::dense_hash_table_find(table, key);
63 if item.is_null() {
64 None
65 } else {
66 Some(unsafe { &(*item).1 })
67 }
68}
69
70pub(crate) fn dense_hash_map_find_mut_no_default<'a, K, V, H, E>(
71 map: &'a mut DenseHashMap<K, V, H, E>,
72 key: &K,
73) -> Option<&'a mut V>
74where
75 K: Clone,
76 H: DenseHasher<K> + Default,
77 E: DenseEq<K> + Default,
78{
79 let table = unsafe {
80 &*(map as *const DenseHashMap<K, V, H, E> as *const DenseHashMapTable<K, V, H, E>)
81 };
82 let item = luaur_common::methods::dense_hash_table_find::dense_hash_table_find(table, key);
83 if item.is_null() {
84 None
85 } else {
86 Some(unsafe { &mut (*(item as *mut (K, V))).1 })
87 }
88}
89
90impl Subtyping {
91 pub fn bind_generic(
92 &mut self,
93 env: &mut SubtypingEnvironment,
94 sub_ty: TypeId,
95 super_ty: TypeId,
96 ) -> bool {
97 let sub_ty = unsafe { follow_type_id(sub_ty) };
98 let super_ty = unsafe { follow_type_id(super_ty) };
99 let mut original_sub_ty_bounds: Option<GenericBounds> = None;
100
101 let super_bounds_snapshot = dense_hash_map_find_no_default(&env.mapped_generics, &super_ty)
102 .and_then(|bounds| bounds.last().cloned());
103
104 let sub_has_local_bounds = dense_hash_map_find_no_default(&env.mapped_generics, &sub_ty)
105 .map_or(false, |bounds| !bounds.is_empty());
106
107 if sub_has_local_bounds {
108 LUAU_ASSERT!(!unsafe { get_type_id::<GenericType>(sub_ty) }.is_null());
109
110 let sub_bounds =
111 dense_hash_map_find_mut_no_default(&mut env.mapped_generics, &sub_ty).unwrap();
112 let sub_bounds_back = sub_bounds.last_mut().unwrap();
113 original_sub_ty_bounds = Some(sub_bounds_back.clone());
114
115 let upper_sub_bounds = &mut sub_bounds_back.upper_bound;
116
117 if let Some(super_bounds) = &super_bounds_snapshot {
118 LUAU_ASSERT!(!unsafe { get_type_id::<GenericType>(super_ty) }.is_null());
119
120 self.maybe_update_bounds(
121 sub_ty,
122 super_ty,
123 upper_sub_bounds,
124 &super_bounds.lower_bound,
125 &super_bounds.upper_bound,
126 );
127 } else {
128 upper_sub_bounds.insert_type_id(super_ty);
129 }
130 } else if env.contains_mapped_type(sub_ty) {
131 unsafe {
132 (*self.ice_reporter)
133 .ice_string("attempting to modify bounds of a potentially visited generic");
134 }
135 }
136
137 let super_has_local_bounds =
138 dense_hash_map_find_no_default(&env.mapped_generics, &super_ty)
139 .map_or(false, |bounds| !bounds.is_empty());
140
141 if super_has_local_bounds {
142 LUAU_ASSERT!(!unsafe { get_type_id::<GenericType>(super_ty) }.is_null());
143
144 let super_bounds =
145 dense_hash_map_find_mut_no_default(&mut env.mapped_generics, &super_ty).unwrap();
146 let super_bounds_back = super_bounds.last_mut().unwrap();
147 let lower_super_bounds = &mut super_bounds_back.lower_bound;
148
149 if let Some(original_sub_ty_bounds) = original_sub_ty_bounds {
150 LUAU_ASSERT!(!unsafe { get_type_id::<GenericType>(sub_ty) }.is_null());
151
152 self.maybe_update_bounds(
153 super_ty,
154 sub_ty,
155 lower_super_bounds,
156 &original_sub_ty_bounds.upper_bound,
157 &original_sub_ty_bounds.lower_bound,
158 );
159 } else {
160 lower_super_bounds.insert_type_id(sub_ty);
161 }
162 } else if env.contains_mapped_type(super_ty) {
163 unsafe {
164 (*self.ice_reporter)
165 .ice_string("attempting to modify bounds of a potentially visited generic");
166 }
167 }
168
169 true
170 }
171}