Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Analysis/src/Simplify.cpp
2725 views
1
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2
3
#include "Luau/Simplify.h"
4
5
#include "Luau/BuiltinDefinitions.h"
6
#include "Luau/Clone.h"
7
#include "Luau/Common.h"
8
#include "Luau/DenseHash.h"
9
#include "Luau/RecursionCounter.h"
10
#include "Luau/Set.h"
11
#include "Luau/Type.h"
12
#include "Luau/TypeArena.h"
13
#include "Luau/TypeIds.h"
14
#include "Luau/TypePairHash.h"
15
#include "Luau/TypeUtils.h"
16
17
#include <algorithm>
18
19
LUAU_FASTINT(LuauTypeReductionRecursionLimit)
20
LUAU_FASTFLAG(LuauSolverV2)
21
LUAU_DYNAMIC_FASTINTVARIABLE(LuauSimplificationComplexityLimit, 8)
22
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeSimplificationIterationLimit, 128)
23
LUAU_FASTFLAGVARIABLE(LuauRelateHandlesCoincidentTables)
24
25
namespace Luau
26
{
27
28
using SimplifierSeenSet = Set<std::pair<TypeId, TypeId>, TypePairHash>;
29
30
struct TypeSimplifier
31
{
32
NotNull<BuiltinTypes> builtinTypes;
33
NotNull<TypeArena> arena;
34
35
DenseHashSet<TypeId> blockedTypes{nullptr};
36
37
int recursionDepth = 0;
38
39
TypeId mkNegation(TypeId ty) const;
40
41
TypeId intersectFromParts(TypeIds parts);
42
43
TypeId intersectUnionWithType(TypeId left, TypeId right);
44
45
TypeId intersectUnions(TypeId left, TypeId right);
46
47
TypeId intersectNegatedUnion(TypeId left, TypeId right);
48
49
TypeId intersectTypeWithNegation(TypeId left, TypeId right);
50
51
TypeId intersectNegations(TypeId left, TypeId right);
52
53
TypeId intersectIntersectionWithType(TypeId left, TypeId right);
54
55
// Attempt to intersect the two types. Does not recurse. Does not handle
56
// unions, intersections, or negations.
57
std::optional<TypeId> basicIntersect(TypeId left, TypeId right);
58
59
std::optional<TypeId> basicIntersectWithTruthy(TypeId target) const;
60
61
std::optional<TypeId> basicIntersectWithFalsy(TypeId target) const;
62
63
TypeId intersect(TypeId left, TypeId right);
64
65
TypeId union_(TypeId left, TypeId right);
66
67
TypeId simplify(TypeId ty);
68
69
TypeId simplify(TypeId ty, DenseHashSet<TypeId>& seen);
70
71
std::optional<TypeId> intersectOne(TypeId target, TypeId discriminant) const;
72
73
std::optional<TypeId> subtractOne(TypeId target, TypeId discriminant) const;
74
75
std::optional<Property> intersectProperty(const Property& target, const Property& discriminant, DenseHashSet<TypeId>& seen) const;
76
77
std::optional<TypeId> intersectWithSimpleDiscriminant(TypeId target, TypeId discriminant, DenseHashSet<TypeId>& seen) const;
78
79
std::optional<TypeId> intersectWithSimpleDiscriminant(TypeId target, TypeId discriminant) const;
80
};
81
82
// Match the exact type false|nil
83
static bool isFalsyType_DEPRECATED(TypeId ty)
84
{
85
ty = follow(ty);
86
const UnionType* ut = get<UnionType>(ty);
87
if (!ut)
88
return false;
89
90
bool hasFalse = false;
91
bool hasNil = false;
92
93
auto it = begin(ut);
94
if (it == end(ut))
95
return false;
96
97
TypeId t = follow(*it);
98
99
if (auto pt = get<PrimitiveType>(t); pt && pt->type == PrimitiveType::NilType)
100
hasNil = true;
101
else if (auto st = get<SingletonType>(t); st && st->variant == BooleanSingleton{false})
102
hasFalse = true;
103
else
104
return false;
105
106
++it;
107
if (it == end(ut))
108
return false;
109
110
t = follow(*it);
111
112
if (auto pt = get<PrimitiveType>(t); pt && pt->type == PrimitiveType::NilType)
113
hasNil = true;
114
else if (auto st = get<SingletonType>(t); st && st->variant == BooleanSingleton{false})
115
hasFalse = true;
116
else
117
return false;
118
119
++it;
120
if (it != end(ut))
121
return false;
122
123
return hasFalse && hasNil;
124
}
125
126
// Match the exact type ~(false|nil)
127
bool isTruthyType_DEPRECATED(TypeId ty)
128
{
129
ty = follow(ty);
130
131
const NegationType* nt = get<NegationType>(ty);
132
if (!nt)
133
return false;
134
135
return isFalsyType_DEPRECATED(nt->ty);
136
}
137
138
Relation flip(Relation rel)
139
{
140
switch (rel)
141
{
142
case Relation::Subset:
143
return Relation::Superset;
144
case Relation::Superset:
145
return Relation::Subset;
146
default:
147
return rel;
148
}
149
}
150
151
// FIXME: I'm not completely certain that this function is theoretically reasonable.
152
Relation combine(Relation a, Relation b)
153
{
154
switch (a)
155
{
156
case Relation::Disjoint:
157
switch (b)
158
{
159
case Relation::Disjoint:
160
return Relation::Disjoint;
161
case Relation::Coincident:
162
return Relation::Superset;
163
case Relation::Intersects:
164
return Relation::Intersects;
165
case Relation::Subset:
166
return Relation::Intersects;
167
case Relation::Superset:
168
return Relation::Intersects;
169
}
170
break;
171
case Relation::Coincident:
172
switch (b)
173
{
174
case Relation::Disjoint:
175
return Relation::Coincident;
176
case Relation::Coincident:
177
return Relation::Coincident;
178
case Relation::Intersects:
179
return Relation::Superset;
180
case Relation::Subset:
181
return Relation::Coincident;
182
case Relation::Superset:
183
return Relation::Intersects;
184
}
185
break;
186
case Relation::Superset:
187
switch (b)
188
{
189
case Relation::Disjoint:
190
return Relation::Superset;
191
case Relation::Coincident:
192
return Relation::Superset;
193
case Relation::Intersects:
194
return Relation::Intersects;
195
case Relation::Subset:
196
return Relation::Intersects;
197
case Relation::Superset:
198
return Relation::Superset;
199
}
200
break;
201
case Relation::Subset:
202
switch (b)
203
{
204
case Relation::Disjoint:
205
return Relation::Subset;
206
case Relation::Coincident:
207
return Relation::Coincident;
208
case Relation::Intersects:
209
return Relation::Intersects;
210
case Relation::Subset:
211
return Relation::Subset;
212
case Relation::Superset:
213
return Relation::Intersects;
214
}
215
break;
216
case Relation::Intersects:
217
switch (b)
218
{
219
case Relation::Disjoint:
220
return Relation::Intersects;
221
case Relation::Coincident:
222
return Relation::Superset;
223
case Relation::Intersects:
224
return Relation::Intersects;
225
case Relation::Subset:
226
return Relation::Intersects;
227
case Relation::Superset:
228
return Relation::Intersects;
229
}
230
break;
231
}
232
233
LUAU_UNREACHABLE();
234
return Relation::Intersects;
235
}
236
237
// Given A & B, what is A & ~B?
238
Relation invert(Relation r)
239
{
240
switch (r)
241
{
242
case Relation::Disjoint:
243
return Relation::Subset;
244
case Relation::Coincident:
245
return Relation::Disjoint;
246
case Relation::Intersects:
247
return Relation::Intersects;
248
case Relation::Subset:
249
return Relation::Disjoint;
250
case Relation::Superset:
251
return Relation::Intersects;
252
}
253
254
LUAU_UNREACHABLE();
255
return Relation::Intersects;
256
}
257
258
static bool isTypeVariable(TypeId ty)
259
{
260
return get<FreeType>(ty) || get<GenericType>(ty) || get<BlockedType>(ty) || get<PendingExpansionType>(ty);
261
}
262
263
Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen);
264
265
Relation relateTableToExternType(const TableType* table, const ExternType* cls, SimplifierSeenSet& seen)
266
{
267
// If either the table or the extern type have an indexer, just bail.
268
// There's rapidly diminishing returns on doing something smart for
269
// indexers compared to refining exact members.
270
if (table->indexer || cls->indexer)
271
return Relation::Intersects;
272
273
for (auto& [name, prop] : table->props)
274
{
275
if (auto propInExternType = lookupExternTypeProp(cls, name))
276
{
277
LUAU_ASSERT(prop.readTy && propInExternType->readTy);
278
// For all examples, consider:
279
//
280
// declare extern type Foobar with
281
// prop: string | number
282
// end
283
//
284
switch (relate(*prop.readTy, *propInExternType->readTy, seen))
285
{
286
case Relation::Disjoint:
287
// Consider `{ read prop: boolean }` and `Foobar`, these types are
288
// disjoint as `_.prop` would be `never.
289
return Relation::Disjoint;
290
case Relation::Coincident:
291
// Consider `{ read prop: string | number }` and `Foobar`, we don't really
292
// learn anything about these types.
293
break;
294
case Relation::Intersects:
295
// Consider `{ read prop: string | boolean }` and `Foobar`, these types
296
// intersect (imagine a `Foobar` initialized with `prop = "foo"`).
297
return Relation::Intersects;
298
case Relation::Subset:
299
// Consider `{ read prop: string }` and `Foobar`: we should _roughly_
300
// consider this the same as intersecting.
301
return Relation::Intersects;
302
case Relation::Superset:
303
// This is the only mildly interesting case, consider
304
// `{ read prop: string | number | boolean }` and `Foobar`.
305
// We can _probably_ consider `Foobar` the subset here.
306
break;
307
}
308
}
309
}
310
311
// If all the properties of the table were either coincident or
312
// supersets of the extern property, then we claim that the table
313
// is a superset.
314
return Relation::Superset;
315
}
316
317
/**
318
* @return The relationship between the single property on the right and its corresponding property
319
* in the left table.
320
*/
321
Relation relateTableToProp(const TableType* leftTable, const std::string& propName, const Property& rightProp, SimplifierSeenSet& seen)
322
{
323
// If the left table does not have the property at all,
324
// assume an intersection.
325
auto leftProp = leftTable->props.find(propName);
326
if (leftProp == leftTable->props.end())
327
return Relation::Intersects;
328
329
if (leftProp->second.isShared() && rightProp.isShared())
330
{
331
switch (relate(*leftProp->second.readTy, *rightProp.readTy, seen))
332
{
333
case Relation::Disjoint:
334
// The two read properties are disjoint, so the tables are disjoint, e.g.:
335
//
336
// { y: string, x: number? } & { read x: string }
337
//
338
return Relation::Disjoint;
339
case Relation::Coincident:
340
return Relation::Coincident;
341
// For _all_ other cases, two shared properties indicate a non-empty intersection.
342
case Relation::Subset:
343
// If the left property is a subset, intersection implies a widening of
344
// write type of the left property, as in:
345
//
346
// { y: string, x: number } & { x: number? }
347
//
348
return Relation::Intersects;
349
case Relation::Superset:
350
// If the left property is a superset, intersection implies a narrowing
351
// of the read type of the left property, as in:
352
//
353
// { y: string, x: number? } & { x: number }
354
//
355
return Relation::Intersects;
356
case Relation::Intersects:
357
// Intersection applies both of the above cases: a widened write type and
358
// a narrowed read type:
359
//
360
// { y: string, x: number? } & { x: string? }
361
//
362
return Relation::Intersects;
363
default:
364
// And for good measure, default to intersection.
365
return Relation::Intersects;
366
}
367
}
368
369
// Otherwise we want to hard match on the case of:
370
//
371
// { ..., x: T } & { read x: U }
372
//
373
// ... or ...
374
//
375
// { ..., read x: T } & { read x: U }
376
//
377
// We will use the relation between T and U here.
378
if (!leftProp->second.readTy || !rightProp.isReadOnly())
379
return Relation::Intersects;
380
381
switch (relate(*leftProp->second.readTy, *rightProp.readTy, seen))
382
{
383
case Relation::Disjoint:
384
// The two read properties are disjoint, so the tables are disjoint, e.g.:
385
//
386
// { y: string, x: number? } & { read x: string }
387
//
388
return Relation::Disjoint;
389
case Relation::Coincident:
390
// If the two read types are coincident, then the left property is a
391
// subset if it also has a write part.
392
return leftProp->second.writeTy ? Relation::Subset : Relation::Coincident;
393
case Relation::Subset:
394
// If the left table's property is a subset of the right property, then
395
// the left table is a subset, as in:
396
//
397
// { y: string, x: number } & { read x: number? } => these tables intersect
398
return Relation::Subset;
399
case Relation::Superset:
400
// If the left table's property is a superset of the right property, then
401
// the two tables intersect, as in:
402
//
403
// { y: string, x: number? } & { read x: number }
404
//
405
return Relation::Intersects;
406
case Relation::Intersects:
407
// If the left table's property intersects with the right property, then
408
// the two tables intersect, as in:
409
//
410
// { y: string, x: number? } & { read x: string? } => these tables intersect
411
return Relation::Intersects;
412
default:
413
// And for good measure, default to intersection.
414
return Relation::Intersects;
415
}
416
}
417
418
Relation relateTables(const TableType* leftTable, const TableType* rightTable, SimplifierSeenSet& seen)
419
{
420
// FIXME CLI-189216: As noted in the body this is not complete.
421
if (leftTable->state != TableState::Sealed || rightTable->state != TableState::Sealed)
422
return Relation::Intersects;
423
424
if (rightTable->props.size() == 1 && !rightTable->indexer)
425
{
426
auto it = rightTable->props.begin();
427
auto res = relateTableToProp(leftTable, it->first, it->second, seen);
428
// If the single property is coincident with the member in the left table, then
429
// by width subtyping the left table is a subset.
430
if (res == Relation::Coincident)
431
return Relation::Subset;
432
return res;
433
}
434
435
if (leftTable->props.size() == 1 && !leftTable->indexer)
436
{
437
auto it = leftTable->props.begin();
438
auto res = flip(relateTableToProp(rightTable, it->first, it->second, seen));
439
// If the single property is coincident with the member in the right table, then
440
// by width subtyping the right table is a subset (so we return superset).
441
if (res == Relation::Coincident)
442
return Relation::Superset;
443
return res;
444
}
445
446
// This can potentially not account for something like
447
//
448
// { x: number, y: number } & { x: number, y: number, z: number }
449
//
450
// ... where we _ought_ to say superset.
451
if (leftTable->props.size() != rightTable->props.size() || leftTable->indexer.has_value() != rightTable->indexer.has_value())
452
return Relation::Intersects;
453
454
bool hasSubset = false;
455
456
for (const auto& [rightName, rightProp] : rightTable->props)
457
{
458
switch (relateTableToProp(leftTable, rightName, rightProp, seen))
459
{
460
case Relation::Disjoint:
461
return Relation::Disjoint;
462
case Relation::Superset:
463
case Relation::Intersects:
464
// We're being _very_ conservative here. We could update this in the future to
465
// account for a case like:
466
//
467
// (T & { x: number }) & (T & { read x: number? })
468
//
469
// ... by running this loop twice.
470
return Relation::Intersects;
471
case Relation::Subset:
472
hasSubset = true;
473
break;
474
case Relation::Coincident:
475
break;
476
}
477
}
478
479
if (!leftTable->indexer)
480
{
481
LUAU_ASSERT(!rightTable->indexer);
482
return hasSubset ? Relation::Subset : Relation::Coincident;
483
}
484
485
if (relate(leftTable->indexer->indexType, rightTable->indexer->indexType, seen) != Relation::Coincident)
486
return Relation::Intersects;
487
488
if (relate(leftTable->indexer->indexType, rightTable->indexer->indexType, seen) != Relation::Coincident)
489
return Relation::Intersects;
490
491
return hasSubset ? Relation::Subset : Relation::Coincident;
492
}
493
494
Relation relateTables_DEPRECATED(TypeId left, TypeId right, SimplifierSeenSet& seen)
495
{
496
NotNull<const TableType> leftTable{get<TableType>(left)};
497
NotNull<const TableType> rightTable{get<TableType>(right)};
498
LUAU_ASSERT(1 == rightTable->props.size());
499
// Disjoint props have nothing in common
500
// t1 with props p1's cannot appear in t2 and t2 with props p2's cannot appear in t1
501
bool foundPropFromLeftInRight = std::any_of(
502
begin(leftTable->props),
503
end(leftTable->props),
504
[&](auto prop)
505
{
506
return rightTable->props.count(prop.first) > 0;
507
}
508
);
509
bool foundPropFromRightInLeft = std::any_of(
510
begin(rightTable->props),
511
end(rightTable->props),
512
[&](auto prop)
513
{
514
return leftTable->props.count(prop.first) > 0;
515
}
516
);
517
518
if (!foundPropFromLeftInRight && !foundPropFromRightInLeft && leftTable->props.size() >= 1 && rightTable->props.size() >= 1)
519
return Relation::Intersects;
520
521
const auto [propName, rightProp] = *begin(rightTable->props);
522
523
auto it = leftTable->props.find(propName);
524
if (it == leftTable->props.end())
525
{
526
// Every table lacking a property is a supertype of a table having that
527
// property but the reverse is not true.
528
return Relation::Superset;
529
}
530
531
const Property leftProp = it->second;
532
533
if (!leftProp.isShared() || !rightProp.isShared())
534
return Relation::Intersects;
535
536
Relation r = relate(*leftProp.readTy, *rightProp.readTy, seen);
537
if (r == Relation::Coincident && 1 != leftTable->props.size())
538
{
539
// eg {tag: "cat", prop: string} & {tag: "cat"}
540
return Relation::Subset;
541
}
542
else
543
return r;
544
}
545
546
// A cheap and approximate subtype test
547
Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
548
{
549
// TODO nice to have: Relate functions of equal argument and return arity
550
551
left = follow(left);
552
right = follow(right);
553
554
if (left == right)
555
return Relation::Coincident;
556
557
std::pair<TypeId, TypeId> typePair{left, right};
558
if (!seen.insert(typePair))
559
{
560
// TODO: is this right at all?
561
// The thinking here is that this is a cycle if we get here, and therefore its coincident.
562
return Relation::Coincident;
563
}
564
565
if (get<UnknownType>(left))
566
{
567
if (get<AnyType>(right))
568
return Relation::Subset;
569
570
if (get<UnknownType>(right))
571
return Relation::Coincident;
572
573
if (get<ErrorType>(right))
574
return Relation::Disjoint;
575
576
return Relation::Superset;
577
}
578
579
if (get<UnknownType>(right))
580
return flip(relate(right, left, seen));
581
582
if (get<AnyType>(left))
583
{
584
if (get<AnyType>(right))
585
return Relation::Coincident;
586
587
return Relation::Superset;
588
}
589
590
if (get<AnyType>(right))
591
return flip(relate(right, left, seen));
592
593
// Type variables
594
// * FreeType
595
// * GenericType
596
// * BlockedType
597
// * PendingExpansionType
598
599
// Tops and bottoms
600
// * ErrorType
601
// * AnyType
602
// * NeverType
603
// * UnknownType
604
605
// Concrete
606
// * PrimitiveType
607
// * SingletonType
608
// * FunctionType
609
// * TableType
610
// * MetatableType
611
// * ExternType
612
// * UnionType
613
// * IntersectionType
614
// * NegationType
615
616
if (isTypeVariable(left) || isTypeVariable(right))
617
return Relation::Intersects;
618
619
// if either type is a type function, we cannot know if they'll be related.
620
if (get<TypeFunctionInstanceType>(left) || get<TypeFunctionInstanceType>(right))
621
return Relation::Intersects;
622
623
if (get<ErrorType>(left))
624
{
625
if (get<ErrorType>(right))
626
return Relation::Coincident;
627
else if (get<AnyType>(right))
628
return Relation::Subset;
629
630
return Relation::Disjoint;
631
}
632
else if (get<ErrorType>(right))
633
return flip(relate(right, left, seen));
634
635
if (get<NeverType>(left))
636
{
637
if (get<NeverType>(right))
638
return Relation::Coincident;
639
640
return Relation::Subset;
641
}
642
else if (get<NeverType>(right))
643
return flip(relate(right, left, seen));
644
645
if (get<IntersectionType>(left))
646
return Relation::Intersects;
647
else if (get<IntersectionType>(right))
648
return Relation::Intersects;
649
650
if (auto ut = get<UnionType>(left))
651
{
652
for (TypeId part : ut)
653
{
654
Relation r = relate(part, right, seen);
655
if (r == Relation::Superset || r == Relation::Coincident)
656
return Relation::Superset;
657
}
658
return Relation::Intersects;
659
}
660
else if (auto ut = get<UnionType>(right))
661
{
662
std::vector<Relation> opts;
663
for (TypeId part : ut)
664
{
665
Relation r = relate(left, part, seen);
666
667
if (r == Relation::Subset || r == Relation::Coincident)
668
return Relation::Subset;
669
}
670
return Relation::Intersects;
671
}
672
673
if (auto rnt = get<NegationType>(right))
674
{
675
Relation a = relate(left, rnt->ty, seen);
676
switch (a)
677
{
678
case Relation::Coincident:
679
// number & ~number
680
return Relation::Disjoint;
681
case Relation::Disjoint:
682
if (get<NegationType>(left))
683
{
684
// ~number & ~string
685
return Relation::Intersects;
686
}
687
else
688
{
689
// number & ~string
690
return Relation::Subset;
691
}
692
case Relation::Intersects:
693
// ~(false?) & ~boolean
694
return Relation::Intersects;
695
case Relation::Subset:
696
// "hello" & ~string
697
return Relation::Disjoint;
698
case Relation::Superset:
699
// ~function & ~(false?) -> ~function
700
// boolean & ~(false?) -> true
701
// string & ~"hello" -> string & ~"hello"
702
return Relation::Intersects;
703
}
704
}
705
else if (get<NegationType>(left))
706
return flip(relate(right, left, seen));
707
708
if (auto lp = get<PrimitiveType>(left))
709
{
710
if (auto rp = get<PrimitiveType>(right))
711
{
712
if (lp->type == rp->type)
713
return Relation::Coincident;
714
715
return Relation::Disjoint;
716
}
717
718
if (auto rs = get<SingletonType>(right))
719
{
720
if (lp->type == PrimitiveType::String && rs->variant.get_if<StringSingleton>())
721
return Relation::Superset;
722
723
if (lp->type == PrimitiveType::Boolean && rs->variant.get_if<BooleanSingleton>())
724
return Relation::Superset;
725
726
return Relation::Disjoint;
727
}
728
729
if (lp->type == PrimitiveType::Function)
730
{
731
if (get<FunctionType>(right))
732
return Relation::Superset;
733
734
return Relation::Disjoint;
735
}
736
if (lp->type == PrimitiveType::Table)
737
{
738
if (get<TableType>(right))
739
return Relation::Superset;
740
741
return Relation::Disjoint;
742
}
743
744
if (get<FunctionType>(right) || get<TableType>(right) || get<MetatableType>(right) || get<ExternType>(right))
745
return Relation::Disjoint;
746
}
747
748
if (auto ls = get<SingletonType>(left))
749
{
750
if (get<FunctionType>(right) || get<TableType>(right) || get<MetatableType>(right) || get<ExternType>(right))
751
return Relation::Disjoint;
752
753
if (get<PrimitiveType>(right))
754
return flip(relate(right, left, seen));
755
756
if (auto rs = get<SingletonType>(right))
757
{
758
if (ls->variant == rs->variant)
759
return Relation::Coincident;
760
761
return Relation::Disjoint;
762
}
763
}
764
765
if (get<FunctionType>(left))
766
{
767
if (auto rp = get<PrimitiveType>(right))
768
{
769
if (rp->type == PrimitiveType::Function)
770
return Relation::Subset;
771
772
return Relation::Disjoint;
773
}
774
775
return Relation::Intersects;
776
}
777
778
if (auto lt = get<TableType>(left))
779
{
780
if (auto rp = get<PrimitiveType>(right))
781
{
782
if (rp->type == PrimitiveType::Table)
783
return Relation::Subset;
784
785
return Relation::Disjoint;
786
}
787
788
if (auto rt = get<TableType>(right))
789
{
790
if (FFlag::LuauRelateHandlesCoincidentTables)
791
{
792
return relateTables(lt, rt, seen);
793
}
794
else
795
{
796
// TODO PROBABLY indexers and metatables.
797
if (1 == rt->props.size())
798
{
799
Relation r = relateTables_DEPRECATED(left, right, seen);
800
/*
801
* A reduction of these intersections is certainly possible, but
802
* it would require minting new table types. Also, I don't think
803
* it's super likely for this to arise from a refinement.
804
*
805
* Time will tell!
806
*
807
* ex we simplify this
808
* {tag: string} & {tag: "cat"}
809
* but not this
810
* {tag: string, prop: number} & {tag: "cat"}
811
*/
812
if (lt->props.size() > 1 && r == Relation::Superset)
813
return Relation::Intersects;
814
815
return r;
816
}
817
818
if (1 == lt->props.size())
819
return flip(relate(right, left, seen));
820
821
return Relation::Intersects;
822
}
823
}
824
825
if (auto re = get<ExternType>(right))
826
return relateTableToExternType(lt, re, seen);
827
828
// TODO metatables
829
830
return Relation::Disjoint;
831
}
832
833
if (auto ct = get<ExternType>(left))
834
{
835
if (auto rct = get<ExternType>(right))
836
{
837
if (isSubclass(ct, rct))
838
return Relation::Subset;
839
840
if (isSubclass(rct, ct))
841
return Relation::Superset;
842
843
return Relation::Disjoint;
844
}
845
846
if (auto tbl = get<TableType>(right))
847
return flip(relateTableToExternType(tbl, ct, seen));
848
849
return Relation::Disjoint;
850
}
851
852
return Relation::Intersects;
853
}
854
855
// A cheap and approximate subtype test
856
Relation relate(TypeId left, TypeId right)
857
{
858
SimplifierSeenSet seen{{}};
859
return relate(left, right, seen);
860
}
861
862
TypeId TypeSimplifier::mkNegation(TypeId ty) const
863
{
864
TypeId result = nullptr;
865
866
if (ty == builtinTypes->truthyType)
867
result = builtinTypes->falsyType;
868
else if (ty == builtinTypes->falsyType)
869
result = builtinTypes->truthyType;
870
else if (auto ntv = get<NegationType>(ty))
871
result = follow(ntv->ty);
872
else
873
result = arena->addType(NegationType{ty});
874
875
return result;
876
}
877
878
namespace
879
{
880
881
enum class Inhabited
882
{
883
Yes,
884
No
885
};
886
887
Inhabited intersectOneWithIntersection(TypeSimplifier& simplifier, TypeIds& source, TypeIds& dest, TypeId candidate)
888
{
889
if (dest.count(candidate) > 0)
890
return Inhabited::Yes;
891
892
if (auto itv = get<IntersectionType>(candidate))
893
{
894
for (auto subPart : itv)
895
{
896
if (intersectOneWithIntersection(simplifier, source, dest, subPart) == Inhabited::No)
897
return Inhabited::No;
898
}
899
900
return Inhabited::Yes;
901
}
902
903
if (source.empty())
904
{
905
dest.insert(candidate);
906
return Inhabited::Yes;
907
}
908
909
for (TypeId ty : source)
910
{
911
// All examples are presented in `candidate & ty` format.
912
switch (relate(candidate, ty))
913
{
914
case Relation::Disjoint:
915
// If the candidate and a member of the intersection are
916
// disjoint, then the entire intersection is uninhabited, for
917
// example:
918
//
919
// boolean & string
920
return Inhabited::No;
921
case Relation::Subset:
922
// If the candidate is a _subset_ of the member of the
923
// intersection, then replace this entry if we don't already
924
// have the candidate in the set, e.g.:
925
//
926
// true & boolean
927
dest.insert(candidate);
928
break;
929
case Relation::Coincident:
930
case Relation::Superset:
931
// If the candidate and a member of the intersection are
932
// coincident, or the incoming part is a superset, then do
933
// nothing, e.g.:
934
//
935
// boolean & true
936
// boolean & boolean
937
dest.insert(ty);
938
break;
939
case Relation::Intersects:
940
{
941
// If the candidate and a member of the intersection may
942
// intersect, then attempt to replace the member with
943
// a simpler type, e.g.:
944
//
945
// boolean & ~(false?)
946
if (std::optional<TypeId> simplified = simplifier.basicIntersect(candidate, ty))
947
dest.insert(*simplified);
948
else
949
{
950
dest.insert(candidate);
951
dest.insert(ty);
952
}
953
break;
954
}
955
}
956
}
957
958
return Inhabited::Yes;
959
}
960
} // namespace
961
962
TypeId TypeSimplifier::intersectFromParts(TypeIds parts)
963
{
964
if (parts.size() == 0)
965
return builtinTypes->unknownType;
966
967
if (parts.size() == 1)
968
return *parts.begin();
969
970
TypeIds source;
971
TypeIds dest;
972
973
source.reserve(parts.size());
974
dest.reserve(parts.size());
975
976
for (TypeId part : parts)
977
{
978
// We use the candidate, part, to construct a new intersection by
979
// intersecting every element in source against part, and then
980
// inserting it into dest.
981
if (intersectOneWithIntersection(*this, source, dest, part) == Inhabited::No)
982
return builtinTypes->neverType;
983
984
// At this point, source will contain some intersection, and dest will contain
985
// the intersection we want to retain for the next interation.
986
987
// We swap the two, so that we can use `source` as the basis for the next iteration.
988
std::swap(source, dest);
989
990
// Then clear the `dest` without reallocating the underlying hashtable, to avoid
991
// allocating.
992
dest.clearWithoutRealloc();
993
}
994
995
IntersectionBuilder ib(arena, builtinTypes);
996
for (auto ty : source)
997
ib.add(ty);
998
999
return ib.build();
1000
}
1001
1002
TypeId TypeSimplifier::intersectUnionWithType(TypeId left, TypeId right)
1003
{
1004
const UnionType* leftUnion = get<UnionType>(left);
1005
LUAU_ASSERT(leftUnion);
1006
1007
bool changed = false;
1008
size_t maxSize = DFInt::LuauSimplificationComplexityLimit;
1009
1010
if (leftUnion->options.size() > maxSize)
1011
return addIntersection(arena, builtinTypes, {left, right});
1012
1013
UnionBuilder ub(arena, builtinTypes);
1014
ub.reserve(leftUnion->options.size());
1015
1016
for (TypeId part : leftUnion)
1017
{
1018
TypeId simplified = intersect(right, part);
1019
changed |= simplified != part;
1020
1021
if (get<NeverType>(simplified))
1022
{
1023
changed = true;
1024
continue;
1025
}
1026
1027
ub.add(simplified);
1028
1029
// Initial combination size check could not predict nested union iteration
1030
if (ub.size() > maxSize)
1031
return addIntersection(arena, builtinTypes, {left, right});
1032
}
1033
1034
if (!changed)
1035
return left;
1036
1037
return ub.build();
1038
}
1039
1040
TypeId TypeSimplifier::intersectUnions(TypeId left, TypeId right)
1041
{
1042
const UnionType* leftUnion = get<UnionType>(left);
1043
LUAU_ASSERT(leftUnion);
1044
1045
const UnionType* rightUnion = get<UnionType>(right);
1046
LUAU_ASSERT(rightUnion);
1047
1048
std::set<TypeId> newParts;
1049
1050
// Combinatorial blowup moment!!
1051
1052
// combination size
1053
size_t optionSize = (int)leftUnion->options.size() * rightUnion->options.size();
1054
size_t maxSize = DFInt::LuauSimplificationComplexityLimit;
1055
1056
if (optionSize > maxSize)
1057
return arena->addType(IntersectionType{{left, right}});
1058
1059
UnionBuilder ub{arena, builtinTypes};
1060
for (TypeId leftPart : leftUnion)
1061
{
1062
for (TypeId rightPart : rightUnion)
1063
{
1064
TypeId simplified = intersect(leftPart, rightPart);
1065
1066
ub.add(simplified);
1067
1068
// Initial combination size check could not predict nested union iteration
1069
if (ub.size() > maxSize)
1070
return addIntersection(arena, builtinTypes, {left, right});
1071
}
1072
}
1073
1074
return ub.build();
1075
}
1076
1077
TypeId TypeSimplifier::intersectNegatedUnion(TypeId left, TypeId right)
1078
{
1079
// ~(A | B) & C
1080
// (~A & C) & (~B & C)
1081
1082
const NegationType* leftNegation = get<NegationType>(left);
1083
LUAU_ASSERT(leftNegation);
1084
1085
TypeId negatedTy = follow(leftNegation->ty);
1086
1087
const UnionType* negatedUnion = get<UnionType>(negatedTy);
1088
LUAU_ASSERT(negatedUnion);
1089
1090
bool changed = false;
1091
TypeIds newParts;
1092
1093
for (TypeId part : negatedUnion)
1094
{
1095
Relation r = relate(part, right);
1096
switch (r)
1097
{
1098
case Relation::Disjoint:
1099
// If A is disjoint from B, then ~A & B is just B.
1100
//
1101
// ~(false?) & true
1102
// (~false & true) & (~nil & true)
1103
// true & true
1104
newParts.insert(right);
1105
break;
1106
case Relation::Coincident:
1107
// If A is coincident with or a superset of B, then ~A & B is never.
1108
//
1109
// ~(false?) & false
1110
// (~false & false) & (~nil & false)
1111
// never & false
1112
//
1113
// fallthrough
1114
case Relation::Superset:
1115
// If A is a superset of B, then ~A & B is never.
1116
//
1117
// ~(boolean | nil) & true
1118
// (~boolean & true) & (~boolean & nil)
1119
// never & nil
1120
return builtinTypes->neverType;
1121
case Relation::Subset:
1122
case Relation::Intersects:
1123
// If A is a subset of B, then ~A & B is a bit more complicated. We need to think harder.
1124
//
1125
// ~(false?) & boolean
1126
// (~false & boolean) & (~nil & boolean)
1127
// true & boolean
1128
TypeId simplified = intersectTypeWithNegation(mkNegation(part), right);
1129
changed |= simplified != right;
1130
if (get<NeverType>(simplified))
1131
changed = true;
1132
else
1133
newParts.insert(simplified);
1134
break;
1135
}
1136
}
1137
1138
if (!changed)
1139
return right;
1140
1141
return intersectFromParts(std::move(newParts));
1142
}
1143
1144
std::optional<TypeId> TypeSimplifier::basicIntersectWithTruthy(TypeId target) const
1145
{
1146
target = follow(target);
1147
1148
if (isApproximatelyTruthyType(target))
1149
return target;
1150
1151
if (isApproximatelyFalsyType(target))
1152
return builtinTypes->neverType;
1153
1154
if (is<UnknownType>(target))
1155
return builtinTypes->truthyType;
1156
1157
if (is<AnyType>(target))
1158
// any = *error-type* | unknown, so truthy & any = *error-type* | truthy
1159
return arena->addType(UnionType{{builtinTypes->truthyType, builtinTypes->errorType}});
1160
1161
if (is<NeverType, ErrorType>(target))
1162
return target;
1163
1164
if (is<FunctionType, TableType, MetatableType, ExternType>(target))
1165
return target;
1166
1167
if (auto pt = get<PrimitiveType>(target))
1168
{
1169
switch (pt->type)
1170
{
1171
case PrimitiveType::NilType:
1172
return builtinTypes->neverType;
1173
case PrimitiveType::Boolean:
1174
return builtinTypes->trueType;
1175
default:
1176
return target;
1177
}
1178
}
1179
1180
if (auto st = get<SingletonType>(target))
1181
return st->variant == BooleanSingleton{false} ? builtinTypes->neverType : target;
1182
1183
return std::nullopt;
1184
}
1185
1186
std::optional<TypeId> TypeSimplifier::basicIntersectWithFalsy(TypeId target) const
1187
{
1188
target = follow(target);
1189
1190
if (isApproximatelyTruthyType(target))
1191
return builtinTypes->neverType;
1192
1193
if (isApproximatelyFalsyType(target))
1194
return target;
1195
1196
if (is<NeverType, ErrorType>(target))
1197
return target;
1198
1199
if (is<AnyType>(target))
1200
// any = *error-type* | unknown, so falsy & any = *error-type* | falsy
1201
return arena->addType(UnionType{{builtinTypes->falsyType, builtinTypes->errorType}});
1202
1203
if (is<UnknownType>(target))
1204
return builtinTypes->falsyType;
1205
1206
if (is<FunctionType, TableType, MetatableType, ExternType>(target))
1207
return builtinTypes->neverType;
1208
1209
if (auto pt = get<PrimitiveType>(target))
1210
{
1211
switch (pt->type)
1212
{
1213
case PrimitiveType::NilType:
1214
return builtinTypes->nilType;
1215
case PrimitiveType::Boolean:
1216
return builtinTypes->falseType;
1217
default:
1218
return builtinTypes->neverType;
1219
}
1220
}
1221
1222
if (auto st = get<SingletonType>(target))
1223
return st->variant == BooleanSingleton{false} ? builtinTypes->falseType : builtinTypes->neverType;
1224
1225
return std::nullopt;
1226
}
1227
1228
TypeId TypeSimplifier::intersectTypeWithNegation(TypeId left, TypeId right)
1229
{
1230
const NegationType* leftNegation = get<NegationType>(left);
1231
LUAU_ASSERT(leftNegation);
1232
1233
TypeId negatedTy = follow(leftNegation->ty);
1234
1235
if (negatedTy == right)
1236
return builtinTypes->neverType;
1237
1238
if (auto ut = get<UnionType>(negatedTy))
1239
{
1240
// ~(A | B) & C
1241
// (~A & C) & (~B & C)
1242
bool changed = false;
1243
TypeIds newParts;
1244
1245
for (TypeId part : ut)
1246
{
1247
Relation r = relate(part, right);
1248
switch (r)
1249
{
1250
case Relation::Coincident:
1251
// ~(false?) & nil
1252
// (~false & nil) & (~nil & nil)
1253
// nil & never
1254
//
1255
// fallthrough
1256
case Relation::Superset:
1257
// ~(boolean | string) & true
1258
// (~boolean & true) & (~boolean & string)
1259
// never & string
1260
1261
return builtinTypes->neverType;
1262
1263
case Relation::Disjoint:
1264
// ~nil & boolean
1265
newParts.insert(right);
1266
break;
1267
1268
case Relation::Subset:
1269
// ~false & boolean
1270
// fallthrough
1271
case Relation::Intersects:
1272
// FIXME: The mkNegation here is pretty unfortunate.
1273
// Memoizing this will probably be important.
1274
changed = true;
1275
newParts.insert(right);
1276
newParts.insert(mkNegation(part));
1277
}
1278
}
1279
1280
if (!changed)
1281
return right;
1282
1283
return intersectFromParts(std::move(newParts));
1284
}
1285
1286
if (auto rightUnion = get<UnionType>(right))
1287
{
1288
// ~A & (B | C)
1289
bool changed = false;
1290
std::set<TypeId> newParts;
1291
1292
for (TypeId part : rightUnion)
1293
{
1294
Relation r = relate(negatedTy, part);
1295
switch (r)
1296
{
1297
case Relation::Coincident:
1298
changed = true;
1299
continue;
1300
case Relation::Disjoint:
1301
newParts.insert(part);
1302
break;
1303
case Relation::Superset:
1304
changed = true;
1305
continue;
1306
case Relation::Subset:
1307
// fallthrough
1308
case Relation::Intersects:
1309
changed = true;
1310
newParts.insert(arena->addType(IntersectionType{{left, part}}));
1311
}
1312
}
1313
1314
if (!changed)
1315
return right;
1316
else if (0 == newParts.size())
1317
return builtinTypes->neverType;
1318
else if (1 == newParts.size())
1319
return *begin(newParts);
1320
else
1321
return arena->addType(UnionType{std::vector<TypeId>{begin(newParts), end(newParts)}});
1322
}
1323
1324
if (auto pt = get<PrimitiveType>(right); pt && pt->type == PrimitiveType::Boolean)
1325
{
1326
if (auto st = get<SingletonType>(negatedTy))
1327
{
1328
if (st->variant == BooleanSingleton{true})
1329
return builtinTypes->falseType;
1330
else if (st->variant == BooleanSingleton{false})
1331
return builtinTypes->trueType;
1332
else
1333
// boolean & ~"hello"
1334
return builtinTypes->booleanType;
1335
}
1336
}
1337
1338
Relation r = relate(negatedTy, right);
1339
1340
switch (r)
1341
{
1342
case Relation::Disjoint:
1343
// ~boolean & string
1344
return right;
1345
case Relation::Coincident:
1346
// ~string & string
1347
// fallthrough
1348
case Relation::Superset:
1349
// ~string & "hello"
1350
return builtinTypes->neverType;
1351
case Relation::Subset:
1352
// ~string & unknown
1353
// ~"hello" & string
1354
// fallthrough
1355
case Relation::Intersects:
1356
// ~("hello" | boolean) & string
1357
// fallthrough
1358
default:
1359
return arena->addType(IntersectionType{{left, right}});
1360
}
1361
}
1362
1363
TypeId TypeSimplifier::intersectNegations(TypeId left, TypeId right)
1364
{
1365
const NegationType* leftNegation = get<NegationType>(left);
1366
LUAU_ASSERT(leftNegation);
1367
1368
if (get<UnionType>(follow(leftNegation->ty)))
1369
return intersectNegatedUnion(left, right);
1370
1371
const NegationType* rightNegation = get<NegationType>(right);
1372
LUAU_ASSERT(rightNegation);
1373
1374
if (get<UnionType>(follow(rightNegation->ty)))
1375
return intersectNegatedUnion(right, left);
1376
1377
Relation r = relate(leftNegation->ty, rightNegation->ty);
1378
1379
switch (r)
1380
{
1381
case Relation::Coincident:
1382
// ~true & ~true
1383
return left;
1384
case Relation::Subset:
1385
// ~true & ~boolean
1386
return right;
1387
case Relation::Superset:
1388
// ~boolean & ~true
1389
return left;
1390
case Relation::Intersects:
1391
case Relation::Disjoint:
1392
default:
1393
// ~boolean & ~string
1394
return arena->addType(IntersectionType{{left, right}});
1395
}
1396
}
1397
1398
TypeId TypeSimplifier::intersectIntersectionWithType(TypeId left, TypeId right)
1399
{
1400
const IntersectionType* leftIntersection = get<IntersectionType>(left);
1401
LUAU_ASSERT(leftIntersection);
1402
1403
if (leftIntersection->parts.size() > (size_t)DFInt::LuauSimplificationComplexityLimit)
1404
return addIntersection(arena, builtinTypes, {left, right});
1405
1406
bool changed = false;
1407
TypeIds newParts;
1408
1409
for (TypeId part : leftIntersection)
1410
{
1411
Relation r = relate(part, right);
1412
switch (r)
1413
{
1414
case Relation::Disjoint:
1415
return builtinTypes->neverType;
1416
case Relation::Coincident:
1417
newParts.insert(part);
1418
continue;
1419
case Relation::Subset:
1420
newParts.insert(part);
1421
continue;
1422
case Relation::Superset:
1423
newParts.insert(right);
1424
changed = true;
1425
continue;
1426
default:
1427
newParts.insert(part);
1428
newParts.insert(right);
1429
changed = true;
1430
continue;
1431
}
1432
}
1433
1434
// It is sometimes the case that an intersection operation will result in
1435
// clipping a free type from the result.
1436
//
1437
// eg (number & 'a) & string --> never
1438
//
1439
// We want to only report the free types that are part of the result.
1440
for (TypeId part : newParts)
1441
{
1442
if (isTypeVariable(part))
1443
blockedTypes.insert(part);
1444
}
1445
1446
if (!changed)
1447
return left;
1448
1449
return intersectFromParts(std::move(newParts));
1450
}
1451
1452
std::optional<TypeId> TypeSimplifier::basicIntersect(TypeId left, TypeId right)
1453
{
1454
left = follow(left);
1455
right = follow(right);
1456
1457
if (get<AnyType>(left) && get<ErrorType>(right))
1458
return right;
1459
if (get<AnyType>(right) && get<ErrorType>(left))
1460
return left;
1461
if (get<AnyType>(left))
1462
return arena->addType(UnionType{{right, builtinTypes->errorType}});
1463
if (get<AnyType>(right))
1464
return arena->addType(UnionType{{left, builtinTypes->errorType}});
1465
if (get<UnknownType>(left))
1466
return right;
1467
if (get<UnknownType>(right))
1468
return left;
1469
if (get<NeverType>(left))
1470
return left;
1471
if (get<NeverType>(right))
1472
return right;
1473
1474
if (auto pt = get<PrimitiveType>(left); pt && pt->type == PrimitiveType::Boolean)
1475
{
1476
if (auto st = get<SingletonType>(right); st && st->variant.get_if<BooleanSingleton>())
1477
return right;
1478
if (auto nt = get<NegationType>(right))
1479
{
1480
if (auto st = get<SingletonType>(follow(nt->ty)); st && st->variant.get_if<BooleanSingleton>())
1481
{
1482
if (st->variant == BooleanSingleton{true})
1483
return builtinTypes->falseType;
1484
else
1485
return builtinTypes->trueType;
1486
}
1487
}
1488
}
1489
else if (auto pt = get<PrimitiveType>(right); pt && pt->type == PrimitiveType::Boolean)
1490
{
1491
if (auto st = get<SingletonType>(left); st && st->variant.get_if<BooleanSingleton>())
1492
return left;
1493
if (auto nt = get<NegationType>(left))
1494
{
1495
if (auto st = get<SingletonType>(follow(nt->ty)); st && st->variant.get_if<BooleanSingleton>())
1496
{
1497
if (st->variant == BooleanSingleton{true})
1498
return builtinTypes->falseType;
1499
else
1500
return builtinTypes->trueType;
1501
}
1502
}
1503
}
1504
1505
if (const auto [lt, rt] = get2<TableType, TableType>(left, right); lt && rt)
1506
{
1507
if (1 == lt->props.size())
1508
{
1509
const auto [propName, leftProp] = *begin(lt->props);
1510
const bool leftPropIsRefinable = leftProp.isShared() || leftProp.isReadOnly();
1511
1512
auto it = rt->props.find(propName);
1513
if (it != rt->props.end() && leftPropIsRefinable && it->second.isShared())
1514
{
1515
Relation r = relate(*leftProp.readTy, *it->second.readTy);
1516
1517
switch (r)
1518
{
1519
case Relation::Disjoint:
1520
return builtinTypes->neverType;
1521
case Relation::Superset:
1522
case Relation::Coincident:
1523
return right;
1524
case Relation::Subset:
1525
if (1 == rt->props.size() && leftProp.isShared())
1526
return left;
1527
break;
1528
default:
1529
break;
1530
}
1531
}
1532
}
1533
else if (1 == rt->props.size())
1534
return basicIntersect(right, left);
1535
1536
// If two tables have disjoint properties and indexers, we can combine them.
1537
if (!lt->indexer && !rt->indexer && lt->state == TableState::Sealed && rt->state == TableState::Sealed)
1538
{
1539
if (rt->props.empty())
1540
return left;
1541
1542
bool areDisjoint = true;
1543
for (const auto& [name, leftProp] : lt->props)
1544
{
1545
if (rt->props.count(name))
1546
{
1547
areDisjoint = false;
1548
break;
1549
}
1550
}
1551
1552
if (areDisjoint)
1553
{
1554
TableType merged{TableState::Sealed, TypeLevel{}, lt->scope};
1555
merged.props = lt->props;
1556
1557
for (const auto& [name, rightProp] : rt->props)
1558
merged.props[name] = rightProp;
1559
1560
return arena->addType(std::move(merged));
1561
}
1562
}
1563
1564
return std::nullopt;
1565
}
1566
1567
if (isApproximatelyTruthyType(left))
1568
if (auto res = basicIntersectWithTruthy(right))
1569
return res;
1570
1571
if (isApproximatelyTruthyType(right))
1572
if (auto res = basicIntersectWithTruthy(left))
1573
return res;
1574
1575
if (isApproximatelyFalsyType(left))
1576
if (auto res = basicIntersectWithFalsy(right))
1577
return res;
1578
1579
if (isApproximatelyFalsyType(right))
1580
if (auto res = basicIntersectWithFalsy(left))
1581
return res;
1582
1583
1584
Relation relation = relate(left, right);
1585
if (left == right || Relation::Coincident == relation)
1586
return left;
1587
1588
if (relation == Relation::Disjoint)
1589
return builtinTypes->neverType;
1590
else if (relation == Relation::Subset)
1591
return left;
1592
else if (relation == Relation::Superset)
1593
return right;
1594
1595
return std::nullopt;
1596
}
1597
1598
TypeId TypeSimplifier::intersect(TypeId left, TypeId right)
1599
{
1600
RecursionLimiter rl("TypeSimplifier::intersect", &recursionDepth, 15);
1601
1602
left = simplify(left);
1603
right = simplify(right);
1604
1605
if (left == right)
1606
return left;
1607
1608
if (get<AnyType>(left) && get<ErrorType>(right))
1609
return right;
1610
if (get<AnyType>(right) && get<ErrorType>(left))
1611
return left;
1612
if (get<UnknownType>(left) && !get<ErrorType>(right))
1613
return right;
1614
if (get<UnknownType>(right) && !get<ErrorType>(left))
1615
return left;
1616
if (get<AnyType>(left) && get<UnionType>(right))
1617
return union_(builtinTypes->errorType, right);
1618
if (get<UnionType>(left) && get<AnyType>(right))
1619
return union_(builtinTypes->errorType, left);
1620
if (get<AnyType>(left))
1621
return arena->addType(UnionType{{right, builtinTypes->errorType}});
1622
if (get<AnyType>(right))
1623
return arena->addType(UnionType{{left, builtinTypes->errorType}});
1624
if (get<UnknownType>(left))
1625
return right;
1626
if (get<UnknownType>(right))
1627
return left;
1628
if (get<NeverType>(left))
1629
return left;
1630
if (get<NeverType>(right))
1631
return right;
1632
1633
if (auto lf = get<FreeType>(left))
1634
{
1635
Relation r = relate(lf->upperBound, right);
1636
if (r == Relation::Subset || r == Relation::Coincident)
1637
return left;
1638
}
1639
else if (auto rf = get<FreeType>(right))
1640
{
1641
Relation r = relate(left, rf->upperBound);
1642
if (r == Relation::Superset || r == Relation::Coincident)
1643
return right;
1644
}
1645
1646
if (isTypeVariable(left))
1647
{
1648
blockedTypes.insert(left);
1649
return addIntersection(arena, builtinTypes, {left, right});
1650
}
1651
1652
if (isTypeVariable(right))
1653
{
1654
blockedTypes.insert(right);
1655
return addIntersection(arena, builtinTypes, {left, right});
1656
}
1657
1658
if (get<UnionType>(left))
1659
{
1660
if (get<UnionType>(right))
1661
return intersectUnions(left, right);
1662
else
1663
return intersectUnionWithType(left, right);
1664
}
1665
else if (get<UnionType>(right))
1666
return intersectUnionWithType(right, left);
1667
1668
if (get<IntersectionType>(left))
1669
return intersectIntersectionWithType(left, right);
1670
else if (get<IntersectionType>(right))
1671
return intersectIntersectionWithType(right, left);
1672
1673
if (get<NegationType>(left))
1674
{
1675
if (get<NegationType>(right))
1676
return intersectNegations(left, right);
1677
else
1678
return intersectTypeWithNegation(left, right);
1679
}
1680
else if (get<NegationType>(right))
1681
return intersectTypeWithNegation(right, left);
1682
1683
std::optional<TypeId> res = basicIntersect(left, right);
1684
if (res)
1685
return *res;
1686
else
1687
return arena->addType(IntersectionType{{left, right}});
1688
}
1689
1690
TypeId TypeSimplifier::union_(TypeId left, TypeId right)
1691
{
1692
RecursionLimiter rl("TypeSimplifier::union", &recursionDepth, 15);
1693
1694
left = simplify(left);
1695
right = simplify(right);
1696
1697
if (get<NeverType>(left))
1698
return right;
1699
if (get<NeverType>(right))
1700
return left;
1701
1702
if (auto leftUnion = get<UnionType>(left))
1703
{
1704
bool changed = false;
1705
1706
UnionBuilder ub(arena, builtinTypes);
1707
ub.reserve(leftUnion->options.size());
1708
for (TypeId part : leftUnion)
1709
{
1710
if (get<NeverType>(part))
1711
{
1712
changed = true;
1713
continue;
1714
}
1715
1716
Relation r = relate(part, right);
1717
switch (r)
1718
{
1719
case Relation::Coincident:
1720
case Relation::Superset:
1721
return left;
1722
case Relation::Subset:
1723
ub.add(right);
1724
changed = true;
1725
break;
1726
default:
1727
ub.add(part);
1728
ub.add(right);
1729
changed = true;
1730
break;
1731
}
1732
}
1733
1734
if (!changed)
1735
return left;
1736
1737
// If the left-side is changed but has no parts, then the left-side union is uninhabited.
1738
if (ub.size() == 0)
1739
return right;
1740
1741
return ub.build();
1742
}
1743
else if (get<UnionType>(right))
1744
return union_(right, left);
1745
1746
Relation r = relate(left, right);
1747
if (left == right || r == Relation::Coincident || r == Relation::Superset)
1748
return left;
1749
1750
if (r == Relation::Subset)
1751
return right;
1752
1753
if (auto as = get<SingletonType>(left))
1754
{
1755
if (auto abs = as->variant.get_if<BooleanSingleton>())
1756
{
1757
if (auto bs = get<SingletonType>(right))
1758
{
1759
if (auto bbs = bs->variant.get_if<BooleanSingleton>())
1760
{
1761
if (abs->value != bbs->value)
1762
return builtinTypes->booleanType;
1763
}
1764
}
1765
}
1766
}
1767
1768
if (const auto [lt, rt] = get2<TableType, TableType>(left, right); lt && rt)
1769
{
1770
if (1 == lt->props.size() && 1 == rt->props.size())
1771
{
1772
const auto [propName, leftProp] = *begin(lt->props);
1773
const auto [rightPropName, rightProp] = *begin(rt->props);
1774
1775
if (rightPropName != propName)
1776
return arena->addType(UnionType{{left, right}});
1777
1778
// Consider:
1779
//
1780
// { prop: number? } | { prop: string? }
1781
//
1782
// Even though these two tables share a property, we cannot
1783
// simplify this type any further, otherwise we can, say,
1784
// launder a `{ prop: number? }` into a `{ prop: string? }`
1785
// and then write a string to it.
1786
//
1787
// We also elect to not simplify unsealed tables.
1788
if (!leftProp.isReadOnly() || !rightProp.isReadOnly() || lt->state != TableState::Sealed || rt->state != TableState::Sealed)
1789
return arena->addType(UnionType{{left, right}});
1790
1791
// At this point, we have two read-only singleton tables, e.g.:
1792
//
1793
// { read prop: number? } | { read prop: string? }
1794
//
1795
// We can relate these two properties and produce a simplified
1796
// version, with some special cases.
1797
1798
switch (relate(*leftProp.readTy, *rightProp.readTy))
1799
{
1800
case Relation::Coincident:
1801
case Relation::Superset:
1802
// The left property is a superset (or coincident) of the
1803
// right, for example:
1804
//
1805
// { read prop: number? } | { read prop: number }
1806
//
1807
return left;
1808
case Relation::Subset:
1809
// The left property is a subset of the right, for example:
1810
//
1811
// { read prop: nil } | { read prop: false? }
1812
//
1813
return right;
1814
case Relation::Disjoint:
1815
case Relation::Intersects:
1816
// If we are disjoint *or* there's some overlap, then
1817
// we can create a new read-only singleton table with
1818
// a single property.
1819
//
1820
// We probably could do something quicker here for disjoint,
1821
// given that the union should just mint a new union type
1822
// anyhow.
1823
TableType result;
1824
result.state = TableState::Sealed;
1825
result.props[propName] = Property::readonly(union_(*leftProp.readTy, *rightProp.readTy));
1826
return arena->addType(std::move(result));
1827
}
1828
}
1829
}
1830
1831
return arena->addType(UnionType{{left, right}});
1832
}
1833
1834
TypeId TypeSimplifier::simplify(TypeId ty)
1835
{
1836
DenseHashSet<TypeId> seen{nullptr};
1837
return simplify(ty, seen);
1838
}
1839
1840
TypeId TypeSimplifier::simplify(TypeId ty, DenseHashSet<TypeId>& seen)
1841
{
1842
RecursionLimiter limiter("TypeSimplifier::simplify", &recursionDepth, 60);
1843
1844
ty = follow(ty);
1845
1846
if (seen.find(ty))
1847
return ty;
1848
seen.insert(ty);
1849
1850
if (auto nt = get<NegationType>(ty))
1851
{
1852
TypeId negatedTy = follow(nt->ty);
1853
if (get<AnyType>(negatedTy))
1854
return arena->addType(UnionType{{builtinTypes->neverType, builtinTypes->errorType}});
1855
else if (get<UnknownType>(negatedTy))
1856
return builtinTypes->neverType;
1857
else if (get<NeverType>(negatedTy))
1858
return builtinTypes->unknownType;
1859
if (auto nnt = get<NegationType>(negatedTy))
1860
return simplify(nnt->ty, seen);
1861
}
1862
1863
// Promote {x: never} to never
1864
if (auto tt = get<TableType>(ty))
1865
{
1866
if (1 == tt->props.size())
1867
{
1868
if (std::optional<TypeId> readTy = begin(tt->props)->second.readTy)
1869
{
1870
TypeId propTy = simplify(*readTy, seen);
1871
if (get<NeverType>(propTy))
1872
return builtinTypes->neverType;
1873
}
1874
}
1875
}
1876
1877
return ty;
1878
}
1879
1880
namespace
1881
{
1882
1883
bool isSimpleDiscriminant(TypeId ty, DenseHashSet<TypeId>& seen)
1884
{
1885
ty = follow(ty);
1886
// If we *ever* see a recursive type, bail right away, clearly that is
1887
// not simple.
1888
if (seen.contains(ty))
1889
return false;
1890
seen.insert(ty);
1891
1892
// NOTE: We could probably support `{}` as a simple discriminant.
1893
if (auto ttv = get<TableType>(ty); ttv && ttv->props.size() == 1 && !ttv->indexer)
1894
{
1895
auto prop = begin(ttv->props)->second;
1896
return (!prop.readTy || isSimpleDiscriminant(*prop.readTy, seen)) && (!prop.writeTy || isSimpleDiscriminant(*prop.writeTy, seen));
1897
}
1898
1899
if (auto nt = get<NegationType>(ty))
1900
return isSimpleDiscriminant(nt->ty, seen);
1901
1902
return is<PrimitiveType, SingletonType, ExternType>(ty) || isApproximatelyTruthyType(ty) || isApproximatelyFalsyType(ty);
1903
}
1904
1905
/**
1906
* There are some types that are "simple", and thus easy to intersect against:
1907
* - The "truthy" (`~(false?)`) and "falsy" (`false?`) types are simple.
1908
* - Primitive types, singleton types, and extern types are simple
1909
* - Table types are simple if they have no indexer, and have a single property
1910
* who's read and write types are also simple.
1911
* - Cyclic types are never simple.
1912
*/
1913
bool isSimpleDiscriminant(TypeId ty)
1914
{
1915
DenseHashSet<TypeId> seenSet{nullptr};
1916
return isSimpleDiscriminant(ty, seenSet);
1917
}
1918
1919
} // namespace
1920
1921
std::optional<TypeId> TypeSimplifier::intersectOne(TypeId target, TypeId discriminant) const
1922
{
1923
switch (relate(target, discriminant))
1924
{
1925
case Relation::Disjoint: // No A is a B or vice versa
1926
return builtinTypes->neverType;
1927
case Relation::Subset: // Every A is in B
1928
case Relation::Coincident: // Every A is in B and vice versa
1929
return target;
1930
case Relation::Superset: // Every B is in A
1931
return discriminant;
1932
case Relation::Intersects:
1933
default:
1934
// Some As are in B and some Bs are in A. ex (number | string) <-> (string | boolean).
1935
return std::nullopt;
1936
}
1937
}
1938
1939
std::optional<TypeId> TypeSimplifier::subtractOne(TypeId target, TypeId discriminant) const
1940
{
1941
target = follow(target);
1942
discriminant = follow(discriminant);
1943
1944
if (auto nt = get<NegationType>(discriminant))
1945
return intersectOne(target, nt->ty);
1946
1947
switch (relate(target, discriminant))
1948
{
1949
case Relation::Disjoint: // A v B is empty => A - B is equivalent to A
1950
return target;
1951
case Relation::Subset: // A v B is A => A - B is empty
1952
case Relation::Coincident: // Same as above: A == B so A - B = {}
1953
return builtinTypes->neverType;
1954
case Relation::Superset:
1955
case Relation::Intersects:
1956
default:
1957
return std::nullopt;
1958
}
1959
}
1960
1961
std::optional<Property> TypeSimplifier::intersectProperty(const Property& target, const Property& discriminant, DenseHashSet<TypeId>& seen) const
1962
{
1963
// NOTE: I invite the reader to refactor the below code as a fun coding
1964
// exercise. It looks ugly to me, but I don't think we can make it
1965
// any cleaner.
1966
1967
Property prop;
1968
prop.deprecated = target.deprecated || discriminant.deprecated;
1969
1970
// We're trying to follow the following rules for both read and write types:
1971
// * If the type is present on both properties, intersect it, and return
1972
// `std::nullopt` if we fail.
1973
// * If the type only exists on one property or the other, take that.
1974
1975
if (target.readTy && discriminant.readTy)
1976
{
1977
prop.readTy = intersectWithSimpleDiscriminant(*target.readTy, *discriminant.readTy, seen);
1978
if (!prop.readTy)
1979
return std::nullopt;
1980
}
1981
else if (target.readTy && !discriminant.readTy)
1982
prop.readTy = target.readTy;
1983
else if (!target.readTy && discriminant.readTy)
1984
prop.readTy = discriminant.readTy;
1985
1986
if (target.writeTy && discriminant.writeTy)
1987
{
1988
prop.writeTy = intersectWithSimpleDiscriminant(*target.writeTy, *discriminant.writeTy, seen);
1989
if (!prop.writeTy)
1990
return std::nullopt;
1991
}
1992
else if (target.writeTy && !discriminant.writeTy)
1993
prop.writeTy = target.writeTy;
1994
else if (!target.writeTy && discriminant.writeTy)
1995
prop.writeTy = discriminant.writeTy;
1996
1997
return {prop};
1998
}
1999
2000
std::optional<TypeId> TypeSimplifier::intersectWithSimpleDiscriminant(TypeId target, TypeId discriminant, DenseHashSet<TypeId>& seen) const
2001
{
2002
if (seen.contains(target))
2003
return std::nullopt;
2004
2005
target = follow(target);
2006
discriminant = follow(discriminant);
2007
2008
if (auto ut = get<UnionType>(target))
2009
{
2010
seen.insert(target);
2011
TypeIds options;
2012
for (TypeId option : ut)
2013
{
2014
auto result = intersectWithSimpleDiscriminant(option, discriminant, seen);
2015
2016
if (!result)
2017
return std::nullopt;
2018
2019
if (is<UnknownType>(result))
2020
return builtinTypes->unknownType;
2021
2022
if (!is<NeverType>(*result))
2023
options.insert(*result);
2024
}
2025
if (options.empty())
2026
return builtinTypes->neverType;
2027
if (options.size() == 1)
2028
return *options.begin();
2029
return arena->addType(UnionType{options.take()});
2030
}
2031
2032
if (auto it = get<IntersectionType>(target))
2033
{
2034
seen.insert(target);
2035
TypeIds parts;
2036
for (TypeId part : it)
2037
{
2038
auto result = intersectWithSimpleDiscriminant(part, discriminant, seen);
2039
if (!result)
2040
return std::nullopt;
2041
2042
if (is<NeverType>(*result))
2043
return builtinTypes->neverType;
2044
2045
if (auto subIntersection = get<IntersectionType>(*result))
2046
{
2047
for (TypeId subOption : subIntersection)
2048
{
2049
if (is<NeverType>(subOption))
2050
return builtinTypes->neverType;
2051
if (!is<UnknownType>(result))
2052
parts.insert(*result);
2053
}
2054
}
2055
else if (!is<UnknownType>(*result))
2056
parts.insert(*result);
2057
}
2058
if (parts.empty())
2059
return builtinTypes->unknownType;
2060
if (parts.size() == 1)
2061
return *parts.begin();
2062
return arena->addType(IntersectionType{parts.take()});
2063
}
2064
2065
if (auto ttv = get<TableType>(target))
2066
{
2067
if (auto discTtv = get<TableType>(discriminant))
2068
{
2069
// The precondition of this function is that `discriminant` is
2070
// simple, so if it's a table it *must* be a sealed table with
2071
// a single property and no indexer.
2072
LUAU_ASSERT(discTtv->props.size() == 1 && !discTtv->indexer);
2073
const auto discProp = begin(discTtv->props);
2074
if (auto tyProp = ttv->props.find(discProp->first); tyProp != ttv->props.end())
2075
{
2076
auto property = intersectProperty(tyProp->second, discProp->second, seen);
2077
if (!property)
2078
return std::nullopt;
2079
if (property->readTy && is<NeverType>(follow(property->readTy)))
2080
return builtinTypes->neverType;
2081
if (property->writeTy && is<NeverType>(follow(property->writeTy)))
2082
return builtinTypes->neverType;
2083
2084
// If the property we get back is pointer identical to the
2085
// original property, return the underlying property as an
2086
// optimization.
2087
if (tyProp->second.readTy == property->readTy && tyProp->second.writeTy == property->writeTy)
2088
return target;
2089
2090
CloneState cs{builtinTypes};
2091
TypeId result = shallowClone(target, *arena, cs, /* clonePersistentTypes */ true);
2092
auto resultTtv = getMutable<TableType>(result);
2093
LUAU_ASSERT(resultTtv);
2094
resultTtv->props[tyProp->first] = *property;
2095
// Shallow cloning clears out scopes, so let's put back the
2096
// scope from the original type.
2097
resultTtv->scope = ttv->scope;
2098
return result;
2099
}
2100
2101
CloneState cs{builtinTypes};
2102
TypeId result = shallowClone(target, *arena, cs, /* clonePersistentTypes */ true);
2103
// Shallow cloning clears out scopes, so let's put back the
2104
// scope from the original type.
2105
auto resultTtv = getMutable<TableType>(result);
2106
LUAU_ASSERT(resultTtv);
2107
resultTtv->props.emplace(discProp->first, discProp->second);
2108
resultTtv->scope = ttv->scope;
2109
return result;
2110
}
2111
2112
// At this point, we're doing something like:
2113
//
2114
// { ... } & ~nil
2115
//
2116
// Which can be handled via fallthrough.
2117
}
2118
2119
// FIXME: We could probably return to this.
2120
if (is<FreeType, GenericType, BlockedType, PendingExpansionType, TypeFunctionInstanceType>(target))
2121
return std::nullopt;
2122
2123
if (isApproximatelyTruthyType(discriminant))
2124
return basicIntersectWithTruthy(target);
2125
2126
if (isApproximatelyTruthyType(target))
2127
return basicIntersectWithTruthy(discriminant);
2128
2129
if (isApproximatelyFalsyType(discriminant))
2130
return basicIntersectWithFalsy(target);
2131
2132
if (isApproximatelyFalsyType(target))
2133
return basicIntersectWithFalsy(discriminant);
2134
2135
if (is<AnyType>(target))
2136
return arena->addType(UnionType{{builtinTypes->errorType, discriminant}});
2137
2138
if (is<ErrorType>(target))
2139
return builtinTypes->errorType;
2140
2141
if (auto nty = get<NegationType>(discriminant))
2142
return subtractOne(target, nty->ty);
2143
2144
return intersectOne(target, discriminant);
2145
}
2146
2147
std::optional<TypeId> TypeSimplifier::intersectWithSimpleDiscriminant(TypeId target, TypeId discriminant) const
2148
{
2149
DenseHashSet<TypeId> seenSet{nullptr};
2150
return intersectWithSimpleDiscriminant(target, discriminant, seenSet);
2151
}
2152
2153
SimplifyResult simplifyIntersection(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, TypeId left, TypeId right)
2154
{
2155
TypeSimplifier s{builtinTypes, arena};
2156
2157
// fprintf(stderr, "Intersect %s and %s ...\n", toString(left).c_str(), toString(right).c_str());
2158
2159
TypeId res = s.intersect(left, right);
2160
2161
// fprintf(stderr, "Intersect %s and %s -> %s\n", toString(left).c_str(), toString(right).c_str(), toString(res).c_str());
2162
2163
return SimplifyResult{res, std::move(s.blockedTypes)};
2164
}
2165
2166
SimplifyResult simplifyIntersection(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, TypeIds parts)
2167
{
2168
TypeSimplifier s{builtinTypes, arena};
2169
2170
TypeId res = s.intersectFromParts(std::move(parts));
2171
2172
return SimplifyResult{res, std::move(s.blockedTypes)};
2173
}
2174
2175
SimplifyResult simplifyUnion(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, TypeId left, TypeId right)
2176
{
2177
TypeSimplifier s{builtinTypes, arena};
2178
2179
TypeId res = s.union_(left, right);
2180
2181
// fprintf(stderr, "Union %s and %s -> %s\n", toString(left).c_str(), toString(right).c_str(), toString(res).c_str());
2182
2183
return SimplifyResult{res, std::move(s.blockedTypes)};
2184
}
2185
2186
2187
std::optional<TypeId> intersectWithSimpleDiscriminant(
2188
NotNull<BuiltinTypes> builtinTypes,
2189
NotNull<TypeArena> arena,
2190
TypeId target,
2191
TypeId discriminant
2192
)
2193
{
2194
if (!isSimpleDiscriminant(discriminant))
2195
{
2196
if (isSimpleDiscriminant(target))
2197
return intersectWithSimpleDiscriminant(builtinTypes, arena, discriminant, target);
2198
return std::nullopt;
2199
}
2200
2201
TypeSimplifier s{builtinTypes, arena};
2202
2203
return s.intersectWithSimpleDiscriminant(target, discriminant);
2204
}
2205
2206
2207
} // namespace Luau
2208
2209