tsz_solver/relations/
subtype_helpers.rs1use crate::relations::subtype::{
8 AnyPropagationMode, INTERSECTION_OBJECT_FAST_PATH_THRESHOLD, SubtypeChecker, SubtypeResult,
9};
10use crate::type_resolver::TypeResolver;
11use crate::types::{ObjectFlags, ObjectShape, RelationCacheKey, TypeId, Visibility};
12use crate::visitor::{
13 callable_shape_id, function_shape_id, index_access_parts, literal_string, object_shape_id,
14 object_with_index_shape_id, type_param_info, union_list_id,
15};
16
17impl<'a, R: TypeResolver> SubtypeChecker<'a, R> {
18 pub(crate) fn can_use_object_intersection_fast_path(&self, members: &[TypeId]) -> bool {
19 if members.len() < INTERSECTION_OBJECT_FAST_PATH_THRESHOLD {
20 return false;
21 }
22
23 for &member in members {
24 let resolved = self.resolve_ref_type(member);
25
26 if callable_shape_id(self.interner, resolved).is_some()
29 || function_shape_id(self.interner, resolved).is_some()
30 {
31 return false;
32 }
33
34 let Some(shape_id) = object_shape_id(self.interner, resolved)
35 .or_else(|| object_with_index_shape_id(self.interner, resolved))
36 else {
37 return false;
38 };
39
40 let shape = self.interner.object_shape(shape_id);
41 if !shape.flags.is_empty() {
42 return false;
43 }
44 if shape
45 .properties
46 .iter()
47 .any(|prop| prop.visibility != Visibility::Public)
48 {
49 return false;
50 }
51 }
52
53 true
54 }
55
56 pub(crate) fn build_object_intersection_target(
57 &self,
58 target_intersection: TypeId,
59 ) -> Option<TypeId> {
60 use crate::objects::{PropertyCollectionResult, collect_properties};
61
62 match collect_properties(target_intersection, self.interner, self.resolver) {
63 PropertyCollectionResult::Properties {
64 properties,
65 string_index,
66 number_index,
67 } => {
68 let shape = ObjectShape {
69 flags: ObjectFlags::empty(),
70 properties,
71 string_index,
72 number_index,
73 symbol: None,
74 };
75
76 if shape.string_index.is_some() || shape.number_index.is_some() {
77 Some(self.interner.object_with_index(shape))
78 } else {
79 Some(self.interner.object(shape.properties))
80 }
81 }
82 PropertyCollectionResult::Any => Some(TypeId::ANY),
83 PropertyCollectionResult::NonObject => None,
84 }
85 }
86
87 pub(crate) const fn make_cache_key(&self, source: TypeId, target: TypeId) -> RelationCacheKey {
95 let mut flags: u16 = 0;
96 if self.strict_null_checks {
97 flags |= RelationCacheKey::FLAG_STRICT_NULL_CHECKS;
98 }
99 if self.strict_function_types {
100 flags |= RelationCacheKey::FLAG_STRICT_FUNCTION_TYPES;
101 }
102 if self.exact_optional_property_types {
103 flags |= RelationCacheKey::FLAG_EXACT_OPTIONAL_PROPERTY_TYPES;
104 }
105 if self.no_unchecked_indexed_access {
106 flags |= RelationCacheKey::FLAG_NO_UNCHECKED_INDEXED_ACCESS;
107 }
108 if self.disable_method_bivariance {
109 flags |= RelationCacheKey::FLAG_DISABLE_METHOD_BIVARIANCE;
110 }
111 if self.allow_void_return {
112 flags |= RelationCacheKey::FLAG_ALLOW_VOID_RETURN;
113 }
114 if self.allow_bivariant_rest {
115 flags |= RelationCacheKey::FLAG_ALLOW_BIVARIANT_REST;
116 }
117 if self.allow_bivariant_param_count {
118 flags |= RelationCacheKey::FLAG_ALLOW_BIVARIANT_PARAM_COUNT;
119 }
120
121 let any_mode = match self.any_propagation {
125 AnyPropagationMode::All => 0,
126 AnyPropagationMode::TopLevelOnly if self.guard.depth() == 0 => 1,
127 AnyPropagationMode::TopLevelOnly => 2, };
129
130 RelationCacheKey::subtype(source, target, flags, any_mode)
131 }
132
133 pub fn is_subtype_of(&mut self, source: TypeId, target: TypeId) -> bool {
141 self.check_subtype(source, target).is_true()
142 }
143
144 pub fn is_assignable_to(&mut self, source: TypeId, target: TypeId) -> bool {
147 self.is_subtype_of(source, target)
148 }
149
150 pub(crate) fn check_object_contract(
170 &mut self,
171 source: TypeId,
172 target: TypeId,
173 ) -> SubtypeResult {
174 use crate::visitor::{object_shape_id, object_with_index_shape_id};
175
176 let source_eval = self.evaluate_type(source);
178 let s_shape_id = match object_shape_id(self.interner, source_eval)
179 .or_else(|| object_with_index_shape_id(self.interner, source_eval))
180 {
181 Some(id) => id,
182 None => return SubtypeResult::True,
183 };
184 let s_shape = self.interner.object_shape(s_shape_id);
185
186 let target_eval = self.evaluate_type(target);
188 let t_shape_id = match object_shape_id(self.interner, target_eval)
189 .or_else(|| object_with_index_shape_id(self.interner, target_eval))
190 {
191 Some(id) => id,
192 None => return SubtypeResult::True, };
194 let t_shape = self.interner.object_shape(t_shape_id);
195
196 for s_prop in &s_shape.properties {
198 if let Some(t_prop) =
200 self.lookup_property(&t_shape.properties, Some(t_shape_id), s_prop.name)
201 {
202 let result = self.check_property_compatibility(s_prop, t_prop);
204 if !result.is_true() {
205 return result;
206 }
207 }
208 }
209
210 SubtypeResult::True
211 }
212
213 pub(crate) fn check_generic_index_access_subtype(
220 &mut self,
221 source: TypeId,
222 target: TypeId,
223 ) -> bool {
224 let Some((t_obj, t_idx)) = index_access_parts(self.interner, target) else {
225 return false;
226 };
227
228 let Some(t_param) = type_param_info(self.interner, t_idx) else {
230 return false;
231 };
232
233 let Some(constraint) = t_param.constraint else {
234 return false;
235 };
236
237 let constraint = self.evaluate_type(constraint);
239
240 let mut literals = Vec::new();
243
244 if let Some(s) = literal_string(self.interner, constraint) {
245 literals.push(self.interner.literal_string_atom(s));
246 } else if let Some(union_id) = union_list_id(self.interner, constraint) {
247 let members = self.interner.type_list(union_id);
248 for &m in members.iter() {
249 if let Some(s) = literal_string(self.interner, m) {
250 literals.push(self.interner.literal_string_atom(s));
251 } else {
252 return false;
255 }
256 }
257 } else {
258 return false;
260 }
261
262 if literals.is_empty() {
263 return false;
264 }
265
266 for lit_type in literals {
268 let indexed_access = self.interner.index_access(t_obj, lit_type);
272 let evaluated = self.evaluate_type(indexed_access);
273
274 if !self.check_subtype(source, evaluated).is_true() {
275 return false;
276 }
277 }
278
279 true
280 }
281}