Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Analysis/src/ConstraintSolver.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
#include "Luau/ConstraintSolver.h"
3
4
#include "Luau/Anyification.h"
5
#include "Luau/ApplyTypeFunction.h"
6
#include "Luau/AstUtils.h"
7
#include "Luau/Clone.h"
8
#include "Luau/Common.h"
9
#include "Luau/DcrLogger.h"
10
#include "Luau/DenseHash.h"
11
#include "Luau/Generalization.h"
12
#include "Luau/HashUtil.h"
13
#include "Luau/Instantiation.h"
14
#include "Luau/Instantiation2.h"
15
#include "Luau/IterativeTypeVisitor.h"
16
#include "Luau/Location.h"
17
#include "Luau/ModuleResolver.h"
18
#include "Luau/OverloadResolution.h"
19
#include "Luau/RecursionCounter.h"
20
#include "Luau/ScopedSeenSet.h"
21
#include "Luau/Simplify.h"
22
#include "Luau/SubtypingUnifier.h"
23
#include "Luau/TableLiteralInference.h"
24
#include "Luau/TimeTrace.h"
25
#include "Luau/ToString.h"
26
#include "Luau/Type.h"
27
#include "Luau/TypeFunction.h"
28
#include "Luau/TypeFwd.h"
29
#include "Luau/TypePack.h"
30
#include "Luau/TypeUtils.h"
31
#include "Luau/Unifier2.h"
32
#include "Luau/VisitType.h"
33
34
#include <algorithm>
35
#include <memory>
36
#include <utility>
37
38
LUAU_FASTINTVARIABLE(LuauSolverConstraintLimit, 1000)
39
LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500)
40
41
LUAU_FASTFLAGVARIABLE(DebugLuauAssertOnForcedConstraint)
42
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolver)
43
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverIncludeDependencies)
44
LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings)
45
LUAU_FASTFLAG(LuauExplicitTypeInstantiationSupport)
46
LUAU_FASTFLAGVARIABLE(LuauUnifyWithSubtyping2)
47
LUAU_FASTFLAG(LuauRelateHandlesCoincidentTables)
48
LUAU_FASTFLAG(LuauUnpackRespectsAnnotations)
49
LUAU_FASTFLAG(LuauReplacerRespectsReboundGenerics)
50
LUAU_FASTFLAGVARIABLE(LuauOverloadGetsInstantiated)
51
LUAU_FASTFLAGVARIABLE(LuauFollowInExplicitInstantiation)
52
LUAU_FASTFLAGVARIABLE(LuauUseConstraintSetsToTrackFreeTypes)
53
54
namespace Luau
55
{
56
57
bool SubtypeConstraintRecord::operator==(const SubtypeConstraintRecord& other) const
58
{
59
return (subTy == other.subTy) && (superTy == other.superTy) && (variance == other.variance);
60
}
61
62
size_t HashSubtypeConstraintRecord::operator()(const SubtypeConstraintRecord& c) const
63
{
64
size_t result = 0;
65
hashCombine(result, intptr_t(c.subTy));
66
hashCombine(result, intptr_t(c.superTy));
67
hashCombine(result, intptr_t(c.variance));
68
return result;
69
}
70
71
static void dump(ConstraintSolver* cs, ToStringOptions& opts);
72
73
size_t HashBlockedConstraintId::operator()(const BlockedConstraintId& bci) const
74
{
75
size_t result = 0;
76
77
if (const TypeId* ty = get_if<TypeId>(&bci))
78
result = std::hash<TypeId>()(*ty);
79
else if (const TypePackId* tp = get_if<TypePackId>(&bci))
80
result = std::hash<TypePackId>()(*tp);
81
else if (Constraint const* const* c = get_if<const Constraint*>(&bci))
82
result = std::hash<const Constraint*>()(*c);
83
else
84
LUAU_ASSERT(!"Should be unreachable");
85
86
return result;
87
}
88
89
[[maybe_unused]] static void dumpBindings(NotNull<Scope> scope, ToStringOptions& opts)
90
{
91
for (const auto& [k, v] : scope->bindings)
92
{
93
auto d = toString(v.typeId, opts);
94
printf("\t%s : %s\n", k.c_str(), d.c_str());
95
}
96
97
for (NotNull<Scope> child : scope->children)
98
dumpBindings(child, opts);
99
}
100
101
// used only in asserts
102
[[maybe_unused]] static bool canMutate(TypeId ty, NotNull<const Constraint> constraint)
103
{
104
if (auto blocked = get<BlockedType>(ty))
105
{
106
const Constraint* owner = blocked->getOwner();
107
LUAU_ASSERT(owner);
108
return owner == constraint;
109
}
110
111
return true;
112
}
113
114
// used only in asserts
115
[[maybe_unused]] static bool canMutate(TypePackId tp, NotNull<const Constraint> constraint)
116
{
117
if (auto blocked = get<BlockedTypePack>(tp))
118
{
119
Constraint* owner = blocked->owner;
120
LUAU_ASSERT(owner);
121
return owner == constraint;
122
}
123
124
return true;
125
}
126
127
std::pair<std::vector<TypeId>, std::vector<TypePackId>> saturateArguments(
128
TypeArena* arena,
129
NotNull<BuiltinTypes> builtinTypes,
130
const TypeFun& fn,
131
const std::vector<TypeId>& rawTypeArguments,
132
const std::vector<TypePackId>& rawPackArguments
133
)
134
{
135
std::vector<TypeId> saturatedTypeArguments;
136
std::vector<TypeId> extraTypes;
137
std::vector<TypePackId> saturatedPackArguments;
138
139
for (size_t i = 0; i < rawTypeArguments.size(); ++i)
140
{
141
TypeId ty = rawTypeArguments[i];
142
143
if (i < fn.typeParams.size())
144
saturatedTypeArguments.push_back(ty);
145
else
146
extraTypes.push_back(ty);
147
}
148
149
// If we collected extra types, put them in a type pack now. This case is
150
// mutually exclusive with the type pack -> type conversion we do below:
151
// extraTypes will only have elements in it if we have more types than we
152
// have parameter slots for them to go into.
153
if (!extraTypes.empty() && !fn.typePackParams.empty())
154
{
155
saturatedPackArguments.push_back(arena->addTypePack(extraTypes));
156
}
157
158
for (size_t i = 0; i < rawPackArguments.size(); ++i)
159
{
160
TypePackId tp = rawPackArguments[i];
161
162
// If we are short on regular type saturatedTypeArguments and we have a single
163
// element type pack, we can decompose that to the type it contains and
164
// use that as a type parameter.
165
if (saturatedTypeArguments.size() < fn.typeParams.size() && size(tp) == 1 && finite(tp) && first(tp) && saturatedPackArguments.empty())
166
{
167
saturatedTypeArguments.push_back(*first(tp));
168
}
169
else if (saturatedPackArguments.size() < fn.typePackParams.size())
170
{
171
saturatedPackArguments.push_back(tp);
172
}
173
}
174
175
size_t typesProvided = saturatedTypeArguments.size();
176
size_t typesRequired = fn.typeParams.size();
177
178
size_t packsProvided = saturatedPackArguments.size();
179
size_t packsRequired = fn.typePackParams.size();
180
181
// Extra types should be accumulated in extraTypes, not saturatedTypeArguments. Extra
182
// packs will be accumulated in saturatedPackArguments, so we don't have an
183
// assertion for that.
184
LUAU_ASSERT(typesProvided <= typesRequired);
185
186
// If we didn't provide enough types, but we did provide a type pack, we
187
// don't want to use defaults. The rationale for this is that if the user
188
// provides a pack but doesn't provide enough types, we want to report an
189
// error, rather than simply using the default saturatedTypeArguments, if they exist. If
190
// they did provide enough types, but not enough packs, we of course want to
191
// use the default packs.
192
bool needsDefaults = (typesProvided < typesRequired && packsProvided == 0) || (typesProvided == typesRequired && packsProvided < packsRequired);
193
194
if (needsDefaults)
195
{
196
// Default types can reference earlier types. It's legal to write
197
// something like
198
// type T<A, B = A> = (A, B) -> number
199
// and we need to respect that. We use an ApplyTypeFunction for this.
200
ApplyTypeFunction atf{arena};
201
202
for (size_t i = 0; i < typesProvided; ++i)
203
atf.typeArguments[fn.typeParams[i].ty] = saturatedTypeArguments[i];
204
205
for (size_t i = typesProvided; i < typesRequired; ++i)
206
{
207
TypeId defaultTy = fn.typeParams[i].defaultValue.value_or(nullptr);
208
209
// We will fill this in with the error type later.
210
if (!defaultTy)
211
break;
212
213
TypeId instantiatedDefault = atf.substitute(defaultTy).value_or(builtinTypes->errorType);
214
atf.typeArguments[fn.typeParams[i].ty] = instantiatedDefault;
215
saturatedTypeArguments.push_back(instantiatedDefault);
216
}
217
218
for (size_t i = 0; i < packsProvided; ++i)
219
{
220
atf.typePackArguments[fn.typePackParams[i].tp] = saturatedPackArguments[i];
221
}
222
223
for (size_t i = packsProvided; i < packsRequired; ++i)
224
{
225
TypePackId defaultTp = fn.typePackParams[i].defaultValue.value_or(nullptr);
226
227
// We will fill this in with the error type pack later.
228
if (!defaultTp)
229
break;
230
231
TypePackId instantiatedDefault = atf.substitute(defaultTp).value_or(builtinTypes->errorTypePack);
232
atf.typePackArguments[fn.typePackParams[i].tp] = instantiatedDefault;
233
saturatedPackArguments.push_back(instantiatedDefault);
234
}
235
}
236
237
// If we didn't create an extra type pack from overflowing parameter packs,
238
// and we're still missing a type pack, plug in an empty type pack as the
239
// value of the empty packs.
240
if (extraTypes.empty() && saturatedPackArguments.size() + 1 == fn.typePackParams.size())
241
{
242
saturatedPackArguments.push_back(arena->addTypePack({}));
243
}
244
245
// We need to have _something_ when we substitute the generic saturatedTypeArguments,
246
// even if they're missing, so we use the error type as a filler.
247
for (size_t i = saturatedTypeArguments.size(); i < typesRequired; ++i)
248
{
249
saturatedTypeArguments.push_back(builtinTypes->errorType);
250
}
251
252
for (size_t i = saturatedPackArguments.size(); i < packsRequired; ++i)
253
{
254
saturatedPackArguments.push_back(builtinTypes->errorTypePack);
255
}
256
257
for (TypeId& arg : saturatedTypeArguments)
258
arg = follow(arg);
259
260
for (TypePackId& pack : saturatedPackArguments)
261
pack = follow(pack);
262
263
// At this point, these two conditions should be true. If they aren't we
264
// will run into access violations.
265
LUAU_ASSERT(saturatedTypeArguments.size() == fn.typeParams.size());
266
LUAU_ASSERT(saturatedPackArguments.size() == fn.typePackParams.size());
267
268
return {saturatedTypeArguments, saturatedPackArguments};
269
}
270
271
bool InstantiationSignature::operator==(const InstantiationSignature& rhs) const
272
{
273
return fn == rhs.fn && arguments == rhs.arguments && packArguments == rhs.packArguments;
274
}
275
276
size_t HashInstantiationSignature::operator()(const InstantiationSignature& signature) const
277
{
278
size_t hash = std::hash<TypeId>{}(signature.fn.type);
279
for (const GenericTypeDefinition& p : signature.fn.typeParams)
280
{
281
hash ^= (std::hash<TypeId>{}(p.ty) << 1);
282
}
283
284
for (const GenericTypePackDefinition& p : signature.fn.typePackParams)
285
{
286
hash ^= (std::hash<TypePackId>{}(p.tp) << 1);
287
}
288
289
for (const TypeId a : signature.arguments)
290
{
291
hash ^= (std::hash<TypeId>{}(a) << 1);
292
}
293
294
for (const TypePackId a : signature.packArguments)
295
{
296
hash ^= (std::hash<TypePackId>{}(a) << 1);
297
}
298
299
return hash;
300
}
301
302
struct InstantiationQueuer : TypeOnceVisitor
303
{
304
ConstraintSolver* solver;
305
NotNull<Scope> scope;
306
Location location;
307
308
explicit InstantiationQueuer(NotNull<Scope> scope, const Location& location, ConstraintSolver* solver)
309
: TypeOnceVisitor("InstantiationQueuer", /* skipBoundTypes */ true)
310
, solver(solver)
311
, scope(scope)
312
, location(location)
313
{
314
}
315
316
bool visit(TypeId ty, const PendingExpansionType& petv) override
317
{
318
solver->pushConstraint(scope, location, TypeAliasExpansionConstraint{ty});
319
return false;
320
}
321
322
bool visit(TypeId ty, const TypeFunctionInstanceType&) override
323
{
324
solver->pushConstraint(scope, location, ReduceConstraint{ty});
325
return true;
326
}
327
328
bool visit(TypeId ty, const ExternType& etv) override
329
{
330
return false;
331
}
332
};
333
334
struct InfiniteTypeFinder : IterativeTypeVisitor
335
{
336
NotNull<ConstraintSolver> solver;
337
const InstantiationSignature& signature;
338
NotNull<Scope> scope;
339
bool foundInfiniteType = false;
340
341
explicit InfiniteTypeFinder(ConstraintSolver* solver, const InstantiationSignature& signature, NotNull<Scope> scope)
342
: IterativeTypeVisitor("InfiniteTypeFinder", /* skipBoundTypes */ true)
343
, solver(solver)
344
, signature(signature)
345
, scope(scope)
346
{
347
}
348
349
bool visit(TypeId ty) override
350
{
351
return !foundInfiniteType;
352
}
353
354
bool visit(TypeId ty, const PendingExpansionType& petv) override
355
{
356
if (foundInfiniteType)
357
return false;
358
359
const std::optional<TypeFun> tf =
360
petv.prefix ? scope->lookupImportedType(petv.prefix->value, petv.name.value) : scope->lookupType(petv.name.value);
361
362
if (!tf)
363
return true;
364
365
// If `tf->type` is different from `signature.fn.type` then we
366
// have two different type aliases.
367
if (follow(tf->type) != follow(signature.fn.type))
368
return true;
369
370
// We want to check that the arguments to this pending expansion
371
// type are exactly the generic arguments provided.
372
for (size_t i = 0; i < std::min(petv.typeArguments.size(), tf->typeParams.size()); ++i)
373
{
374
if (petv.typeArguments[i] != tf->typeParams[i].ty)
375
{
376
foundInfiniteType = true;
377
return false;
378
}
379
}
380
381
// Ditto with packs.
382
for (size_t i = 0; i < std::min(petv.packArguments.size(), tf->typePackParams.size()); ++i)
383
{
384
if (petv.packArguments[i] != tf->typePackParams[i].tp)
385
{
386
foundInfiniteType = true;
387
return false;
388
}
389
}
390
391
return false;
392
}
393
};
394
395
396
ConstraintSolver::ConstraintSolver(
397
NotNull<Normalizer> normalizer,
398
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
399
ModulePtr module,
400
NotNull<ModuleResolver> moduleResolver,
401
std::vector<RequireCycle> requireCycles,
402
DcrLogger* logger,
403
NotNull<const DataFlowGraph> dfg,
404
TypeCheckLimits limits,
405
ConstraintSet constraintSet_
406
)
407
: arena(normalizer->arena)
408
, builtinTypes(normalizer->builtinTypes)
409
, normalizer(normalizer)
410
, typeFunctionRuntime(typeFunctionRuntime)
411
, constraintSet(std::move(constraintSet_))
412
, constraints(borrowConstraints(constraintSet.constraints))
413
, scopeToFunction(&constraintSet.scopeToFunction)
414
, rootScope(constraintSet.rootScope)
415
, module(std::move(module))
416
, dfg(dfg)
417
, solverConstraintLimit(FInt::LuauSolverConstraintLimit)
418
, moduleResolver(moduleResolver)
419
, requireCycles(std::move(requireCycles))
420
, logger(logger)
421
, limits(std::move(limits))
422
, opts{/*exhaustive*/ true}
423
{
424
initFreeTypeTracking();
425
}
426
427
ConstraintSolver::ConstraintSolver(
428
NotNull<Normalizer> normalizer,
429
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
430
NotNull<Scope> rootScope,
431
std::vector<NotNull<Constraint>> constraints,
432
NotNull<DenseHashMap<Scope*, TypeId>> scopeToFunction,
433
ModulePtr module,
434
NotNull<ModuleResolver> moduleResolver,
435
std::vector<RequireCycle> requireCycles,
436
DcrLogger* logger,
437
NotNull<const DataFlowGraph> dfg,
438
TypeCheckLimits limits
439
)
440
: arena(normalizer->arena)
441
, builtinTypes(normalizer->builtinTypes)
442
, normalizer(normalizer)
443
, typeFunctionRuntime(typeFunctionRuntime)
444
, constraintSet{rootScope}
445
, constraints(std::move(constraints))
446
, scopeToFunction(scopeToFunction)
447
, rootScope(rootScope)
448
, module(std::move(module))
449
, dfg(dfg)
450
, solverConstraintLimit(FInt::LuauSolverConstraintLimit)
451
, moduleResolver(moduleResolver)
452
, requireCycles(std::move(requireCycles))
453
, logger(logger)
454
, limits(std::move(limits))
455
, opts{/*exhaustive*/ true}
456
{
457
initFreeTypeTracking();
458
}
459
460
void ConstraintSolver::randomize(unsigned seed)
461
{
462
if (unsolvedConstraints.empty())
463
return;
464
465
unsigned int rng = seed;
466
467
for (size_t i = unsolvedConstraints.size() - 1; i > 0; --i)
468
{
469
// Fisher-Yates shuffle
470
size_t j = rng % (i + 1);
471
472
std::swap(unsolvedConstraints[i], unsolvedConstraints[j]);
473
474
// LCG RNG, constants from Numerical Recipes
475
// This may occasionally result in skewed shuffles due to distribution properties, but this is a debugging tool so it should be good enough
476
rng = rng * 1664525 + 1013904223;
477
}
478
}
479
480
void ConstraintSolver::run()
481
{
482
LUAU_TIMETRACE_SCOPE("ConstraintSolver::run", "Typechecking");
483
484
if (isDone())
485
return;
486
487
if (FFlag::DebugLuauLogSolver)
488
{
489
printf("Starting solver for module %s (%s)\n", module->humanReadableName.c_str(), module->name.c_str());
490
dump(this, opts);
491
printf("Bindings:\n");
492
dumpBindings(rootScope, opts);
493
}
494
495
if (logger)
496
{
497
logger->captureInitialSolverState(rootScope, unsolvedConstraints);
498
}
499
500
// Free types that have no constraints at all can be generalized right away.
501
if (FFlag::LuauUseConstraintSetsToTrackFreeTypes)
502
{
503
for (TypeId ty : constraintSet.freeTypes)
504
{
505
if (auto it = typeToConstraintSet.find(ty); it == typeToConstraintSet.end() || it->second.empty())
506
generalizeOneType(ty);
507
}
508
}
509
else
510
{
511
for (TypeId ty : constraintSet.freeTypes)
512
{
513
if (auto it = DEPRECATED_mutatedFreeTypeToConstraint.find(ty); it == DEPRECATED_mutatedFreeTypeToConstraint.end() || it->second.empty())
514
generalizeOneType(ty);
515
}
516
}
517
518
constraintSet.freeTypes.clear();
519
520
auto runSolverPass = [&](bool force)
521
{
522
bool progress = false;
523
524
size_t i = 0;
525
while (i < unsolvedConstraints.size())
526
{
527
NotNull<const Constraint> c = unsolvedConstraints[i];
528
if (!force && isBlocked(c))
529
{
530
++i;
531
continue;
532
}
533
534
if (limits.finishTime && TimeTrace::getClock() > *limits.finishTime)
535
throwTimeLimitError();
536
if (limits.cancellationToken && limits.cancellationToken->requested())
537
throwUserCancelError();
538
539
// If we were _given_ a limit, and the current limit has hit zero, ]
540
// then early exit from constraint solving.
541
if (FInt::LuauSolverConstraintLimit > 0 && solverConstraintLimit == 0)
542
break;
543
544
std::string saveMe = FFlag::DebugLuauLogSolver ? toString(*c, opts) : std::string{};
545
StepSnapshot snapshot;
546
547
if (logger)
548
{
549
snapshot = logger->prepareStepSnapshot(rootScope, c, force, unsolvedConstraints);
550
}
551
552
if (FFlag::DebugLuauAssertOnForcedConstraint)
553
LUAU_ASSERT(!force);
554
555
bool success = tryDispatch(c, force);
556
557
progress |= success;
558
559
if (success)
560
{
561
unblock(c);
562
unsolvedConstraints.erase(unsolvedConstraints.begin() + ptrdiff_t(i));
563
564
if (FFlag::LuauUseConstraintSetsToTrackFreeTypes)
565
{
566
if (auto entry = constraintToMutatedTypes.find(c.get()))
567
{
568
DenseHashSet<TypeId> seen{nullptr};
569
for (auto ty : *entry)
570
{
571
// There is a high chance that this type has been rebound
572
// across blocked types, rebound free types, pending
573
// expansion types, etc, so we need to follow it.
574
ty = follow(ty);
575
if (seen.contains(ty))
576
continue;
577
seen.insert(ty);
578
579
if (auto it = typeToConstraintSet.find(ty); it != typeToConstraintSet.end())
580
{
581
// TODO CLI-195994
582
//
583
// Eager generalization of free types is
584
// analagous to garbage collection (and ref
585
// counting). In a GC, we need to identify
586
// the roots for reachable objects. For
587
// generalization those roots are the unsolved
588
// constraints. We keep a mapping from types
589
// to their roots in order to quickly check which
590
// free types might need to get generalized.
591
//
592
// We would like to assert that the constraint set
593
// contained this constraint prior to trying to
594
// erase it, but we are not in a posture to be
595
// able to do so right now.
596
//
597
it->second.erase(c.get());
598
if (it->second.size() <= 1)
599
unblock(ty, Location{});
600
601
if (it->second.empty())
602
generalizeOneType(ty);
603
}
604
}
605
}
606
}
607
else
608
{
609
610
if (const auto maybeMutated = DEPRECATED_maybeMutatedFreeTypes.find(c); maybeMutated != DEPRECATED_maybeMutatedFreeTypes.end())
611
{
612
DenseHashSet<TypeId> seen{nullptr};
613
for (auto ty : maybeMutated->second)
614
{
615
// There is a high chance that this type has been rebound
616
// across blocked types, rebound free types, pending
617
// expansion types, etc, so we need to follow it.
618
ty = follow(ty);
619
620
if (seen.contains(ty))
621
continue;
622
seen.insert(ty);
623
624
size_t& refCount = DEPRECATED_unresolvedConstraints[ty];
625
if (refCount > 0)
626
refCount -= 1;
627
628
// We have two constraints that are designed to wait for the
629
// refCount on a free type to be equal to 1: the
630
// PrimitiveTypeConstraint and ReduceConstraint. We
631
// therefore wake any constraint waiting for a free type's
632
// refcount to be 1 or 0.
633
if (refCount <= 1)
634
unblock(ty, Location{});
635
636
if (refCount == 0)
637
generalizeOneType(ty);
638
}
639
}
640
}
641
642
if (logger)
643
{
644
logger->commitStepSnapshot(snapshot);
645
}
646
647
if (FFlag::DebugLuauLogSolver)
648
{
649
if (force)
650
printf("Force ");
651
printf("Dispatched\n\t%s\n", saveMe.c_str());
652
653
if (force)
654
{
655
printf("Blocked on:\n");
656
657
for (const auto& [bci, cv] : blocked)
658
{
659
if (end(cv) == std::find(begin(cv), end(cv), c))
660
continue;
661
662
if (auto bty = get_if<TypeId>(&bci))
663
printf("\tType %s\n", toString(*bty, opts).c_str());
664
else if (auto btp = get_if<TypePackId>(&bci))
665
printf("\tPack %s\n", toString(*btp, opts).c_str());
666
else if (auto cc = get_if<const Constraint*>(&bci))
667
printf("\tCons %s\n", toString(**cc, opts).c_str());
668
else
669
LUAU_ASSERT(!"Unreachable??");
670
}
671
}
672
673
dump(this, opts);
674
}
675
}
676
else
677
++i;
678
679
if (force && success)
680
return true;
681
}
682
683
return progress;
684
};
685
686
bool progress = false;
687
do
688
{
689
progress = runSolverPass(false);
690
if (!progress)
691
progress |= runSolverPass(true);
692
} while (progress);
693
694
if (!unsolvedConstraints.empty())
695
reportError(ConstraintSolvingIncompleteError{}, Location{});
696
697
// After we have run all the constraints, type functions should be generalized
698
// At this point, we can try to perform one final simplification to suss out
699
// whether type functions are truly uninhabited or if they can reduce
700
701
finalizeTypeFunctions();
702
703
if (FFlag::DebugLuauLogSolver || FFlag::DebugLuauLogBindings)
704
dumpBindings(rootScope, opts);
705
706
if (logger)
707
{
708
logger->captureFinalSolverState(rootScope, unsolvedConstraints);
709
}
710
}
711
712
void ConstraintSolver::finalizeTypeFunctions()
713
{
714
// At this point, we've generalized. Let's try to finish reducing as much as we can, we'll leave warning to the typechecker
715
for (auto [t, constraint] : typeFunctionsToFinalize)
716
{
717
TypeId ty = follow(t);
718
if (get<TypeFunctionInstanceType>(ty))
719
{
720
TypeFunctionContext context{NotNull{this}, constraint->scope, NotNull{constraint}};
721
FunctionGraphReductionResult result = reduceTypeFunctions(t, constraint->location, NotNull{&context}, true);
722
723
for (TypeId r : result.reducedTypes)
724
unblock(r, constraint->location);
725
for (TypePackId r : result.reducedPacks)
726
unblock(r, constraint->location);
727
}
728
}
729
}
730
731
bool ConstraintSolver::isDone() const
732
{
733
return unsolvedConstraints.empty();
734
}
735
736
struct TypeSearcher : TypeVisitor
737
{
738
TypeId needle;
739
Polarity current = Polarity::Positive;
740
741
size_t count = 0;
742
Polarity result = Polarity::None;
743
744
explicit TypeSearcher(TypeId needle)
745
: TypeSearcher(needle, Polarity::Positive)
746
{
747
}
748
749
explicit TypeSearcher(TypeId needle, Polarity initialPolarity)
750
: TypeVisitor("TypeSearcher", /* skipBoundTypes */ true)
751
, needle(needle)
752
, current(initialPolarity)
753
{
754
}
755
756
bool visit(TypeId ty) override
757
{
758
if (ty == needle)
759
{
760
++count;
761
result = Polarity(size_t(result) | size_t(current));
762
}
763
764
return true;
765
}
766
767
void flip()
768
{
769
switch (current)
770
{
771
case Polarity::Positive:
772
current = Polarity::Negative;
773
break;
774
case Polarity::Negative:
775
current = Polarity::Positive;
776
break;
777
default:
778
break;
779
}
780
}
781
782
bool visit(TypeId ty, const FunctionType& ft) override
783
{
784
flip();
785
traverse(ft.argTypes);
786
787
flip();
788
traverse(ft.retTypes);
789
790
return false;
791
}
792
793
// bool visit(TypeId ty, const TableType& tt) override
794
// {
795
796
// }
797
798
bool visit(TypeId ty, const ExternType&) override
799
{
800
return false;
801
}
802
};
803
804
void ConstraintSolver::initFreeTypeTracking()
805
{
806
if (FFlag::LuauUseConstraintSetsToTrackFreeTypes)
807
{
808
for (auto c : this->constraints)
809
{
810
unsolvedConstraints.emplace_back(c);
811
auto [types, _typePacks] = c->getMaybeMutatedTypes();
812
for (auto ty: types)
813
{
814
auto [it, _] = typeToConstraintSet.try_emplace(ty, Set<const Constraint*>{nullptr});
815
// We don't care if this is fresh, we can blindly insert.
816
it->second.insert(c.get());
817
}
818
const auto [_types, fresh1] = constraintToMutatedTypes.try_insert(c.get(), std::move(types));
819
LUAU_ASSERT(fresh1);
820
821
for (NotNull<const Constraint> dep : c->dependencies)
822
{
823
block(dep, c);
824
}
825
826
}
827
}
828
else
829
{
830
for (auto c : this->constraints)
831
{
832
unsolvedConstraints.emplace_back(c);
833
834
auto maybeMutatedTypesPerConstraint = c->DEPRECATED_getMaybeMutatedFreeTypes();
835
for (auto ty : maybeMutatedTypesPerConstraint)
836
{
837
auto [refCount, _] = DEPRECATED_unresolvedConstraints.try_insert(ty, 0);
838
refCount += 1;
839
840
auto [it, fresh] = DEPRECATED_mutatedFreeTypeToConstraint.try_emplace(ty);
841
it->second.insert(c.get());
842
}
843
DEPRECATED_maybeMutatedFreeTypes.emplace(c, maybeMutatedTypesPerConstraint);
844
845
for (NotNull<const Constraint> dep : c->dependencies)
846
{
847
block(dep, c);
848
}
849
}
850
}
851
}
852
853
void ConstraintSolver::generalizeOneType(TypeId ty)
854
{
855
ty = follow(ty);
856
const FreeType* freeTy = get<FreeType>(ty);
857
858
std::string saveme = FFlag::DebugLuauLogSolver ? toString(ty, opts) : "[FFlag::DebugLuauLogSolver Off]";
859
860
// Some constraints (like prim) will also replace a free type with something
861
// concrete. If so, our work is already done.
862
if (!freeTy)
863
return;
864
865
TypeId* functionType = scopeToFunction->find(freeTy->scope);
866
if (!functionType)
867
return;
868
869
std::optional<TypeId> resultTy = generalize(arena, builtinTypes, NotNull{freeTy->scope}, generalizedTypes, *functionType, ty);
870
871
if (FFlag::DebugLuauLogSolver)
872
{
873
printf(
874
"Eagerly generalized %s (now %s)\n\tin function %s\n",
875
saveme.c_str(),
876
toString(ty, opts).c_str(),
877
toString(resultTy.value_or(*functionType), opts).c_str()
878
);
879
}
880
}
881
882
void ConstraintSolver::bind(NotNull<const Constraint> constraint, TypeId ty, TypeId boundTo)
883
{
884
LUAU_ASSERT(get<BlockedType>(ty) || get<FreeType>(ty) || get<PendingExpansionType>(ty));
885
LUAU_ASSERT(canMutate(ty, constraint));
886
887
boundTo = follow(boundTo);
888
if (get<BlockedType>(ty) && ty == boundTo)
889
{
890
emplace<FreeType>(
891
constraint, ty, constraint->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed
892
); // FIXME? Is this the right polarity?
893
894
trackInteriorFreeType(constraint->scope, ty);
895
896
return;
897
}
898
899
shiftReferences(ty, boundTo);
900
emplaceType<BoundType>(asMutable(ty), boundTo);
901
unblock(ty, constraint->location);
902
}
903
904
void ConstraintSolver::bind(NotNull<const Constraint> constraint, TypePackId tp, TypePackId boundTo)
905
{
906
LUAU_ASSERT(get<BlockedTypePack>(tp) || get<FreeTypePack>(tp));
907
LUAU_ASSERT(canMutate(tp, constraint));
908
909
boundTo = follow(boundTo);
910
LUAU_ASSERT(tp != boundTo);
911
912
emplaceTypePack<BoundTypePack>(asMutable(tp), boundTo);
913
unblock(tp, constraint->location);
914
}
915
916
template<typename T, typename... Args>
917
void ConstraintSolver::emplace(NotNull<const Constraint> constraint, TypeId ty, Args&&... args)
918
{
919
static_assert(!std::is_same_v<T, BoundType>, "cannot use `emplace<BoundType>`! use `bind`");
920
921
LUAU_ASSERT(get<BlockedType>(ty) || get<FreeType>(ty) || get<PendingExpansionType>(ty));
922
LUAU_ASSERT(canMutate(ty, constraint));
923
924
emplaceType<T>(asMutable(ty), std::forward<Args>(args)...);
925
unblock(ty, constraint->location);
926
}
927
928
template<typename T, typename... Args>
929
void ConstraintSolver::emplace(NotNull<const Constraint> constraint, TypePackId tp, Args&&... args)
930
{
931
static_assert(!std::is_same_v<T, BoundTypePack>, "cannot use `emplace<BoundTypePack>`! use `bind`");
932
933
LUAU_ASSERT(get<BlockedTypePack>(tp) || get<FreeTypePack>(tp));
934
LUAU_ASSERT(canMutate(tp, constraint));
935
936
emplaceTypePack<T>(asMutable(tp), std::forward<Args>(args)...);
937
unblock(tp, constraint->location);
938
}
939
940
bool ConstraintSolver::tryDispatch(NotNull<const Constraint> constraint, bool force)
941
{
942
if (!force && isBlocked(constraint))
943
return false;
944
945
bool success = false;
946
947
if (auto sc = get<SubtypeConstraint>(*constraint))
948
success = tryDispatch(*sc, constraint);
949
else if (auto psc = get<PackSubtypeConstraint>(*constraint))
950
success = tryDispatch(*psc, constraint);
951
else if (auto gc = get<GeneralizationConstraint>(*constraint))
952
success = tryDispatch(*gc, constraint);
953
else if (auto ic = get<IterableConstraint>(*constraint))
954
success = tryDispatch(*ic, constraint, force);
955
else if (auto nc = get<NameConstraint>(*constraint))
956
success = tryDispatch(*nc, constraint);
957
else if (auto taec = get<TypeAliasExpansionConstraint>(*constraint))
958
success = tryDispatch(*taec, constraint);
959
else if (auto fcc = get<FunctionCallConstraint>(*constraint))
960
success = tryDispatch(*fcc, constraint, force);
961
else if (auto fcc = get<FunctionCheckConstraint>(*constraint))
962
success = tryDispatch(*fcc, constraint, force);
963
else if (auto fcc = get<PrimitiveTypeConstraint>(*constraint))
964
success = tryDispatch(*fcc, constraint);
965
else if (auto hpc = get<HasPropConstraint>(*constraint))
966
success = tryDispatch(*hpc, constraint);
967
else if (auto spc = get<HasIndexerConstraint>(*constraint))
968
success = tryDispatch(*spc, constraint);
969
else if (auto uc = get<AssignPropConstraint>(*constraint))
970
success = tryDispatch(*uc, constraint);
971
else if (auto uc = get<AssignIndexConstraint>(*constraint))
972
success = tryDispatch(*uc, constraint);
973
else if (auto uc = get<UnpackConstraint>(*constraint))
974
success = tryDispatch(*uc, constraint);
975
else if (auto rc = get<ReduceConstraint>(*constraint))
976
success = tryDispatch(*rc, constraint, force);
977
else if (auto rpc = get<ReducePackConstraint>(*constraint))
978
success = tryDispatch(*rpc, constraint, force);
979
else if (auto eqc = get<EqualityConstraint>(*constraint))
980
success = tryDispatch(*eqc, constraint);
981
else if (auto sc = get<SimplifyConstraint>(*constraint))
982
success = tryDispatch(*sc, constraint, force);
983
else if (auto pftc = get<PushFunctionTypeConstraint>(*constraint))
984
success = tryDispatch(*pftc, constraint);
985
else if (auto esgc = get<TypeInstantiationConstraint>(*constraint))
986
{
987
LUAU_ASSERT(FFlag::LuauExplicitTypeInstantiationSupport);
988
success = tryDispatch(*esgc, constraint);
989
}
990
else if (auto ptc = get<PushTypeConstraint>(*constraint))
991
success = tryDispatch(*ptc, constraint, force);
992
else
993
LUAU_ASSERT(false);
994
995
return success;
996
}
997
998
bool ConstraintSolver::tryDispatch(const SubtypeConstraint& c, NotNull<const Constraint> constraint)
999
{
1000
if (isBlocked(c.subType))
1001
return block(c.subType, constraint);
1002
else if (isBlocked(c.superType))
1003
return block(c.superType, constraint);
1004
1005
unify(constraint, c.subType, c.superType);
1006
1007
return true;
1008
}
1009
1010
bool ConstraintSolver::tryDispatch(const PackSubtypeConstraint& c, NotNull<const Constraint> constraint)
1011
{
1012
if (isBlocked(c.subPack))
1013
return block(c.subPack, constraint);
1014
else if (isBlocked(c.superPack))
1015
return block(c.superPack, constraint);
1016
1017
unify(constraint, c.subPack, c.superPack);
1018
1019
return true;
1020
}
1021
1022
bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<const Constraint> constraint)
1023
{
1024
TypeId generalizedType = follow(c.generalizedType);
1025
1026
if (isBlocked(c.sourceType))
1027
return block(c.sourceType, constraint);
1028
else if (get<PendingExpansionType>(generalizedType))
1029
return block(generalizedType, constraint);
1030
1031
std::optional<TypeId> generalizedTy = generalize(NotNull{arena}, builtinTypes, constraint->scope, generalizedTypes, c.sourceType);
1032
if (!generalizedTy)
1033
reportError(CodeTooComplex{}, constraint->location);
1034
1035
if (generalizedTy)
1036
{
1037
pruneUnnecessaryGenerics(arena, builtinTypes, constraint->scope, generalizedTypes, *generalizedTy);
1038
if (get<BlockedType>(generalizedType))
1039
bind(constraint, generalizedType, *generalizedTy);
1040
else
1041
unify(constraint, generalizedType, *generalizedTy);
1042
1043
if (FunctionType* fty = getMutable<FunctionType>(follow(generalizedType)))
1044
{
1045
if (c.hasDeprecatedAttribute)
1046
{
1047
fty->isDeprecatedFunction = true;
1048
fty->deprecatedInfo = std::make_shared<AstAttr::DeprecatedInfo>(c.deprecatedInfo);
1049
}
1050
}
1051
}
1052
else
1053
{
1054
reportError(CodeTooComplex{}, constraint->location);
1055
bind(constraint, c.generalizedType, builtinTypes->errorType);
1056
}
1057
1058
// We check if this member is initialized and then access it, but
1059
// clang-tidy doesn't understand this is safe.
1060
if (constraint->scope->interiorFreeTypes)
1061
{
1062
for (TypeId ty : *constraint->scope->interiorFreeTypes) // NOLINT(bugprone-unchecked-optional-access)
1063
{
1064
ty = follow(ty);
1065
if (auto freeTy = get<FreeType>(ty))
1066
{
1067
GeneralizationParams<TypeId> params;
1068
params.foundOutsideFunctions = true;
1069
params.useCount = 1;
1070
params.polarity = freeTy->polarity;
1071
GeneralizationResult<TypeId> res = generalizeType(arena, builtinTypes, constraint->scope, ty, params);
1072
if (res.resourceLimitsExceeded)
1073
reportError(CodeTooComplex{}, constraint->scope->location); // FIXME: We don't have a very good location for this.
1074
}
1075
else if (get<TableType>(ty))
1076
sealTable(constraint->scope, ty);
1077
1078
if (FFlag::LuauRelateHandlesCoincidentTables)
1079
unblock(ty, constraint->location);
1080
}
1081
}
1082
1083
if (constraint->scope->interiorFreeTypePacks)
1084
{
1085
for (TypePackId tp : *constraint->scope->interiorFreeTypePacks) // NOLINT(bugprone-unchecked-optional-access)
1086
{
1087
tp = follow(tp);
1088
if (auto freeTp = get<FreeTypePack>(tp))
1089
{
1090
GeneralizationParams<TypePackId> params;
1091
params.foundOutsideFunctions = true;
1092
params.useCount = 1;
1093
params.polarity = freeTp->polarity;
1094
LUAU_ASSERT(isKnown(params.polarity));
1095
generalizeTypePack(arena, builtinTypes, constraint->scope, tp, params);
1096
}
1097
}
1098
}
1099
1100
if (c.noGenerics)
1101
{
1102
if (auto ft = getMutable<FunctionType>(c.sourceType))
1103
{
1104
for (TypeId gen : ft->generics)
1105
asMutable(gen)->ty.emplace<BoundType>(builtinTypes->unknownType);
1106
ft->generics.clear();
1107
1108
for (TypePackId gen : ft->genericPacks)
1109
asMutable(gen)->ty.emplace<BoundTypePack>(builtinTypes->unknownTypePack);
1110
ft->genericPacks.clear();
1111
}
1112
}
1113
1114
return true;
1115
}
1116
1117
bool ConstraintSolver::tryDispatch(const IterableConstraint& c, NotNull<const Constraint> constraint, bool force)
1118
{
1119
/*
1120
* for .. in loops can play out in a bunch of different ways depending on
1121
* the shape of iteratee.
1122
*
1123
* iteratee might be:
1124
* * (nextFn)
1125
* * (nextFn, table)
1126
* * (nextFn, table, firstIndex)
1127
* * table with a metatable and __index
1128
* * table with a metatable and __call but no __index (if the metatable has
1129
* both, __index takes precedence)
1130
* * table with an indexer but no __index or __call (or no metatable)
1131
*
1132
* To dispatch this constraint, we need first to know enough about iteratee
1133
* to figure out which of the above shapes we are actually working with.
1134
*
1135
* If `force` is true and we still do not know, we must flag a warning. Type
1136
* functions are the fix for this.
1137
*
1138
* Since we need to know all of this stuff about the types of the iteratee,
1139
* we have no choice but for ConstraintSolver to also be the thing that
1140
* applies constraints to the types of the iterators.
1141
*/
1142
1143
auto block_ = [&](auto&& t)
1144
{
1145
if (force)
1146
{
1147
// If we haven't figured out the type of the iteratee by now,
1148
// there's nothing we can do.
1149
return true;
1150
}
1151
1152
block(t, constraint);
1153
return false;
1154
};
1155
1156
TypePack iterator = extendTypePack(*arena, builtinTypes, c.iterator, 3);
1157
if (iterator.head.size() < 3 && iterator.tail && isBlocked(*iterator.tail))
1158
return block_(*iterator.tail);
1159
1160
{
1161
bool blocked = false;
1162
for (TypeId t : iterator.head)
1163
{
1164
if (isBlocked(t))
1165
{
1166
block(t, constraint);
1167
blocked = true;
1168
}
1169
}
1170
1171
if (blocked)
1172
return false;
1173
}
1174
1175
if (0 == iterator.head.size())
1176
{
1177
for (TypeId ty : c.variables)
1178
bind(constraint, ty, builtinTypes->errorType);
1179
return true;
1180
}
1181
1182
TypeId nextTy = follow(iterator.head[0]);
1183
if (get<FreeType>(nextTy))
1184
{
1185
TypeId keyTy = freshType(arena, builtinTypes, constraint->scope, Polarity::Mixed);
1186
TypeId valueTy = freshType(arena, builtinTypes, constraint->scope, Polarity::Mixed);
1187
trackInteriorFreeType(constraint->scope, keyTy);
1188
trackInteriorFreeType(constraint->scope, valueTy);
1189
TypeId tableTy =
1190
arena->addType(TableType{TableType::Props{}, TableIndexer{keyTy, valueTy}, TypeLevel{}, constraint->scope, TableState::Free});
1191
1192
trackInteriorFreeType(constraint->scope, tableTy);
1193
1194
unify(constraint, nextTy, tableTy);
1195
1196
auto it = begin(c.variables);
1197
auto endIt = end(c.variables);
1198
1199
if (it != endIt)
1200
{
1201
bind(constraint, *it, keyTy);
1202
++it;
1203
}
1204
if (it != endIt)
1205
{
1206
bind(constraint, *it, valueTy);
1207
++it;
1208
}
1209
1210
while (it != endIt)
1211
{
1212
bind(constraint, *it, builtinTypes->nilType);
1213
++it;
1214
}
1215
1216
return true;
1217
}
1218
1219
if (get<FunctionType>(nextTy))
1220
{
1221
TypeId tableTy = builtinTypes->nilType;
1222
if (iterator.head.size() >= 2)
1223
tableTy = iterator.head[1];
1224
1225
return tryDispatchIterableFunction(nextTy, tableTy, c, constraint);
1226
}
1227
1228
else
1229
return tryDispatchIterableTable(iterator.head[0], c, constraint, force);
1230
1231
return true;
1232
}
1233
1234
bool ConstraintSolver::tryDispatch(const NameConstraint& c, NotNull<const Constraint> constraint)
1235
{
1236
if (isBlocked(c.namedType))
1237
return block(c.namedType, constraint);
1238
1239
TypeId target = follow(c.namedType);
1240
1241
if (target->persistent || target->owningArena != arena)
1242
return true;
1243
1244
if (std::optional<TypeFun> tf = constraint->scope->lookupType(c.name))
1245
{
1246
// We check to see if this type alias violates the recursion restriction
1247
InstantiationSignature signature{
1248
*tf,
1249
c.typeParameters,
1250
c.typePackParameters,
1251
};
1252
1253
InfiniteTypeFinder itf{this, signature, constraint->scope};
1254
itf.run(target);
1255
1256
if (itf.foundInfiniteType)
1257
{
1258
constraint->scope->invalidTypeAliases[c.name] = constraint->location;
1259
shiftReferences(target, builtinTypes->errorType);
1260
emplaceType<BoundType>(asMutable(target), builtinTypes->errorType);
1261
return true;
1262
}
1263
}
1264
1265
if (TableType* ttv = getMutable<TableType>(target))
1266
{
1267
if (c.synthetic && !ttv->name)
1268
ttv->syntheticName = c.name;
1269
else
1270
{
1271
ttv->name = c.name;
1272
ttv->instantiatedTypeParams = c.typeParameters;
1273
ttv->instantiatedTypePackParams = c.typePackParameters;
1274
}
1275
}
1276
else if (MetatableType* mtv = getMutable<MetatableType>(target))
1277
mtv->syntheticName = c.name;
1278
else if (get<IntersectionType>(target) || get<UnionType>(target))
1279
{
1280
// nothing (yet)
1281
}
1282
1283
return true;
1284
}
1285
1286
bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNull<const Constraint> constraint)
1287
{
1288
const PendingExpansionType* petv = get<PendingExpansionType>(follow(c.target));
1289
if (!petv)
1290
{
1291
unblock(c.target, constraint->location); // TODO: do we need this? any re-entrancy?
1292
return true;
1293
}
1294
1295
auto bindResult = [this, &c, constraint](TypeId result)
1296
{
1297
auto cTarget = follow(c.target);
1298
LUAU_ASSERT(get<PendingExpansionType>(cTarget));
1299
// We do this check here to ensure that we don't bind an alias to itself
1300
if (occursCheck(cTarget, result))
1301
{
1302
reportError(OccursCheckFailed{}, constraint->location);
1303
bind(constraint, cTarget, builtinTypes->errorType);
1304
}
1305
else
1306
{
1307
shiftReferences(cTarget, result);
1308
bind(constraint, cTarget, result);
1309
}
1310
};
1311
1312
std::optional<TypeFun> tf = (petv->prefix) ? constraint->scope->lookupImportedType(petv->prefix->value, petv->name.value)
1313
: constraint->scope->lookupType(petv->name.value);
1314
1315
if (!tf.has_value())
1316
{
1317
reportError(UnknownSymbol{petv->name.value, UnknownSymbol::Context::Type}, constraint->location);
1318
bindResult(builtinTypes->errorType);
1319
return true;
1320
}
1321
1322
// Adding ReduceConstraint on type function for the constraint solver
1323
if (get<TypeFunctionInstanceType>(follow(tf->type)))
1324
pushConstraint(NotNull(constraint->scope.get()), constraint->location, ReduceConstraint{tf->type});
1325
1326
// Due to how pending expansion types and TypeFun's are created
1327
// If this check passes, we have created a cyclic / corecursive type alias
1328
// of size 0
1329
TypeId lhs = follow(c.target);
1330
TypeId rhs = tf->type;
1331
if (occursCheck(lhs, rhs))
1332
{
1333
reportError(OccursCheckFailed{}, constraint->location);
1334
bindResult(builtinTypes->errorType);
1335
return true;
1336
}
1337
1338
// If there are no parameters to the type function we can just use the type directly
1339
if (tf->typeParams.empty() && tf->typePackParams.empty())
1340
{
1341
bindResult(tf->type);
1342
return true;
1343
}
1344
1345
auto [typeArguments, packArguments] = saturateArguments(arena, builtinTypes, *tf, petv->typeArguments, petv->packArguments);
1346
1347
bool sameTypes = std::equal(
1348
typeArguments.begin(),
1349
typeArguments.end(),
1350
tf->typeParams.begin(),
1351
tf->typeParams.end(),
1352
[](auto&& itp, auto&& p)
1353
{
1354
return itp == p.ty;
1355
}
1356
);
1357
1358
bool samePacks = std::equal(
1359
packArguments.begin(),
1360
packArguments.end(),
1361
tf->typePackParams.begin(),
1362
tf->typePackParams.end(),
1363
[](auto&& itp, auto&& p)
1364
{
1365
return itp == p.tp;
1366
}
1367
);
1368
1369
// If we're instantiating the type with its generic saturatedTypeArguments we are
1370
// performing the identity substitution. We can just short-circuit and bind
1371
// to the TypeFun's type.
1372
if (sameTypes && samePacks)
1373
{
1374
bindResult(tf->type);
1375
return true;
1376
}
1377
1378
InstantiationSignature signature{
1379
*tf,
1380
typeArguments,
1381
packArguments,
1382
};
1383
1384
// If we use the same signature, we don't need to bother trying to
1385
// instantiate the alias again, since the instantiation should be
1386
// deterministic.
1387
if (TypeId* cached = instantiatedAliases.find(signature))
1388
{
1389
bindResult(*cached);
1390
return true;
1391
}
1392
1393
// In order to prevent infinite types from being expanded and causing us to
1394
// cycle infinitely, we need to scan the type function for cases where we
1395
// expand the same alias with different type saturatedTypeArguments. See
1396
// https://github.com/luau-lang/luau/pull/68 for the RFC responsible for
1397
// this. This is a little nicer than using a recursion limit because we can
1398
// catch the infinite expansion before actually trying to expand it.
1399
InfiniteTypeFinder itf{this, signature, constraint->scope};
1400
itf.run(tf->type);
1401
1402
if (itf.foundInfiniteType)
1403
{
1404
bindResult(builtinTypes->errorType);
1405
constraint->scope->invalidTypeAliases[petv->name.value] = constraint->location;
1406
return true;
1407
}
1408
1409
// FIXME: this does not actually implement instantiation properly, it puts
1410
// the values into the _binders_ as well, which is a mess.
1411
// e.g. `<T...>(T...) -> T...` instantiated with `any` for `T...` becomes
1412
// `<any>(any) -> any`, where none of these things are _generics_.
1413
ApplyTypeFunction applyTypeFunction{arena};
1414
for (size_t i = 0; i < typeArguments.size(); ++i)
1415
{
1416
applyTypeFunction.typeArguments[tf->typeParams[i].ty] = typeArguments[i];
1417
}
1418
1419
for (size_t i = 0; i < packArguments.size(); ++i)
1420
{
1421
applyTypeFunction.typePackArguments[tf->typePackParams[i].tp] = packArguments[i];
1422
}
1423
1424
std::optional<TypeId> maybeInstantiated = applyTypeFunction.substitute(tf->type);
1425
// Note that ApplyTypeFunction::encounteredForwardedType is never set in
1426
// DCR, because we do not use free types for forward-declared generic
1427
// aliases.
1428
1429
if (!maybeInstantiated.has_value())
1430
{
1431
// TODO (CLI-56761): Report an error.
1432
bindResult(builtinTypes->errorType);
1433
return true;
1434
}
1435
1436
TypeId instantiated = *maybeInstantiated;
1437
TypeId target = follow(instantiated);
1438
1439
// The application is not recursive, so we need to queue up application of
1440
// any child type function instantiations within the result in order for it
1441
// to be complete.
1442
InstantiationQueuer queuer{constraint->scope, constraint->location, this};
1443
queuer.traverse(target);
1444
1445
if (target->persistent || target->owningArena != arena)
1446
{
1447
bindResult(target);
1448
return true;
1449
}
1450
1451
// Type function application will happily give us the exact same type if
1452
// there are e.g. generic saturatedTypeArguments that go unused.
1453
const TableType* tfTable = getTableType(tf->type);
1454
1455
bool needsClone = follow(tf->type) == target || (tfTable != nullptr && tfTable == getTableType(target)) ||
1456
std::any_of(
1457
typeArguments.begin(),
1458
typeArguments.end(),
1459
[&](const auto& other)
1460
{
1461
return other == target;
1462
}
1463
);
1464
1465
// Only tables have the properties we're trying to set.
1466
TableType* ttv = getMutableTableType(target);
1467
1468
if (ttv)
1469
{
1470
if (needsClone)
1471
{
1472
if (get<MetatableType>(target))
1473
{
1474
CloneState cloneState{builtinTypes};
1475
instantiated = shallowClone(target, *arena.get(), cloneState, true);
1476
MetatableType* mtv = getMutable<MetatableType>(instantiated);
1477
mtv->table = shallowClone(mtv->table, *arena.get(), cloneState, true);
1478
ttv = getMutable<TableType>(mtv->table);
1479
}
1480
else if (get<TableType>(target))
1481
{
1482
CloneState cloneState{builtinTypes};
1483
instantiated = shallowClone(target, *arena.get(), cloneState, true);
1484
ttv = getMutable<TableType>(instantiated);
1485
}
1486
1487
target = follow(instantiated);
1488
}
1489
1490
// This is a new type - redefine the location.
1491
ttv->definitionLocation = constraint->location;
1492
ttv->definitionModuleName = module->name;
1493
1494
ttv->instantiatedTypeParams = typeArguments;
1495
ttv->instantiatedTypePackParams = packArguments;
1496
}
1497
1498
bindResult(target);
1499
1500
instantiatedAliases[signature] = target;
1501
1502
return true;
1503
}
1504
1505
void ConstraintSolver::fillInDiscriminantTypes(NotNull<const Constraint> constraint, const std::vector<std::optional<TypeId>>& discriminantTypes)
1506
{
1507
for (std::optional<TypeId> ty : discriminantTypes)
1508
{
1509
if (!ty)
1510
continue;
1511
1512
if (isBlocked(*ty))
1513
// We bind any unused discriminants to the `*no-refine*` type indicating that it can be safely ignored.
1514
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->noRefineType);
1515
1516
// We also need to unconditionally unblock these types, otherwise
1517
// you end up with funky looking "Blocked on *no-refine*."
1518
unblock(*ty, constraint->location);
1519
}
1520
}
1521
1522
bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<const Constraint> constraint, bool force)
1523
{
1524
TypeId fn = follow(c.fn);
1525
TypePackId argsPack = follow(c.argsPack);
1526
TypePackId result = follow(c.result);
1527
1528
if (isBlocked(fn))
1529
return block(c.fn, constraint);
1530
1531
if (get<AnyType>(fn))
1532
{
1533
emplaceTypePack<BoundTypePack>(asMutable(c.result), builtinTypes->anyTypePack);
1534
unblock(c.result, constraint->location);
1535
fillInDiscriminantTypes(constraint, c.discriminantTypes);
1536
return true;
1537
}
1538
1539
// if we're calling an error type, the result is an error type, and that's that.
1540
if (get<ErrorType>(fn))
1541
{
1542
bind(constraint, c.result, builtinTypes->errorTypePack);
1543
fillInDiscriminantTypes(constraint, c.discriminantTypes);
1544
return true;
1545
}
1546
1547
if (get<NeverType>(fn))
1548
{
1549
bind(constraint, c.result, builtinTypes->neverTypePack);
1550
fillInDiscriminantTypes(constraint, c.discriminantTypes);
1551
return true;
1552
}
1553
1554
auto [argsHead, argsTail] = flatten(argsPack);
1555
1556
bool blocked = false;
1557
for (TypeId t : argsHead)
1558
{
1559
if (isBlocked(t))
1560
{
1561
block(t, constraint);
1562
blocked = true;
1563
}
1564
}
1565
1566
if (argsTail && isBlocked(*argsTail))
1567
{
1568
block(*argsTail, constraint);
1569
blocked = true;
1570
}
1571
1572
if (blocked)
1573
return false;
1574
1575
auto collapse = [](const auto* t) -> std::optional<TypeId>
1576
{
1577
auto it = begin(t);
1578
auto endIt = end(t);
1579
1580
if (it == endIt)
1581
return std::nullopt;
1582
1583
TypeId fst = follow(*it);
1584
while (it != endIt)
1585
{
1586
if (follow(*it) != fst)
1587
return std::nullopt;
1588
++it;
1589
}
1590
1591
return fst;
1592
};
1593
1594
// Sometimes the `fn` type is a union/intersection, but whose constituents are all the same pointer.
1595
if (auto ut = get<UnionType>(fn))
1596
fn = collapse(ut).value_or(fn);
1597
else if (auto it = get<IntersectionType>(fn))
1598
fn = collapse(it).value_or(fn);
1599
1600
bool usedMagic = false;
1601
1602
const FunctionType* ftv = get<FunctionType>(fn);
1603
if (ftv)
1604
{
1605
if (ftv->magic && c.callSite)
1606
{
1607
usedMagic = ftv->magic->infer(MagicFunctionCallContext{NotNull{this}, constraint, NotNull{c.callSite}, c.argsPack, result});
1608
ftv->magic->refine(MagicRefinementContext{constraint->scope, c.callSite, c.discriminantTypes});
1609
}
1610
}
1611
1612
if (FFlag::LuauExplicitTypeInstantiationSupport)
1613
{
1614
if (!c.typeArguments.empty() || !c.typePackArguments.empty())
1615
{
1616
fn = instantiateFunctionType(c.fn, c.typeArguments, c.typePackArguments, constraint->scope, constraint->location);
1617
}
1618
}
1619
1620
fillInDiscriminantTypes(constraint, c.discriminantTypes);
1621
1622
OverloadResolver resolver{
1623
builtinTypes,
1624
NotNull{arena},
1625
normalizer,
1626
typeFunctionRuntime,
1627
constraint->scope,
1628
NotNull{&iceReporter},
1629
NotNull{&limits},
1630
constraint->location
1631
};
1632
1633
DenseHashSet<TypeId> uniqueTypes{nullptr};
1634
if (c.callSite)
1635
findUniqueTypes(NotNull{&uniqueTypes}, c.callSite->args, NotNull{&module->astTypes});
1636
1637
TypeId overloadToUse = fn;
1638
1639
// If the function we have is already a function type, then there's no
1640
// point in trying to do overload selection: we're just wasting CPU.
1641
if (!is<FunctionType>(fn))
1642
{
1643
auto res = resolver.resolveOverload(
1644
fn,
1645
argsPack,
1646
c.callSite ? c.callSite->func->location : Location{},
1647
NotNull{&uniqueTypes},
1648
/* useFreeTypeBounds */ true
1649
);
1650
1651
// For now we never retry as it's prohibitively expensive.
1652
auto [overload, _constraints, _shouldRetry] = res.getUnambiguousOverload();
1653
1654
if (overload)
1655
{
1656
// NOTE: We may need to enqueue constraints here.
1657
overloadToUse = *overload;
1658
}
1659
else
1660
{
1661
// If we fail to select an unambiguous overload then unify the
1662
// result with error and move on.
1663
bind(constraint, c.result, builtinTypes->errorTypePack);
1664
return true;
1665
}
1666
1667
if (res.metamethods.contains(overloadToUse))
1668
argsPack = arena->addTypePack(TypePack{{fn}, argsPack});
1669
}
1670
1671
if (!usedMagic)
1672
{
1673
emplace<FreeTypePack>(constraint, c.result, constraint->scope, Polarity::Positive);
1674
trackInteriorFreeTypePack(constraint->scope, c.result);
1675
}
1676
1677
TypeId inferredTy = arena->addType(FunctionType{TypeLevel{}, argsPack, c.result});
1678
1679
Unifier2 u2{NotNull{arena}, builtinTypes, constraint->scope, NotNull{&iceReporter}};
1680
1681
// TODO: This should probably use ConstraintSolver::unify
1682
const UnifyResult unifyResult = u2.unify(overloadToUse, inferredTy);
1683
1684
for (TypeId freeTy : u2.newFreshTypes)
1685
trackInteriorFreeType(constraint->scope, freeTy);
1686
for (TypePackId freeTp : u2.newFreshTypePacks)
1687
trackInteriorFreeTypePack(constraint->scope, freeTp);
1688
1689
if (FFlag::LuauOverloadGetsInstantiated)
1690
{
1691
if (!u2.genericSubstitutions.empty() || !u2.genericPackSubstitutions.empty())
1692
{
1693
Subtyping subtyping{builtinTypes, arena, normalizer, typeFunctionRuntime, NotNull{&iceReporter}};
1694
1695
// FIXME CLI-191965: Consider:
1696
//
1697
// local tbl = {}
1698
// for _ in 0..3 do
1699
// table.insert(tbl, i)
1700
// end
1701
// return table.unpack(tbl)
1702
//
1703
// When we resolve the constraints for table.unpack, whose
1704
// type is `<T>( { T } ) -> ...T`, we may not end up with any
1705
// bounds for `T`. We will create an indexer on `tbl` but not
1706
// unify it with anything. This is incorrect, and causes
1707
// us to store a resolved overloaded type of
1708
// `( { unknown } ) -> ...unknown`, which errors in type checking.
1709
//
1710
// Our solution for now is, if there are no bounds on any
1711
// generics, we do not store the resolved overload.
1712
bool hasBound = false;
1713
for (auto& [_, ty] : u2.genericSubstitutions)
1714
if (auto ft = get<FreeType>(ty))
1715
hasBound |= !is<NeverType>(follow(ft->lowerBound)) || !is<UnknownType>(follow(ft->upperBound));
1716
1717
if (auto overloadAsFn = get<FunctionType>(overloadToUse))
1718
{
1719
if (hasBound)
1720
{
1721
CloneState cs{builtinTypes};
1722
// We want to clone persistent types here, for example if we try to instantiate
1723
// `table.insert`
1724
auto clonedTy = shallowClone(overloadToUse, *arena, cs, true);
1725
auto clonedFn = getMutable<FunctionType>(clonedTy);
1726
LUAU_ASSERT(clonedFn);
1727
clonedFn->generics.clear();
1728
clonedFn->genericPacks.clear();
1729
// NOTE: This can be one call!
1730
if (auto inst = instantiate2(
1731
arena,
1732
// Intentional copy, could be by reference.
1733
std::move(u2.genericSubstitutions),
1734
// Intentional copy, could be by reference.
1735
std::move(u2.genericPackSubstitutions),
1736
NotNull{&subtyping},
1737
constraint->scope,
1738
clonedTy
1739
))
1740
{
1741
auto instantiatedFn = get<FunctionType>(inst);
1742
LUAU_ASSERT(instantiatedFn);
1743
overloadToUse = *inst;
1744
result = follow(instantiatedFn->retTypes);
1745
}
1746
else
1747
{
1748
reportError(CodeTooComplex{}, constraint->location);
1749
result = builtinTypes->errorTypePack;
1750
}
1751
}
1752
else
1753
{
1754
auto tp = instantiate2(
1755
arena,
1756
std::move(u2.genericSubstitutions),
1757
std::move(u2.genericPackSubstitutions),
1758
NotNull{&subtyping},
1759
constraint->scope,
1760
overloadAsFn->retTypes
1761
);
1762
if (tp)
1763
result = *tp;
1764
else
1765
{
1766
reportError(CodeTooComplex{}, constraint->location);
1767
result = builtinTypes->errorTypePack;
1768
}
1769
}
1770
}
1771
else
1772
{
1773
std::optional<TypePackId> subst = instantiate2(
1774
arena, std::move(u2.genericSubstitutions), std::move(u2.genericPackSubstitutions), NotNull{&subtyping}, constraint->scope, result
1775
);
1776
if (!subst)
1777
{
1778
reportError(CodeTooComplex{}, constraint->location);
1779
result = builtinTypes->errorTypePack;
1780
}
1781
else
1782
result = *subst;
1783
}
1784
}
1785
1786
if (c.result != result && !usedMagic)
1787
emplaceTypePack<BoundTypePack>(asMutable(c.result), result);
1788
1789
for (const auto& [expanded, additions] : u2.expandedFreeTypes)
1790
{
1791
for (TypeId addition : additions)
1792
upperBoundContributors[expanded].emplace_back(constraint->location, addition);
1793
}
1794
1795
switch (unifyResult)
1796
{
1797
case UnifyResult::Ok:
1798
if (c.callSite)
1799
{
1800
// FIXME CLI-192090
1801
// For now, due to how bidirectional inference of function
1802
// arguments is implemented, magic functions rely on getting
1803
// the "inferred" type here.
1804
(*c.astOverloadResolvedTypes)[c.callSite] = usedMagic ? inferredTy : overloadToUse;
1805
}
1806
break;
1807
case UnifyResult::TooComplex:
1808
reportError(UnificationTooComplex{}, constraint->location);
1809
break;
1810
case UnifyResult::OccursCheckFailed:
1811
reportError(OccursCheckFailed{}, constraint->location);
1812
break;
1813
}
1814
}
1815
else
1816
{
1817
if (!u2.genericSubstitutions.empty() || !u2.genericPackSubstitutions.empty())
1818
{
1819
Subtyping subtyping{builtinTypes, arena, normalizer, typeFunctionRuntime, NotNull{&iceReporter}};
1820
std::optional<TypePackId> subst = instantiate2(
1821
arena, std::move(u2.genericSubstitutions), std::move(u2.genericPackSubstitutions), NotNull{&subtyping}, constraint->scope, result
1822
);
1823
if (!subst)
1824
{
1825
reportError(CodeTooComplex{}, constraint->location);
1826
result = builtinTypes->errorTypePack;
1827
}
1828
else
1829
result = *subst;
1830
}
1831
1832
if (c.result != result)
1833
emplaceTypePack<BoundTypePack>(asMutable(c.result), result);
1834
1835
for (const auto& [expanded, additions] : u2.expandedFreeTypes)
1836
{
1837
for (TypeId addition : additions)
1838
upperBoundContributors[expanded].emplace_back(constraint->location, addition);
1839
}
1840
1841
if (UnifyResult::Ok == unifyResult && c.callSite)
1842
(*c.astOverloadResolvedTypes)[c.callSite] = inferredTy;
1843
else if (UnifyResult::Ok != unifyResult)
1844
{
1845
switch (unifyResult)
1846
{
1847
case UnifyResult::Ok:
1848
break;
1849
case UnifyResult::TooComplex:
1850
reportError(UnificationTooComplex{}, constraint->location);
1851
break;
1852
case UnifyResult::OccursCheckFailed:
1853
reportError(OccursCheckFailed{}, constraint->location);
1854
break;
1855
}
1856
}
1857
}
1858
1859
InstantiationQueuer queuer{constraint->scope, constraint->location, this};
1860
queuer.traverse(overloadToUse);
1861
queuer.traverse(inferredTy);
1862
1863
// This can potentially contain free types if the return type of
1864
// `inferredTy` is never unified elsewhere.
1865
trackInteriorFreeType(constraint->scope, inferredTy);
1866
1867
unblock(c.result, constraint->location);
1868
1869
return true;
1870
}
1871
1872
bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<const Constraint> constraint, bool force)
1873
{
1874
TypeId fn = follow(c.fn);
1875
const TypePackId argsPack = follow(c.argsPack);
1876
1877
if (isBlocked(fn))
1878
return block(fn, constraint);
1879
1880
if (isBlocked(argsPack))
1881
return true;
1882
1883
// This is expensive as we need to traverse a (potentially large)
1884
// literal up front in order to determine if there are any blocked
1885
// types, otherwise we may run `matchTypeLiteral` multiple times,
1886
// which right now may fail due to being non-idempotent (it
1887
// destructively updates the underlying literal type).
1888
auto blockedTypes = findBlockedArgTypesIn(c.callSite, c.astTypes);
1889
for (TypeId ty : blockedTypes)
1890
{
1891
block(ty, constraint);
1892
}
1893
if (!blockedTypes.empty())
1894
return false;
1895
1896
// We know the type of the function and the arguments it expects to receive.
1897
// We also know the TypeIds of the actual arguments that will be passed.
1898
//
1899
// Bidirectional type checking: Force those TypeIds to be the expected
1900
// arguments. If something is incoherent, we'll spot it in type checking.
1901
//
1902
// Most important detail: If a function argument is a lambda, we also want
1903
// to force unannotated argument types of that lambda to be the expected
1904
// types.
1905
1906
// FIXME: Bidirectional type checking of overloaded functions is not yet supported.
1907
const FunctionType* ftv = get<FunctionType>(fn);
1908
if (!ftv)
1909
return true;
1910
1911
DenseHashMap<TypeId, TypeId> replacements{nullptr};
1912
DenseHashMap<TypePackId, TypePackId> replacementPacks{nullptr};
1913
1914
DenseHashSet<const void*> genericTypesAndPacks{nullptr};
1915
1916
Unifier2 u2{arena, builtinTypes, constraint->scope, NotNull{&iceReporter}};
1917
1918
for (auto generic : ftv->generics)
1919
{
1920
// We may see non-generic types here, for example when evaluating a
1921
// recursive function call.
1922
if (auto gty = get<GenericType>(follow(generic)))
1923
{
1924
replacements[generic] = gty->polarity == Polarity::Negative ? builtinTypes->neverType : builtinTypes->unknownType;
1925
genericTypesAndPacks.insert(generic);
1926
}
1927
}
1928
1929
for (auto genericPack : ftv->genericPacks)
1930
{
1931
replacementPacks[genericPack] = builtinTypes->unknownTypePack;
1932
genericTypesAndPacks.insert(genericPack);
1933
}
1934
1935
const std::vector<TypeId> expectedArgs = flatten(ftv->argTypes).first;
1936
const std::vector<TypeId> argPackHead = flatten(argsPack).first;
1937
1938
// If this is a self call, the types will have more elements than the AST call.
1939
// We don't attempt to perform bidirectional inference on the self type.
1940
const size_t typeOffset = c.callSite->self ? 1 : 0;
1941
1942
Subtyping subtyping{builtinTypes, arena, normalizer, typeFunctionRuntime, NotNull{&iceReporter}};
1943
1944
for (size_t i = 0; i < c.callSite->args.size && i + typeOffset < expectedArgs.size() && i + typeOffset < argPackHead.size(); ++i)
1945
{
1946
TypeId expectedArgTy = follow(expectedArgs[i + typeOffset]);
1947
AstExpr* expr = unwrapGroup(c.callSite->args.data[i]);
1948
1949
PushTypeResult result = pushTypeInto(
1950
c.astTypes,
1951
c.astExpectedTypes,
1952
NotNull{this},
1953
constraint,
1954
NotNull{&genericTypesAndPacks},
1955
NotNull{&u2},
1956
NotNull{&subtyping},
1957
expectedArgTy,
1958
expr
1959
);
1960
1961
// Consider:
1962
//
1963
// local Direction = { Left = 1, Right = 2 }
1964
// type Direction = keyof<Direction>
1965
//
1966
// local function move(dirs: { Direction }) --[[...]] end
1967
//
1968
// move({ "Left", "Right", "Left", "Right" })
1969
//
1970
// We need `keyof<Direction>` to reduce prior to inferring that the
1971
// arguments to `move` must generalize to their lower bounds. This
1972
// is how we ensure that ordering.
1973
if (!force && !result.incompleteTypes.empty())
1974
{
1975
for (const auto& [newExpectedTy, newTargetTy, newExpr] : result.incompleteTypes)
1976
{
1977
auto addition = pushConstraint(
1978
constraint->scope,
1979
constraint->location,
1980
PushTypeConstraint{
1981
newExpectedTy,
1982
newTargetTy,
1983
/* astTypes */ c.astTypes,
1984
/* astExpectedTypes */ c.astExpectedTypes,
1985
/* expr */ NotNull{newExpr},
1986
}
1987
);
1988
inheritBlocks(constraint, addition);
1989
}
1990
}
1991
}
1992
1993
// Consider:
1994
//
1995
// local Direction = { Left = 1, Right = 2 }
1996
// type Direction = keyof<Direction>
1997
//
1998
// local function move(dirs: { Direction }) --[[...]] end
1999
//
2000
// move({ "Left", "Right", "Left", "Right" })
2001
//
2002
// We need `keyof<Direction>` to reduce prior to inferring that the
2003
// arguments to `move` must generalize to their lower bounds. This
2004
// is how we ensure that ordering.
2005
for (auto& c : u2.incompleteSubtypes)
2006
{
2007
NotNull<Constraint> addition = pushConstraint(constraint->scope, constraint->location, std::move(c));
2008
inheritBlocks(constraint, addition);
2009
}
2010
2011
return true;
2012
}
2013
2014
bool ConstraintSolver::tryDispatch(const PrimitiveTypeConstraint& c, NotNull<const Constraint> constraint)
2015
{
2016
std::optional<TypeId> expectedType = c.expectedType ? std::make_optional<TypeId>(follow(*c.expectedType)) : std::nullopt;
2017
if (expectedType && (isBlocked(*expectedType) || get<PendingExpansionType>(*expectedType)))
2018
return block(*expectedType, constraint);
2019
2020
const FreeType* freeType = get<FreeType>(follow(c.freeType));
2021
2022
// if this is no longer a free type, then we're done.
2023
if (!freeType)
2024
return true;
2025
2026
// We will wait if there are any other references to the free type mentioned here.
2027
// This is probably the only thing that makes this not insane to do.
2028
if (FFlag::LuauUseConstraintSetsToTrackFreeTypes)
2029
{
2030
if (auto it = typeToConstraintSet.find(c.freeType); it != typeToConstraintSet.end() && it->second.size() > 1)
2031
{
2032
block(c.freeType, constraint);
2033
return false;
2034
}
2035
}
2036
else
2037
{
2038
if (auto refCount = DEPRECATED_unresolvedConstraints.find(c.freeType); refCount && *refCount > 1)
2039
{
2040
block(c.freeType, constraint);
2041
return false;
2042
}
2043
}
2044
2045
TypeId bindTo = c.primitiveType;
2046
2047
if (freeType->upperBound != c.primitiveType && maybeSingleton(freeType->upperBound))
2048
bindTo = freeType->lowerBound;
2049
else if (expectedType && maybeSingleton(*expectedType))
2050
bindTo = freeType->lowerBound;
2051
2052
auto ty = follow(c.freeType);
2053
shiftReferences(ty, bindTo);
2054
bind(constraint, ty, bindTo);
2055
2056
return true;
2057
}
2058
2059
bool ConstraintSolver::tryDispatch(const HasPropConstraint& c, NotNull<const Constraint> constraint)
2060
{
2061
const TypeId subjectType = follow(c.subjectType);
2062
const TypeId resultType = follow(c.resultType);
2063
2064
LUAU_ASSERT(get<BlockedType>(resultType));
2065
LUAU_ASSERT(canMutate(resultType, constraint));
2066
2067
if (isBlocked(subjectType))
2068
return block(subjectType, constraint);
2069
2070
if (const TableType* subjectTable = getTableType(subjectType))
2071
{
2072
if (subjectTable->state == TableState::Unsealed && subjectTable->remainingProps > 0 && subjectTable->props.count(c.prop) == 0)
2073
{
2074
return block(subjectType, constraint);
2075
}
2076
}
2077
2078
// It doesn't matter whether this type came from an indexer or not.
2079
auto [blocked, result, _isIndex] = lookupTableProp(constraint, subjectType, c.prop, c.context, c.inConditional, c.suppressSimplification);
2080
if (!blocked.empty())
2081
{
2082
for (TypeId blocked : blocked)
2083
block(blocked, constraint);
2084
2085
return false;
2086
}
2087
2088
bind(constraint, resultType, result.value_or(builtinTypes->anyType));
2089
return true;
2090
}
2091
2092
bool ConstraintSolver::tryDispatchHasIndexer(
2093
int& recursionDepth,
2094
NotNull<const Constraint> constraint,
2095
TypeId subjectType,
2096
TypeId indexType,
2097
TypeId resultType,
2098
Set<TypeId>& seen
2099
)
2100
{
2101
RecursionLimiter _rl{"ConstraintSolver::tryDispatchHasIndexer", &recursionDepth, FInt::LuauSolverRecursionLimit};
2102
2103
subjectType = follow(subjectType);
2104
indexType = follow(indexType);
2105
2106
if (seen.contains(subjectType))
2107
return false;
2108
seen.insert(subjectType);
2109
2110
LUAU_ASSERT(get<BlockedType>(resultType));
2111
LUAU_ASSERT(canMutate(resultType, constraint));
2112
2113
if (get<AnyType>(subjectType))
2114
{
2115
bind(constraint, resultType, builtinTypes->anyType);
2116
return true;
2117
}
2118
2119
if (auto ft = getMutable<FreeType>(subjectType))
2120
{
2121
if (auto tbl = get<TableType>(follow(ft->upperBound)); tbl && tbl->indexer)
2122
{
2123
unify(constraint, indexType, tbl->indexer->indexType);
2124
bind(constraint, resultType, tbl->indexer->indexResultType);
2125
return true;
2126
}
2127
else if (auto mt = get<MetatableType>(follow(ft->upperBound)))
2128
return tryDispatchHasIndexer(recursionDepth, constraint, mt->table, indexType, resultType, seen);
2129
2130
FreeType freeResult{ft->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed};
2131
emplace<FreeType>(constraint, resultType, freeResult);
2132
2133
TypeId upperBound =
2134
arena->addType(TableType{/* props */ {}, TableIndexer{indexType, resultType}, TypeLevel{}, ft->scope, TableState::Unsealed});
2135
2136
TypeId sr = follow(simplifyIntersection(constraint->scope, constraint->location, ft->upperBound, upperBound));
2137
2138
if (get<NeverType>(sr))
2139
bind(constraint, resultType, builtinTypes->errorType);
2140
else
2141
ft->upperBound = sr;
2142
2143
return true;
2144
}
2145
else if (auto tt = getMutable<TableType>(subjectType))
2146
{
2147
if (auto indexer = tt->indexer)
2148
{
2149
unify(constraint, indexType, indexer->indexType);
2150
bind(constraint, resultType, indexer->indexResultType);
2151
return true;
2152
}
2153
2154
if (tt->state == TableState::Unsealed)
2155
{
2156
// FIXME this is greedy.
2157
2158
FreeType freeResult{tt->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed};
2159
emplace<FreeType>(constraint, resultType, freeResult);
2160
trackInteriorFreeType(constraint->scope, resultType);
2161
2162
tt->indexer = TableIndexer{indexType, resultType};
2163
return true;
2164
}
2165
}
2166
else if (auto mt = get<MetatableType>(subjectType))
2167
return tryDispatchHasIndexer(recursionDepth, constraint, mt->table, indexType, resultType, seen);
2168
else if (auto ct = get<ExternType>(subjectType))
2169
{
2170
if (auto indexer = ct->indexer)
2171
{
2172
unify(constraint, indexType, indexer->indexType);
2173
bind(constraint, resultType, indexer->indexResultType);
2174
return true;
2175
}
2176
else if (isString(indexType))
2177
{
2178
bind(constraint, resultType, builtinTypes->unknownType);
2179
return true;
2180
}
2181
}
2182
else if (auto it = get<IntersectionType>(subjectType))
2183
{
2184
// subjectType <: {[indexType]: resultType}
2185
//
2186
// 'a & ~(false | nil) <: {[indexType]: resultType}
2187
//
2188
// 'a <: {[indexType]: resultType}
2189
// ~(false | nil) <: {[indexType]: resultType}
2190
2191
Set<TypeId> parts{nullptr};
2192
for (TypeId part : it)
2193
parts.insert(follow(part));
2194
2195
Set<TypeId> results{nullptr};
2196
2197
for (TypeId part : parts)
2198
{
2199
TypeId r = arena->addType(BlockedType{});
2200
getMutable<BlockedType>(r)->setOwner(constraint.get());
2201
2202
bool ok = tryDispatchHasIndexer(recursionDepth, constraint, part, indexType, r, seen);
2203
// If we've cut a recursive loop short, skip it.
2204
if (!ok)
2205
continue;
2206
2207
r = follow(r);
2208
if (!get<ErrorType>(r))
2209
results.insert(r);
2210
}
2211
2212
if (0 == results.size())
2213
bind(constraint, resultType, builtinTypes->errorType);
2214
else if (1 == results.size())
2215
bind(constraint, resultType, *results.begin());
2216
else
2217
emplace<IntersectionType>(constraint, resultType, std::vector(results.begin(), results.end()));
2218
2219
return true;
2220
}
2221
else if (auto ut = get<UnionType>(subjectType))
2222
{
2223
Set<TypeId> parts{nullptr};
2224
for (TypeId part : ut)
2225
parts.insert(follow(part));
2226
2227
Set<TypeId> results{nullptr};
2228
2229
for (TypeId part : parts)
2230
{
2231
TypeId r = arena->addType(BlockedType{});
2232
getMutable<BlockedType>(r)->setOwner(constraint.get());
2233
2234
bool ok = tryDispatchHasIndexer(recursionDepth, constraint, part, indexType, r, seen);
2235
// If we've cut a recursive loop short, skip it.
2236
if (!ok)
2237
continue;
2238
2239
r = follow(r);
2240
results.insert(r);
2241
}
2242
2243
if (0 == results.size())
2244
bind(constraint, resultType, builtinTypes->errorType);
2245
else if (1 == results.size())
2246
{
2247
TypeId firstResult = *results.begin();
2248
shiftReferences(resultType, firstResult);
2249
bind(constraint, resultType, firstResult);
2250
}
2251
else
2252
emplace<UnionType>(constraint, resultType, std::vector(results.begin(), results.end()));
2253
2254
return true;
2255
}
2256
2257
bind(constraint, resultType, builtinTypes->errorType);
2258
2259
return true;
2260
}
2261
2262
namespace
2263
{
2264
2265
struct BlockedTypeFinder : TypeOnceVisitor
2266
{
2267
std::optional<TypeId> blocked;
2268
2269
BlockedTypeFinder()
2270
: TypeOnceVisitor("ContainsGenerics_DEPRECATED", /* skipBoundTypes */ true)
2271
{
2272
}
2273
2274
bool visit(TypeId ty) override
2275
{
2276
// If we've already found one, stop traversing.
2277
return !blocked.has_value();
2278
}
2279
2280
bool visit(TypeId ty, const BlockedType&) override
2281
{
2282
blocked = ty;
2283
return false;
2284
}
2285
};
2286
2287
} // namespace
2288
2289
bool ConstraintSolver::tryDispatch(const HasIndexerConstraint& c, NotNull<const Constraint> constraint)
2290
{
2291
const TypeId subjectType = follow(c.subjectType);
2292
const TypeId indexType = follow(c.indexType);
2293
2294
if (isBlocked(subjectType))
2295
return block(subjectType, constraint);
2296
2297
if (isBlocked(indexType))
2298
return block(indexType, constraint);
2299
2300
BlockedTypeFinder btf;
2301
2302
btf.visit(subjectType);
2303
2304
if (btf.blocked)
2305
return block(*btf.blocked, constraint);
2306
int recursionDepth = 0;
2307
2308
Set<TypeId> seen{nullptr};
2309
2310
return tryDispatchHasIndexer(recursionDepth, constraint, subjectType, indexType, c.resultType, seen);
2311
}
2312
2313
bool ConstraintSolver::tryDispatch(const AssignPropConstraint& c, NotNull<const Constraint> constraint)
2314
{
2315
TypeId lhsType = follow(c.lhsType);
2316
const std::string& propName = c.propName;
2317
const TypeId rhsType = follow(c.rhsType);
2318
2319
if (isBlocked(lhsType))
2320
return block(lhsType, constraint);
2321
2322
// 1. lhsType is a class that already has the prop
2323
// 2. lhsType is a table that already has the prop (or a union or
2324
// intersection that has the prop in aggregate)
2325
// 3. lhsType has a metatable that already has the prop
2326
// 4. lhsType is an unsealed table that does not have the prop, but has a
2327
// string indexer
2328
// 5. lhsType is an unsealed table that does not have the prop or a string
2329
// indexer
2330
2331
// Important: In every codepath through this function, the type `c.propType`
2332
// must be bound to something, even if it's just the errorType.
2333
2334
if (auto lhsExternType = get<ExternType>(lhsType))
2335
{
2336
const Property* prop = lookupExternTypeProp(lhsExternType, propName);
2337
if (!prop || !prop->writeTy.has_value())
2338
{
2339
bind(constraint, c.propType, builtinTypes->anyType);
2340
return true;
2341
}
2342
2343
bind(constraint, c.propType, *prop->writeTy);
2344
unify(constraint, rhsType, *prop->writeTy);
2345
return true;
2346
}
2347
2348
if (auto lhsFree = getMutable<FreeType>(lhsType))
2349
{
2350
auto lhsFreeUpperBound = follow(lhsFree->upperBound);
2351
2352
const auto [blocked, maybeTy, isIndex] = lookupTableProp(constraint, lhsType, propName, ValueContext::LValue);
2353
if (!blocked.empty())
2354
{
2355
for (TypeId t : blocked)
2356
block(t, constraint);
2357
return false;
2358
}
2359
else if (maybeTy)
2360
{
2361
bind(constraint, c.propType, isIndex ? arena->addType(UnionType{{*maybeTy, builtinTypes->nilType}}) : *maybeTy);
2362
unify(constraint, rhsType, *maybeTy);
2363
return true;
2364
}
2365
else
2366
{
2367
TypeId newUpperBound = arena->addType(TableType{TableState::Free, TypeLevel{}, constraint->scope});
2368
2369
trackInteriorFreeType(constraint->scope, newUpperBound);
2370
2371
TableType* upperTable = getMutable<TableType>(newUpperBound);
2372
LUAU_ASSERT(upperTable);
2373
2374
upperTable->props[c.propName] = rhsType;
2375
2376
// Food for thought: Could we block if simplification encounters a blocked type?
2377
lhsFree->upperBound = simplifyIntersection(constraint->scope, constraint->location, lhsFreeUpperBound, newUpperBound);
2378
2379
bind(constraint, c.propType, rhsType);
2380
return true;
2381
}
2382
}
2383
2384
// Handle the case that lhsType is a table that already has the property or
2385
// a matching indexer. This also handles unions and intersections.
2386
const auto [blocked, maybeTy, isIndex] = lookupTableProp(constraint, lhsType, propName, ValueContext::LValue);
2387
if (!blocked.empty())
2388
{
2389
for (TypeId t : blocked)
2390
block(t, constraint);
2391
return false;
2392
}
2393
2394
if (maybeTy)
2395
{
2396
TypeId propTy = *maybeTy;
2397
bind(constraint, c.propType, isIndex ? arena->addType(UnionType{{propTy, builtinTypes->nilType}}) : propTy);
2398
unify(constraint, rhsType, propTy);
2399
return true;
2400
}
2401
2402
if (auto lhsMeta = get<MetatableType>(lhsType))
2403
lhsType = follow(lhsMeta->table);
2404
2405
// Handle the case where the lhs type is a table that does not have the
2406
// named property. It could be a table with a string indexer, or an unsealed
2407
// or free table that can grow.
2408
if (auto lhsTable = getMutable<TableType>(lhsType))
2409
{
2410
if (auto it = lhsTable->props.find(propName); it != lhsTable->props.end())
2411
{
2412
Property& prop = it->second;
2413
2414
if (prop.writeTy.has_value())
2415
{
2416
bind(constraint, c.propType, *prop.writeTy);
2417
unify(constraint, rhsType, *prop.writeTy);
2418
return true;
2419
}
2420
else
2421
{
2422
LUAU_ASSERT(prop.isReadOnly());
2423
if (lhsTable->state == TableState::Unsealed || lhsTable->state == TableState::Free)
2424
{
2425
prop.writeTy = prop.readTy;
2426
bind(constraint, c.propType, *prop.writeTy);
2427
unify(constraint, rhsType, *prop.writeTy);
2428
return true;
2429
}
2430
else
2431
{
2432
bind(constraint, c.propType, builtinTypes->errorType);
2433
return true;
2434
}
2435
}
2436
}
2437
2438
if (lhsTable->indexer && maybeString(lhsTable->indexer->indexType))
2439
{
2440
bind(constraint, c.propType, rhsType);
2441
unify(constraint, rhsType, lhsTable->indexer->indexResultType);
2442
return true;
2443
}
2444
2445
if (lhsTable->state == TableState::Unsealed || lhsTable->state == TableState::Free)
2446
{
2447
// eg if inserting a free type 'a into a table {| |}, anything that
2448
// might affect {| |} is now known to potentially affect 'a
2449
shiftReferences(lhsType, rhsType);
2450
2451
bind(constraint, c.propType, rhsType);
2452
Property& newProp = lhsTable->props[propName];
2453
newProp.readTy = rhsType;
2454
newProp.writeTy = rhsType;
2455
newProp.location = c.propLocation;
2456
2457
if (lhsTable->state == TableState::Unsealed && c.decrementPropCount)
2458
{
2459
LUAU_ASSERT(lhsTable->remainingProps > 0);
2460
lhsTable->remainingProps -= 1;
2461
2462
// For some code like:
2463
//
2464
// local T = {}
2465
// function T:foo()
2466
// return T:bar(5)
2467
// end
2468
// function T:bar(i)
2469
// return i
2470
// end
2471
//
2472
// We need to wake up an unsealed table if it previously
2473
// was blocked on missing a member. In the above, we may
2474
// try to solve for `hasProp T "bar"`, block, then never
2475
// wake up without forcing a constraint.
2476
unblock(lhsType, constraint->location);
2477
}
2478
2479
return true;
2480
}
2481
}
2482
2483
bind(constraint, c.propType, builtinTypes->errorType);
2484
2485
return true;
2486
}
2487
2488
bool ConstraintSolver::tryDispatch(const AssignIndexConstraint& c, NotNull<const Constraint> constraint)
2489
{
2490
const TypeId lhsType = follow(c.lhsType);
2491
const TypeId indexType = follow(c.indexType);
2492
const TypeId rhsType = follow(c.rhsType);
2493
2494
if (isBlocked(lhsType))
2495
return block(lhsType, constraint);
2496
2497
// 0. lhsType could be an intersection or union.
2498
// 1. lhsType is a class with an indexer
2499
// 2. lhsType is a table with an indexer, or it has a metatable that has an indexer
2500
// 3. lhsType is a free or unsealed table and can grow an indexer
2501
2502
// Important: In every codepath through this function, the type `c.propType`
2503
// must be bound to something, even if it's just the errorType.
2504
2505
auto tableStuff = [&](TableType* lhsTable) -> std::optional<bool>
2506
{
2507
if (lhsTable->indexer)
2508
{
2509
unify(constraint, indexType, lhsTable->indexer->indexType);
2510
unify(constraint, rhsType, lhsTable->indexer->indexResultType);
2511
bind(constraint, c.propType, addUnion(arena, builtinTypes, {lhsTable->indexer->indexResultType, builtinTypes->nilType}));
2512
return true;
2513
}
2514
2515
if (lhsTable->state == TableState::Unsealed || lhsTable->state == TableState::Free)
2516
{
2517
lhsTable->indexer = TableIndexer{indexType, rhsType};
2518
bind(constraint, c.propType, rhsType);
2519
return true;
2520
}
2521
2522
return {};
2523
};
2524
2525
if (auto lhsFree = getMutable<FreeType>(lhsType))
2526
{
2527
if (auto lhsTable = getMutable<TableType>(follow(lhsFree->upperBound)))
2528
{
2529
if (auto res = tableStuff(lhsTable))
2530
return *res;
2531
}
2532
2533
TypeId newUpperBound =
2534
arena->addType(TableType{/*props*/ {}, TableIndexer{indexType, rhsType}, TypeLevel{}, constraint->scope, TableState::Free});
2535
const TableType* newTable = get<TableType>(newUpperBound);
2536
LUAU_ASSERT(newTable);
2537
2538
unify(constraint, lhsType, newUpperBound);
2539
2540
LUAU_ASSERT(newTable->indexer);
2541
bind(constraint, c.propType, newTable->indexer->indexResultType);
2542
return true;
2543
}
2544
2545
if (auto lhsTable = getMutable<TableType>(lhsType))
2546
{
2547
std::optional<bool> res = tableStuff(lhsTable);
2548
if (res.has_value())
2549
return *res;
2550
}
2551
2552
if (auto lhsExternType = get<ExternType>(lhsType))
2553
{
2554
while (true)
2555
{
2556
if (lhsExternType->indexer)
2557
{
2558
unify(constraint, indexType, lhsExternType->indexer->indexType);
2559
unify(constraint, rhsType, lhsExternType->indexer->indexResultType);
2560
bind(constraint, c.propType, arena->addType(UnionType{{lhsExternType->indexer->indexResultType, builtinTypes->nilType}}));
2561
return true;
2562
}
2563
2564
if (lhsExternType->parent)
2565
lhsExternType = get<ExternType>(lhsExternType->parent);
2566
else
2567
break;
2568
}
2569
return true;
2570
}
2571
2572
if (auto lhsIntersection = getMutable<IntersectionType>(lhsType))
2573
{
2574
2575
TypeIds parts;
2576
2577
for (TypeId t : lhsIntersection)
2578
{
2579
if (auto tbl = getMutable<TableType>(follow(t)))
2580
{
2581
if (tbl->indexer)
2582
{
2583
unify(constraint, indexType, tbl->indexer->indexType);
2584
parts.insert(tbl->indexer->indexResultType);
2585
}
2586
2587
if (tbl->state == TableState::Unsealed || tbl->state == TableState::Free)
2588
{
2589
tbl->indexer = TableIndexer{indexType, rhsType};
2590
parts.insert(rhsType);
2591
}
2592
}
2593
else if (auto cls = get<ExternType>(follow(t)))
2594
{
2595
while (true)
2596
{
2597
if (cls->indexer)
2598
{
2599
unify(constraint, indexType, cls->indexer->indexType);
2600
parts.insert(cls->indexer->indexResultType);
2601
break;
2602
}
2603
2604
if (cls->parent)
2605
cls = get<ExternType>(cls->parent);
2606
else
2607
break;
2608
}
2609
}
2610
}
2611
2612
TypeId res = simplifyIntersection(constraint->scope, constraint->location, std::move(parts));
2613
2614
unify(constraint, rhsType, res);
2615
}
2616
2617
// Other types do not support index assignment.
2618
bind(constraint, c.propType, builtinTypes->errorType);
2619
2620
return true;
2621
}
2622
2623
bool ConstraintSolver::tryDispatch(const UnpackConstraint& c, NotNull<const Constraint> constraint)
2624
{
2625
TypePackId sourcePack = follow(c.sourcePack);
2626
2627
if (isBlocked(sourcePack))
2628
return block(sourcePack, constraint);
2629
2630
TypePack srcPack = extendTypePack(*arena, builtinTypes, sourcePack, c.resultPack.size());
2631
2632
auto resultIter = begin(c.resultPack);
2633
auto resultEnd = end(c.resultPack);
2634
2635
size_t i = 0;
2636
while (resultIter != resultEnd)
2637
{
2638
if (i >= srcPack.head.size())
2639
break;
2640
2641
TypeId srcTy = follow(srcPack.head[i]);
2642
TypeId resultTy = follow(*resultIter);
2643
2644
if (!FFlag::LuauUnpackRespectsAnnotations)
2645
{
2646
LUAU_ASSERT(get<BlockedType>(resultTy));
2647
LUAU_ASSERT(canMutate(resultTy, constraint));
2648
}
2649
2650
if (get<BlockedType>(resultTy))
2651
{
2652
if (FFlag::LuauUnpackRespectsAnnotations)
2653
LUAU_ASSERT(canMutate(resultTy, constraint));
2654
if (follow(srcTy) == resultTy)
2655
{
2656
// It is sometimes the case that we find that a blocked type
2657
// is only blocked on itself. This doesn't actually
2658
// constitute any meaningful constraint, so we replace it
2659
// with a free type.
2660
TypeId f = freshType(arena, builtinTypes, constraint->scope, Polarity::Positive); // FIXME? Is this the right polarity?
2661
trackInteriorFreeType(constraint->scope, f);
2662
shiftReferences(resultTy, f);
2663
emplaceType<BoundType>(asMutable(resultTy), f);
2664
}
2665
else
2666
bind(constraint, resultTy, srcTy);
2667
}
2668
else
2669
unify(constraint, srcTy, resultTy);
2670
2671
unblock(resultTy, constraint->location);
2672
2673
++resultIter;
2674
++i;
2675
}
2676
2677
// We know that resultPack does not have a tail, but we don't know if
2678
// sourcePack is long enough to fill every value. Replace every remaining
2679
// result TypeId with `nil`.
2680
2681
while (resultIter != resultEnd)
2682
{
2683
TypeId resultTy = follow(*resultIter);
2684
LUAU_ASSERT(canMutate(resultTy, constraint));
2685
if (get<BlockedType>(resultTy) || get<PendingExpansionType>(resultTy))
2686
{
2687
bind(constraint, resultTy, builtinTypes->nilType);
2688
}
2689
2690
++resultIter;
2691
}
2692
2693
return true;
2694
}
2695
2696
bool ConstraintSolver::tryDispatch(const ReduceConstraint& c, NotNull<const Constraint> constraint, bool force)
2697
{
2698
TypeId ty = follow(c.ty);
2699
2700
TypeFunctionContext context{NotNull{this}, constraint->scope, constraint};
2701
FunctionGraphReductionResult result = reduceTypeFunctions(ty, constraint->location, NotNull{&context}, force);
2702
2703
for (TypeId r : result.reducedTypes)
2704
unblock(r, constraint->location);
2705
2706
for (TypePackId r : result.reducedPacks)
2707
unblock(r, constraint->location);
2708
2709
for (TypeId ity : result.irreducibleTypes)
2710
{
2711
uninhabitedTypeFunctions.insert(ity);
2712
unblock(ity, constraint->location);
2713
}
2714
2715
bool reductionFinished = result.blockedTypes.empty() && result.blockedPacks.empty();
2716
2717
ty = follow(ty);
2718
2719
// If we couldn't reduce this type function, stick it in the set!
2720
if (get<TypeFunctionInstanceType>(ty) && !result.irreducibleTypes.find(ty))
2721
typeFunctionsToFinalize[ty] = constraint;
2722
2723
if (force || reductionFinished)
2724
{
2725
for (auto& message : result.messages)
2726
{
2727
reportError(std::move(message));
2728
}
2729
2730
// if we're completely dispatching this constraint, we want to record any uninhabited type functions to unblock.
2731
for (auto error : result.errors)
2732
{
2733
if (auto utf = get<UninhabitedTypeFunction>(error))
2734
uninhabitedTypeFunctions.insert(utf->ty);
2735
else if (auto utpf = get<UninhabitedTypePackFunction>(error))
2736
uninhabitedTypeFunctions.insert(utpf->tp);
2737
}
2738
}
2739
2740
if (force)
2741
return true;
2742
2743
for (TypeId b : result.blockedTypes)
2744
block(b, constraint);
2745
2746
for (TypePackId b : result.blockedPacks)
2747
block(b, constraint);
2748
2749
return reductionFinished;
2750
}
2751
2752
bool ConstraintSolver::tryDispatch(const ReducePackConstraint& c, NotNull<const Constraint> constraint, bool force)
2753
{
2754
TypePackId tp = follow(c.tp);
2755
2756
TypeFunctionContext context{NotNull{this}, constraint->scope, constraint};
2757
FunctionGraphReductionResult result = reduceTypeFunctions(tp, constraint->location, NotNull{&context}, force);
2758
2759
for (TypeId r : result.reducedTypes)
2760
unblock(r, constraint->location);
2761
2762
for (TypePackId r : result.reducedPacks)
2763
unblock(r, constraint->location);
2764
2765
bool reductionFinished = result.blockedTypes.empty() && result.blockedPacks.empty();
2766
2767
if (force || reductionFinished)
2768
{
2769
// if we're completely dispatching this constraint, we want to record any uninhabited type functions to unblock.
2770
for (auto error : result.errors)
2771
{
2772
if (auto utf = get<UninhabitedTypeFunction>(error))
2773
uninhabitedTypeFunctions.insert(utf->ty);
2774
else if (auto utpf = get<UninhabitedTypePackFunction>(error))
2775
uninhabitedTypeFunctions.insert(utpf->tp);
2776
}
2777
}
2778
2779
if (force)
2780
return true;
2781
2782
for (TypeId b : result.blockedTypes)
2783
block(b, constraint);
2784
2785
for (TypePackId b : result.blockedPacks)
2786
block(b, constraint);
2787
2788
return reductionFinished;
2789
}
2790
2791
bool ConstraintSolver::tryDispatch(const EqualityConstraint& c, NotNull<const Constraint> constraint)
2792
{
2793
unify(constraint, c.resultType, c.assignmentType);
2794
unify(constraint, c.assignmentType, c.resultType);
2795
return true;
2796
}
2797
2798
struct FindAllUnionMembers : TypeOnceVisitor
2799
{
2800
TypeIds recordedTys;
2801
TypeIds blockedTys;
2802
2803
FindAllUnionMembers()
2804
: TypeOnceVisitor("FindAllUnionMembers", /* skipBoundTypes */ true)
2805
{
2806
}
2807
2808
bool visit(TypeId ty) override
2809
{
2810
recordedTys.insert(ty);
2811
return false;
2812
}
2813
2814
bool visit(TypeId ty, const BlockedType&) override
2815
{
2816
blockedTys.insert(ty);
2817
return false;
2818
}
2819
bool visit(TypeId ty, const PendingExpansionType&) override
2820
{
2821
blockedTys.insert(ty);
2822
return false;
2823
}
2824
bool visit(TypeId ty, const FreeType&) override
2825
{
2826
blockedTys.insert(ty);
2827
return false;
2828
}
2829
bool visit(TypeId ty, const TypeFunctionInstanceType&) override
2830
{
2831
blockedTys.insert(ty);
2832
return false;
2833
}
2834
2835
bool visit(TypeId, const UnionType&) override
2836
{
2837
return true;
2838
}
2839
2840
bool visit(TypeId ty, const TableType& tbl) override
2841
{
2842
if (FFlag::LuauRelateHandlesCoincidentTables && tbl.state != TableState::Sealed)
2843
blockedTys.insert(ty);
2844
else
2845
recordedTys.insert(ty);
2846
return false;
2847
}
2848
};
2849
2850
bool ConstraintSolver::tryDispatch(const SimplifyConstraint& c, NotNull<const Constraint> constraint, bool force)
2851
{
2852
TypeId target = follow(c.ty);
2853
2854
if (target->persistent || target->owningArena != arena || !is<UnionType>(target))
2855
{
2856
// If our target ends up being:
2857
// - A persistent union like `false?`
2858
// - A union from another arena
2859
// - Something other than a union type
2860
// Then it's either harmful or useless to fire this constraint, so we exit early.
2861
return true;
2862
}
2863
2864
FindAllUnionMembers finder;
2865
finder.traverse(target);
2866
if (!finder.blockedTys.empty() && !force)
2867
{
2868
for (TypeId ty : finder.blockedTys)
2869
block(ty, constraint);
2870
return false;
2871
}
2872
TypeId result = builtinTypes->neverType;
2873
for (TypeId ty : finder.recordedTys)
2874
{
2875
ty = follow(ty);
2876
if (ty == target)
2877
continue;
2878
result = simplifyUnion(constraint->scope, constraint->location, result, ty);
2879
}
2880
// If we forced, then there _may_ be blocked types, and we should
2881
// include those in the union as well.
2882
for (TypeId ty : finder.blockedTys)
2883
{
2884
ty = follow(ty);
2885
if (ty == target)
2886
continue;
2887
result = simplifyUnion(constraint->scope, constraint->location, result, ty);
2888
}
2889
emplaceType<BoundType>(asMutable(target), result);
2890
return true;
2891
}
2892
2893
bool ConstraintSolver::tryDispatch(const PushFunctionTypeConstraint& c, NotNull<const Constraint> constraint)
2894
{
2895
// NOTE: This logic could probably be combined with that of
2896
// `FunctionCheckConstraint`, but that constraint currently does a few
2897
// different things.
2898
2899
auto expectedFn = get<FunctionType>(follow(c.expectedFunctionType));
2900
auto fn = get<FunctionType>(follow(c.functionType));
2901
2902
// If either the expected type or given type aren't functions, then bail.
2903
if (!expectedFn || !fn)
2904
return true;
2905
2906
auto expectedParams = begin(expectedFn->argTypes);
2907
auto params = begin(fn->argTypes);
2908
2909
if (expectedParams == end(expectedFn->argTypes) || params == end(fn->argTypes))
2910
return true;
2911
2912
if (c.isSelf)
2913
{
2914
if (is<FreeType>(follow(*params)))
2915
{
2916
shiftReferences(*params, *expectedParams);
2917
bind(constraint, *params, *expectedParams);
2918
}
2919
expectedParams++;
2920
params++;
2921
}
2922
2923
// `idx` is an index into the arguments of the attached `AstExprFunction`,
2924
// we don't need to increment it with respect to arguments in case of a
2925
// `self` type.
2926
size_t idx = 0;
2927
while (idx < c.expr->args.size && expectedParams != end(expectedFn->argTypes) && params != end(fn->argTypes))
2928
{
2929
// If we have an explicitly annotated parameter, a non-free type for
2930
// the parameter, or the expected type contains a generic, bail.
2931
// - Annotations should be respected above all else;
2932
// - a non-free-type is unexpected, so just bail;
2933
// - a generic in the expected type might cause us to leak a generic, so bail.
2934
if (!c.expr->args.data[idx]->annotation && get<FreeType>(*params) && !ContainsAnyGeneric::hasAnyGeneric(*expectedParams))
2935
{
2936
shiftReferences(*params, *expectedParams);
2937
bind(constraint, *params, *expectedParams);
2938
}
2939
expectedParams++;
2940
params++;
2941
idx++;
2942
}
2943
2944
if (!c.expr->returnAnnotation && get<FreeTypePack>(fn->retTypes) && !ContainsAnyGeneric::hasAnyGeneric(expectedFn->retTypes))
2945
bind(constraint, fn->retTypes, expectedFn->retTypes);
2946
2947
return true;
2948
}
2949
2950
bool ConstraintSolver::tryDispatch(const TypeInstantiationConstraint& c, NotNull<const Constraint> constraint)
2951
{
2952
LUAU_ASSERT(FFlag::LuauExplicitTypeInstantiationSupport);
2953
2954
if (isBlocked(c.functionType))
2955
return block(c.functionType, constraint);
2956
2957
bind(
2958
constraint,
2959
c.placeholderType,
2960
instantiateFunctionType(c.functionType, c.typeArguments, c.typePackArguments, constraint->scope, constraint->location)
2961
);
2962
2963
return true;
2964
}
2965
2966
template<typename T, typename Predicate>
2967
void dropWhile(std::vector<T>& vec, Predicate pred)
2968
{
2969
auto it = std::find_if(
2970
vec.begin(),
2971
vec.end(),
2972
[&pred](const T& elem)
2973
{
2974
return !pred(elem);
2975
}
2976
);
2977
vec.erase(vec.begin(), it);
2978
}
2979
2980
TypeId ConstraintSolver::instantiateFunctionType(
2981
TypeId functionTypeId,
2982
const std::vector<TypeId>& typeArguments,
2983
const std::vector<TypePackId>& typePackArguments,
2984
NotNull<Scope> scope,
2985
const Location& location
2986
)
2987
{
2988
if (FFlag::LuauFollowInExplicitInstantiation)
2989
functionTypeId = follow(functionTypeId);
2990
2991
// no work to be done if we're not instantiating with anything
2992
if (typeArguments.empty() && typePackArguments.empty())
2993
return functionTypeId;
2994
2995
const FunctionType* ft = get<FunctionType>(FFlag::LuauFollowInExplicitInstantiation ? functionTypeId : follow(functionTypeId));
2996
if (!ft)
2997
{
2998
return functionTypeId;
2999
}
3000
3001
DenseHashMap<TypeId, TypeId> replacements{nullptr};
3002
auto typeParametersIter = ft->generics.begin();
3003
3004
for (const TypeId typeArgument : typeArguments)
3005
{
3006
if (typeParametersIter == ft->generics.end())
3007
{
3008
break;
3009
}
3010
3011
replacements[*typeParametersIter++] = typeArgument;
3012
}
3013
3014
while (typeParametersIter != ft->generics.end())
3015
{
3016
replacements[*typeParametersIter++] = freshType(arena, builtinTypes, scope, Polarity::Mixed);
3017
}
3018
3019
DenseHashMap<TypePackId, TypePackId> replacementPacks{nullptr};
3020
auto typePackParametersIter = ft->genericPacks.begin();
3021
3022
for (const TypePackId typePackArgument : typePackArguments)
3023
{
3024
if (typePackParametersIter == ft->genericPacks.end())
3025
{
3026
break;
3027
}
3028
3029
replacementPacks[*typePackParametersIter++] = typePackArgument;
3030
}
3031
3032
if (FFlag::LuauReplacerRespectsReboundGenerics)
3033
{
3034
Replacer r{arena, NotNull{&replacements}, NotNull{&replacementPacks}};
3035
3036
CloneState cs{builtinTypes};
3037
// We clone persistent types here to enable instantiation for generic
3038
// builtins like `table.find`; otherwise, the lines after would
3039
// immediately corrupt the definitions of the original function.
3040
auto clonedFunctionTypeId = shallowClone(functionTypeId, *arena, cs, /* clonePersistentTypes */ true);
3041
FunctionType* ft2 = getMutable<FunctionType>(clonedFunctionTypeId);
3042
LUAU_ASSERT(ft != ft2);
3043
3044
// We instantiate all generics, replacing any with free types.
3045
ft2->generics.clear();
3046
3047
// However, we only instantiate as many type pack arguments as are given.
3048
if (!ft2->genericPacks.empty() && typePackArguments.size() < ft2->genericPacks.size())
3049
ft2->genericPacks.erase(ft2->genericPacks.begin(), ft2->genericPacks.begin() + typePackArguments.size());
3050
else
3051
ft2->genericPacks.clear();
3052
3053
auto result = r.substitute(clonedFunctionTypeId);
3054
if (!result)
3055
return builtinTypes->errorType;
3056
return *result;
3057
}
3058
else
3059
{
3060
Replacer_DEPRECATED r{arena, std::move(replacements), std::move(replacementPacks)};
3061
3062
std::optional<TypeId> result = r.substitute(functionTypeId);
3063
if (!result)
3064
return builtinTypes->errorType;
3065
3066
FunctionType* ft2 = getMutable<FunctionType>(*result);
3067
3068
// we must remove the portions we successfully instantiated
3069
dropWhile(
3070
ft2->generics,
3071
[](const TypeId& ty)
3072
{
3073
return !is<GenericType>(follow(ty));
3074
}
3075
);
3076
dropWhile(
3077
ft2->genericPacks,
3078
[](const TypePackId& ty)
3079
{
3080
return !is<GenericTypePack>(follow(ty));
3081
}
3082
);
3083
3084
return *result;
3085
}
3086
}
3087
3088
bool ConstraintSolver::tryDispatch(const PushTypeConstraint& c, NotNull<const Constraint> constraint, bool force)
3089
{
3090
Unifier2 u2{arena, builtinTypes, constraint->scope, NotNull{&iceReporter}, &uninhabitedTypeFunctions};
3091
Subtyping subtyping{builtinTypes, arena, normalizer, typeFunctionRuntime, NotNull{&iceReporter}};
3092
3093
// NOTE: If we don't do this check up front, we almost immediately start
3094
// spawning tons of push type constraints. It's pretty important.
3095
if (isBlocked(c.expectedType))
3096
{
3097
block(c.expectedType, constraint);
3098
// If we're forcing this constraint and the expected type is blocked, we
3099
// should just bail.
3100
return force;
3101
}
3102
3103
DenseHashSet<const void*> empty{nullptr};
3104
PushTypeResult result = pushTypeInto(
3105
c.astTypes, c.astExpectedTypes, NotNull{this}, NotNull{constraint}, NotNull{&empty}, NotNull{&u2}, NotNull{&subtyping}, c.expectedType, c.expr
3106
);
3107
3108
// If we're forcing this constraint, just early exit: we can continue
3109
// inferring the rest of the file, we might just error when we shouldn't.
3110
if (force || result.incompleteTypes.empty())
3111
return true;
3112
3113
for (auto [newExpectedTy, newTargetTy, newExpr] : result.incompleteTypes)
3114
{
3115
auto addition = pushConstraint(
3116
constraint->scope,
3117
constraint->location,
3118
PushTypeConstraint{
3119
/* expectedType */ newExpectedTy,
3120
/* targetType */ newTargetTy,
3121
/* astTypes */ c.astTypes,
3122
/* astExpectedTypes */ c.astExpectedTypes,
3123
/* expr */ NotNull{newExpr},
3124
}
3125
);
3126
inheritBlocks(constraint, addition);
3127
}
3128
3129
return true;
3130
}
3131
3132
bool ConstraintSolver::tryDispatchIterableTable(TypeId iteratorTy, const IterableConstraint& c, NotNull<const Constraint> constraint, bool force)
3133
{
3134
iteratorTy = follow(iteratorTy);
3135
3136
if (get<FreeType>(iteratorTy))
3137
{
3138
TypeId keyTy = freshType(arena, builtinTypes, constraint->scope, Polarity::Mixed);
3139
TypeId valueTy = freshType(arena, builtinTypes, constraint->scope, Polarity::Mixed);
3140
trackInteriorFreeType(constraint->scope, keyTy);
3141
trackInteriorFreeType(constraint->scope, valueTy);
3142
TypeId tableTy = arena->addType(TableType{TableState::Sealed, {}, constraint->scope});
3143
getMutable<TableType>(tableTy)->indexer = TableIndexer{keyTy, valueTy};
3144
3145
pushConstraint(constraint->scope, constraint->location, SubtypeConstraint{iteratorTy, tableTy});
3146
3147
auto it = begin(c.variables);
3148
auto endIt = end(c.variables);
3149
if (it != endIt)
3150
{
3151
bind(constraint, *it, keyTy);
3152
++it;
3153
}
3154
if (it != endIt)
3155
bind(constraint, *it, valueTy);
3156
3157
return true;
3158
}
3159
3160
auto unpack = [&](TypeId ty)
3161
{
3162
for (TypeId varTy : c.variables)
3163
{
3164
LUAU_ASSERT(get<BlockedType>(varTy));
3165
LUAU_ASSERT(varTy != ty);
3166
bind(constraint, varTy, ty);
3167
}
3168
};
3169
3170
if (get<AnyType>(iteratorTy))
3171
{
3172
unpack(builtinTypes->anyType);
3173
return true;
3174
}
3175
3176
if (get<ErrorType>(iteratorTy))
3177
{
3178
unpack(builtinTypes->errorType);
3179
return true;
3180
}
3181
3182
if (get<NeverType>(iteratorTy))
3183
{
3184
unpack(builtinTypes->neverType);
3185
return true;
3186
}
3187
3188
// Irksome: I don't think we have any way to guarantee that this table
3189
// type never has a metatable.
3190
3191
if (auto iteratorTable = get<TableType>(iteratorTy))
3192
{
3193
/*
3194
* We try not to dispatch IterableConstraints over free tables because
3195
* it's possible that there are other constraints on the table that will
3196
* clarify what we should do.
3197
*
3198
* We should eventually introduce a type function to talk about iteration.
3199
*/
3200
if (iteratorTable->state == TableState::Free && !force)
3201
return block(iteratorTy, constraint);
3202
3203
if (iteratorTable->indexer)
3204
{
3205
std::vector<TypeId> expectedVariables{iteratorTable->indexer->indexType, iteratorTable->indexer->indexResultType};
3206
while (c.variables.size() >= expectedVariables.size())
3207
expectedVariables.push_back(builtinTypes->errorType);
3208
3209
for (size_t i = 0; i < c.variables.size(); ++i)
3210
{
3211
LUAU_ASSERT(c.variables[i] != expectedVariables[i]);
3212
3213
unify(constraint, c.variables[i], expectedVariables[i]);
3214
3215
bind(constraint, c.variables[i], expectedVariables[i]);
3216
}
3217
}
3218
else
3219
unpack(builtinTypes->errorType);
3220
}
3221
else if (std::optional<TypeId> iterFn = findMetatableEntry(builtinTypes, errors, iteratorTy, "__iter", Location{}))
3222
{
3223
if (isBlocked(*iterFn))
3224
{
3225
return block(*iterFn, constraint);
3226
}
3227
3228
if (std::optional<TypeId> instantiatedIterFn = instantiate(builtinTypes, arena, NotNull{&limits}, constraint->scope, *iterFn))
3229
{
3230
if (auto iterFtv = get<FunctionType>(*instantiatedIterFn))
3231
{
3232
TypePackId expectedIterArgs = arena->addTypePack({iteratorTy});
3233
unify(constraint, iterFtv->argTypes, expectedIterArgs);
3234
3235
TypePack iterRets = extendTypePack(*arena, builtinTypes, iterFtv->retTypes, 2);
3236
3237
if (iterRets.head.size() < 1)
3238
{
3239
// We've done what we can; this will get reported as an
3240
// error by the type checker.
3241
return true;
3242
}
3243
3244
TypeId nextFn = iterRets.head[0];
3245
3246
if (std::optional<TypeId> instantiatedNextFn = instantiate(builtinTypes, arena, NotNull{&limits}, constraint->scope, nextFn))
3247
{
3248
const FunctionType* nextFn = get<FunctionType>(*instantiatedNextFn);
3249
3250
// If nextFn is nullptr, then the iterator function has an improper signature.
3251
if (nextFn)
3252
unpackAndAssign(c.variables, nextFn->retTypes, constraint);
3253
3254
return true;
3255
}
3256
else
3257
{
3258
reportError(UnificationTooComplex{}, constraint->location);
3259
}
3260
}
3261
else
3262
{
3263
// TODO: Support __call and function overloads (what does an overload even mean for this?)
3264
}
3265
}
3266
else
3267
{
3268
reportError(UnificationTooComplex{}, constraint->location);
3269
}
3270
}
3271
else if (auto iteratorMetatable = get<MetatableType>(iteratorTy))
3272
{
3273
// If the metatable does not contain a `__iter` metamethod, then we iterate over the table part of the metatable.
3274
return tryDispatchIterableTable(iteratorMetatable->table, c, constraint, force);
3275
}
3276
else if (auto primitiveTy = get<PrimitiveType>(iteratorTy); primitiveTy && primitiveTy->type == PrimitiveType::Type::Table)
3277
unpack(builtinTypes->unknownType);
3278
else
3279
{
3280
unpack(builtinTypes->errorType);
3281
}
3282
3283
return true;
3284
}
3285
3286
bool ConstraintSolver::tryDispatchIterableFunction(TypeId nextTy, TypeId tableTy, const IterableConstraint& c, NotNull<const Constraint> constraint)
3287
{
3288
const FunctionType* nextFn = get<FunctionType>(nextTy);
3289
// If this does not hold, we should've never called `tryDispatchIterableFunction` in the first place.
3290
LUAU_ASSERT(nextFn);
3291
3292
// the type of the `nextAstFragment` is the `nextTy`.
3293
(*c.astForInNextTypes)[c.nextAstFragment] = nextTy;
3294
3295
// Construct a FunctionCallConstraint, to help us learn about the type of the loop variables being assigned to in this iterable
3296
TypePackId tableTyPack = arena->addTypePack({tableTy});
3297
3298
TypePackId variablesPack = arena->addTypePack(BlockedTypePack{});
3299
3300
auto callConstraint = pushConstraint(constraint->scope, constraint->location, FunctionCallConstraint{nextTy, tableTyPack, variablesPack});
3301
3302
getMutable<BlockedTypePack>(variablesPack)->owner = callConstraint.get();
3303
3304
auto unpackConstraint = unpackAndAssign(c.variables, variablesPack, constraint);
3305
3306
inheritBlocks(constraint, callConstraint);
3307
3308
inheritBlocks(unpackConstraint, callConstraint);
3309
return true;
3310
}
3311
3312
NotNull<const Constraint> ConstraintSolver::unpackAndAssign(
3313
const std::vector<TypeId> destTypes,
3314
TypePackId srcTypes,
3315
NotNull<const Constraint> constraint
3316
)
3317
{
3318
auto c = pushConstraint(constraint->scope, constraint->location, UnpackConstraint{destTypes, srcTypes});
3319
3320
for (TypeId t : destTypes)
3321
{
3322
BlockedType* bt = getMutable<BlockedType>(t);
3323
LUAU_ASSERT(bt);
3324
bt->replaceOwner(c);
3325
}
3326
3327
return c;
3328
}
3329
3330
TablePropLookupResult ConstraintSolver::lookupTableProp(
3331
NotNull<const Constraint> constraint,
3332
TypeId subjectType,
3333
const std::string& propName,
3334
ValueContext context,
3335
bool inConditional,
3336
bool suppressSimplification
3337
)
3338
{
3339
Set<TypeId> seen{nullptr};
3340
return lookupTableProp(constraint, subjectType, propName, context, inConditional, suppressSimplification, seen);
3341
}
3342
3343
TablePropLookupResult ConstraintSolver::lookupTableProp(
3344
NotNull<const Constraint> constraint,
3345
TypeId subjectType,
3346
const std::string& propName,
3347
ValueContext context,
3348
bool inConditional,
3349
bool suppressSimplification,
3350
Set<TypeId>& seen
3351
)
3352
{
3353
if (seen.contains(subjectType))
3354
return {};
3355
3356
ScopedSeenSet<Set<TypeId>, TypeId> ss{seen, subjectType};
3357
3358
subjectType = follow(subjectType);
3359
3360
if (isBlocked(subjectType))
3361
return {{subjectType}, std::nullopt};
3362
else if (get<AnyType>(subjectType) || get<NeverType>(subjectType))
3363
{
3364
return {{}, subjectType};
3365
}
3366
else if (auto ttv = getMutable<TableType>(subjectType))
3367
{
3368
if (auto prop = ttv->props.find(propName); prop != ttv->props.end())
3369
{
3370
switch (context)
3371
{
3372
case ValueContext::RValue:
3373
if (auto rt = prop->second.readTy)
3374
return {{}, rt};
3375
break;
3376
case ValueContext::LValue:
3377
if (auto wt = prop->second.writeTy)
3378
return {{}, wt};
3379
break;
3380
}
3381
}
3382
3383
if (ttv->indexer)
3384
{
3385
if (isBlocked(ttv->indexer->indexType))
3386
return {{ttv->indexer->indexType}, std::nullopt, true};
3387
3388
// CLI-169235: This is silly but this needs to use the same
3389
// logic as `index<_, _>`
3390
TypeId fauxLiteral = arena->addType(SingletonType{StringSingleton{propName}});
3391
if (fastIsSubtype(fauxLiteral, ttv->indexer->indexType))
3392
return {/* blockedTypes */ {}, ttv->indexer->indexResultType, /* isIndex */ true};
3393
}
3394
3395
if (ttv->state == TableState::Free)
3396
{
3397
TypeId result = freshType(arena, builtinTypes, ttv->scope, Polarity::Mixed);
3398
trackInteriorFreeType(ttv->scope, result);
3399
switch (context)
3400
{
3401
case ValueContext::RValue:
3402
ttv->props[propName].readTy = result;
3403
break;
3404
case ValueContext::LValue:
3405
if (auto it = ttv->props.find(propName); it != ttv->props.end() && it->second.isReadOnly())
3406
{
3407
// We do infer read-only properties, but we do not infer
3408
// separate read and write types.
3409
//
3410
// If we encounter a case where a free table has a read-only
3411
// property that we subsequently sense a write to, we make
3412
// the judgement that the property is read-write and that
3413
// both the read and write types are the same.
3414
3415
Property& prop = it->second;
3416
3417
prop.writeTy = prop.readTy;
3418
return {{}, *prop.readTy};
3419
}
3420
else
3421
ttv->props[propName] = Property::rw(result);
3422
3423
break;
3424
}
3425
return {{}, result};
3426
}
3427
3428
// if we are in a conditional context, we treat the property as present and `unknown` because
3429
// we may be _refining_ a table to include that property. we will want to revisit this a bit
3430
// in the future once luau has support for exact tables since this only applies when inexact.
3431
if (inConditional)
3432
return {{}, builtinTypes->unknownType};
3433
}
3434
else if (auto mt = get<MetatableType>(subjectType); mt && context == ValueContext::RValue)
3435
{
3436
auto result = lookupTableProp(constraint, mt->table, propName, context, inConditional, suppressSimplification, seen);
3437
if (!result.blockedTypes.empty() || result.propType)
3438
return result;
3439
3440
TypeId mtt = follow(mt->metatable);
3441
3442
if (get<BlockedType>(mtt))
3443
return {{mtt}, std::nullopt};
3444
else if (auto metatable = get<TableType>(mtt))
3445
{
3446
auto indexProp = metatable->props.find("__index");
3447
if (indexProp == metatable->props.end())
3448
return {{}, result.propType};
3449
3450
// TODO: __index can be an overloaded function.
3451
3452
// if the property is write-only, then surely we cannot read from it.
3453
if (indexProp->second.isWriteOnly())
3454
return {{}, builtinTypes->errorType};
3455
3456
TypeId indexType = follow(*indexProp->second.readTy);
3457
3458
if (auto ft = get<FunctionType>(indexType))
3459
{
3460
TypePack rets = extendTypePack(*arena, builtinTypes, ft->retTypes, 1);
3461
if (1 == rets.head.size())
3462
return {{}, rets.head[0]};
3463
else
3464
{
3465
// This should probably be an error: We need the first result of the MT.__index method,
3466
// but it returns 0 values. See CLI-68672
3467
return {{}, builtinTypes->nilType};
3468
}
3469
}
3470
else
3471
return lookupTableProp(constraint, indexType, propName, context, inConditional, suppressSimplification, seen);
3472
}
3473
else if (get<MetatableType>(mtt))
3474
return lookupTableProp(constraint, mtt, propName, context, inConditional, suppressSimplification, seen);
3475
}
3476
else if (auto ct = get<ExternType>(subjectType))
3477
{
3478
if (auto p = lookupExternTypeProp(ct, propName))
3479
return {{}, context == ValueContext::RValue ? p->readTy : p->writeTy};
3480
if (ct->indexer)
3481
{
3482
return {{}, ct->indexer->indexResultType, /* isIndex = */ true};
3483
}
3484
}
3485
else if (auto pt = get<PrimitiveType>(subjectType); pt && pt->metatable)
3486
{
3487
const TableType* metatable = get<TableType>(follow(*pt->metatable));
3488
LUAU_ASSERT(metatable);
3489
3490
auto indexProp = metatable->props.find("__index");
3491
if (indexProp == metatable->props.end())
3492
return {{}, std::nullopt};
3493
3494
// if the property is write-only, then surely we cannot read from it.
3495
if (indexProp->second.isWriteOnly())
3496
return {{}, builtinTypes->errorType};
3497
3498
return lookupTableProp(constraint, *indexProp->second.readTy, propName, context, inConditional, suppressSimplification, seen);
3499
}
3500
else if (auto ft = get<FreeType>(subjectType))
3501
{
3502
const TypeId upperBound = follow(ft->upperBound);
3503
3504
if (get<TableType>(upperBound) || get<PrimitiveType>(upperBound))
3505
{
3506
TablePropLookupResult res = lookupTableProp(constraint, upperBound, propName, context, inConditional, suppressSimplification, seen);
3507
// Here, res.propType is empty if res is a sealed table or a primitive that lacks the property.
3508
// When this happens, we still want to add to the upper bound of the type.
3509
if (res.propType)
3510
return res;
3511
}
3512
3513
NotNull<Scope> scope{ft->scope};
3514
3515
const TypeId newUpperBound = arena->addType(TableType{TableState::Free, TypeLevel{}, scope});
3516
3517
trackInteriorFreeType(constraint->scope, newUpperBound);
3518
3519
TableType* tt = getMutable<TableType>(newUpperBound);
3520
LUAU_ASSERT(tt);
3521
TypeId propType = freshType(arena, builtinTypes, scope, Polarity::Mixed);
3522
trackInteriorFreeType(scope, propType);
3523
3524
switch (context)
3525
{
3526
case ValueContext::RValue:
3527
tt->props[propName] = Property::readonly(propType);
3528
break;
3529
case ValueContext::LValue:
3530
tt->props[propName] = Property::rw(propType);
3531
break;
3532
}
3533
3534
unify(constraint, subjectType, newUpperBound);
3535
3536
return {{}, propType};
3537
}
3538
else if (auto utv = get<UnionType>(subjectType))
3539
{
3540
std::vector<TypeId> blocked;
3541
std::set<TypeId> options;
3542
3543
for (TypeId ty : utv)
3544
{
3545
auto result = lookupTableProp(constraint, ty, propName, context, inConditional, suppressSimplification, seen);
3546
blocked.insert(blocked.end(), result.blockedTypes.begin(), result.blockedTypes.end());
3547
if (result.propType)
3548
options.insert(*result.propType);
3549
}
3550
3551
if (!blocked.empty())
3552
return {std::move(blocked), std::nullopt};
3553
3554
if (options.empty())
3555
return {{}, std::nullopt};
3556
else if (options.size() == 1)
3557
return {{}, *begin(options)};
3558
else if (options.size() == 2 && !suppressSimplification)
3559
{
3560
TypeId one = *begin(options);
3561
TypeId two = *(++begin(options));
3562
3563
// if we're in an lvalue context, we need the _common_ type here.
3564
if (context == ValueContext::LValue)
3565
return {{}, simplifyIntersection(constraint->scope, constraint->location, one, two)};
3566
3567
return {{}, simplifyUnion(constraint->scope, constraint->location, one, two)};
3568
}
3569
// if we're in an lvalue context, we need the _common_ type here.
3570
else if (context == ValueContext::LValue)
3571
return {{}, arena->addType(IntersectionType{std::vector<TypeId>(begin(options), end(options))})};
3572
else
3573
return {{}, arena->addType(UnionType{std::vector<TypeId>(begin(options), end(options))})};
3574
}
3575
else if (auto itv = get<IntersectionType>(subjectType))
3576
{
3577
std::vector<TypeId> blocked;
3578
std::set<TypeId> options;
3579
3580
for (TypeId ty : itv)
3581
{
3582
auto result = lookupTableProp(constraint, ty, propName, context, inConditional, suppressSimplification, seen);
3583
blocked.insert(blocked.end(), result.blockedTypes.begin(), result.blockedTypes.end());
3584
if (result.propType)
3585
options.insert(*result.propType);
3586
}
3587
3588
if (!blocked.empty())
3589
return {std::move(blocked), std::nullopt};
3590
3591
if (options.empty())
3592
return {{}, std::nullopt};
3593
else if (options.size() == 1)
3594
return {{}, *begin(options)};
3595
else if (options.size() == 2 && !suppressSimplification)
3596
{
3597
TypeId one = *begin(options);
3598
TypeId two = *(++begin(options));
3599
return {{}, simplifyIntersection(constraint->scope, constraint->location, one, two)};
3600
}
3601
else
3602
return {{}, arena->addType(IntersectionType{std::vector<TypeId>(begin(options), end(options))})};
3603
}
3604
else if (auto pt = get<PrimitiveType>(subjectType))
3605
{
3606
// if we are in a conditional context, we treat the property as present and `unknown` because
3607
// we may be _refining_ a table to include that property. we will want to revisit this a bit
3608
// in the future once luau has support for exact tables since this only applies when inexact.
3609
if (inConditional && pt->type == PrimitiveType::Table)
3610
return {{}, builtinTypes->unknownType};
3611
}
3612
3613
return {{}, std::nullopt};
3614
}
3615
3616
template<typename TID>
3617
bool ConstraintSolver::unify(NotNull<const Constraint> constraint, TID subTy, TID superTy)
3618
{
3619
static_assert(std::is_same_v<TID, TypeId> || std::is_same_v<TID, TypePackId>);
3620
3621
if (FFlag::LuauUnifyWithSubtyping2)
3622
{
3623
Subtyping subtyping{builtinTypes, arena, normalizer, typeFunctionRuntime, NotNull{&iceReporter}};
3624
SubtypingUnifier stu{arena, builtinTypes, NotNull{&iceReporter}};
3625
SubtypingResult result;
3626
if constexpr (std::is_same_v<TID, TypeId>)
3627
result = subtyping.isSubtype(subTy, superTy, constraint->scope);
3628
else if constexpr (std::is_same_v<TID, TypePackId>)
3629
result = subtyping.isSubtype(subTy, superTy, constraint->scope, {});
3630
3631
auto unifierResult = stu.dispatchConstraints(constraint, std::move(result.assumedConstraints));
3632
3633
for (auto& cv : unifierResult.outstandingConstraints)
3634
{
3635
auto newConstraint = pushConstraint(constraint->scope, constraint->location, std::move(cv));
3636
inheritBlocks(constraint, newConstraint);
3637
}
3638
3639
for (const auto& [ty, newUpperBounds] : unifierResult.upperBoundContributors)
3640
{
3641
auto& upperBounds = upperBoundContributors[ty];
3642
upperBounds.insert(upperBounds.end(), newUpperBounds.begin(), newUpperBounds.end());
3643
}
3644
3645
switch (unifierResult.unified)
3646
{
3647
case UnifyResult::OccursCheckFailed:
3648
reportError(OccursCheckFailed{}, constraint->location);
3649
return false;
3650
case UnifyResult::TooComplex:
3651
reportError(UnificationTooComplex{}, constraint->location);
3652
return false;
3653
case UnifyResult::Ok:
3654
default:
3655
return true;
3656
}
3657
}
3658
else
3659
{
3660
Unifier2 u2{NotNull{arena}, builtinTypes, constraint->scope, NotNull{&iceReporter}, &uninhabitedTypeFunctions};
3661
3662
const UnifyResult unifyResult = u2.unify(subTy, superTy);
3663
3664
for (ConstraintV& c : u2.incompleteSubtypes)
3665
{
3666
NotNull<Constraint> addition = pushConstraint(constraint->scope, constraint->location, std::move(c));
3667
inheritBlocks(constraint, addition);
3668
}
3669
3670
if (UnifyResult::Ok == unifyResult)
3671
{
3672
for (const auto& [expanded, additions] : u2.expandedFreeTypes)
3673
{
3674
for (TypeId addition : additions)
3675
upperBoundContributors[expanded].emplace_back(constraint->location, addition);
3676
}
3677
}
3678
else
3679
{
3680
switch (unifyResult)
3681
{
3682
case Luau::UnifyResult::Ok:
3683
break;
3684
case Luau::UnifyResult::OccursCheckFailed:
3685
reportError(OccursCheckFailed{}, constraint->location);
3686
break;
3687
case Luau::UnifyResult::TooComplex:
3688
reportError(UnificationTooComplex{}, constraint->location);
3689
break;
3690
}
3691
return false;
3692
}
3693
3694
return true;
3695
}
3696
3697
}
3698
3699
bool ConstraintSolver::block_(BlockedConstraintId target, NotNull<const Constraint> constraint)
3700
{
3701
// If a set is not present for the target, construct a new DenseHashSet for it,
3702
// else grab the address of the existing set.
3703
auto [iter, inserted] = blocked.try_emplace(target, nullptr);
3704
auto& [key, blockVec] = *iter;
3705
3706
if (blockVec.find(constraint))
3707
return false;
3708
3709
blockVec.insert(constraint);
3710
3711
size_t& count = blockedConstraints[constraint];
3712
count += 1;
3713
3714
return true;
3715
}
3716
3717
void ConstraintSolver::block(NotNull<const Constraint> target, NotNull<const Constraint> constraint)
3718
{
3719
const bool newBlock = block_(target.get(), constraint);
3720
if (newBlock)
3721
{
3722
if (logger)
3723
logger->pushBlock(constraint, target);
3724
3725
if (FFlag::DebugLuauLogSolver)
3726
printf("%s depends on constraint %s\n", toString(*constraint, opts).c_str(), toString(*target, opts).c_str());
3727
}
3728
}
3729
3730
bool ConstraintSolver::block(TypeId target, NotNull<const Constraint> constraint)
3731
{
3732
const bool newBlock = block_(follow(target), constraint);
3733
if (newBlock)
3734
{
3735
if (logger)
3736
logger->pushBlock(constraint, target);
3737
3738
if (FFlag::DebugLuauLogSolver)
3739
printf("%s depends on TypeId %s\n", toString(*constraint, opts).c_str(), toString(target, opts).c_str());
3740
}
3741
3742
return false;
3743
}
3744
3745
bool ConstraintSolver::block(TypePackId target, NotNull<const Constraint> constraint)
3746
{
3747
const bool newBlock = block_(target, constraint);
3748
if (newBlock)
3749
{
3750
if (logger)
3751
logger->pushBlock(constraint, target);
3752
3753
if (FFlag::DebugLuauLogSolver)
3754
printf("%s depends on TypePackId %s\n", toString(*constraint, opts).c_str(), toString(target, opts).c_str());
3755
}
3756
3757
return false;
3758
}
3759
3760
void ConstraintSolver::inheritBlocks(NotNull<const Constraint> source, NotNull<const Constraint> addition)
3761
{
3762
// Anything that is blocked on this constraint must also be blocked on our
3763
// synthesized constraints.
3764
auto blockedIt = blocked.find(source.get());
3765
if (blockedIt != blocked.end())
3766
{
3767
for (const Constraint* blockedConstraint : blockedIt->second)
3768
{
3769
block(addition, NotNull{blockedConstraint});
3770
}
3771
}
3772
}
3773
3774
struct Blocker : TypeOnceVisitor
3775
{
3776
NotNull<ConstraintSolver> solver;
3777
NotNull<const Constraint> constraint;
3778
3779
bool blocked = false;
3780
3781
explicit Blocker(NotNull<ConstraintSolver> solver, NotNull<const Constraint> constraint)
3782
: TypeOnceVisitor("Blocker", /* skipBoundTypes */ true)
3783
, solver(solver)
3784
, constraint(constraint)
3785
{
3786
}
3787
3788
bool visit(TypeId ty, const PendingExpansionType&) override
3789
{
3790
blocked = true;
3791
solver->block(ty, constraint);
3792
return false;
3793
}
3794
3795
bool visit(TypeId ty, const ExternType&) override
3796
{
3797
return false;
3798
}
3799
};
3800
3801
bool ConstraintSolver::blockOnPendingTypes(TypeId target, NotNull<const Constraint> constraint)
3802
{
3803
Blocker blocker{NotNull{this}, constraint};
3804
blocker.traverse(target);
3805
return !blocker.blocked;
3806
}
3807
3808
bool ConstraintSolver::blockOnPendingTypes(TypePackId targetPack, NotNull<const Constraint> constraint)
3809
{
3810
Blocker blocker{NotNull{this}, constraint};
3811
blocker.traverse(targetPack);
3812
return !blocker.blocked;
3813
}
3814
3815
void ConstraintSolver::unblock_(BlockedConstraintId progressed)
3816
{
3817
auto it = blocked.find(progressed);
3818
if (it == blocked.end())
3819
return;
3820
3821
// unblocked should contain a value always, because of the above check
3822
for (const Constraint* unblockedConstraint : it->second)
3823
{
3824
auto& count = blockedConstraints[NotNull{unblockedConstraint}];
3825
if (FFlag::DebugLuauLogSolver)
3826
printf("Unblocking count=%d\t%s\n", int(count), toString(*unblockedConstraint, opts).c_str());
3827
3828
// This assertion being hit indicates that `blocked` and
3829
// `blockedConstraints` de-synchronized at some point. This is problematic
3830
// because we rely on this count being correct to skip over blocked
3831
// constraints.
3832
LUAU_ASSERT(count > 0);
3833
count -= 1;
3834
}
3835
3836
blocked.erase(it);
3837
}
3838
3839
void ConstraintSolver::unblock(NotNull<const Constraint> progressed)
3840
{
3841
if (logger)
3842
logger->popBlock(progressed);
3843
3844
return unblock_(progressed.get());
3845
}
3846
3847
void ConstraintSolver::unblock(TypeId ty, Location location)
3848
{
3849
DenseHashSet<TypeId> seen{nullptr};
3850
3851
TypeId progressed = ty;
3852
while (true)
3853
{
3854
if (seen.find(progressed))
3855
iceReporter.ice("ConstraintSolver::unblock encountered a self-bound type!", location);
3856
seen.insert(progressed);
3857
3858
if (logger)
3859
logger->popBlock(progressed);
3860
3861
unblock_(progressed);
3862
3863
if (auto bt = get<BoundType>(progressed))
3864
progressed = bt->boundTo;
3865
else
3866
break;
3867
}
3868
}
3869
3870
void ConstraintSolver::unblock(TypePackId progressed, Location)
3871
{
3872
if (logger)
3873
logger->popBlock(progressed);
3874
3875
return unblock_(progressed);
3876
}
3877
3878
void ConstraintSolver::unblock(const std::vector<TypeId>& types, Location location)
3879
{
3880
for (TypeId t : types)
3881
unblock(t, location);
3882
}
3883
3884
void ConstraintSolver::unblock(const std::vector<TypePackId>& packs, Location location)
3885
{
3886
for (TypePackId t : packs)
3887
unblock(t, location);
3888
}
3889
3890
void ConstraintSolver::reproduceConstraints(NotNull<Scope> scope, const Location& location, const Substitution& subst)
3891
{
3892
for (auto [_, newTy] : subst.newTypes)
3893
{
3894
if (get<TypeFunctionInstanceType>(newTy))
3895
pushConstraint(scope, location, ReduceConstraint{newTy});
3896
}
3897
3898
for (auto [_, newPack] : subst.newPacks)
3899
{
3900
if (get<TypeFunctionInstanceTypePack>(newPack))
3901
pushConstraint(scope, location, ReducePackConstraint{newPack});
3902
}
3903
}
3904
3905
bool ConstraintSolver::isBlocked(TypeId ty) const
3906
{
3907
// FIXME CLI-180636: Eventually this should use the same logic as
3908
// `SubtypingUnifier`, which is that blocked types are only based
3909
// on their type and any additional state, rather than looking at
3910
// `uninhabitedTypeFunctions`.
3911
ty = follow(ty);
3912
3913
if (auto tfit = get<TypeFunctionInstanceType>(ty))
3914
{
3915
if (tfit->state != TypeFunctionInstanceState::Unsolved)
3916
return false;
3917
return uninhabitedTypeFunctions.contains(ty) == false;
3918
}
3919
3920
return nullptr != get<BlockedType>(ty) || nullptr != get<PendingExpansionType>(ty);
3921
}
3922
3923
bool ConstraintSolver::isBlocked(TypePackId tp) const
3924
{
3925
tp = follow(tp);
3926
3927
if (get<TypeFunctionInstanceTypePack>(tp))
3928
return uninhabitedTypeFunctions.contains(tp) == false;
3929
3930
return nullptr != get<BlockedTypePack>(tp);
3931
}
3932
3933
bool ConstraintSolver::isBlocked(NotNull<const Constraint> constraint) const
3934
{
3935
auto blockedIt = blockedConstraints.find(constraint);
3936
return blockedIt != blockedConstraints.end() && blockedIt->second > 0;
3937
}
3938
3939
NotNull<Constraint> ConstraintSolver::pushConstraint(NotNull<Scope> scope, const Location& location, ConstraintV cv)
3940
{
3941
std::optional<SubtypeConstraintRecord> scr;
3942
if (auto sc = cv.get_if<SubtypeConstraint>())
3943
scr.emplace(SubtypeConstraintRecord{sc->subType, sc->superType, SubtypingVariance::Covariant});
3944
else if (auto ec = cv.get_if<EqualityConstraint>())
3945
scr.emplace(SubtypeConstraintRecord{ec->assignmentType, ec->resultType, SubtypingVariance::Invariant});
3946
3947
if (scr)
3948
{
3949
if (auto f = seenConstraints.find(*scr))
3950
return NotNull{*f};
3951
}
3952
3953
std::unique_ptr<Constraint> c = std::make_unique<Constraint>(scope, location, std::move(cv));
3954
NotNull<Constraint> borrow = NotNull(c.get());
3955
3956
if (scr)
3957
seenConstraints[*scr] = borrow;
3958
3959
solverConstraints.push_back(std::move(c));
3960
unsolvedConstraints.emplace_back(borrow);
3961
3962
if (solverConstraintLimit > 0)
3963
{
3964
--solverConstraintLimit;
3965
3966
if (solverConstraintLimit == 0)
3967
reportError(CodeTooComplex{}, location);
3968
}
3969
3970
return borrow;
3971
}
3972
3973
TypeId ConstraintSolver::resolveModule(const ModuleInfo& info, const Location& location)
3974
{
3975
if (info.name.empty())
3976
{
3977
reportError(UnknownRequire{}, location);
3978
return builtinTypes->errorType;
3979
}
3980
3981
for (const auto& [location, path] : requireCycles)
3982
{
3983
if (!path.empty() && path.front() == info.name)
3984
return builtinTypes->anyType;
3985
}
3986
3987
ModulePtr module = moduleResolver->getModule(info.name);
3988
if (!module)
3989
{
3990
if (!moduleResolver->moduleExists(info.name) && !info.optional)
3991
reportError(UnknownRequire{moduleResolver->getHumanReadableModuleName(info.name)}, location);
3992
3993
return builtinTypes->errorType;
3994
}
3995
3996
if (module->type != SourceCode::Type::Module)
3997
{
3998
reportError(IllegalRequire{module->humanReadableName, "Module is not a ModuleScript. It cannot be required."}, location);
3999
return builtinTypes->errorType;
4000
}
4001
4002
TypePackId modulePack = module->returnType;
4003
if (get<ErrorTypePack>(modulePack))
4004
return builtinTypes->errorType;
4005
4006
std::optional<TypeId> moduleType = first(modulePack);
4007
if (!moduleType)
4008
{
4009
reportError(IllegalRequire{module->humanReadableName, "Module does not return exactly 1 value. It cannot be required."}, location);
4010
return builtinTypes->errorType;
4011
}
4012
4013
return *moduleType;
4014
}
4015
4016
void ConstraintSolver::reportError(TypeErrorData&& data, const Location& location)
4017
{
4018
errors.emplace_back(location, std::move(data));
4019
errors.back().moduleName = module->name;
4020
}
4021
4022
void ConstraintSolver::reportError(TypeError e)
4023
{
4024
errors.emplace_back(std::move(e));
4025
errors.back().moduleName = module->name;
4026
}
4027
4028
void ConstraintSolver::shiftReferences(TypeId source, TypeId target)
4029
{
4030
target = follow(target);
4031
4032
// if the target isn't a reference counted type, there's nothing to do.
4033
// this stops us from keeping unnecessary counts for e.g. primitive types.
4034
if (!isReferenceCountedType(target))
4035
return;
4036
4037
// This can happen in the _very_ specific case of:
4038
//
4039
// local Tbl = {}
4040
// Tbl.__index = Tbl
4041
//
4042
// This would probably not be required if table type stating worked in
4043
// a reasonable manner.
4044
if (source == target)
4045
return;
4046
4047
if (FFlag::LuauUseConstraintSetsToTrackFreeTypes)
4048
{
4049
if (auto sourcerefs = typeToConstraintSet.find(source); sourcerefs != typeToConstraintSet.end())
4050
{
4051
auto [targetrefs, _] = typeToConstraintSet.try_emplace(target, Set<const Constraint*>{nullptr});
4052
4053
// This is a little sketchy as we are iterating over a hash set.
4054
// It _should_ be fine as we aren't depending on the order here,
4055
// this is all just moving values into different hash sets.
4056
//
4057
// NOTE: I wonder if there's a way we could preemptively resize
4058
// `targetrefs` so that we only ever do one extra allocation here.
4059
for (const auto* constraint : sourcerefs->second)
4060
{
4061
// For every constraint that the source might be modified by,
4062
// add that constraint to the set of constraints the target
4063
// might be modified by.
4064
targetrefs->second.insert(constraint);
4065
4066
// Additionally, note that said constraint now may modify the target.
4067
auto [it, _] = constraintToMutatedTypes.try_insert(constraint, TypeIds{});
4068
it.insert(target);
4069
}
4070
}
4071
}
4072
else
4073
{
4074
4075
auto sourceRefs = DEPRECATED_unresolvedConstraints.find(source);
4076
if (sourceRefs)
4077
{
4078
// we read out the count before proceeding to avoid hash invalidation issues.
4079
size_t count = *sourceRefs;
4080
4081
auto [targetRefs, _] = DEPRECATED_unresolvedConstraints.try_insert(target, 0);
4082
targetRefs += count;
4083
}
4084
4085
// Any constraint that might have mutated source may now mutate target
4086
if (auto it = DEPRECATED_mutatedFreeTypeToConstraint.find(source); it != DEPRECATED_mutatedFreeTypeToConstraint.end())
4087
{
4088
const OrderedSet<const Constraint*>& constraintsAffectedBySource = it->second;
4089
auto [it2, fresh2] = DEPRECATED_mutatedFreeTypeToConstraint.try_emplace(target);
4090
4091
OrderedSet<const Constraint*>& constraintsAffectedByTarget = it2->second;
4092
4093
for (const Constraint* constraint : constraintsAffectedBySource)
4094
{
4095
constraintsAffectedByTarget.insert(constraint);
4096
auto [it3, fresh3] = DEPRECATED_maybeMutatedFreeTypes.try_emplace(NotNull{constraint}, TypeIds{});
4097
it3->second.insert(target);
4098
}
4099
}
4100
}
4101
}
4102
4103
bool ConstraintSolver::hasUnresolvedConstraints(TypeId ty)
4104
{
4105
if (FFlag::LuauUseConstraintSetsToTrackFreeTypes)
4106
{
4107
ty = follow(ty);
4108
if (auto it = typeToConstraintSet.find(ty); it != typeToConstraintSet.end())
4109
return !it->second.empty();
4110
}
4111
else
4112
{
4113
if (auto refCount = DEPRECATED_unresolvedConstraints.find(ty))
4114
return *refCount > 0;
4115
}
4116
4117
return false;
4118
}
4119
4120
TypeId ConstraintSolver::simplifyIntersection(NotNull<Scope> scope, Location location, TypeId left, TypeId right)
4121
{
4122
return ::Luau::simplifyIntersection(builtinTypes, arena, left, right).result;
4123
}
4124
4125
TypeId ConstraintSolver::simplifyIntersection(NotNull<Scope> scope, Location location, TypeIds parts)
4126
{
4127
return ::Luau::simplifyIntersection(builtinTypes, arena, std::move(parts)).result;
4128
}
4129
4130
TypeId ConstraintSolver::simplifyUnion(NotNull<Scope> scope, Location location, TypeId left, TypeId right)
4131
{
4132
return ::Luau::simplifyUnion(builtinTypes, arena, left, right).result;
4133
}
4134
4135
TypePackId ConstraintSolver::anyifyModuleReturnTypePackGenerics(TypePackId tp)
4136
{
4137
tp = follow(tp);
4138
4139
if (const VariadicTypePack* vtp = get<VariadicTypePack>(tp))
4140
{
4141
TypeId ty = follow(vtp->ty);
4142
return get<GenericType>(ty) ? builtinTypes->anyTypePack : tp;
4143
}
4144
4145
if (!get<TypePack>(follow(tp)))
4146
return tp;
4147
4148
std::vector<TypeId> resultTypes;
4149
std::optional<TypePackId> resultTail;
4150
4151
TypePackIterator it = begin(tp);
4152
4153
for (TypePackIterator e = end(tp); it != e; ++it)
4154
{
4155
TypeId ty = follow(*it);
4156
resultTypes.push_back(get<GenericType>(ty) ? builtinTypes->anyType : ty);
4157
}
4158
4159
if (std::optional<TypePackId> tail = it.tail())
4160
resultTail = anyifyModuleReturnTypePackGenerics(*tail);
4161
4162
return arena->addTypePack(std::move(resultTypes), resultTail);
4163
}
4164
4165
LUAU_NOINLINE void ConstraintSolver::throwTimeLimitError() const
4166
{
4167
throw TimeLimitError(module->name);
4168
}
4169
4170
LUAU_NOINLINE void ConstraintSolver::throwUserCancelError() const
4171
{
4172
throw UserCancelError(module->name);
4173
}
4174
4175
// Instantiate private template implementations for external callers
4176
template bool ConstraintSolver::unify(NotNull<const Constraint> constraint, TypeId subTy, TypeId superTy);
4177
template bool ConstraintSolver::unify(NotNull<const Constraint> constraint, TypePackId subTy, TypePackId superTy);
4178
4179
std::vector<NotNull<Constraint>> borrowConstraints(const std::vector<ConstraintPtr>& constraints)
4180
{
4181
std::vector<NotNull<Constraint>> result;
4182
result.reserve(constraints.size());
4183
4184
for (const auto& c : constraints)
4185
result.emplace_back(c.get());
4186
4187
return result;
4188
}
4189
4190
void dump(ConstraintSolver* cs, ToStringOptions& opts)
4191
{
4192
printf("constraints:\n");
4193
for (NotNull<const Constraint> c : cs->unsolvedConstraints)
4194
{
4195
auto it = cs->blockedConstraints.find(c);
4196
int blockCount = it == cs->blockedConstraints.end() ? 0 : int(it->second);
4197
printf("\t%d\t%s\n", blockCount, toString(*c, opts).c_str());
4198
4199
if (FFlag::DebugLuauLogSolverIncludeDependencies)
4200
{
4201
for (NotNull<Constraint> dep : c->dependencies)
4202
{
4203
if (std::find(cs->unsolvedConstraints.begin(), cs->unsolvedConstraints.end(), dep) != cs->unsolvedConstraints.end())
4204
printf("\t\t|\t%s\n", toString(*dep, opts).c_str());
4205
}
4206
}
4207
}
4208
}
4209
4210
} // namespace Luau
4211
4212