Skip to main content

luaur_analysis/functions/
relate_simplify.rs

1use crate::enums::relation::Relation;
2use crate::functions::flip::flip;
3use crate::functions::follow_type::follow_type_id;
4use crate::functions::get_type_alt_j::get_type_id;
5use crate::functions::is_subclass_normalize::is_subclass_type_id_type_id;
6use crate::functions::is_type_variable::is_type_variable;
7use crate::functions::relate_table_to_extern_type::relate_table_to_extern_type;
8use crate::functions::relate_tables::relate_tables;
9use crate::records::any_type::AnyType;
10use crate::records::boolean_singleton::BooleanSingleton;
11use crate::records::extern_type::ExternType;
12use crate::records::function_type::FunctionType;
13use crate::records::intersection_type::IntersectionType;
14use crate::records::metatable_type::MetatableType;
15use crate::records::negation_type::NegationType;
16use crate::records::never_type::NeverType;
17use crate::records::primitive_type::PrimitiveType;
18use crate::records::singleton_type::SingletonType;
19use crate::records::string_singleton::StringSingleton;
20use crate::records::table_type::TableType;
21use crate::records::type_function_instance_type::TypeFunctionInstanceType;
22use crate::records::union_type::UnionType;
23use crate::records::unknown_type::UnknownType;
24use crate::type_aliases::error_type::ErrorType;
25use crate::type_aliases::simplifier_seen_set::SimplifierSeenSet;
26use crate::type_aliases::type_id::TypeId;
27
28// A cheap and approximate subtype test
29pub fn relate(left: TypeId, right: TypeId, seen: &mut SimplifierSeenSet) -> Relation {
30    // TODO nice to have: Relate functions of equal argument and return arity
31
32    let left = unsafe { follow_type_id(left) };
33    let right = unsafe { follow_type_id(right) };
34
35    if left == right {
36        return Relation::Coincident;
37    }
38
39    let type_pair = (left, right);
40    if !seen.try_insert(type_pair, true).1 {
41        // TODO: is this right at all?
42        // The thinking here is that this is a cycle if we get here, and therefore its coincident.
43        return Relation::Coincident;
44    }
45
46    unsafe {
47        if !get_type_id::<UnknownType>(left).is_null() {
48            if !get_type_id::<AnyType>(right).is_null() {
49                return Relation::Subset;
50            }
51
52            if !get_type_id::<UnknownType>(right).is_null() {
53                return Relation::Coincident;
54            }
55
56            if !get_type_id::<ErrorType>(right).is_null() {
57                return Relation::Disjoint;
58            }
59
60            return Relation::Superset;
61        }
62
63        if !get_type_id::<UnknownType>(right).is_null() {
64            return flip(relate(right, left, seen));
65        }
66
67        if !get_type_id::<AnyType>(left).is_null() {
68            if !get_type_id::<AnyType>(right).is_null() {
69                return Relation::Coincident;
70            }
71
72            return Relation::Superset;
73        }
74
75        if !get_type_id::<AnyType>(right).is_null() {
76            return flip(relate(right, left, seen));
77        }
78
79        // Type variables
80        // * FreeType
81        // * GenericType
82        // * BlockedType
83        // * PendingExpansionType
84
85        // Tops and bottoms
86        // * ErrorType
87        // * AnyType
88        // * NeverType
89        // * UnknownType
90
91        // Concrete
92        // * PrimitiveType
93        // * SingletonType
94        // * FunctionType
95        // * TableType
96        // * MetatableType
97        // * ExternType
98        // * UnionType
99        // * IntersectionType
100        // * NegationType
101
102        if is_type_variable(left) || is_type_variable(right) {
103            return Relation::Intersects;
104        }
105
106        // if either type is a type function, we cannot know if they'll be related.
107        if !get_type_id::<TypeFunctionInstanceType>(left).is_null()
108            || !get_type_id::<TypeFunctionInstanceType>(right).is_null()
109        {
110            return Relation::Intersects;
111        }
112
113        if !get_type_id::<ErrorType>(left).is_null() {
114            if !get_type_id::<ErrorType>(right).is_null() {
115                return Relation::Coincident;
116            } else if !get_type_id::<AnyType>(right).is_null() {
117                return Relation::Subset;
118            }
119
120            return Relation::Disjoint;
121        } else if !get_type_id::<ErrorType>(right).is_null() {
122            return flip(relate(right, left, seen));
123        }
124
125        if !get_type_id::<NeverType>(left).is_null() {
126            if !get_type_id::<NeverType>(right).is_null() {
127                return Relation::Coincident;
128            }
129
130            return Relation::Subset;
131        } else if !get_type_id::<NeverType>(right).is_null() {
132            return flip(relate(right, left, seen));
133        }
134
135        if !get_type_id::<IntersectionType>(left).is_null() {
136            return Relation::Intersects;
137        } else if !get_type_id::<IntersectionType>(right).is_null() {
138            return Relation::Intersects;
139        }
140
141        if let Some(ut) = get_type_id::<UnionType>(left).as_ref() {
142            for &part in &ut.options {
143                let r = relate(part, right, seen);
144                if r == Relation::Superset || r == Relation::Coincident {
145                    return Relation::Superset;
146                }
147            }
148            return Relation::Intersects;
149        } else if let Some(ut) = get_type_id::<UnionType>(right).as_ref() {
150            for &part in &ut.options {
151                let r = relate(left, part, seen);
152                if r == Relation::Subset || r == Relation::Coincident {
153                    return Relation::Subset;
154                }
155            }
156            return Relation::Intersects;
157        }
158
159        if let Some(rnt) = get_type_id::<NegationType>(right).as_ref() {
160            let a = relate(left, rnt.ty, seen);
161            match a {
162                Relation::Coincident => {
163                    // number & ~number
164                    return Relation::Disjoint;
165                }
166                Relation::Disjoint => {
167                    if !get_type_id::<NegationType>(left).is_null() {
168                        // ~number & ~string
169                        return Relation::Intersects;
170                    } else {
171                        // number & ~string
172                        return Relation::Subset;
173                    }
174                }
175                Relation::Intersects => {
176                    // ~(false?) & ~boolean
177                    return Relation::Intersects;
178                }
179                Relation::Subset => {
180                    // "hello" & ~string
181                    return Relation::Disjoint;
182                }
183                Relation::Superset => {
184                    // ~function & ~(false?)  -> ~function
185                    // boolean & ~(false?)    -> true
186                    // string & ~"hello"      -> string & ~"hello"
187                    return Relation::Intersects;
188                }
189            }
190        } else if !get_type_id::<NegationType>(left).is_null() {
191            return flip(relate(right, left, seen));
192        }
193
194        if let Some(lp) = get_type_id::<PrimitiveType>(left).as_ref() {
195            if let Some(rp) = get_type_id::<PrimitiveType>(right).as_ref() {
196                if lp.r#type == rp.r#type {
197                    return Relation::Coincident;
198                }
199
200                return Relation::Disjoint;
201            }
202
203            if let Some(rs) = get_type_id::<SingletonType>(right).as_ref() {
204                if lp.r#type == PrimitiveType::String
205                    && rs.variant.get_if::<StringSingleton>().is_some()
206                {
207                    return Relation::Superset;
208                }
209
210                if lp.r#type == PrimitiveType::Boolean
211                    && rs.variant.get_if::<BooleanSingleton>().is_some()
212                {
213                    return Relation::Superset;
214                }
215
216                return Relation::Disjoint;
217            }
218
219            if lp.r#type == PrimitiveType::Function {
220                if !get_type_id::<FunctionType>(right).is_null() {
221                    return Relation::Superset;
222                }
223
224                return Relation::Disjoint;
225            }
226            if lp.r#type == PrimitiveType::Table {
227                if !get_type_id::<TableType>(right).is_null() {
228                    return Relation::Superset;
229                }
230
231                return Relation::Disjoint;
232            }
233
234            if !get_type_id::<FunctionType>(right).is_null()
235                || !get_type_id::<TableType>(right).is_null()
236                || !get_type_id::<MetatableType>(right).is_null()
237                || !get_type_id::<ExternType>(right).is_null()
238            {
239                return Relation::Disjoint;
240            }
241        }
242
243        if let Some(ls) = get_type_id::<SingletonType>(left).as_ref() {
244            if !get_type_id::<FunctionType>(right).is_null()
245                || !get_type_id::<TableType>(right).is_null()
246                || !get_type_id::<MetatableType>(right).is_null()
247                || !get_type_id::<ExternType>(right).is_null()
248            {
249                return Relation::Disjoint;
250            }
251
252            if !get_type_id::<PrimitiveType>(right).is_null() {
253                return flip(relate(right, left, seen));
254            }
255
256            if let Some(rs) = get_type_id::<SingletonType>(right).as_ref() {
257                if ls.variant == rs.variant {
258                    return Relation::Coincident;
259                }
260
261                return Relation::Disjoint;
262            }
263        }
264
265        if !get_type_id::<FunctionType>(left).is_null() {
266            if let Some(rp) = get_type_id::<PrimitiveType>(right).as_ref() {
267                if rp.r#type == PrimitiveType::Function {
268                    return Relation::Subset;
269                }
270
271                return Relation::Disjoint;
272            }
273
274            return Relation::Intersects;
275        }
276
277        if let Some(lt) = get_type_id::<TableType>(left).as_ref() {
278            if let Some(rp) = get_type_id::<PrimitiveType>(right).as_ref() {
279                if rp.r#type == PrimitiveType::Table {
280                    return Relation::Subset;
281                }
282
283                return Relation::Disjoint;
284            }
285
286            if let Some(rt) = get_type_id::<TableType>(right).as_ref() {
287                return relate_tables(lt, rt, seen);
288            }
289
290            if let Some(re) = get_type_id::<ExternType>(right).as_ref() {
291                return relate_table_to_extern_type(lt, re, seen);
292            }
293
294            // TODO metatables
295
296            return Relation::Disjoint;
297        }
298
299        if let Some(ct) = get_type_id::<ExternType>(left).as_ref() {
300            if get_type_id::<ExternType>(right).as_ref().is_some() {
301                if is_subclass_type_id_type_id(left, right) {
302                    return Relation::Subset;
303                }
304
305                if is_subclass_type_id_type_id(right, left) {
306                    return Relation::Superset;
307                }
308
309                return Relation::Disjoint;
310            }
311
312            if let Some(tbl) = get_type_id::<TableType>(right).as_ref() {
313                return flip(relate_table_to_extern_type(tbl, ct, seen));
314            }
315
316            return Relation::Disjoint;
317        }
318    }
319
320    Relation::Intersects
321}