Skip to main content

luaur_analysis/methods/
subtyping_bind_generic.rs

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}