Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Analysis/src/Normalize.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/Normalize.h"
4
#include "Luau/ToString.h"
5
6
#include <algorithm>
7
8
#include "Luau/Common.h"
9
#include "Luau/RecursionCounter.h"
10
#include "Luau/Set.h"
11
#include "Luau/Simplify.h"
12
#include "Luau/Subtyping.h"
13
#include "Luau/Type.h"
14
#include "Luau/TypeFwd.h"
15
#include "Luau/TypeUtils.h"
16
#include "Luau/Unifier.h"
17
18
LUAU_FASTFLAGVARIABLE(DebugLuauCheckNormalizeInvariant)
19
20
LUAU_FASTINTVARIABLE(LuauNormalizeCacheLimit, 100000)
21
LUAU_FASTFLAG(LuauSolverV2)
22
LUAU_FASTINTVARIABLE(LuauNormalizerInitialFuel, 3000)
23
LUAU_FASTFLAG(LuauIntegerType)
24
25
LUAU_FASTFLAGVARIABLE(LuauExternTypesNormalizeWithShapes)
26
27
namespace Luau
28
{
29
30
static bool shouldEarlyExit(NormalizationResult res)
31
{
32
// if res is hit limits, return control flow
33
if (res == NormalizationResult::HitLimits || res == NormalizationResult::False)
34
return true;
35
return false;
36
}
37
38
class NormalizerHitLimits : public std::exception
39
{
40
};
41
42
struct FuelInitializer
43
{
44
NotNull<Normalizer> normalizer;
45
bool initializedFuel;
46
47
explicit FuelInitializer(NotNull<Normalizer> normalizer)
48
: normalizer(normalizer)
49
, initializedFuel(normalizer->initializeFuel())
50
{
51
}
52
53
FuelInitializer(const FuelInitializer& rhs) = delete;
54
55
FuelInitializer& operator=(const FuelInitializer&) = delete;
56
57
~FuelInitializer()
58
{
59
if (initializedFuel)
60
normalizer->clearFuel();
61
}
62
};
63
64
NormalizedStringType::NormalizedStringType() {}
65
66
NormalizedStringType::NormalizedStringType(bool isCofinite, std::map<std::string, TypeId> singletons)
67
: isCofinite(isCofinite)
68
, singletons(std::move(singletons))
69
{
70
}
71
72
void NormalizedStringType::resetToString()
73
{
74
isCofinite = true;
75
singletons.clear();
76
}
77
78
void NormalizedStringType::resetToNever()
79
{
80
isCofinite = false;
81
singletons.clear();
82
}
83
84
bool NormalizedStringType::isNever() const
85
{
86
return !isCofinite && singletons.empty();
87
}
88
89
bool NormalizedStringType::isString() const
90
{
91
return isCofinite && singletons.empty();
92
}
93
94
bool NormalizedStringType::isUnion() const
95
{
96
return !isCofinite;
97
}
98
99
bool NormalizedStringType::isIntersection() const
100
{
101
return isCofinite;
102
}
103
104
bool NormalizedStringType::includes(const std::string& str) const
105
{
106
if (isString())
107
return true;
108
else if (isUnion() && singletons.count(str))
109
return true;
110
else if (isIntersection() && !singletons.count(str))
111
return true;
112
else
113
return false;
114
}
115
116
const NormalizedStringType NormalizedStringType::never;
117
118
bool isSubtype(const NormalizedStringType& subStr, const NormalizedStringType& superStr)
119
{
120
if (subStr.isUnion() && (superStr.isUnion() && !superStr.isNever()))
121
{
122
for (auto [name, ty] : subStr.singletons)
123
{
124
if (!superStr.singletons.count(name))
125
return false;
126
}
127
}
128
else if (subStr.isString() && superStr.isUnion())
129
return false;
130
131
return true;
132
}
133
134
void NormalizedExternType::pushPair(TypeId ty, TypeIds negations)
135
{
136
auto result = externTypes.insert(std::make_pair(ty, std::move(negations)));
137
if (result.second)
138
ordering.push_back(ty);
139
LUAU_ASSERT(ordering.size() == externTypes.size());
140
}
141
142
void NormalizedExternType::resetToNever()
143
{
144
ordering.clear();
145
externTypes.clear();
146
if (FFlag::LuauExternTypesNormalizeWithShapes)
147
shapeExtensions.clear();
148
}
149
150
bool NormalizedExternType::isNever() const
151
{
152
return externTypes.empty();
153
}
154
155
void NormalizedFunctionType::resetToTop()
156
{
157
isTop = true;
158
parts.clear();
159
}
160
161
void NormalizedFunctionType::resetToNever()
162
{
163
isTop = false;
164
parts.clear();
165
}
166
167
bool NormalizedFunctionType::isNever() const
168
{
169
return !isTop && parts.empty();
170
}
171
172
NormalizedType::NormalizedType(NotNull<BuiltinTypes> builtinTypes)
173
: builtinTypes(builtinTypes)
174
, tops(builtinTypes->neverType)
175
, booleans(builtinTypes->neverType)
176
, errors(builtinTypes->neverType)
177
, nils(builtinTypes->neverType)
178
, numbers(builtinTypes->neverType)
179
, integers(builtinTypes->neverType)
180
, strings{NormalizedStringType::never}
181
, threads(builtinTypes->neverType)
182
, buffers(builtinTypes->neverType)
183
{
184
}
185
186
bool NormalizedType::isUnknown() const
187
{
188
if (get<UnknownType>(tops))
189
return true;
190
191
// Otherwise, we can still be unknown!
192
bool hasAllPrimitives;
193
if (FFlag::LuauIntegerType)
194
{
195
hasAllPrimitives = isPrim(booleans, PrimitiveType::Boolean) && isPrim(nils, PrimitiveType::NilType) && isNumber(numbers) &&
196
strings.isString() && isThread(threads) && isBuffer(buffers) && isInteger(integers);
197
}
198
else
199
{
200
hasAllPrimitives = isPrim(booleans, PrimitiveType::Boolean) && isPrim(nils, PrimitiveType::NilType) && isNumber(numbers) &&
201
strings.isString() && isThread(threads) && isBuffer(buffers);
202
}
203
204
// Check is extern type
205
bool isTopExternType = false;
206
for (const auto& [t, disj] : externTypes.externTypes)
207
{
208
if (get<ExternType>(t))
209
{
210
if (t == builtinTypes->externType && disj.empty())
211
{
212
isTopExternType = true;
213
break;
214
}
215
}
216
}
217
// Check is table
218
bool isTopTable = false;
219
for (auto t : tables)
220
{
221
if (isPrim(t, PrimitiveType::Table))
222
{
223
isTopTable = true;
224
break;
225
}
226
}
227
// any = unknown or error ==> we need to make sure we have all the unknown components, but not errors
228
return get<NeverType>(errors) && hasAllPrimitives && isTopExternType && isTopTable && functions.isTop;
229
}
230
231
bool NormalizedType::isExactlyNumber() const
232
{
233
if (FFlag::LuauIntegerType)
234
return hasNumbers() && !hasTops() && !hasBooleans() && !hasExternTypes() && !hasErrors() && !hasNils() && !hasStrings() && !hasThreads() &&
235
!hasBuffers() && !hasTables() && !hasFunctions() && !hasTyvars() && !hasIntegers();
236
else
237
return hasNumbers() && !hasTops() && !hasBooleans() && !hasExternTypes() && !hasErrors() && !hasNils() && !hasStrings() && !hasThreads() &&
238
!hasBuffers() && !hasTables() && !hasFunctions() && !hasTyvars();
239
}
240
241
bool NormalizedType::isSubtypeOfString() const
242
{
243
if (FFlag::LuauIntegerType)
244
return hasStrings() && !hasTops() && !hasBooleans() && !hasExternTypes() && !hasErrors() && !hasNils() && !hasNumbers() && !hasThreads() &&
245
!hasBuffers() && !hasTables() && !hasFunctions() && !hasTyvars() && !hasIntegers();
246
else
247
return hasStrings() && !hasTops() && !hasBooleans() && !hasExternTypes() && !hasErrors() && !hasNils() && !hasNumbers() && !hasThreads() &&
248
!hasBuffers() && !hasTables() && !hasFunctions() && !hasTyvars();
249
}
250
251
bool NormalizedType::isSubtypeOfBooleans() const
252
{
253
if (FFlag::LuauIntegerType)
254
return hasBooleans() && !hasTops() && !hasExternTypes() && !hasErrors() && !hasNils() && !hasNumbers() && !hasStrings() && !hasThreads() &&
255
!hasBuffers() && !hasTables() && !hasFunctions() && !hasTyvars() && !hasIntegers();
256
else
257
return hasBooleans() && !hasTops() && !hasExternTypes() && !hasErrors() && !hasNils() && !hasNumbers() && !hasStrings() && !hasThreads() &&
258
!hasBuffers() && !hasTables() && !hasFunctions() && !hasTyvars();
259
}
260
261
bool NormalizedType::shouldSuppressErrors() const
262
{
263
return hasErrors() || get<AnyType>(tops);
264
}
265
266
bool NormalizedType::hasTopTable() const
267
{
268
return hasTables() && std::any_of(
269
tables.begin(),
270
tables.end(),
271
[&](TypeId ty)
272
{
273
auto primTy = get<PrimitiveType>(ty);
274
return primTy && primTy->type == PrimitiveType::Type::Table;
275
}
276
);
277
}
278
279
bool NormalizedType::hasTops() const
280
{
281
return !get<NeverType>(tops);
282
}
283
284
285
bool NormalizedType::hasBooleans() const
286
{
287
return !get<NeverType>(booleans);
288
}
289
290
bool NormalizedType::hasExternTypes() const
291
{
292
return !externTypes.isNever();
293
}
294
295
bool NormalizedType::hasErrors() const
296
{
297
return !get<NeverType>(errors);
298
}
299
300
bool NormalizedType::hasNils() const
301
{
302
return !get<NeverType>(nils);
303
}
304
305
bool NormalizedType::hasNumbers() const
306
{
307
return !get<NeverType>(numbers);
308
}
309
310
bool NormalizedType::hasIntegers() const
311
{
312
if (FFlag::LuauIntegerType)
313
return get<NeverType>(integers) == nullptr;
314
else
315
return false;
316
}
317
318
bool NormalizedType::hasStrings() const
319
{
320
return !strings.isNever();
321
}
322
323
bool NormalizedType::hasThreads() const
324
{
325
return !get<NeverType>(threads);
326
}
327
328
bool NormalizedType::hasBuffers() const
329
{
330
return !get<NeverType>(buffers);
331
}
332
333
bool NormalizedType::hasTables() const
334
{
335
return !tables.isNever();
336
}
337
338
bool NormalizedType::hasFunctions() const
339
{
340
return !functions.isNever();
341
}
342
343
bool NormalizedType::hasTyvars() const
344
{
345
return !tyvars.empty();
346
}
347
348
bool NormalizedType::isFalsy() const
349
{
350
351
bool hasAFalse = false;
352
if (auto singleton = get<SingletonType>(booleans))
353
{
354
if (auto bs = singleton->variant.get_if<BooleanSingleton>())
355
hasAFalse = !bs->value;
356
}
357
358
if (FFlag::LuauIntegerType)
359
return (hasAFalse || hasNils()) && (!hasTops() && !hasExternTypes() && !hasErrors() && !hasNumbers() && !hasStrings() && !hasThreads() &&
360
!hasBuffers() && !hasTables() && !hasFunctions() && !hasTyvars() && !hasIntegers());
361
else
362
return (hasAFalse || hasNils()) && (!hasTops() && !hasExternTypes() && !hasErrors() && !hasNumbers() && !hasStrings() && !hasThreads() &&
363
!hasBuffers() && !hasTables() && !hasFunctions() && !hasTyvars());
364
}
365
366
bool NormalizedType::isTruthy() const
367
{
368
return !isFalsy();
369
}
370
371
bool NormalizedType::isNil() const
372
{
373
if (!hasNils())
374
return false;
375
376
if (FFlag::LuauIntegerType)
377
return !hasTops() && !hasBooleans() && !hasExternTypes() && !hasNumbers() && !hasStrings() && !hasThreads() && !hasBuffers() &&
378
!hasTables() && !hasFunctions() && !hasTyvars() && !hasIntegers();
379
else
380
return !hasTops() && !hasBooleans() && !hasExternTypes() && !hasNumbers() && !hasStrings() && !hasThreads() && !hasBuffers() &&
381
!hasTables() && !hasFunctions() && !hasTyvars();
382
}
383
384
static bool isShallowInhabited(const NormalizedType& norm)
385
{
386
// This test is just a shallow check, for example it returns `true` for `{ p : never }`
387
if (FFlag::LuauIntegerType)
388
{
389
return !get<NeverType>(norm.tops) || !get<NeverType>(norm.booleans) || !norm.externTypes.isNever() || !get<NeverType>(norm.errors) ||
390
!get<NeverType>(norm.nils) || !get<NeverType>(norm.numbers) || !norm.strings.isNever() || !get<NeverType>(norm.threads) ||
391
(get<NeverType>(norm.buffers) == nullptr) || !norm.functions.isNever() || !norm.tables.empty() || !norm.tyvars.empty() ||
392
(get<NeverType>(norm.integers) == nullptr);
393
}
394
else
395
{
396
return !get<NeverType>(norm.tops) || !get<NeverType>(norm.booleans) || !norm.externTypes.isNever() || !get<NeverType>(norm.errors) ||
397
!get<NeverType>(norm.nils) || !get<NeverType>(norm.numbers) || !norm.strings.isNever() || !get<NeverType>(norm.threads) ||
398
!get<NeverType>(norm.buffers) || !norm.functions.isNever() || !norm.tables.empty() || !norm.tyvars.empty();
399
}
400
}
401
402
NormalizationResult Normalizer::isInhabited(const NormalizedType* norm)
403
{
404
Set<TypeId> seen{nullptr};
405
try
406
{
407
FuelInitializer fi{NotNull{this}};
408
return isInhabited(norm, seen);
409
}
410
catch (const NormalizerHitLimits&)
411
{
412
return NormalizationResult::HitLimits;
413
}
414
}
415
416
NormalizationResult Normalizer::isInhabited(const NormalizedType* norm, Set<TypeId>& seen)
417
{
418
RecursionCounter _rc(&sharedState->counters.recursionCount);
419
if (!withinResourceLimits() || !norm)
420
return NormalizationResult::HitLimits;
421
422
consumeFuel();
423
424
if (FFlag::LuauIntegerType)
425
{
426
if (!get<NeverType>(norm->tops) || !get<NeverType>(norm->booleans) || !get<NeverType>(norm->errors) || !get<NeverType>(norm->nils) ||
427
!get<NeverType>(norm->numbers) || !get<NeverType>(norm->threads) || !get<NeverType>(norm->buffers) || !norm->externTypes.isNever() ||
428
!get<NeverType>(norm->integers) || !norm->strings.isNever() || !norm->functions.isNever())
429
return NormalizationResult::True;
430
}
431
else
432
{
433
if (!get<NeverType>(norm->tops) || !get<NeverType>(norm->booleans) || !get<NeverType>(norm->errors) || !get<NeverType>(norm->nils) ||
434
!get<NeverType>(norm->numbers) || !get<NeverType>(norm->threads) || !get<NeverType>(norm->buffers) || !norm->externTypes.isNever() ||
435
!norm->strings.isNever() || !norm->functions.isNever())
436
return NormalizationResult::True;
437
}
438
439
for (const auto& [_, intersect] : norm->tyvars)
440
{
441
NormalizationResult res = isInhabited(intersect.get(), seen);
442
if (res != NormalizationResult::False)
443
return res;
444
}
445
446
for (TypeId table : norm->tables)
447
{
448
NormalizationResult res = isInhabited(table, seen);
449
if (res != NormalizationResult::False)
450
return res;
451
}
452
453
return NormalizationResult::False;
454
}
455
456
NormalizationResult Normalizer::isInhabited(TypeId ty)
457
{
458
if (cacheInhabitance)
459
{
460
if (bool* result = cachedIsInhabited.find(ty))
461
return *result ? NormalizationResult::True : NormalizationResult::False;
462
}
463
464
Set<TypeId> seen{nullptr};
465
try
466
{
467
FuelInitializer fi{NotNull{this}};
468
NormalizationResult result = isInhabited(ty, seen);
469
470
if (cacheInhabitance && result == NormalizationResult::True)
471
cachedIsInhabited[ty] = true;
472
else if (cacheInhabitance && result == NormalizationResult::False)
473
cachedIsInhabited[ty] = false;
474
475
return result;
476
}
477
catch (const NormalizerHitLimits&)
478
{
479
return NormalizationResult::HitLimits;
480
}
481
}
482
483
NormalizationResult Normalizer::isInhabited(TypeId ty, Set<TypeId>& seen)
484
{
485
RecursionCounter _rc(&sharedState->counters.recursionCount);
486
if (!withinResourceLimits())
487
return NormalizationResult::HitLimits;
488
489
consumeFuel();
490
// TODO: use log.follow(ty), CLI-64291
491
ty = follow(ty);
492
493
if (get<NeverType>(ty))
494
return NormalizationResult::False;
495
496
if (!get<IntersectionType>(ty) && !get<UnionType>(ty) && !get<TableType>(ty) && !get<MetatableType>(ty))
497
return NormalizationResult::True;
498
499
if (seen.count(ty))
500
return NormalizationResult::True;
501
502
seen.insert(ty);
503
504
if (const TableType* ttv = get<TableType>(ty))
505
{
506
for (const auto& [_, prop] : ttv->props)
507
{
508
if (useNewLuauSolver())
509
{
510
// A table enclosing a read property whose type is uninhabitable is also itself uninhabitable,
511
// but not its write property. That just means the write property doesn't exist, and so is readonly.
512
if (auto ty = prop.readTy)
513
{
514
NormalizationResult res = isInhabited(*ty, seen);
515
if (res != NormalizationResult::True)
516
return res;
517
}
518
}
519
else
520
{
521
NormalizationResult res = isInhabited(prop.type_DEPRECATED(), seen);
522
if (res != NormalizationResult::True)
523
return res;
524
}
525
}
526
return NormalizationResult::True;
527
}
528
529
if (const MetatableType* mtv = get<MetatableType>(ty))
530
{
531
NormalizationResult res = isInhabited(mtv->table, seen);
532
if (res != NormalizationResult::True)
533
return res;
534
return isInhabited(mtv->metatable, seen);
535
}
536
537
std::shared_ptr<const NormalizedType> norm = normalize(ty);
538
return isInhabited(norm.get(), seen);
539
}
540
541
NormalizationResult Normalizer::isIntersectionInhabited(TypeId left, TypeId right)
542
{
543
Set<TypeId> seen{nullptr};
544
SeenTablePropPairs seenTablePropPairs{{nullptr, nullptr}};
545
try
546
{
547
FuelInitializer fi{NotNull{this}};
548
return isIntersectionInhabited(left, right, seenTablePropPairs, seen);
549
}
550
catch (const NormalizerHitLimits&)
551
{
552
return NormalizationResult::HitLimits;
553
}
554
}
555
556
NormalizationResult Normalizer::isIntersectionInhabited(TypeId left, TypeId right, SeenTablePropPairs& seenTablePropPairs, Set<TypeId>& seenSet)
557
{
558
consumeFuel();
559
560
left = follow(left);
561
right = follow(right);
562
// We're asking if intersection is inhabited between left and right but we've already seen them ....
563
564
if (cacheInhabitance)
565
{
566
if (bool* result = cachedIsInhabitedIntersection.find({left, right}))
567
return *result ? NormalizationResult::True : NormalizationResult::False;
568
}
569
570
NormalizedType norm{builtinTypes};
571
NormalizationResult res = normalizeIntersections({left, right}, norm, seenTablePropPairs, seenSet);
572
if (res != NormalizationResult::True)
573
{
574
if (cacheInhabitance && res == NormalizationResult::False)
575
cachedIsInhabitedIntersection[{left, right}] = false;
576
577
return res;
578
}
579
580
NormalizationResult result = isInhabited(&norm, seenSet);
581
582
if (cacheInhabitance && result == NormalizationResult::True)
583
cachedIsInhabitedIntersection[{left, right}] = true;
584
else if (cacheInhabitance && result == NormalizationResult::False)
585
cachedIsInhabitedIntersection[{left, right}] = false;
586
587
return result;
588
}
589
590
static int tyvarIndex(TypeId ty)
591
{
592
if (const GenericType* gtv = get<GenericType>(ty))
593
return gtv->index;
594
else if (const FreeType* ftv = get<FreeType>(ty))
595
return ftv->index;
596
else if (const BlockedType* btv = get<BlockedType>(ty))
597
return btv->index;
598
else
599
return 0;
600
}
601
602
static bool isTop(NotNull<BuiltinTypes> builtinTypes, const NormalizedExternType& externTypes)
603
{
604
if (externTypes.externTypes.size() != 1)
605
return false;
606
607
auto first = externTypes.externTypes.begin();
608
if (first->first != builtinTypes->externType)
609
return false;
610
611
if (!first->second.empty())
612
return false;
613
614
return true;
615
}
616
617
static void resetToTop(NotNull<BuiltinTypes> builtinTypes, NormalizedExternType& externTypes)
618
{
619
externTypes.ordering.clear();
620
externTypes.externTypes.clear();
621
externTypes.pushPair(builtinTypes->externType, TypeIds{});
622
}
623
624
#ifdef LUAU_ASSERTENABLED
625
626
static bool isNormalizedTop(TypeId ty)
627
{
628
return get<NeverType>(ty) || get<AnyType>(ty) || get<UnknownType>(ty);
629
}
630
631
static bool isNormalizedBoolean(TypeId ty)
632
{
633
if (get<NeverType>(ty))
634
return true;
635
else if (const PrimitiveType* ptv = get<PrimitiveType>(ty))
636
return ptv->type == PrimitiveType::Boolean;
637
else if (const SingletonType* stv = get<SingletonType>(ty))
638
return get<BooleanSingleton>(stv);
639
else
640
return false;
641
}
642
643
static bool isNormalizedError(TypeId ty)
644
{
645
if (get<NeverType>(ty) || get<ErrorType>(ty))
646
return true;
647
else
648
return false;
649
}
650
651
static bool isNormalizedNil(TypeId ty)
652
{
653
if (get<NeverType>(ty))
654
return true;
655
else if (const PrimitiveType* ptv = get<PrimitiveType>(ty))
656
return ptv->type == PrimitiveType::NilType;
657
else
658
return false;
659
}
660
661
static bool isNormalizedNumber(TypeId ty)
662
{
663
if (get<NeverType>(ty))
664
return true;
665
else if (const PrimitiveType* ptv = get<PrimitiveType>(ty))
666
return ptv->type == PrimitiveType::Number;
667
else
668
return false;
669
}
670
671
static bool isNormalizedInteger(TypeId ty)
672
{
673
if (get<NeverType>(ty))
674
return true;
675
else if (const PrimitiveType* ptv = get<PrimitiveType>(ty))
676
return ptv->type == PrimitiveType::Integer;
677
else
678
return false;
679
}
680
681
static bool isNormalizedString(const NormalizedStringType& ty)
682
{
683
if (ty.isString())
684
return true;
685
686
for (auto& [str, ty] : ty.singletons)
687
{
688
if (const SingletonType* stv = get<SingletonType>(ty))
689
{
690
if (const StringSingleton* sstv = get<StringSingleton>(stv))
691
{
692
if (sstv->value != str)
693
return false;
694
}
695
else
696
return false;
697
}
698
else
699
return false;
700
}
701
702
return true;
703
}
704
705
static bool isNormalizedThread(TypeId ty)
706
{
707
if (get<NeverType>(ty))
708
return true;
709
else if (const PrimitiveType* ptv = get<PrimitiveType>(ty))
710
return ptv->type == PrimitiveType::Thread;
711
else
712
return false;
713
}
714
715
static bool isNormalizedBuffer(TypeId ty)
716
{
717
if (get<NeverType>(ty))
718
return true;
719
else if (const PrimitiveType* ptv = get<PrimitiveType>(ty))
720
return ptv->type == PrimitiveType::Buffer;
721
else
722
return false;
723
}
724
725
static bool areNormalizedFunctions(const NormalizedFunctionType& tys)
726
{
727
for (TypeId ty : tys.parts)
728
{
729
if (!get<FunctionType>(ty) && !get<ErrorType>(ty))
730
return false;
731
}
732
return true;
733
}
734
735
static bool areNormalizedTables(const TypeIds& tys)
736
{
737
for (TypeId ty : tys)
738
{
739
if (get<TableType>(ty) || get<MetatableType>(ty))
740
continue;
741
742
const PrimitiveType* pt = get<PrimitiveType>(ty);
743
if (!pt)
744
return false;
745
746
if (pt->type == PrimitiveType::Table)
747
continue;
748
749
return false;
750
}
751
752
return true;
753
}
754
755
static bool areNormalizedExternTypes(const NormalizedExternType& tys)
756
{
757
for (const auto& [ty, negations] : tys.externTypes)
758
{
759
const ExternType* etv = get<ExternType>(ty);
760
if (!etv)
761
{
762
return false;
763
}
764
765
for (TypeId negation : negations)
766
{
767
const ExternType* nctv = get<ExternType>(negation);
768
if (!nctv)
769
{
770
return false;
771
}
772
773
if (!isSubclass(nctv, etv))
774
{
775
return false;
776
}
777
}
778
779
for (const auto& [otherTy, otherNegations] : tys.externTypes)
780
{
781
if (otherTy == ty)
782
continue;
783
784
const ExternType* octv = get<ExternType>(otherTy);
785
if (!octv)
786
{
787
return false;
788
}
789
790
if (isSubclass(etv, octv))
791
{
792
auto iss = [etv](TypeId t)
793
{
794
const ExternType* c = get<ExternType>(t);
795
if (!c)
796
return false;
797
798
return isSubclass(etv, c);
799
};
800
801
if (!std::any_of(otherNegations.begin(), otherNegations.end(), iss))
802
return false;
803
}
804
}
805
}
806
807
return true;
808
}
809
810
static bool isPlainTyvar(TypeId ty)
811
{
812
return (get<FreeType>(ty) || get<GenericType>(ty) || get<BlockedType>(ty) || get<PendingExpansionType>(ty) || get<TypeFunctionInstanceType>(ty));
813
}
814
815
static bool isNormalizedTyvar(const NormalizedTyvars& tyvars)
816
{
817
for (auto& [tyvar, intersect] : tyvars)
818
{
819
if (!isPlainTyvar(tyvar))
820
return false;
821
if (!isShallowInhabited(*intersect))
822
return false;
823
for (auto& [other, _] : intersect->tyvars)
824
if (tyvarIndex(other) <= tyvarIndex(tyvar))
825
return false;
826
}
827
return true;
828
}
829
830
#endif // LUAU_ASSERTENABLED
831
832
static void assertInvariant(const NormalizedType& norm)
833
{
834
#ifdef LUAU_ASSERTENABLED
835
if (!FFlag::DebugLuauCheckNormalizeInvariant)
836
return;
837
838
LUAU_ASSERT(isNormalizedTop(norm.tops));
839
LUAU_ASSERT(isNormalizedBoolean(norm.booleans));
840
LUAU_ASSERT(areNormalizedExternTypes(norm.externTypes));
841
LUAU_ASSERT(isNormalizedError(norm.errors));
842
LUAU_ASSERT(isNormalizedNil(norm.nils));
843
LUAU_ASSERT(isNormalizedNumber(norm.numbers));
844
if (FFlag::LuauIntegerType)
845
LUAU_ASSERT(isNormalizedInteger(norm.integers));
846
LUAU_ASSERT(isNormalizedString(norm.strings));
847
LUAU_ASSERT(isNormalizedThread(norm.threads));
848
LUAU_ASSERT(isNormalizedBuffer(norm.buffers));
849
LUAU_ASSERT(areNormalizedFunctions(norm.functions));
850
LUAU_ASSERT(areNormalizedTables(norm.tables));
851
LUAU_ASSERT(isNormalizedTyvar(norm.tyvars));
852
for (auto& [_, child] : norm.tyvars)
853
assertInvariant(*child);
854
#endif
855
}
856
857
Normalizer::Normalizer(
858
TypeArena* arena,
859
NotNull<BuiltinTypes> builtinTypes,
860
NotNull<UnifierSharedState> sharedState,
861
SolverMode solverMode,
862
bool cacheInhabitance
863
)
864
: arena(arena)
865
, builtinTypes(builtinTypes)
866
, sharedState(sharedState)
867
, cacheInhabitance(cacheInhabitance)
868
, solverMode(solverMode)
869
{
870
}
871
872
static bool isCacheable(TypeId ty, Set<TypeId>& seen);
873
874
static bool isCacheable(TypePackId tp, Set<TypeId>& seen)
875
{
876
tp = follow(tp);
877
878
auto it = begin(tp);
879
auto endIt = end(tp);
880
for (; it != endIt; ++it)
881
{
882
if (!isCacheable(*it, seen))
883
return false;
884
}
885
886
if (auto tail = it.tail())
887
{
888
if (get<FreeTypePack>(*tail) || get<BlockedTypePack>(*tail) || get<TypeFunctionInstanceTypePack>(*tail))
889
return false;
890
}
891
892
return true;
893
}
894
895
static bool isCacheable(TypeId ty, Set<TypeId>& seen)
896
{
897
if (seen.contains(ty))
898
return true;
899
seen.insert(ty);
900
901
ty = follow(ty);
902
903
if (get<FreeType>(ty) || get<BlockedType>(ty) || get<PendingExpansionType>(ty))
904
return false;
905
906
if (auto tfi = get<TypeFunctionInstanceType>(ty))
907
{
908
for (TypeId t : tfi->typeArguments)
909
{
910
if (!isCacheable(t, seen))
911
return false;
912
}
913
914
for (TypePackId tp : tfi->packArguments)
915
{
916
if (!isCacheable(tp, seen))
917
return false;
918
}
919
}
920
921
return true;
922
}
923
924
static bool isCacheable(TypeId ty)
925
{
926
Set<TypeId> seen{nullptr};
927
return isCacheable(ty, seen);
928
}
929
930
std::shared_ptr<const NormalizedType> Normalizer::normalize(TypeId ty)
931
{
932
if (!arena)
933
sharedState->iceHandler->ice("Normalizing types outside a module");
934
935
auto found = cachedNormals.find(ty);
936
if (found != cachedNormals.end())
937
return found->second;
938
939
NormalizedType norm{builtinTypes};
940
Set<TypeId> seenSetTypes{nullptr};
941
SeenTablePropPairs seenTablePropPairs{{nullptr, nullptr}};
942
943
try
944
{
945
FuelInitializer fi{NotNull{this}};
946
NormalizationResult res = unionNormalWithTy(norm, ty, seenTablePropPairs, seenSetTypes);
947
if (res != NormalizationResult::True)
948
return nullptr;
949
}
950
catch (const NormalizerHitLimits&)
951
{
952
return nullptr;
953
}
954
955
if (norm.isUnknown())
956
{
957
clearNormal(norm);
958
norm.tops = builtinTypes->unknownType;
959
}
960
961
std::shared_ptr<NormalizedType> shared = std::make_shared<NormalizedType>(std::move(norm));
962
963
if (shared->isCacheable)
964
cachedNormals[ty] = shared;
965
966
return shared;
967
}
968
969
NormalizationResult Normalizer::normalizeIntersections(
970
const std::vector<TypeId>& intersections,
971
NormalizedType& outType,
972
SeenTablePropPairs& seenTablePropPairs,
973
Set<TypeId>& seenSet
974
)
975
{
976
if (!arena)
977
sharedState->iceHandler->ice("Normalizing types outside a module");
978
979
consumeFuel();
980
981
NormalizedType norm{builtinTypes};
982
norm.tops = builtinTypes->unknownType;
983
// Now we need to intersect the two types
984
for (auto ty : intersections)
985
{
986
NormalizationResult res = intersectNormalWithTy(norm, ty, seenTablePropPairs, seenSet);
987
if (res != NormalizationResult::True)
988
return res;
989
}
990
991
NormalizationResult res = unionNormals(outType, norm);
992
if (res != NormalizationResult::True)
993
return res;
994
995
return NormalizationResult::True;
996
}
997
998
void Normalizer::clearNormal(NormalizedType& norm)
999
{
1000
norm.tops = builtinTypes->neverType;
1001
norm.booleans = builtinTypes->neverType;
1002
norm.externTypes.resetToNever();
1003
norm.errors = builtinTypes->neverType;
1004
norm.nils = builtinTypes->neverType;
1005
norm.numbers = builtinTypes->neverType;
1006
norm.integers = builtinTypes->neverType;
1007
norm.strings.resetToNever();
1008
norm.threads = builtinTypes->neverType;
1009
norm.buffers = builtinTypes->neverType;
1010
norm.tables.clear();
1011
norm.functions.resetToNever();
1012
norm.tyvars.clear();
1013
}
1014
1015
// ------- Cached TypeIds
1016
const TypeIds* Normalizer::cacheTypeIds(TypeIds tys)
1017
{
1018
auto found = cachedTypeIds.find(&tys);
1019
if (found != cachedTypeIds.end())
1020
return found->first;
1021
1022
std::unique_ptr<TypeIds> uniq = std::make_unique<TypeIds>(std::move(tys));
1023
const TypeIds* result = uniq.get();
1024
cachedTypeIds[result] = std::move(uniq);
1025
return result;
1026
}
1027
1028
TypeId Normalizer::unionType(TypeId here, TypeId there)
1029
{
1030
consumeFuel();
1031
1032
here = follow(here);
1033
there = follow(there);
1034
1035
if (here == there)
1036
return here;
1037
if (get<NeverType>(here) || get<AnyType>(there))
1038
return there;
1039
if (get<NeverType>(there) || get<AnyType>(here))
1040
return here;
1041
1042
TypeIds tmps;
1043
1044
if (const UnionType* utv = get<UnionType>(here))
1045
{
1046
TypeIds heres;
1047
heres.insert(begin(utv), end(utv));
1048
tmps.insert(heres.begin(), heres.end());
1049
cachedUnions[cacheTypeIds(std::move(heres))] = here;
1050
}
1051
else
1052
tmps.insert(here);
1053
1054
if (const UnionType* utv = get<UnionType>(there))
1055
{
1056
TypeIds theres;
1057
theres.insert(begin(utv), end(utv));
1058
tmps.insert(theres.begin(), theres.end());
1059
cachedUnions[cacheTypeIds(std::move(theres))] = there;
1060
}
1061
else
1062
tmps.insert(there);
1063
1064
auto cacheHit = cachedUnions.find(&tmps);
1065
if (cacheHit != cachedUnions.end())
1066
return cacheHit->second;
1067
1068
std::vector<TypeId> parts;
1069
parts.insert(parts.end(), tmps.begin(), tmps.end());
1070
TypeId result = arena->addType(UnionType{std::move(parts)});
1071
cachedUnions[cacheTypeIds(std::move(tmps))] = result;
1072
1073
return result;
1074
}
1075
1076
TypeId Normalizer::intersectionType(TypeId here, TypeId there)
1077
{
1078
consumeFuel();
1079
1080
here = follow(here);
1081
there = follow(there);
1082
1083
if (here == there)
1084
return here;
1085
if (get<NeverType>(here) || get<AnyType>(there))
1086
return here;
1087
if (get<NeverType>(there) || get<AnyType>(here))
1088
return there;
1089
1090
TypeIds tmps;
1091
1092
if (const IntersectionType* utv = get<IntersectionType>(here))
1093
{
1094
TypeIds heres;
1095
heres.insert(begin(utv), end(utv));
1096
tmps.insert(heres.begin(), heres.end());
1097
cachedIntersections[cacheTypeIds(std::move(heres))] = here;
1098
}
1099
else
1100
tmps.insert(here);
1101
1102
if (const IntersectionType* utv = get<IntersectionType>(there))
1103
{
1104
TypeIds theres;
1105
theres.insert(begin(utv), end(utv));
1106
tmps.insert(theres.begin(), theres.end());
1107
cachedIntersections[cacheTypeIds(std::move(theres))] = there;
1108
}
1109
else
1110
tmps.insert(there);
1111
1112
if (tmps.size() == 1)
1113
return *tmps.begin();
1114
1115
auto cacheHit = cachedIntersections.find(&tmps);
1116
if (cacheHit != cachedIntersections.end())
1117
return cacheHit->second;
1118
1119
std::vector<TypeId> parts;
1120
parts.insert(parts.end(), tmps.begin(), tmps.end());
1121
TypeId result = arena->addType(IntersectionType{std::move(parts)});
1122
cachedIntersections[cacheTypeIds(std::move(tmps))] = result;
1123
1124
return result;
1125
}
1126
1127
void Normalizer::clearCaches()
1128
{
1129
cachedNormals.clear();
1130
cachedIntersections.clear();
1131
cachedUnions.clear();
1132
cachedTypeIds.clear();
1133
}
1134
1135
// ------- Normalizing unions
1136
TypeId Normalizer::unionOfTops(TypeId here, TypeId there)
1137
{
1138
consumeFuel();
1139
1140
if (get<NeverType>(here) || get<AnyType>(there))
1141
return there;
1142
else
1143
return here;
1144
}
1145
1146
TypeId Normalizer::unionOfBools(TypeId here, TypeId there)
1147
{
1148
consumeFuel();
1149
1150
if (get<NeverType>(here))
1151
return there;
1152
if (get<NeverType>(there))
1153
return here;
1154
if (const BooleanSingleton* hbool = get<BooleanSingleton>(get<SingletonType>(here)))
1155
if (const BooleanSingleton* tbool = get<BooleanSingleton>(get<SingletonType>(there)))
1156
if (hbool->value == tbool->value)
1157
return here;
1158
return builtinTypes->booleanType;
1159
}
1160
1161
void Normalizer::unionExternTypesWithExternType(TypeIds& heres, TypeId there)
1162
{
1163
consumeFuel();
1164
1165
if (heres.count(there))
1166
return;
1167
1168
const ExternType* tctv = get<ExternType>(there);
1169
1170
for (auto it = heres.begin(); it != heres.end();)
1171
{
1172
TypeId here = *it;
1173
const ExternType* hctv = get<ExternType>(here);
1174
if (isSubclass(tctv, hctv))
1175
return;
1176
else if (isSubclass(hctv, tctv))
1177
it = heres.erase(it);
1178
else
1179
it++;
1180
}
1181
1182
heres.insert(there);
1183
}
1184
1185
void Normalizer::unionExternTypes(TypeIds& heres, const TypeIds& theres)
1186
{
1187
consumeFuel();
1188
1189
for (TypeId there : theres)
1190
unionExternTypesWithExternType(heres, there);
1191
}
1192
1193
static bool isSubclass(TypeId test, TypeId parent)
1194
{
1195
const ExternType* testCtv = get<ExternType>(test);
1196
const ExternType* parentCtv = get<ExternType>(parent);
1197
1198
LUAU_ASSERT(testCtv);
1199
LUAU_ASSERT(parentCtv);
1200
1201
return isSubclass(testCtv, parentCtv);
1202
}
1203
1204
void Normalizer::unionExternTypesWithExternType(NormalizedExternType& heres, TypeId there)
1205
{
1206
consumeFuel();
1207
1208
for (auto it = heres.ordering.begin(); it != heres.ordering.end();)
1209
{
1210
TypeId hereTy = *it;
1211
TypeIds& hereNegations = heres.externTypes.at(hereTy);
1212
1213
// If the incoming class is a subclass of another class in the map, we
1214
// must ensure that it is negated by one of the negations in the same
1215
// cluster. If it isn't, we do not need to insert it - the subtyping
1216
// relationship is already handled by this entry. If it is, we must
1217
// insert it, to capture the presence of this particular subtype.
1218
if (isSubclass(there, hereTy))
1219
{
1220
for (auto nIt = hereNegations.begin(); nIt != hereNegations.end();)
1221
{
1222
TypeId hereNegation = *nIt;
1223
1224
// If the incoming class is a subclass of one of the negations,
1225
// we must insert it into the class map.
1226
if (isSubclass(there, hereNegation))
1227
{
1228
heres.pushPair(there, TypeIds{});
1229
return;
1230
}
1231
// If the incoming class is a superclass of one of the
1232
// negations, then the negation no longer applies and must be
1233
// removed. This is also true if they are equal. Since extern types
1234
// are, at this time, entirely persistent (we do not clone
1235
// them), a pointer identity check is sufficient.
1236
else if (isSubclass(hereNegation, there))
1237
{
1238
nIt = hereNegations.erase(nIt);
1239
}
1240
// If the incoming class is unrelated to the negation, we move
1241
// on to the next item.
1242
else
1243
{
1244
++nIt;
1245
}
1246
}
1247
1248
// If, at the end of the above loop, we haven't returned, that means
1249
// that the class is not a subclass of one of the negations, and is
1250
// covered by the existing subtype relationship. We can return now.
1251
return;
1252
}
1253
// If the incoming class is a superclass of another class in the map, we
1254
// need to replace the existing class with the incoming class,
1255
// preserving the relevant negations.
1256
else if (isSubclass(hereTy, there))
1257
{
1258
TypeIds negations = std::move(hereNegations);
1259
it = heres.ordering.erase(it);
1260
heres.externTypes.erase(hereTy);
1261
1262
heres.pushPair(there, std::move(negations));
1263
return;
1264
}
1265
1266
// If the incoming class is unrelated to the class in the map, we move
1267
// on. If we do not otherwise exit from this method body, we will
1268
// eventually fall out of this loop and insert the incoming class, which
1269
// we have proven to be completely unrelated to any class in the map,
1270
// into the map itself.
1271
++it;
1272
}
1273
1274
heres.pushPair(there, TypeIds{});
1275
}
1276
1277
void Normalizer::unionExternTypes(NormalizedExternType& heres, const NormalizedExternType& theres)
1278
{
1279
// This method bears much similarity with unionExternTypesWithExternType, but is
1280
// solving a more general problem. In unionExternTypesWithExternType, we are dealing
1281
// with a singular positive type. Since it's one type, we can use early
1282
// returns as control flow. Since it's guaranteed to be positive, we do not
1283
// have negations to worry about combining. The two aspects combine to make
1284
// the tasks this method must perform different enough to warrant a separate
1285
// implementation.
1286
consumeFuel();
1287
1288
for (const TypeId thereTy : theres.ordering)
1289
{
1290
const TypeIds& thereNegations = theres.externTypes.at(thereTy);
1291
1292
// If it happens that there are _no_ extern types in the current map, or the
1293
// incoming class is completely unrelated to any class in the current
1294
// map, we must insert the incoming pair as-is.
1295
bool insert = true;
1296
1297
for (auto it = heres.ordering.begin(); it != heres.ordering.end();)
1298
{
1299
TypeId hereTy = *it;
1300
TypeIds& hereNegations = heres.externTypes.at(hereTy);
1301
1302
if (isSubclass(thereTy, hereTy))
1303
{
1304
bool inserted = false;
1305
for (auto nIt = hereNegations.begin(); nIt != hereNegations.end();)
1306
{
1307
TypeId hereNegateTy = *nIt;
1308
1309
// If the incoming class is a subclass of one of the negations,
1310
// we must insert it into the class map.
1311
if (isSubclass(thereTy, hereNegateTy))
1312
{
1313
// We do not concern ourselves with iterator
1314
// invalidation here because we will break out of the
1315
// loop over `heres` when `inserted` is set, and we do
1316
// not read from the iterator after this point.
1317
inserted = true;
1318
heres.pushPair(thereTy, thereNegations);
1319
break;
1320
}
1321
// If the incoming class is a superclass of one of the
1322
// negations, then the negation no longer applies and must
1323
// be removed. This is also true if they are equal. Since
1324
// extern types are, at this time, entirely persistent (we do not
1325
// clone them), a pointer identity check is sufficient.
1326
else if (isSubclass(hereNegateTy, thereTy))
1327
{
1328
inserted = true;
1329
nIt = hereNegations.erase(nIt);
1330
break;
1331
}
1332
// If the incoming class is unrelated to the negation, we
1333
// move on to the next item.
1334
else
1335
{
1336
++nIt;
1337
}
1338
}
1339
1340
if (inserted)
1341
{
1342
insert = false;
1343
break;
1344
}
1345
}
1346
else if (isSubclass(hereTy, thereTy))
1347
{
1348
TypeIds negations = std::move(hereNegations);
1349
unionExternTypes(negations, thereNegations);
1350
1351
it = heres.ordering.erase(it);
1352
heres.externTypes.erase(hereTy);
1353
heres.pushPair(thereTy, std::move(negations));
1354
insert = false;
1355
break;
1356
}
1357
else if (hereTy == thereTy)
1358
{
1359
unionExternTypes(hereNegations, thereNegations);
1360
insert = false;
1361
break;
1362
}
1363
1364
++it;
1365
}
1366
1367
if (insert)
1368
{
1369
heres.pushPair(thereTy, thereNegations);
1370
1371
if (FFlag::LuauExternTypesNormalizeWithShapes)
1372
{
1373
for (TypeId shape : theres.shapeExtensions)
1374
heres.shapeExtensions.insert(shape);
1375
}
1376
}
1377
}
1378
}
1379
1380
void Normalizer::unionStrings(NormalizedStringType& here, const NormalizedStringType& there)
1381
{
1382
consumeFuel();
1383
1384
if (there.isString())
1385
here.resetToString();
1386
else if (here.isUnion() && there.isUnion())
1387
here.singletons.insert(there.singletons.begin(), there.singletons.end());
1388
else if (here.isUnion() && there.isIntersection())
1389
{
1390
here.isCofinite = true;
1391
for (const auto& pair : there.singletons)
1392
{
1393
auto it = here.singletons.find(pair.first);
1394
if (it != end(here.singletons))
1395
here.singletons.erase(it);
1396
else
1397
here.singletons.insert(pair);
1398
}
1399
}
1400
else if (here.isIntersection() && there.isUnion())
1401
{
1402
for (const auto& [name, ty] : there.singletons)
1403
here.singletons.erase(name);
1404
}
1405
else if (here.isIntersection() && there.isIntersection())
1406
{
1407
auto iter = begin(here.singletons);
1408
auto endIter = end(here.singletons);
1409
1410
while (iter != endIter)
1411
{
1412
if (!there.singletons.count(iter->first))
1413
{
1414
auto eraseIt = iter;
1415
++iter;
1416
here.singletons.erase(eraseIt);
1417
}
1418
else
1419
++iter;
1420
}
1421
}
1422
else
1423
LUAU_ASSERT(!"Unreachable");
1424
}
1425
1426
std::optional<TypePackId> Normalizer::unionOfTypePacks(TypePackId here, TypePackId there)
1427
{
1428
consumeFuel();
1429
1430
if (here == there)
1431
return here;
1432
1433
std::vector<TypeId> head;
1434
std::optional<TypePackId> tail;
1435
1436
bool hereSubThere = true;
1437
bool thereSubHere = true;
1438
1439
TypePackIterator ith = begin(here);
1440
TypePackIterator itt = begin(there);
1441
1442
while (ith != end(here) && itt != end(there))
1443
{
1444
TypeId hty = *ith;
1445
TypeId tty = *itt;
1446
TypeId ty = unionType(hty, tty);
1447
if (ty != hty)
1448
thereSubHere = false;
1449
if (ty != tty)
1450
hereSubThere = false;
1451
head.push_back(ty);
1452
ith++;
1453
itt++;
1454
}
1455
1456
auto dealWithDifferentArities =
1457
[&](TypePackIterator& ith, TypePackIterator itt, TypePackId here, TypePackId there, bool& hereSubThere, bool& thereSubHere)
1458
{
1459
if (ith != end(here))
1460
{
1461
TypeId tty = builtinTypes->nilType;
1462
if (std::optional<TypePackId> ttail = itt.tail())
1463
{
1464
if (const VariadicTypePack* tvtp = get<VariadicTypePack>(*ttail))
1465
tty = tvtp->ty;
1466
else
1467
// Luau doesn't have unions of type pack variables
1468
return false;
1469
}
1470
else
1471
// Type packs of different arities are incomparable
1472
return false;
1473
1474
while (ith != end(here))
1475
{
1476
TypeId hty = *ith;
1477
TypeId ty = unionType(hty, tty);
1478
if (ty != hty)
1479
thereSubHere = false;
1480
if (ty != tty)
1481
hereSubThere = false;
1482
head.push_back(ty);
1483
ith++;
1484
}
1485
}
1486
return true;
1487
};
1488
1489
if (!dealWithDifferentArities(ith, itt, here, there, hereSubThere, thereSubHere))
1490
return std::nullopt;
1491
1492
if (!dealWithDifferentArities(itt, ith, there, here, thereSubHere, hereSubThere))
1493
return std::nullopt;
1494
1495
if (std::optional<TypePackId> htail = ith.tail())
1496
{
1497
if (std::optional<TypePackId> ttail = itt.tail())
1498
{
1499
if (*htail == *ttail)
1500
tail = htail;
1501
else if (const VariadicTypePack* hvtp = get<VariadicTypePack>(*htail))
1502
{
1503
if (const VariadicTypePack* tvtp = get<VariadicTypePack>(*ttail))
1504
{
1505
TypeId ty = unionType(hvtp->ty, tvtp->ty);
1506
if (ty != hvtp->ty)
1507
thereSubHere = false;
1508
if (ty != tvtp->ty)
1509
hereSubThere = false;
1510
bool hidden = hvtp->hidden & tvtp->hidden;
1511
tail = arena->addTypePack(VariadicTypePack{ty, hidden});
1512
}
1513
else
1514
// Luau doesn't have unions of type pack variables
1515
return std::nullopt;
1516
}
1517
else
1518
// Luau doesn't have unions of type pack variables
1519
return std::nullopt;
1520
}
1521
else if (get<VariadicTypePack>(*htail))
1522
{
1523
hereSubThere = false;
1524
tail = htail;
1525
}
1526
else
1527
// Luau doesn't have unions of type pack variables
1528
return std::nullopt;
1529
}
1530
else if (std::optional<TypePackId> ttail = itt.tail())
1531
{
1532
if (get<VariadicTypePack>(*ttail))
1533
{
1534
thereSubHere = false;
1535
tail = htail;
1536
}
1537
else
1538
// Luau doesn't have unions of type pack variables
1539
return std::nullopt;
1540
}
1541
1542
if (hereSubThere)
1543
return there;
1544
else if (thereSubHere)
1545
return here;
1546
if (!head.empty())
1547
return arena->addTypePack(TypePack{std::move(head), tail});
1548
else if (tail)
1549
return *tail;
1550
else
1551
// TODO: Add an emptyPack to singleton types
1552
return arena->addTypePack({});
1553
}
1554
1555
std::optional<TypeId> Normalizer::unionOfFunctions(TypeId here, TypeId there)
1556
{
1557
consumeFuel();
1558
1559
if (get<ErrorType>(here))
1560
return here;
1561
1562
if (get<ErrorType>(there))
1563
return there;
1564
1565
const FunctionType* hftv = get<FunctionType>(here);
1566
LUAU_ASSERT(hftv);
1567
const FunctionType* tftv = get<FunctionType>(there);
1568
LUAU_ASSERT(tftv);
1569
1570
if (hftv->generics != tftv->generics)
1571
return std::nullopt;
1572
if (hftv->genericPacks != tftv->genericPacks)
1573
return std::nullopt;
1574
1575
std::optional<TypePackId> argTypes = intersectionOfTypePacks_INTERNAL(hftv->argTypes, tftv->argTypes);
1576
if (!argTypes)
1577
return std::nullopt;
1578
1579
std::optional<TypePackId> retTypes = unionOfTypePacks(hftv->retTypes, tftv->retTypes);
1580
if (!retTypes)
1581
return std::nullopt;
1582
1583
if (*argTypes == hftv->argTypes && *retTypes == hftv->retTypes)
1584
return here;
1585
if (*argTypes == tftv->argTypes && *retTypes == tftv->retTypes)
1586
return there;
1587
1588
FunctionType result{*argTypes, *retTypes};
1589
result.generics = hftv->generics;
1590
result.genericPacks = hftv->genericPacks;
1591
return arena->addType(std::move(result));
1592
}
1593
1594
void Normalizer::unionFunctions(NormalizedFunctionType& heres, const NormalizedFunctionType& theres)
1595
{
1596
consumeFuel();
1597
1598
if (heres.isTop)
1599
return;
1600
if (theres.isTop)
1601
heres.resetToTop();
1602
1603
if (theres.isNever())
1604
return;
1605
1606
TypeIds tmps;
1607
1608
if (heres.isNever())
1609
{
1610
tmps.insert(theres.parts.begin(), theres.parts.end());
1611
heres.parts = std::move(tmps);
1612
return;
1613
}
1614
1615
for (TypeId here : heres.parts)
1616
for (TypeId there : theres.parts)
1617
{
1618
if (std::optional<TypeId> fun = unionOfFunctions(here, there))
1619
tmps.insert(*fun);
1620
else
1621
tmps.insert(builtinTypes->errorRecoveryType(there));
1622
}
1623
1624
heres.parts = std::move(tmps);
1625
}
1626
1627
void Normalizer::unionFunctionsWithFunction(NormalizedFunctionType& heres, TypeId there)
1628
{
1629
consumeFuel();
1630
1631
if (heres.isNever())
1632
{
1633
TypeIds tmps;
1634
tmps.insert(there);
1635
heres.parts = std::move(tmps);
1636
return;
1637
}
1638
1639
TypeIds tmps;
1640
for (TypeId here : heres.parts)
1641
{
1642
if (std::optional<TypeId> fun = unionOfFunctions(here, there))
1643
tmps.insert(*fun);
1644
else
1645
tmps.insert(builtinTypes->errorRecoveryType(there));
1646
}
1647
heres.parts = std::move(tmps);
1648
}
1649
1650
void Normalizer::unionTablesWithTable(TypeIds& heres, TypeId there)
1651
{
1652
// TODO: remove unions of tables where possible
1653
1654
// we can always skip `never`
1655
if (get<NeverType>(there))
1656
return;
1657
1658
heres.insert(there);
1659
}
1660
1661
void Normalizer::unionTables(TypeIds& heres, const TypeIds& theres)
1662
{
1663
consumeFuel();
1664
1665
for (TypeId there : theres)
1666
{
1667
if (there == builtinTypes->tableType)
1668
{
1669
heres.clear();
1670
heres.insert(there);
1671
return;
1672
}
1673
else
1674
{
1675
unionTablesWithTable(heres, there);
1676
}
1677
}
1678
}
1679
1680
// So why `ignoreSmallerTyvars`?
1681
//
1682
// First up, what it does... Every tyvar has an index, and this parameter says to ignore
1683
// any tyvars in `there` if their index is less than or equal to the parameter.
1684
// The parameter is always greater than any tyvars mentioned in here, so the result is
1685
// a lower bound on any tyvars in `here.tyvars`.
1686
//
1687
// This is used to maintain in invariant, which is that in any tyvar `X&T`, any any tyvar
1688
// `Y&U` in `T`, the index of `X` is less than the index of `Y`. This is an implementation
1689
// of *ordered decision diagrams* (https://en.wikipedia.org/wiki/Binary_decision_diagram#Variable_ordering)
1690
// which are a compression technique used to save memory usage when representing boolean formulae.
1691
//
1692
// The idea is that if you have an out-of-order decision diagram
1693
// like `Z&(X|Y)`, to re-order it in this case to `(X&Z)|(Y&Z)`.
1694
// The hope is that by imposing a global order, there's a higher chance of sharing opportunities,
1695
// and hence reduced memory.
1696
//
1697
// And yes, this is essentially a SAT solver hidden inside a typechecker.
1698
// That's what you get for having a type system with generics, intersection and union types.
1699
NormalizationResult Normalizer::unionNormals(NormalizedType& here, const NormalizedType& there, int ignoreSmallerTyvars)
1700
{
1701
consumeFuel();
1702
1703
here.isCacheable &= there.isCacheable;
1704
1705
TypeId tops = unionOfTops(here.tops, there.tops);
1706
if (get<UnknownType>(tops) && (get<ErrorType>(here.errors) || get<ErrorType>(there.errors)))
1707
tops = builtinTypes->anyType;
1708
if (!get<NeverType>(tops))
1709
{
1710
clearNormal(here);
1711
here.tops = tops;
1712
return NormalizationResult::True;
1713
}
1714
1715
for (auto it = there.tyvars.begin(); it != there.tyvars.end(); it++)
1716
{
1717
TypeId tyvar = it->first;
1718
const NormalizedType& inter = *it->second;
1719
int index = tyvarIndex(tyvar);
1720
if (index <= ignoreSmallerTyvars)
1721
continue;
1722
auto [emplaced, fresh] = here.tyvars.emplace(tyvar, std::make_unique<NormalizedType>(NormalizedType{builtinTypes}));
1723
if (fresh)
1724
{
1725
NormalizationResult res = unionNormals(*emplaced->second, here, index);
1726
if (res != NormalizationResult::True)
1727
return res;
1728
}
1729
1730
NormalizationResult res = unionNormals(*emplaced->second, inter, index);
1731
if (res != NormalizationResult::True)
1732
return res;
1733
}
1734
1735
here.booleans = unionOfBools(here.booleans, there.booleans);
1736
unionExternTypes(here.externTypes, there.externTypes);
1737
1738
here.errors = (get<NeverType>(there.errors) ? here.errors : there.errors);
1739
here.nils = (get<NeverType>(there.nils) ? here.nils : there.nils);
1740
here.numbers = (get<NeverType>(there.numbers) ? here.numbers : there.numbers);
1741
if (FFlag::LuauIntegerType)
1742
here.integers = (get<NeverType>(there.integers) ? here.integers : there.integers);
1743
unionStrings(here.strings, there.strings);
1744
here.threads = (get<NeverType>(there.threads) ? here.threads : there.threads);
1745
here.buffers = (get<NeverType>(there.buffers) ? here.buffers : there.buffers);
1746
unionFunctions(here.functions, there.functions);
1747
unionTables(here.tables, there.tables);
1748
1749
return NormalizationResult::True;
1750
}
1751
1752
bool Normalizer::withinResourceLimits()
1753
{
1754
// If cache is too large, clear it
1755
if (FInt::LuauNormalizeCacheLimit > 0)
1756
{
1757
size_t cacheUsage = cachedNormals.size() + cachedIntersections.size() + cachedUnions.size() + cachedTypeIds.size() +
1758
cachedIsInhabited.size() + cachedIsInhabitedIntersection.size();
1759
if (cacheUsage > size_t(FInt::LuauNormalizeCacheLimit))
1760
{
1761
clearCaches();
1762
return false;
1763
}
1764
}
1765
1766
// Check the recursion count
1767
if (sharedState->counters.recursionLimit > 0)
1768
if (sharedState->counters.recursionLimit < sharedState->counters.recursionCount)
1769
return false;
1770
1771
return true;
1772
}
1773
1774
bool Normalizer::useNewLuauSolver() const
1775
{
1776
return solverMode == SolverMode::New;
1777
}
1778
1779
NormalizationResult Normalizer::intersectNormalWithNegationTy(TypeId toNegate, NormalizedType& intersect)
1780
{
1781
consumeFuel();
1782
1783
std::optional<NormalizedType> negated;
1784
1785
std::shared_ptr<const NormalizedType> normal = normalize(toNegate);
1786
negated = negateNormal(*normal);
1787
1788
if (!negated)
1789
return NormalizationResult::False;
1790
intersectNormals(intersect, *negated);
1791
return NormalizationResult::True;
1792
}
1793
1794
// See above for an explanation of `ignoreSmallerTyvars`.
1795
NormalizationResult Normalizer::unionNormalWithTy(
1796
NormalizedType& here,
1797
TypeId there,
1798
SeenTablePropPairs& seenTablePropPairs,
1799
Set<TypeId>& seenSetTypes,
1800
int ignoreSmallerTyvars
1801
)
1802
{
1803
RecursionCounter _rc(&sharedState->counters.recursionCount);
1804
if (!withinResourceLimits())
1805
return NormalizationResult::HitLimits;
1806
1807
consumeFuel();
1808
1809
there = follow(there);
1810
1811
if (get<AnyType>(there) || get<UnknownType>(there))
1812
{
1813
TypeId tops = unionOfTops(here.tops, there);
1814
if (get<UnknownType>(tops) && get<ErrorType>(here.errors))
1815
tops = builtinTypes->anyType;
1816
clearNormal(here);
1817
here.tops = tops;
1818
return NormalizationResult::True;
1819
}
1820
else if (get<NeverType>(there) || get<AnyType>(here.tops))
1821
return NormalizationResult::True;
1822
else if (get<ErrorType>(there) && get<UnknownType>(here.tops))
1823
{
1824
here.tops = builtinTypes->anyType;
1825
return NormalizationResult::True;
1826
}
1827
else if (const UnionType* utv = get<UnionType>(there))
1828
{
1829
if (seenSetTypes.count(there))
1830
return NormalizationResult::True;
1831
seenSetTypes.insert(there);
1832
1833
for (UnionTypeIterator it = begin(utv); it != end(utv); ++it)
1834
{
1835
NormalizationResult res = unionNormalWithTy(here, *it, seenTablePropPairs, seenSetTypes);
1836
if (res != NormalizationResult::True)
1837
{
1838
seenSetTypes.erase(there);
1839
return res;
1840
}
1841
}
1842
1843
seenSetTypes.erase(there);
1844
return NormalizationResult::True;
1845
}
1846
else if (const IntersectionType* itv = get<IntersectionType>(there))
1847
{
1848
if (seenSetTypes.count(there))
1849
return NormalizationResult::True;
1850
seenSetTypes.insert(there);
1851
1852
NormalizedType norm{builtinTypes};
1853
norm.tops = builtinTypes->unknownType;
1854
for (IntersectionTypeIterator it = begin(itv); it != end(itv); ++it)
1855
{
1856
NormalizationResult res = intersectNormalWithTy(norm, *it, seenTablePropPairs, seenSetTypes);
1857
if (res != NormalizationResult::True)
1858
{
1859
seenSetTypes.erase(there);
1860
return res;
1861
}
1862
}
1863
1864
seenSetTypes.erase(there);
1865
1866
return unionNormals(here, norm);
1867
}
1868
else if (get<UnknownType>(here.tops))
1869
return NormalizationResult::True;
1870
else if (get<GenericType>(there) || get<FreeType>(there) || get<BlockedType>(there) || get<PendingExpansionType>(there) ||
1871
get<TypeFunctionInstanceType>(there))
1872
{
1873
if (tyvarIndex(there) <= ignoreSmallerTyvars)
1874
return NormalizationResult::True;
1875
NormalizedType inter{builtinTypes};
1876
inter.tops = builtinTypes->unknownType;
1877
here.tyvars.insert_or_assign(there, std::make_unique<NormalizedType>(std::move(inter)));
1878
1879
if (!isCacheable(there))
1880
here.isCacheable = false;
1881
}
1882
else if (get<FunctionType>(there))
1883
unionFunctionsWithFunction(here.functions, there);
1884
else if (get<TableType>(there) || get<MetatableType>(there))
1885
unionTablesWithTable(here.tables, there);
1886
else if (get<ExternType>(there))
1887
unionExternTypesWithExternType(here.externTypes, there);
1888
else if (get<ErrorType>(there))
1889
here.errors = there;
1890
else if (const PrimitiveType* ptv = get<PrimitiveType>(there))
1891
{
1892
if (ptv->type == PrimitiveType::Boolean)
1893
here.booleans = there;
1894
else if (ptv->type == PrimitiveType::NilType)
1895
here.nils = there;
1896
else if (ptv->type == PrimitiveType::Number)
1897
here.numbers = there;
1898
else if (FFlag::LuauIntegerType && (ptv->type == PrimitiveType::Integer))
1899
here.integers = there;
1900
else if (ptv->type == PrimitiveType::String)
1901
here.strings.resetToString();
1902
else if (ptv->type == PrimitiveType::Thread)
1903
here.threads = there;
1904
else if (ptv->type == PrimitiveType::Buffer)
1905
here.buffers = there;
1906
else if (ptv->type == PrimitiveType::Function)
1907
{
1908
here.functions.resetToTop();
1909
}
1910
else if (ptv->type == PrimitiveType::Table)
1911
{
1912
here.tables.clear();
1913
here.tables.insert(there);
1914
}
1915
else
1916
LUAU_ASSERT(!"Unreachable");
1917
}
1918
else if (const SingletonType* stv = get<SingletonType>(there))
1919
{
1920
if (get<BooleanSingleton>(stv))
1921
here.booleans = unionOfBools(here.booleans, there);
1922
else if (const StringSingleton* sstv = get<StringSingleton>(stv))
1923
{
1924
if (here.strings.isCofinite)
1925
{
1926
auto it = here.strings.singletons.find(sstv->value);
1927
if (it != here.strings.singletons.end())
1928
here.strings.singletons.erase(it);
1929
}
1930
else
1931
here.strings.singletons.insert({sstv->value, there});
1932
}
1933
else
1934
LUAU_ASSERT(!"Unreachable");
1935
}
1936
else if (const NegationType* ntv = get<NegationType>(there))
1937
{
1938
std::optional<NormalizedType> tn;
1939
1940
std::shared_ptr<const NormalizedType> thereNormal = normalize(ntv->ty);
1941
tn = negateNormal(*thereNormal);
1942
1943
if (!tn)
1944
return NormalizationResult::False;
1945
1946
NormalizationResult res = unionNormals(here, *tn);
1947
if (res != NormalizationResult::True)
1948
return res;
1949
}
1950
else if (get<PendingExpansionType>(there) || get<TypeFunctionInstanceType>(there) || get<NoRefineType>(there))
1951
{
1952
// nothing
1953
}
1954
else
1955
LUAU_ASSERT(!"Unreachable");
1956
1957
for (auto& [tyvar, intersect] : here.tyvars)
1958
{
1959
NormalizationResult res = unionNormalWithTy(*intersect, there, seenTablePropPairs, seenSetTypes, tyvarIndex(tyvar));
1960
if (res != NormalizationResult::True)
1961
return res;
1962
}
1963
1964
assertInvariant(here);
1965
return NormalizationResult::True;
1966
}
1967
1968
// ------- Negations
1969
1970
std::optional<NormalizedType> Normalizer::negateNormal(const NormalizedType& here)
1971
{
1972
consumeFuel();
1973
1974
NormalizedType result{builtinTypes};
1975
result.isCacheable = here.isCacheable;
1976
1977
if (!get<NeverType>(here.tops))
1978
{
1979
// The negation of unknown or any is never. Easy.
1980
return result;
1981
}
1982
1983
if (!get<NeverType>(here.errors))
1984
{
1985
// Negating an error yields the same error.
1986
result.errors = here.errors;
1987
return result;
1988
}
1989
1990
if (get<NeverType>(here.booleans))
1991
result.booleans = builtinTypes->booleanType;
1992
else if (get<PrimitiveType>(here.booleans))
1993
result.booleans = builtinTypes->neverType;
1994
else if (auto stv = get<SingletonType>(here.booleans))
1995
{
1996
auto boolean = get<BooleanSingleton>(stv);
1997
LUAU_ASSERT(boolean != nullptr);
1998
if (boolean->value)
1999
result.booleans = builtinTypes->falseType;
2000
else
2001
result.booleans = builtinTypes->trueType;
2002
}
2003
2004
if (here.externTypes.isNever())
2005
{
2006
resetToTop(builtinTypes, result.externTypes);
2007
}
2008
else if (isTop(builtinTypes, result.externTypes))
2009
{
2010
result.externTypes.resetToNever();
2011
}
2012
else
2013
{
2014
TypeIds rootNegations{};
2015
2016
for (const auto& [hereParent, hereNegations] : here.externTypes.externTypes)
2017
{
2018
if (hereParent != builtinTypes->externType)
2019
rootNegations.insert(hereParent);
2020
2021
for (TypeId hereNegation : hereNegations)
2022
unionExternTypesWithExternType(result.externTypes, hereNegation);
2023
}
2024
2025
if (!rootNegations.empty())
2026
result.externTypes.pushPair(builtinTypes->externType, std::move(rootNegations));
2027
}
2028
2029
result.nils = get<NeverType>(here.nils) ? builtinTypes->nilType : builtinTypes->neverType;
2030
result.numbers = get<NeverType>(here.numbers) ? builtinTypes->numberType : builtinTypes->neverType;
2031
if (FFlag::LuauIntegerType)
2032
result.integers = get<NeverType>(here.integers) ? builtinTypes->integerType : builtinTypes->neverType;
2033
2034
result.strings = here.strings;
2035
result.strings.isCofinite = !result.strings.isCofinite;
2036
2037
result.threads = get<NeverType>(here.threads) ? builtinTypes->threadType : builtinTypes->neverType;
2038
result.buffers = get<NeverType>(here.buffers) ? builtinTypes->bufferType : builtinTypes->neverType;
2039
2040
/*
2041
* Things get weird and so, so complicated if we allow negations of
2042
* arbitrary function types. Ordinary code can never form these kinds of
2043
* types, so we decline to negate them.
2044
*/
2045
if (here.functions.isNever())
2046
result.functions.resetToTop();
2047
else if (here.functions.isTop)
2048
result.functions.resetToNever();
2049
else
2050
return std::nullopt;
2051
2052
/*
2053
* It is not possible to negate an arbitrary table type, because function
2054
* types are not runtime-testable. Thus, we prohibit negation of anything
2055
* other than `table` and `never`.
2056
*/
2057
if (here.tables.empty())
2058
result.tables.insert(builtinTypes->tableType);
2059
else if (here.tables.size() == 1 && here.tables.front() == builtinTypes->tableType)
2060
result.tables.clear();
2061
else
2062
return std::nullopt;
2063
2064
// TODO: negating tables
2065
// TODO: negating tyvars?
2066
2067
return result;
2068
}
2069
2070
TypeIds Normalizer::negateAll(const TypeIds& theres)
2071
{
2072
consumeFuel();
2073
2074
TypeIds tys;
2075
for (TypeId there : theres)
2076
tys.insert(negate(there));
2077
return tys;
2078
}
2079
2080
TypeId Normalizer::negate(TypeId there)
2081
{
2082
consumeFuel();
2083
2084
there = follow(there);
2085
if (get<AnyType>(there))
2086
return there;
2087
else if (get<UnknownType>(there))
2088
return builtinTypes->neverType;
2089
else if (get<NeverType>(there))
2090
return builtinTypes->unknownType;
2091
else if (auto ntv = get<NegationType>(there))
2092
return ntv->ty; // TODO: do we want to normalize this?
2093
else if (auto utv = get<UnionType>(there))
2094
{
2095
std::vector<TypeId> parts;
2096
for (TypeId option : utv)
2097
parts.push_back(negate(option));
2098
return arena->addType(IntersectionType{std::move(parts)});
2099
}
2100
else if (auto itv = get<IntersectionType>(there))
2101
{
2102
std::vector<TypeId> options;
2103
for (TypeId part : itv)
2104
options.push_back(negate(part));
2105
return arena->addType(UnionType{std::move(options)});
2106
}
2107
else
2108
return there;
2109
}
2110
2111
void Normalizer::subtractPrimitive(NormalizedType& here, TypeId ty)
2112
{
2113
consumeFuel();
2114
2115
const PrimitiveType* ptv = get<PrimitiveType>(follow(ty));
2116
LUAU_ASSERT(ptv);
2117
switch (ptv->type)
2118
{
2119
case PrimitiveType::NilType:
2120
here.nils = builtinTypes->neverType;
2121
break;
2122
case PrimitiveType::Boolean:
2123
here.booleans = builtinTypes->neverType;
2124
break;
2125
case PrimitiveType::Number:
2126
here.numbers = builtinTypes->neverType;
2127
break;
2128
case PrimitiveType::Integer:
2129
here.integers = builtinTypes->neverType;
2130
break;
2131
case PrimitiveType::String:
2132
here.strings.resetToNever();
2133
break;
2134
case PrimitiveType::Thread:
2135
here.threads = builtinTypes->neverType;
2136
break;
2137
case PrimitiveType::Buffer:
2138
here.buffers = builtinTypes->neverType;
2139
break;
2140
case PrimitiveType::Function:
2141
here.functions.resetToNever();
2142
break;
2143
case PrimitiveType::Table:
2144
here.tables.clear();
2145
break;
2146
}
2147
}
2148
2149
void Normalizer::subtractSingleton(NormalizedType& here, TypeId ty)
2150
{
2151
consumeFuel();
2152
2153
const SingletonType* stv = get<SingletonType>(ty);
2154
LUAU_ASSERT(stv);
2155
2156
if (const StringSingleton* ss = get<StringSingleton>(stv))
2157
{
2158
if (here.strings.isCofinite)
2159
here.strings.singletons.insert({ss->value, ty});
2160
else
2161
{
2162
auto it = here.strings.singletons.find(ss->value);
2163
if (it != here.strings.singletons.end())
2164
here.strings.singletons.erase(it);
2165
}
2166
}
2167
else if (const BooleanSingleton* bs = get<BooleanSingleton>(stv))
2168
{
2169
if (get<NeverType>(here.booleans))
2170
{
2171
// Nothing
2172
}
2173
else if (get<PrimitiveType>(here.booleans))
2174
here.booleans = bs->value ? builtinTypes->falseType : builtinTypes->trueType;
2175
else if (auto hereSingleton = get<SingletonType>(here.booleans))
2176
{
2177
const BooleanSingleton* hereBooleanSingleton = get<BooleanSingleton>(hereSingleton);
2178
LUAU_ASSERT(hereBooleanSingleton);
2179
2180
// Crucial subtlety: ty (and thus bs) are the value that is being
2181
// negated out. We therefore reduce to never when the values match,
2182
// rather than when they differ.
2183
if (bs->value == hereBooleanSingleton->value)
2184
here.booleans = builtinTypes->neverType;
2185
}
2186
else
2187
LUAU_ASSERT(!"Unreachable");
2188
}
2189
else
2190
LUAU_ASSERT(!"Unreachable");
2191
}
2192
2193
// ------- Normalizing intersections
2194
TypeId Normalizer::intersectionOfTops(TypeId here, TypeId there)
2195
{
2196
consumeFuel();
2197
// NOTE: We need to wrap these in parens as C++'s parser isn't _quite_
2198
// able to recognize these are generic function calls after macro
2199
// expansion.
2200
LUAU_ASSERT((is<NeverType, UnknownType, AnyType>(here)));
2201
LUAU_ASSERT((is<NeverType, UnknownType, AnyType>(there)));
2202
2203
if (get<NeverType>(here) || get<NeverType>(there))
2204
return builtinTypes->neverType;
2205
2206
if (get<AnyType>(here) || get<AnyType>(there))
2207
return builtinTypes->anyType;
2208
2209
return builtinTypes->unknownType;
2210
}
2211
2212
TypeId Normalizer::intersectionOfBools(TypeId here, TypeId there)
2213
{
2214
consumeFuel();
2215
2216
if (get<NeverType>(here))
2217
return here;
2218
if (get<NeverType>(there))
2219
return there;
2220
if (const BooleanSingleton* hbool = get<BooleanSingleton>(get<SingletonType>(here)))
2221
if (const BooleanSingleton* tbool = get<BooleanSingleton>(get<SingletonType>(there)))
2222
return (hbool->value == tbool->value ? here : builtinTypes->neverType);
2223
else
2224
return here;
2225
else
2226
return there;
2227
}
2228
2229
void Normalizer::intersectExternTypes(NormalizedExternType& heres, const NormalizedExternType& theres)
2230
{
2231
consumeFuel();
2232
2233
if (theres.isNever())
2234
{
2235
heres.resetToNever();
2236
return;
2237
}
2238
else if (isTop(builtinTypes, theres))
2239
{
2240
return;
2241
}
2242
2243
// For intersections of two distinct class sets, we must normalize to a map
2244
// where, for each entry, one of the following is true:
2245
// - The class is the superclass of all other classes in the map
2246
// - The class is a subclass of another class B in the map _and_ a subclass
2247
// of one of B's negations.
2248
//
2249
// Once we have identified the common superclass, we proceed down the list
2250
// of class types. For each class and negation pair in the incoming set, we
2251
// check each entry in the current set.
2252
// - If the incoming class is exactly identical to a class in the current
2253
// set, we union the negations together and move on.
2254
// - If the incoming class is a subclass of a class in the current set, we
2255
// replace the current class with the incoming class. We keep negations
2256
// that are a subclass of the incoming class, and discard ones that
2257
// aren't.
2258
// - If the incoming class is a superclass of a class in the current set, we
2259
// take the negations that are a subclass of the current class and union
2260
// them with the negations for the current class.
2261
// - If the incoming class is unrelated to any class in the current set, we
2262
// declare the result of the intersection operation to be never.
2263
for (const TypeId thereTy : theres.ordering)
2264
{
2265
const TypeIds& thereNegations = theres.externTypes.at(thereTy);
2266
2267
for (auto it = heres.ordering.begin(); it != heres.ordering.end();)
2268
{
2269
TypeId hereTy = *it;
2270
TypeIds& hereNegations = heres.externTypes.at(hereTy);
2271
2272
if (isSubclass(thereTy, hereTy))
2273
{
2274
// If thereTy is a subtype of hereTy, we need to replace hereTy
2275
// by thereTy and combine their negation lists.
2276
//
2277
// If any types in the negation list are not subtypes of
2278
// thereTy, they need to be removed from the negation list.
2279
TypeIds negations = std::move(hereNegations);
2280
2281
for (auto nIt = negations.begin(); nIt != negations.end();)
2282
{
2283
if (!isSubclass(*nIt, thereTy))
2284
{
2285
nIt = negations.erase(nIt);
2286
}
2287
else
2288
{
2289
++nIt;
2290
}
2291
}
2292
2293
unionExternTypes(negations, thereNegations);
2294
2295
it = heres.ordering.erase(it);
2296
heres.externTypes.erase(hereTy);
2297
heres.pushPair(thereTy, std::move(negations));
2298
break;
2299
}
2300
else if (isSubclass(hereTy, thereTy))
2301
{
2302
// If thereTy is a supertype of hereTy, we need to extend the
2303
// negation list of hereTy by that of thereTy.
2304
//
2305
// If any of the types of thereTy's negations are not subtypes
2306
// of hereTy, they must not be added to hereTy's negation list.
2307
//
2308
// If any of the types of thereTy's negations are supertypes of
2309
// hereTy, then hereTy must be removed entirely.
2310
//
2311
// If any of the types of thereTy's negations are supertypes of
2312
// the negations of herety, the former must supplant the latter.
2313
TypeIds negations = thereNegations;
2314
2315
bool erasedHere = false;
2316
2317
for (auto nIt = negations.begin(); nIt != negations.end();)
2318
{
2319
if (isSubclass(hereTy, *nIt))
2320
{
2321
// eg SomeExternType & (class & ~SomeExternType)
2322
// or SomeExternType & (class & ~ParentExternType)
2323
heres.externTypes.erase(hereTy);
2324
it = heres.ordering.erase(it);
2325
erasedHere = true;
2326
break;
2327
}
2328
2329
// eg SomeExternType & (class & ~Unrelated)
2330
if (!isSubclass(*nIt, hereTy))
2331
nIt = negations.erase(nIt);
2332
else
2333
++nIt;
2334
}
2335
2336
if (!erasedHere)
2337
{
2338
unionExternTypes(hereNegations, negations);
2339
++it;
2340
}
2341
}
2342
else if (hereTy == thereTy)
2343
{
2344
unionExternTypes(hereNegations, thereNegations);
2345
break;
2346
}
2347
else
2348
{
2349
it = heres.ordering.erase(it);
2350
heres.externTypes.erase(hereTy);
2351
}
2352
}
2353
}
2354
}
2355
2356
void Normalizer::intersectExternTypesWithExternType(NormalizedExternType& heres, TypeId there)
2357
{
2358
consumeFuel();
2359
2360
for (auto it = heres.ordering.begin(); it != heres.ordering.end();)
2361
{
2362
TypeId hereTy = *it;
2363
const TypeIds& hereNegations = heres.externTypes.at(hereTy);
2364
2365
// If the incoming class _is_ the current class, we skip it. Maybe
2366
// another entry will have a different story. We check for this first
2367
// because isSubclass will be true if the types are equal, and entering
2368
// either of those branches below will trigger wrong behaviors.
2369
if (hereTy == there)
2370
{
2371
++it;
2372
}
2373
// If the incoming class is a subclass of this type, we replace the
2374
// current class with the incoming class. We preserve negations that are
2375
// a subclass of the incoming class, and discard ones that aren't.
2376
else if (isSubclass(there, hereTy))
2377
{
2378
TypeIds negations = std::move(hereNegations);
2379
bool emptyIntersectWithNegation = false;
2380
2381
for (auto nIt = negations.begin(); nIt != negations.end();)
2382
{
2383
if (isSubclass(there, *nIt))
2384
{
2385
// Hitting this block means that the incoming class is a
2386
// subclass of this type, _and_ one of its negations is a
2387
// superclass of this type, e.g.:
2388
//
2389
// Dog & ~Animal
2390
//
2391
// Clearly this intersects to never, so we mark this class as
2392
// being removed from the normalized class type.
2393
emptyIntersectWithNegation = true;
2394
break;
2395
}
2396
2397
if (!isSubclass(*nIt, there))
2398
{
2399
nIt = negations.erase(nIt);
2400
}
2401
else
2402
{
2403
++nIt;
2404
}
2405
}
2406
2407
it = heres.ordering.erase(it);
2408
heres.externTypes.erase(hereTy);
2409
if (!emptyIntersectWithNegation)
2410
heres.pushPair(there, std::move(negations));
2411
break;
2412
}
2413
// If the incoming class is a superclass of the current class, we don't
2414
// insert it into the map.
2415
else if (isSubclass(hereTy, there))
2416
{
2417
return;
2418
}
2419
// If the incoming class is completely unrelated to the current class,
2420
// we drop the current class from the map.
2421
else
2422
{
2423
it = heres.ordering.erase(it);
2424
heres.externTypes.erase(hereTy);
2425
}
2426
}
2427
}
2428
2429
void Normalizer::intersectExternTypesWithShape(NormalizedExternType& heres, TypeId there)
2430
{
2431
LUAU_ASSERT(FFlag::LuauExternTypesNormalizeWithShapes);
2432
2433
consumeFuel();
2434
2435
// in this case, we want to take the foreign function types we have here, and we want to intersect a table type into them.
2436
// the idea here is that table types function as structural definitions for the shape of some data type.
2437
2438
auto shape = get<TableType>(there);
2439
2440
// if the type we're intersecting with isn't a table type, it can't be used to describe a shape.
2441
if (!shape)
2442
return;
2443
2444
// we need to check that every property in the shape is compatible with the main extern type, `hereTy`.
2445
// if any intersection of their property types is uninhabited, then the whole thing is uninhabited.
2446
// but if the type is inhabited, we'll want to add it to the shapes on the externtype.
2447
2448
bool isCoincident = true;
2449
for (const auto& [name, shapeProp] : shape->props)
2450
{
2451
for (auto it = heres.ordering.begin(); it != heres.ordering.end(); it++)
2452
{
2453
// TODO: do we need to take into account any of the negations here as well for the compatibility check?
2454
2455
TypeId hereTy = *it;
2456
ExternType* externTy = getMutable<ExternType>(hereTy);
2457
2458
auto found = externTy->props.find(name);
2459
// if the property isn't present, we can move onto the next property since it's a fine extension.
2460
if (found == externTy->props.end())
2461
{
2462
isCoincident = false;
2463
continue;
2464
}
2465
2466
// if the property is present, we need to check that the two properties are compatible.
2467
// if they're not, then the whole thing is `never`, as with other incompatible intersections.
2468
2469
Property prop = found->second;
2470
2471
// if they both have read properties, we have to check that the intersection of those read properties is inhabited
2472
if (prop.readTy && shapeProp.readTy)
2473
{
2474
// if the intersection is uninhabited, then we can reset to `never` and we're done.
2475
if (isIntersectionInhabited(*prop.readTy, *shapeProp.readTy) != NormalizationResult::True)
2476
{
2477
heres.resetToNever();
2478
return;
2479
}
2480
2481
if (relate(*prop.readTy, *shapeProp.readTy) != Relation::Coincident)
2482
isCoincident = false;
2483
}
2484
2485
// if they both have write properties, we also want to check if they're coincident.
2486
// unlike with read types, we don't have to check that the intersection is inhabited because
2487
// even if the types don't overlap, something like `{ write prop: string } & { write prop: number }`
2488
// behaviorally suggests that we could write `string` or `number` into `prop`.
2489
if (prop.writeTy && shapeProp.writeTy)
2490
{
2491
// if the types aren't coincident, then the whole shapes can't be coincident
2492
if (relate(*prop.writeTy, *shapeProp.writeTy) != Relation::Coincident)
2493
isCoincident = false;
2494
}
2495
}
2496
}
2497
2498
// if we've made it here, then we should've validated that the intersection between the shape and the extern type is legal, so we can add it to
2499
// the collection of shapes.
2500
// TODO: we may want to look into some more deduplication here.
2501
if (!isCoincident)
2502
heres.shapeExtensions.insert(there);
2503
}
2504
2505
void Normalizer::intersectStrings(NormalizedStringType& here, const NormalizedStringType& there)
2506
{
2507
consumeFuel();
2508
2509
/* There are 9 cases to worry about here
2510
Normalized Left | Normalized Right
2511
C1 string | string ===> trivial
2512
C2 string - {u_1,..} | string ===> trivial
2513
C3 {u_1, ..} | string ===> trivial
2514
C4 string | string - {v_1, ..} ===> string - {v_1, ..}
2515
C5 string - {u_1,..} | string - {v_1, ..} ===> string - ({u_s} U {v_s})
2516
C6 {u_1, ..} | string - {v_1, ..} ===> {u_s} - {v_s}
2517
C7 string | {v_1, ..} ===> {v_s}
2518
C8 string - {u_1,..} | {v_1, ..} ===> {v_s} - {u_s}
2519
C9 {u_1, ..} | {v_1, ..} ===> {u_s} ∩ {v_s}
2520
*/
2521
// Case 1,2,3
2522
if (there.isString())
2523
return;
2524
// Case 4, Case 7
2525
else if (here.isString())
2526
{
2527
here.singletons.clear();
2528
for (const auto& [key, type] : there.singletons)
2529
here.singletons[key] = type;
2530
here.isCofinite = here.isCofinite && there.isCofinite;
2531
}
2532
// Case 5
2533
else if (here.isIntersection() && there.isIntersection())
2534
{
2535
here.isCofinite = true;
2536
for (const auto& [key, type] : there.singletons)
2537
here.singletons[key] = type;
2538
}
2539
// Case 6
2540
else if (here.isUnion() && there.isIntersection())
2541
{
2542
here.isCofinite = false;
2543
for (const auto& [key, _] : there.singletons)
2544
here.singletons.erase(key);
2545
}
2546
// Case 8
2547
else if (here.isIntersection() && there.isUnion())
2548
{
2549
here.isCofinite = false;
2550
std::map<std::string, TypeId> result(there.singletons);
2551
for (const auto& [key, _] : here.singletons)
2552
result.erase(key);
2553
here.singletons = result;
2554
}
2555
// Case 9
2556
else if (here.isUnion() && there.isUnion())
2557
{
2558
here.isCofinite = false;
2559
std::map<std::string, TypeId> result;
2560
result.insert(here.singletons.begin(), here.singletons.end());
2561
result.insert(there.singletons.begin(), there.singletons.end());
2562
for (auto it = result.begin(); it != result.end();)
2563
if (!here.singletons.count(it->first) || !there.singletons.count(it->first))
2564
it = result.erase(it);
2565
else
2566
++it;
2567
here.singletons = result;
2568
}
2569
else
2570
LUAU_ASSERT(0 && "Internal Error - unrecognized case");
2571
}
2572
2573
std::optional<TypePackId> Normalizer::intersectionOfTypePacks(TypePackId here, TypePackId there)
2574
{
2575
FuelInitializer fi{NotNull{this}};
2576
2577
try
2578
{
2579
return intersectionOfTypePacks_INTERNAL(here, there);
2580
}
2581
catch (const NormalizerHitLimits&)
2582
{
2583
return std::nullopt;
2584
}
2585
}
2586
2587
std::optional<TypePackId> Normalizer::intersectionOfTypePacks_INTERNAL(TypePackId here, TypePackId there)
2588
{
2589
consumeFuel();
2590
2591
if (here == there)
2592
return here;
2593
2594
std::vector<TypeId> head;
2595
std::optional<TypePackId> tail;
2596
2597
bool hereSubThere = true;
2598
bool thereSubHere = true;
2599
2600
TypePackIterator ith = begin(here);
2601
TypePackIterator itt = begin(there);
2602
2603
while (ith != end(here) && itt != end(there))
2604
{
2605
TypeId hty = *ith;
2606
TypeId tty = *itt;
2607
TypeId ty = intersectionType(hty, tty);
2608
if (ty != hty)
2609
hereSubThere = false;
2610
if (ty != tty)
2611
thereSubHere = false;
2612
head.push_back(ty);
2613
ith++;
2614
itt++;
2615
}
2616
2617
auto dealWithDifferentArities =
2618
[&](TypePackIterator& ith, TypePackIterator itt, TypePackId here, TypePackId there, bool& hereSubThere, bool& thereSubHere)
2619
{
2620
if (ith != end(here))
2621
{
2622
TypeId tty = builtinTypes->nilType;
2623
if (std::optional<TypePackId> ttail = itt.tail())
2624
{
2625
if (const VariadicTypePack* tvtp = get<VariadicTypePack>(*ttail))
2626
tty = tvtp->ty;
2627
else
2628
// Luau doesn't have intersections of type pack variables
2629
return false;
2630
}
2631
else
2632
// Type packs of different arities are incomparable
2633
return false;
2634
2635
while (ith != end(here))
2636
{
2637
TypeId hty = *ith;
2638
TypeId ty = intersectionType(hty, tty);
2639
if (ty != hty)
2640
hereSubThere = false;
2641
if (ty != tty)
2642
thereSubHere = false;
2643
head.push_back(ty);
2644
ith++;
2645
}
2646
}
2647
return true;
2648
};
2649
2650
if (!dealWithDifferentArities(ith, itt, here, there, hereSubThere, thereSubHere))
2651
return std::nullopt;
2652
2653
if (!dealWithDifferentArities(itt, ith, there, here, thereSubHere, hereSubThere))
2654
return std::nullopt;
2655
2656
if (std::optional<TypePackId> htail = ith.tail())
2657
{
2658
if (std::optional<TypePackId> ttail = itt.tail())
2659
{
2660
if (*htail == *ttail)
2661
tail = htail;
2662
else if (const VariadicTypePack* hvtp = get<VariadicTypePack>(*htail))
2663
{
2664
if (const VariadicTypePack* tvtp = get<VariadicTypePack>(*ttail))
2665
{
2666
TypeId ty = intersectionType(hvtp->ty, tvtp->ty);
2667
if (ty != hvtp->ty)
2668
thereSubHere = false;
2669
if (ty != tvtp->ty)
2670
hereSubThere = false;
2671
bool hidden = hvtp->hidden & tvtp->hidden;
2672
tail = arena->addTypePack(VariadicTypePack{ty, hidden});
2673
}
2674
else
2675
// Luau doesn't have unions of type pack variables
2676
return std::nullopt;
2677
}
2678
else
2679
// Luau doesn't have unions of type pack variables
2680
return std::nullopt;
2681
}
2682
else if (get<VariadicTypePack>(*htail))
2683
hereSubThere = false;
2684
else
2685
// Luau doesn't have unions of type pack variables
2686
return std::nullopt;
2687
}
2688
else if (std::optional<TypePackId> ttail = itt.tail())
2689
{
2690
if (get<VariadicTypePack>(*ttail))
2691
thereSubHere = false;
2692
else
2693
// Luau doesn't have unions of type pack variables
2694
return std::nullopt;
2695
}
2696
2697
if (hereSubThere)
2698
return here;
2699
else if (thereSubHere)
2700
return there;
2701
if (!head.empty())
2702
return arena->addTypePack(TypePack{std::move(head), tail});
2703
else if (tail)
2704
return *tail;
2705
else
2706
// TODO: Add an emptyPack to singleton types
2707
return arena->addTypePack({});
2708
}
2709
2710
std::optional<TypeId> Normalizer::intersectionOfTables(TypeId here, TypeId there, SeenTablePropPairs& seenTablePropPairs, Set<TypeId>& seenSet)
2711
{
2712
consumeFuel();
2713
2714
if (here == there)
2715
return here;
2716
2717
RecursionCounter _rc(&sharedState->counters.recursionCount);
2718
if (sharedState->counters.recursionLimit > 0 && sharedState->counters.recursionLimit < sharedState->counters.recursionCount)
2719
return std::nullopt;
2720
2721
if (isPrim(here, PrimitiveType::Table))
2722
return there;
2723
else if (isPrim(there, PrimitiveType::Table))
2724
return here;
2725
2726
if (get<NeverType>(here))
2727
return there;
2728
else if (get<NeverType>(there))
2729
return here;
2730
else if (get<AnyType>(here))
2731
return there;
2732
else if (get<AnyType>(there))
2733
return here;
2734
2735
TypeId htable = here;
2736
TypeId hmtable = nullptr;
2737
if (const MetatableType* hmtv = get<MetatableType>(here))
2738
{
2739
htable = follow(hmtv->table);
2740
hmtable = follow(hmtv->metatable);
2741
}
2742
TypeId ttable = there;
2743
TypeId tmtable = nullptr;
2744
if (const MetatableType* tmtv = get<MetatableType>(there))
2745
{
2746
ttable = follow(tmtv->table);
2747
tmtable = follow(tmtv->metatable);
2748
}
2749
2750
const TableType* httv = get<TableType>(htable);
2751
if (!httv)
2752
return std::nullopt;
2753
2754
const TableType* tttv = get<TableType>(ttable);
2755
if (!tttv)
2756
return std::nullopt;
2757
2758
2759
if (httv->state == TableState::Free || tttv->state == TableState::Free)
2760
return std::nullopt;
2761
if (httv->state == TableState::Generic || tttv->state == TableState::Generic)
2762
return std::nullopt;
2763
2764
TableState state = httv->state;
2765
if (tttv->state == TableState::Unsealed)
2766
state = tttv->state;
2767
2768
TypeLevel level = max(httv->level, tttv->level);
2769
Scope* scope = max(httv->scope, tttv->scope);
2770
2771
std::unique_ptr<TableType> result = nullptr;
2772
bool hereSubThere = true;
2773
bool thereSubHere = true;
2774
2775
for (const auto& [name, hprop] : httv->props)
2776
{
2777
Property prop = hprop;
2778
auto tfound = tttv->props.find(name);
2779
if (tfound == tttv->props.end())
2780
thereSubHere = false;
2781
else
2782
{
2783
const auto& [_name, tprop] = *tfound;
2784
// TODO: variance issues here, which can't be fixed until we have read/write property types
2785
if (useNewLuauSolver())
2786
{
2787
if (hprop.readTy.has_value())
2788
{
2789
if (tprop.readTy.has_value())
2790
{
2791
TypeId ty = simplifyIntersection(builtinTypes, NotNull{arena}, *hprop.readTy, *tprop.readTy).result;
2792
2793
// If any property is going to get mapped to `never`, we can just call the entire table `never`.
2794
// Since this check is syntactic, we may sometimes miss simplifying tables with complex uninhabited properties.
2795
// Prior versions of this code attempted to do this semantically using the normalization machinery, but this
2796
// mistakenly causes infinite loops when giving more complex recursive table types. As it stands, this approach
2797
// will continue to scale as simplification is improved, but we may wish to reintroduce the semantic approach
2798
// once we have revisited the usage of seen sets systematically (and possibly with some additional guarding to recognize
2799
// when types are infinitely-recursive with non-pointer identical instances of them, or some guard to prevent that
2800
// construction altogether). See also: `gh1632_no_infinite_recursion_in_normalization`
2801
if (get<NeverType>(ty))
2802
return {builtinTypes->neverType};
2803
2804
prop.readTy = ty;
2805
hereSubThere &= (ty == hprop.readTy);
2806
thereSubHere &= (ty == tprop.readTy);
2807
}
2808
else
2809
{
2810
prop.readTy = *hprop.readTy;
2811
thereSubHere = false;
2812
}
2813
}
2814
else if (tprop.readTy.has_value())
2815
{
2816
prop.readTy = *tprop.readTy;
2817
hereSubThere = false;
2818
}
2819
2820
if (hprop.writeTy.has_value())
2821
{
2822
if (tprop.writeTy.has_value())
2823
{
2824
prop.writeTy = simplifyIntersection(builtinTypes, NotNull{arena}, *hprop.writeTy, *tprop.writeTy).result;
2825
hereSubThere &= (prop.writeTy == hprop.writeTy);
2826
thereSubHere &= (prop.writeTy == tprop.writeTy);
2827
}
2828
else
2829
{
2830
prop.writeTy = *hprop.writeTy;
2831
thereSubHere = false;
2832
}
2833
}
2834
else if (tprop.writeTy.has_value())
2835
{
2836
prop.writeTy = *tprop.writeTy;
2837
hereSubThere = false;
2838
}
2839
}
2840
else
2841
{
2842
prop.setType(intersectionType(hprop.type_DEPRECATED(), tprop.type_DEPRECATED()));
2843
hereSubThere &= (prop.type_DEPRECATED() == hprop.type_DEPRECATED());
2844
thereSubHere &= (prop.type_DEPRECATED() == tprop.type_DEPRECATED());
2845
}
2846
}
2847
2848
// TODO: string indexers
2849
2850
if (prop.readTy || prop.writeTy)
2851
{
2852
if (!result.get())
2853
result = std::make_unique<TableType>(TableType{state, level, scope});
2854
result->props[name] = prop;
2855
}
2856
}
2857
2858
for (const auto& [name, tprop] : tttv->props)
2859
{
2860
if (httv->props.count(name) == 0)
2861
{
2862
if (!result.get())
2863
result = std::make_unique<TableType>(TableType{state, level, scope});
2864
2865
result->props[name] = tprop;
2866
hereSubThere = false;
2867
}
2868
}
2869
2870
if (httv->indexer && tttv->indexer)
2871
{
2872
// TODO: What should intersection of indexes be?
2873
TypeId index = unionType(httv->indexer->indexType, tttv->indexer->indexType);
2874
TypeId indexResult = intersectionType(httv->indexer->indexResultType, tttv->indexer->indexResultType);
2875
if (!result.get())
2876
result = std::make_unique<TableType>(TableType{state, level, scope});
2877
result->indexer = {index, indexResult};
2878
hereSubThere &= (httv->indexer->indexType == index) && (httv->indexer->indexResultType == indexResult);
2879
thereSubHere &= (tttv->indexer->indexType == index) && (tttv->indexer->indexResultType == indexResult);
2880
}
2881
else if (httv->indexer)
2882
{
2883
if (!result.get())
2884
result = std::make_unique<TableType>(TableType{state, level, scope});
2885
result->indexer = httv->indexer;
2886
thereSubHere = false;
2887
}
2888
else if (tttv->indexer)
2889
{
2890
if (!result.get())
2891
result = std::make_unique<TableType>(TableType{state, level, scope});
2892
result->indexer = tttv->indexer;
2893
hereSubThere = false;
2894
}
2895
2896
TypeId table;
2897
if (hereSubThere)
2898
table = htable;
2899
else if (thereSubHere)
2900
table = ttable;
2901
else
2902
{
2903
if (result.get())
2904
table = arena->addType(std::move(*result));
2905
else
2906
table = arena->addType(TableType{state, level, scope});
2907
}
2908
2909
if (tmtable && hmtable)
2910
{
2911
// NOTE: this assumes metatables are ivariant
2912
if (std::optional<TypeId> mtable = intersectionOfTables(hmtable, tmtable, seenTablePropPairs, seenSet))
2913
{
2914
if (table == htable && *mtable == hmtable)
2915
return here;
2916
else if (table == ttable && *mtable == tmtable)
2917
return there;
2918
else
2919
return arena->addType(MetatableType{table, *mtable});
2920
}
2921
else
2922
return std::nullopt;
2923
}
2924
else if (hmtable)
2925
{
2926
if (table == htable)
2927
return here;
2928
else
2929
return arena->addType(MetatableType{table, hmtable});
2930
}
2931
else if (tmtable)
2932
{
2933
if (table == ttable)
2934
return there;
2935
else
2936
return arena->addType(MetatableType{table, tmtable});
2937
}
2938
else
2939
return table;
2940
}
2941
2942
void Normalizer::intersectTablesWithTable(TypeIds& heres, TypeId there, SeenTablePropPairs& seenTablePropPairs, Set<TypeId>& seenSetTypes)
2943
{
2944
consumeFuel();
2945
2946
TypeIds tmp;
2947
for (TypeId here : heres)
2948
{
2949
if (std::optional<TypeId> inter = intersectionOfTables(here, there, seenTablePropPairs, seenSetTypes))
2950
tmp.insert(*inter);
2951
}
2952
heres.retain(tmp);
2953
heres.insert(tmp.begin(), tmp.end());
2954
}
2955
2956
void Normalizer::intersectTables(TypeIds& heres, const TypeIds& theres)
2957
{
2958
consumeFuel();
2959
2960
TypeIds tmp;
2961
for (TypeId here : heres)
2962
{
2963
for (TypeId there : theres)
2964
{
2965
Set<TypeId> seenSetTypes{nullptr};
2966
SeenTablePropPairs seenTablePropPairs{{nullptr, nullptr}};
2967
if (std::optional<TypeId> inter = intersectionOfTables(here, there, seenTablePropPairs, seenSetTypes))
2968
tmp.insert(*inter);
2969
}
2970
}
2971
2972
heres.retain(tmp);
2973
heres.insert(tmp.begin(), tmp.end());
2974
}
2975
2976
std::optional<TypeId> Normalizer::intersectionOfFunctions(TypeId here, TypeId there)
2977
{
2978
consumeFuel();
2979
2980
const FunctionType* hftv = get<FunctionType>(here);
2981
LUAU_ASSERT(hftv);
2982
const FunctionType* tftv = get<FunctionType>(there);
2983
LUAU_ASSERT(tftv);
2984
2985
if (hftv->generics != tftv->generics)
2986
return std::nullopt;
2987
if (hftv->genericPacks != tftv->genericPacks)
2988
return std::nullopt;
2989
2990
TypePackId argTypes;
2991
TypePackId retTypes;
2992
2993
if (hftv->retTypes == tftv->retTypes)
2994
{
2995
std::optional<TypePackId> argTypesOpt = unionOfTypePacks(hftv->argTypes, tftv->argTypes);
2996
if (!argTypesOpt)
2997
return std::nullopt;
2998
argTypes = *argTypesOpt;
2999
retTypes = hftv->retTypes;
3000
}
3001
else if (hftv->argTypes == tftv->argTypes)
3002
{
3003
std::optional<TypePackId> retTypesOpt = intersectionOfTypePacks_INTERNAL(hftv->argTypes, tftv->argTypes);
3004
if (!retTypesOpt)
3005
return std::nullopt;
3006
argTypes = hftv->argTypes;
3007
retTypes = *retTypesOpt;
3008
}
3009
else
3010
return std::nullopt;
3011
3012
if (argTypes == hftv->argTypes && retTypes == hftv->retTypes)
3013
return here;
3014
if (argTypes == tftv->argTypes && retTypes == tftv->retTypes)
3015
return there;
3016
3017
FunctionType result{argTypes, retTypes};
3018
result.generics = hftv->generics;
3019
result.genericPacks = hftv->genericPacks;
3020
return arena->addType(std::move(result));
3021
}
3022
3023
std::optional<TypeId> Normalizer::unionSaturatedFunctions(TypeId here, TypeId there)
3024
{
3025
// Deep breath...
3026
//
3027
// When we come to check overloaded functions for subtyping,
3028
// we have to compare (F1 & ... & FM) <: (G1 & ... G GN)
3029
// where each Fi or Gj is a function type. Now that intersection on the right is no
3030
// problem, since that's true if and only if (F1 & ... & FM) <: Gj for every j.
3031
// But the intersection on the left is annoying, since we might have
3032
// (F1 & ... & FM) <: G but no Fi <: G. For example
3033
//
3034
// ((number? -> number?) & (string? -> string?)) <: (nil -> nil)
3035
//
3036
// So in this case, what we do is define Apply<F, T> for the result of applying
3037
// a function of type F to an argument of type T, and then F <: (T -> U)
3038
// if and only if Apply<F, T> <: U. For example:
3039
//
3040
// if f : ((number? -> number?) & (string? -> string?))
3041
// then f(nil) must be nil, so
3042
// Apply<((number? -> number?) & (string? -> string?)), nil> is nil
3043
//
3044
// So subtyping on overloaded functions "just" boils down to defining Apply<F, T>.
3045
//
3046
// Now for non-overloaded functions, this is easy!
3047
// Apply<(R -> S), T> is S if T <: R, and an error type otherwise.
3048
//
3049
// But for overloaded functions it's not so simple. We'd like Apply<F1 & ... & FM, T>
3050
// to just be Apply<F1, T> & ... & Apply<FM, T> but oh dear
3051
//
3052
// if f : ((number -> number) & (string -> string))
3053
// and x : (number | string)
3054
// then f(x) : (number | string)
3055
//
3056
// so we want
3057
//
3058
// Apply<((number -> number) & (string -> string)), (number | string)> is (number | string)
3059
//
3060
// but
3061
//
3062
// Apply<(number -> number), (number | string)> is an error
3063
// Apply<(string -> string), (number | string)> is an error
3064
//
3065
// that is Apply<F, T> should consider all possible combinations of overloads of F,
3066
// not just individual overloads.
3067
//
3068
// For this reason, when we're normalizing function types (in order to check subtyping
3069
// or perform overload resolution) we should first *union-saturate* them. An overloaded
3070
// function is union-saturated whenever:
3071
//
3072
// if (R -> S) is an overload of F
3073
// and (T -> U) is an overload of F
3074
// then ((R | T) -> (S | U)) is a subtype of an overload of F
3075
//
3076
// Any overloaded function can be normalized to a union-saturated one by adding enough extra overloads.
3077
// For example, union-saturating
3078
//
3079
// ((number -> number) & (string -> string))
3080
//
3081
// is
3082
//
3083
// ((number -> number) & (string -> string) & ((number | string) -> (number | string)))
3084
//
3085
// For union-saturated overloaded functions, the "obvious" algorithm works:
3086
//
3087
// Apply<F1 & ... & FM, T> is Apply<F1, T> & ... & Apply<FM, T>
3088
//
3089
// so we can define Apply, so we can perform overloaded function resolution
3090
// and check subtyping on overloaded function types, yay!
3091
//
3092
// This is yet another potential source of exponential blow-up, sigh, since
3093
// the union-saturation of a function with N overloads may have 2^N overloads
3094
// (one for every subset). In practice, that hopefully won't happen that often,
3095
// in particular we only union-saturate overloads with different return types,
3096
// and there are hopefully not very many cases of that.
3097
//
3098
// All of this is mechanically verified in Agda, at https://github.com/luau-lang/agda-typeck
3099
//
3100
// It is essentially the algorithm defined in https://pnwamk.github.io/sst-tutorial/
3101
// except that we're precomputing the union-saturation rather than converting
3102
// to disjunctive normal form on the fly.
3103
//
3104
// This is all built on semantic subtyping:
3105
//
3106
// Covariance and Contravariance, Giuseppe Castagna,
3107
// Logical Methods in Computer Science 16(1), 2022
3108
// https://arxiv.org/abs/1809.01427
3109
//
3110
// A gentle introduction to semantic subtyping, Giuseppe Castagna and Alain Frisch,
3111
// Proc. Principles and practice of declarative programming 2005, pp 198–208
3112
// https://doi.org/10.1145/1069774.1069793
3113
3114
consumeFuel();
3115
3116
const FunctionType* hftv = get<FunctionType>(here);
3117
if (!hftv)
3118
return std::nullopt;
3119
const FunctionType* tftv = get<FunctionType>(there);
3120
if (!tftv)
3121
return std::nullopt;
3122
3123
if (hftv->generics != tftv->generics)
3124
return std::nullopt;
3125
if (hftv->genericPacks != tftv->genericPacks)
3126
return std::nullopt;
3127
3128
std::optional<TypePackId> argTypes = unionOfTypePacks(hftv->argTypes, tftv->argTypes);
3129
if (!argTypes)
3130
return std::nullopt;
3131
std::optional<TypePackId> retTypes = unionOfTypePacks(hftv->retTypes, tftv->retTypes);
3132
if (!retTypes)
3133
return std::nullopt;
3134
3135
FunctionType result{*argTypes, *retTypes};
3136
result.generics = hftv->generics;
3137
result.genericPacks = hftv->genericPacks;
3138
return arena->addType(std::move(result));
3139
}
3140
3141
void Normalizer::intersectFunctionsWithFunction(NormalizedFunctionType& heres, TypeId there)
3142
{
3143
consumeFuel();
3144
3145
if (heres.isNever())
3146
return;
3147
3148
heres.isTop = false;
3149
3150
for (auto it = heres.parts.begin(); it != heres.parts.end();)
3151
{
3152
TypeId here = *it;
3153
if (get<ErrorType>(here))
3154
it++;
3155
else if (std::optional<TypeId> tmp = intersectionOfFunctions(here, there))
3156
{
3157
heres.parts.erase(it);
3158
heres.parts.insert(*tmp);
3159
return;
3160
}
3161
else
3162
it++;
3163
}
3164
3165
TypeIds tmps;
3166
for (TypeId here : heres.parts)
3167
{
3168
if (std::optional<TypeId> tmp = unionSaturatedFunctions(here, there))
3169
tmps.insert(*tmp);
3170
}
3171
heres.parts.insert(there);
3172
heres.parts.insert(tmps.begin(), tmps.end());
3173
}
3174
3175
void Normalizer::intersectFunctions(NormalizedFunctionType& heres, const NormalizedFunctionType& theres)
3176
{
3177
consumeFuel();
3178
3179
if (heres.isNever())
3180
return;
3181
else if (theres.isNever())
3182
{
3183
heres.resetToNever();
3184
return;
3185
}
3186
else
3187
{
3188
for (TypeId there : theres.parts)
3189
intersectFunctionsWithFunction(heres, there);
3190
}
3191
}
3192
3193
NormalizationResult Normalizer::intersectTyvarsWithTy(
3194
NormalizedTyvars& here,
3195
TypeId there,
3196
SeenTablePropPairs& seenTablePropPairs,
3197
Set<TypeId>& seenSetTypes
3198
)
3199
{
3200
consumeFuel();
3201
3202
for (auto it = here.begin(); it != here.end();)
3203
{
3204
NormalizedType& inter = *it->second;
3205
NormalizationResult res = intersectNormalWithTy(inter, there, seenTablePropPairs, seenSetTypes);
3206
if (res != NormalizationResult::True)
3207
return res;
3208
if (isShallowInhabited(inter))
3209
++it;
3210
else
3211
it = here.erase(it);
3212
}
3213
return NormalizationResult::True;
3214
}
3215
3216
// See above for an explanation of `ignoreSmallerTyvars`.
3217
NormalizationResult Normalizer::intersectNormals(NormalizedType& here, const NormalizedType& there, int ignoreSmallerTyvars)
3218
{
3219
RecursionCounter _rc(&sharedState->counters.recursionCount);
3220
if (!withinResourceLimits())
3221
return NormalizationResult::HitLimits;
3222
3223
consumeFuel();
3224
3225
if (!get<NeverType>(there.tops))
3226
{
3227
here.tops = intersectionOfTops(here.tops, there.tops);
3228
return NormalizationResult::True;
3229
}
3230
else if (!get<NeverType>(here.tops))
3231
{
3232
clearNormal(here);
3233
return unionNormals(here, there, ignoreSmallerTyvars);
3234
}
3235
3236
for (auto& [tyvar, inter] : there.tyvars)
3237
{
3238
int index = tyvarIndex(tyvar);
3239
if (ignoreSmallerTyvars < index)
3240
{
3241
auto [found, fresh] = here.tyvars.emplace(tyvar, std::make_unique<NormalizedType>(NormalizedType{builtinTypes}));
3242
if (fresh)
3243
{
3244
NormalizationResult res = unionNormals(*found->second, here, index);
3245
if (res != NormalizationResult::True)
3246
return res;
3247
}
3248
}
3249
}
3250
3251
here.booleans = intersectionOfBools(here.booleans, there.booleans);
3252
3253
intersectExternTypes(here.externTypes, there.externTypes);
3254
here.errors = (get<NeverType>(there.errors) ? there.errors : here.errors);
3255
here.nils = (get<NeverType>(there.nils) ? there.nils : here.nils);
3256
here.numbers = (get<NeverType>(there.numbers) ? there.numbers : here.numbers);
3257
if (FFlag::LuauIntegerType)
3258
here.integers = (get<NeverType>(there.integers) ? there.integers : here.integers);
3259
intersectStrings(here.strings, there.strings);
3260
here.threads = (get<NeverType>(there.threads) ? there.threads : here.threads);
3261
here.buffers = (get<NeverType>(there.buffers) ? there.buffers : here.buffers);
3262
intersectFunctions(here.functions, there.functions);
3263
intersectTables(here.tables, there.tables);
3264
3265
for (auto it = here.tyvars.begin(); it != here.tyvars.end();)
3266
{
3267
TypeId tyvar = it->first;
3268
NormalizedType& inter = *it->second;
3269
int index = tyvarIndex(tyvar);
3270
LUAU_ASSERT(ignoreSmallerTyvars < index);
3271
auto found = there.tyvars.find(tyvar);
3272
if (found == there.tyvars.end())
3273
{
3274
NormalizationResult res = intersectNormals(inter, there, index);
3275
if (res != NormalizationResult::True)
3276
return res;
3277
}
3278
else
3279
{
3280
NormalizationResult res = intersectNormals(inter, *found->second, index);
3281
if (res != NormalizationResult::True)
3282
return res;
3283
}
3284
if (isShallowInhabited(inter))
3285
it++;
3286
else
3287
it = here.tyvars.erase(it);
3288
}
3289
return NormalizationResult::True;
3290
}
3291
3292
NormalizationResult Normalizer::intersectNormalWithTy(
3293
NormalizedType& here,
3294
TypeId there,
3295
SeenTablePropPairs& seenTablePropPairs,
3296
Set<TypeId>& seenSetTypes
3297
)
3298
{
3299
RecursionCounter _rc(&sharedState->counters.recursionCount);
3300
if (!withinResourceLimits())
3301
return NormalizationResult::HitLimits;
3302
3303
consumeFuel();
3304
3305
there = follow(there);
3306
3307
if (get<AnyType>(there) || get<UnknownType>(there))
3308
{
3309
here.tops = intersectionOfTops(here.tops, there);
3310
return NormalizationResult::True;
3311
}
3312
else if (!get<NeverType>(here.tops))
3313
{
3314
clearNormal(here);
3315
return unionNormalWithTy(here, there, seenTablePropPairs, seenSetTypes);
3316
}
3317
else if (const UnionType* utv = get<UnionType>(there))
3318
{
3319
NormalizedType norm{builtinTypes};
3320
for (UnionTypeIterator it = begin(utv); it != end(utv); ++it)
3321
{
3322
NormalizationResult res = unionNormalWithTy(norm, *it, seenTablePropPairs, seenSetTypes);
3323
if (res != NormalizationResult::True)
3324
return res;
3325
}
3326
return intersectNormals(here, norm);
3327
}
3328
else if (const IntersectionType* itv = get<IntersectionType>(there))
3329
{
3330
for (IntersectionTypeIterator it = begin(itv); it != end(itv); ++it)
3331
{
3332
NormalizationResult res = intersectNormalWithTy(here, *it, seenTablePropPairs, seenSetTypes);
3333
if (res != NormalizationResult::True)
3334
return res;
3335
}
3336
return NormalizationResult::True;
3337
}
3338
else if (get<GenericType>(there) || get<FreeType>(there) || get<BlockedType>(there) || get<PendingExpansionType>(there) ||
3339
get<TypeFunctionInstanceType>(there))
3340
{
3341
NormalizedType thereNorm{builtinTypes};
3342
NormalizedType topNorm{builtinTypes};
3343
topNorm.tops = builtinTypes->unknownType;
3344
thereNorm.tyvars.insert_or_assign(there, std::make_unique<NormalizedType>(std::move(topNorm)));
3345
here.isCacheable = false;
3346
return intersectNormals(here, thereNorm);
3347
}
3348
3349
NormalizedTyvars tyvars = std::move(here.tyvars);
3350
3351
if (get<FunctionType>(there))
3352
{
3353
NormalizedFunctionType functions = std::move(here.functions);
3354
clearNormal(here);
3355
intersectFunctionsWithFunction(functions, there);
3356
here.functions = std::move(functions);
3357
}
3358
else if (get<TableType>(there) || get<MetatableType>(there))
3359
{
3360
if (useNewLuauSolver())
3361
{
3362
NormalizedExternType externTypes = std::move(here.externTypes);
3363
TypeIds tables = std::move(here.tables);
3364
clearNormal(here);
3365
3366
if (FFlag::LuauExternTypesNormalizeWithShapes)
3367
{
3368
if (externTypes.isNever())
3369
intersectTablesWithTable(tables, there, seenTablePropPairs, seenSetTypes);
3370
else
3371
intersectExternTypesWithShape(externTypes, there);
3372
}
3373
else
3374
{
3375
intersectTablesWithTable(tables, there, seenTablePropPairs, seenSetTypes);
3376
}
3377
3378
here.tables = std::move(tables);
3379
here.externTypes = std::move(externTypes);
3380
}
3381
else
3382
{
3383
TypeIds tables = std::move(here.tables);
3384
clearNormal(here);
3385
intersectTablesWithTable(tables, there, seenTablePropPairs, seenSetTypes);
3386
here.tables = std::move(tables);
3387
}
3388
}
3389
else if (get<ExternType>(there))
3390
{
3391
NormalizedExternType nct = std::move(here.externTypes);
3392
clearNormal(here);
3393
intersectExternTypesWithExternType(nct, there);
3394
here.externTypes = std::move(nct);
3395
}
3396
else if (get<ErrorType>(there))
3397
{
3398
TypeId errors = here.errors;
3399
clearNormal(here);
3400
here.errors = get<ErrorType>(errors) ? errors : there;
3401
}
3402
else if (const PrimitiveType* ptv = get<PrimitiveType>(there))
3403
{
3404
TypeId booleans = here.booleans;
3405
TypeId nils = here.nils;
3406
TypeId numbers = here.numbers;
3407
TypeId integers = here.integers;
3408
NormalizedStringType strings = std::move(here.strings);
3409
NormalizedFunctionType functions = std::move(here.functions);
3410
TypeId threads = here.threads;
3411
TypeId buffers = here.buffers;
3412
TypeIds tables = std::move(here.tables);
3413
3414
clearNormal(here);
3415
3416
if (ptv->type == PrimitiveType::Boolean)
3417
here.booleans = booleans;
3418
else if (ptv->type == PrimitiveType::NilType)
3419
here.nils = nils;
3420
else if (ptv->type == PrimitiveType::Number)
3421
here.numbers = numbers;
3422
else if (FFlag::LuauIntegerType && (ptv->type == PrimitiveType::Integer))
3423
here.integers = integers;
3424
else if (ptv->type == PrimitiveType::String)
3425
here.strings = std::move(strings);
3426
else if (ptv->type == PrimitiveType::Thread)
3427
here.threads = threads;
3428
else if (ptv->type == PrimitiveType::Buffer)
3429
here.buffers = buffers;
3430
else if (ptv->type == PrimitiveType::Function)
3431
here.functions = std::move(functions);
3432
else if (ptv->type == PrimitiveType::Table)
3433
here.tables = std::move(tables);
3434
else
3435
LUAU_ASSERT(!"Unreachable");
3436
}
3437
else if (const SingletonType* stv = get<SingletonType>(there))
3438
{
3439
TypeId booleans = here.booleans;
3440
NormalizedStringType strings = std::move(here.strings);
3441
3442
clearNormal(here);
3443
3444
if (get<BooleanSingleton>(stv))
3445
here.booleans = intersectionOfBools(booleans, there);
3446
else if (const StringSingleton* sstv = get<StringSingleton>(stv))
3447
{
3448
if (strings.includes(sstv->value))
3449
here.strings.singletons.insert({sstv->value, there});
3450
}
3451
else
3452
LUAU_ASSERT(!"Unreachable");
3453
}
3454
else if (const NegationType* ntv = get<NegationType>(there))
3455
{
3456
TypeId t = follow(ntv->ty);
3457
if (get<PrimitiveType>(t))
3458
subtractPrimitive(here, ntv->ty);
3459
else if (get<SingletonType>(t))
3460
subtractSingleton(here, follow(ntv->ty));
3461
else if (get<ExternType>(t))
3462
{
3463
NormalizationResult res = intersectNormalWithNegationTy(t, here);
3464
if (shouldEarlyExit(res))
3465
return res;
3466
}
3467
else if (const UnionType* itv = get<UnionType>(t))
3468
{
3469
for (TypeId part : itv->options)
3470
{
3471
NormalizationResult res = intersectNormalWithNegationTy(part, here);
3472
if (shouldEarlyExit(res))
3473
return res;
3474
}
3475
}
3476
else if (get<AnyType>(t))
3477
{
3478
// HACK: Refinements sometimes intersect with ~any under the
3479
// assumption that it is the same as any.
3480
return NormalizationResult::True;
3481
}
3482
else if (get<NoRefineType>(t))
3483
{
3484
// `*no-refine*` means we will never do anything to affect the intersection.
3485
return NormalizationResult::True;
3486
}
3487
else if (get<NeverType>(t))
3488
{
3489
// if we're intersecting with `~never`, this is equivalent to intersecting with `unknown`
3490
// this is a noop since an intersection with `unknown` is trivial.
3491
return NormalizationResult::True;
3492
}
3493
else if (get<UnknownType>(t))
3494
{
3495
// if we're intersecting with `~unknown`, this is equivalent to intersecting with `never`
3496
// this means we should clear the type entirely.
3497
clearNormal(here);
3498
return NormalizationResult::True;
3499
}
3500
else if (get<ErrorType>(t))
3501
{
3502
// ~error is still an error, so intersecting with the negation is the same as intersecting with a type
3503
TypeId errors = here.errors;
3504
clearNormal(here);
3505
here.errors = get<ErrorType>(errors) ? errors : t;
3506
}
3507
else if (auto nt = get<NegationType>(t))
3508
{
3509
here.tyvars = std::move(tyvars);
3510
return intersectNormalWithTy(here, nt->ty, seenTablePropPairs, seenSetTypes);
3511
}
3512
else
3513
{
3514
// TODO negated unions, intersections, table, and function.
3515
// Report a TypeError for other types.
3516
LUAU_ASSERT(!"Unimplemented");
3517
}
3518
}
3519
else if (get<NeverType>(there))
3520
{
3521
here.externTypes.resetToNever();
3522
}
3523
else if (get<NoRefineType>(there))
3524
{
3525
// `*no-refine*` means we will never do anything to affect the intersection.
3526
return NormalizationResult::True;
3527
}
3528
else
3529
LUAU_ASSERT(!"Unreachable");
3530
3531
NormalizationResult res = intersectTyvarsWithTy(tyvars, there, seenTablePropPairs, seenSetTypes);
3532
if (res != NormalizationResult::True)
3533
return res;
3534
here.tyvars = std::move(tyvars);
3535
3536
return NormalizationResult::True;
3537
}
3538
3539
void makeTableShared(TypeId ty, DenseHashSet<TypeId>& seen)
3540
{
3541
ty = follow(ty);
3542
if (seen.contains(ty))
3543
return;
3544
seen.insert(ty);
3545
if (auto tableTy = getMutable<TableType>(ty))
3546
{
3547
for (auto& [_, prop] : tableTy->props)
3548
prop.makeShared();
3549
}
3550
else if (auto metatableTy = get<MetatableType>(ty))
3551
{
3552
makeTableShared(metatableTy->metatable, seen);
3553
makeTableShared(metatableTy->table, seen);
3554
}
3555
}
3556
3557
void makeTableShared(TypeId ty)
3558
{
3559
DenseHashSet<TypeId> seen{nullptr};
3560
makeTableShared(ty, seen);
3561
}
3562
3563
// -------- Convert back from a normalized type to a type
3564
TypeId Normalizer::typeFromNormal(const NormalizedType& norm)
3565
{
3566
assertInvariant(norm);
3567
if (!get<NeverType>(norm.tops))
3568
return norm.tops;
3569
3570
std::vector<TypeId> result;
3571
3572
if (!get<NeverType>(norm.booleans))
3573
result.push_back(norm.booleans);
3574
3575
if (isTop(builtinTypes, norm.externTypes))
3576
{
3577
result.push_back(builtinTypes->externType);
3578
}
3579
else if (!norm.externTypes.isNever())
3580
{
3581
std::vector<TypeId> parts;
3582
parts.reserve(norm.externTypes.externTypes.size());
3583
3584
for (const TypeId normTy : norm.externTypes.ordering)
3585
{
3586
const TypeIds& normNegations = norm.externTypes.externTypes.at(normTy);
3587
3588
if (normNegations.empty() && (!FFlag::LuauExternTypesNormalizeWithShapes || norm.externTypes.shapeExtensions.empty()))
3589
{
3590
parts.push_back(normTy);
3591
}
3592
else
3593
{
3594
std::vector<TypeId> intersection;
3595
intersection.reserve(normNegations.size() + 1);
3596
3597
intersection.push_back(normTy);
3598
for (TypeId negation : normNegations)
3599
{
3600
intersection.push_back(arena->addType(NegationType{negation}));
3601
}
3602
3603
if (FFlag::LuauExternTypesNormalizeWithShapes)
3604
{
3605
for (TypeId shape : norm.externTypes.shapeExtensions)
3606
{
3607
intersection.push_back(shape);
3608
}
3609
}
3610
3611
parts.push_back(arena->addType(IntersectionType{std::move(intersection)}));
3612
}
3613
}
3614
3615
if (parts.size() == 1)
3616
{
3617
result.push_back(parts.at(0));
3618
}
3619
else if (parts.size() > 1)
3620
{
3621
result.push_back(arena->addType(UnionType{std::move(parts)}));
3622
}
3623
}
3624
3625
if (!get<NeverType>(norm.errors))
3626
result.push_back(norm.errors);
3627
if (norm.functions.isTop)
3628
result.push_back(builtinTypes->functionType);
3629
else if (!norm.functions.isNever())
3630
{
3631
if (norm.functions.parts.size() == 1)
3632
result.push_back(*norm.functions.parts.begin());
3633
else
3634
{
3635
std::vector<TypeId> parts;
3636
parts.insert(parts.end(), norm.functions.parts.begin(), norm.functions.parts.end());
3637
result.push_back(arena->addType(IntersectionType{std::move(parts)}));
3638
}
3639
}
3640
if (!get<NeverType>(norm.nils))
3641
result.push_back(norm.nils);
3642
if (!get<NeverType>(norm.numbers))
3643
result.push_back(norm.numbers);
3644
if (FFlag::LuauIntegerType)
3645
{
3646
if (get<NeverType>(norm.integers) == nullptr)
3647
result.push_back(norm.integers);
3648
}
3649
if (norm.strings.isString())
3650
result.push_back(builtinTypes->stringType);
3651
else if (norm.strings.isUnion())
3652
{
3653
for (auto& [_, ty] : norm.strings.singletons)
3654
result.push_back(ty);
3655
}
3656
else if (norm.strings.isIntersection())
3657
{
3658
std::vector<TypeId> parts;
3659
parts.push_back(builtinTypes->stringType);
3660
for (const auto& [name, ty] : norm.strings.singletons)
3661
parts.push_back(arena->addType(NegationType{ty}));
3662
3663
result.push_back(arena->addType(IntersectionType{std::move(parts)}));
3664
}
3665
if (!get<NeverType>(norm.threads))
3666
result.push_back(builtinTypes->threadType);
3667
if (!get<NeverType>(norm.buffers))
3668
result.push_back(builtinTypes->bufferType);
3669
3670
if (useNewLuauSolver())
3671
{
3672
result.reserve(result.size() + norm.tables.size());
3673
for (auto table : norm.tables)
3674
result.push_back(table);
3675
}
3676
else
3677
result.insert(result.end(), norm.tables.begin(), norm.tables.end());
3678
3679
for (auto& [tyvar, intersect] : norm.tyvars)
3680
{
3681
if (get<NeverType>(intersect->tops))
3682
{
3683
TypeId ty = typeFromNormal(*intersect);
3684
result.push_back(addIntersection(NotNull{arena}, builtinTypes, {tyvar, ty}));
3685
}
3686
else
3687
result.push_back(tyvar);
3688
}
3689
3690
if (result.size() == 0)
3691
return builtinTypes->neverType;
3692
else if (result.size() == 1)
3693
return result[0];
3694
else
3695
return arena->addType(UnionType{std::move(result)});
3696
}
3697
3698
bool Normalizer::initializeFuel()
3699
{
3700
if (fuel)
3701
return false;
3702
3703
fuel = FInt::LuauNormalizerInitialFuel;
3704
return true;
3705
}
3706
3707
void Normalizer::clearFuel()
3708
{
3709
fuel = std::nullopt;
3710
}
3711
3712
void Normalizer::consumeFuel()
3713
{
3714
if (fuel)
3715
{
3716
(*fuel)--;
3717
if (fuel <= 0)
3718
throw NormalizerHitLimits();
3719
}
3720
}
3721
3722
bool isSubtype(
3723
TypeId subTy,
3724
TypeId superTy,
3725
NotNull<TypeArena> arena,
3726
NotNull<BuiltinTypes> builtinTypes,
3727
NotNull<Scope> scope,
3728
NotNull<Normalizer> normalizer,
3729
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
3730
NotNull<InternalErrorReporter> reporter
3731
)
3732
{
3733
Subtyping subtyping{builtinTypes, arena, normalizer, typeFunctionRuntime, reporter};
3734
return subtyping.isSubtype(subTy, superTy, scope).isSubtype;
3735
}
3736
3737
3738
bool isSubtype_DEPRECATED(
3739
TypeId subTy,
3740
TypeId superTy,
3741
NotNull<Scope> scope,
3742
NotNull<BuiltinTypes> builtinTypes,
3743
InternalErrorReporter& ice,
3744
SolverMode solverMode
3745
)
3746
{
3747
UnifierSharedState sharedState{&ice};
3748
TypeArena arena;
3749
TypeCheckLimits limits;
3750
TypeFunctionRuntime typeFunctionRuntime{
3751
NotNull{&ice}, NotNull{&limits}
3752
}; // TODO: maybe subtyping checks should not invoke user-defined type function runtime
3753
3754
Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}, solverMode};
3755
if (solverMode == SolverMode::New)
3756
{
3757
Subtyping subtyping{builtinTypes, NotNull{&arena}, NotNull{&normalizer}, NotNull{&typeFunctionRuntime}, NotNull{&ice}};
3758
3759
return subtyping.isSubtype(subTy, superTy, scope).isSubtype;
3760
}
3761
else
3762
{
3763
Unifier u{NotNull{&normalizer}, scope, Location{}, Covariant};
3764
3765
u.tryUnify(subTy, superTy);
3766
return !u.failure;
3767
}
3768
}
3769
3770
} // namespace Luau
3771
3772