Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Analysis/include/Luau/ConstraintSolver.h
2727 views
1
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2
3
#pragma once
4
5
#include "Luau/Constraint.h"
6
#include "Luau/ConstraintSet.h"
7
#include "Luau/DataFlowGraph.h"
8
#include "Luau/DenseHash.h"
9
#include "Luau/Error.h"
10
#include "Luau/Location.h"
11
#include "Luau/Module.h"
12
#include "Luau/Normalize.h"
13
#include "Luau/OrderedSet.h"
14
#include "Luau/Substitution.h"
15
#include "Luau/SubtypingVariance.h"
16
#include "Luau/ToString.h"
17
#include "Luau/Type.h"
18
#include "Luau/TypeCheckLimits.h"
19
#include "Luau/TypeFunction.h"
20
#include "Luau/TypeFwd.h"
21
#include "Luau/Variant.h"
22
23
#include <utility>
24
#include <vector>
25
26
namespace Luau
27
{
28
29
enum class ValueContext;
30
31
struct DcrLogger;
32
33
class AstExpr;
34
35
// TypeId, TypePackId, or Constraint*. It is impossible to know which, but we
36
// never dereference this pointer.
37
using BlockedConstraintId = Variant<TypeId, TypePackId, const Constraint*>;
38
39
struct HashBlockedConstraintId
40
{
41
size_t operator()(const BlockedConstraintId& bci) const;
42
};
43
44
struct SubtypeConstraintRecord
45
{
46
TypeId subTy = nullptr;
47
TypeId superTy = nullptr;
48
SubtypingVariance variance = SubtypingVariance::Invalid;
49
50
bool operator==(const SubtypeConstraintRecord& other) const;
51
};
52
53
struct HashSubtypeConstraintRecord
54
{
55
size_t operator()(const SubtypeConstraintRecord& c) const;
56
};
57
58
struct ModuleResolver;
59
60
struct InstantiationSignature
61
{
62
TypeFun fn;
63
std::vector<TypeId> arguments;
64
std::vector<TypePackId> packArguments;
65
66
bool operator==(const InstantiationSignature& rhs) const;
67
bool operator!=(const InstantiationSignature& rhs) const
68
{
69
return !((*this) == rhs);
70
}
71
};
72
73
struct HashInstantiationSignature
74
{
75
size_t operator()(const InstantiationSignature& signature) const;
76
};
77
78
79
struct TablePropLookupResult
80
{
81
// What types are we blocked on for determining this type?
82
std::vector<TypeId> blockedTypes;
83
// The type of the property (if we were able to determine it).
84
std::optional<TypeId> propType;
85
// Whether or not this is _definitely_ derived as the result of an indexer.
86
// We use this to determine whether or not code like:
87
//
88
// t.lol = nil;
89
//
90
// ... is legal. If `t: { [string]: ~nil }` then this is legal as
91
// there's no guarantee on whether "lol" specifically exists.
92
// However, if `t: { lol: ~nil }`, then we cannot allow assignment as
93
// that would remove "lol" from the table entirely.
94
bool isIndex = false;
95
};
96
97
struct ConstraintSolver
98
{
99
NotNull<TypeArena> arena;
100
NotNull<BuiltinTypes> builtinTypes;
101
InternalErrorReporter iceReporter;
102
NotNull<Normalizer> normalizer;
103
NotNull<TypeFunctionRuntime> typeFunctionRuntime;
104
// The entire set of constraints that the solver is trying to resolve.
105
ConstraintSet constraintSet;
106
std::vector<NotNull<Constraint>> constraints;
107
NotNull<DenseHashMap<Scope*, TypeId>> scopeToFunction;
108
NotNull<Scope> rootScope;
109
ModulePtr module;
110
111
// The dataflow graph of the program, used in constraint generation and for magic functions.
112
NotNull<const DataFlowGraph> dfg;
113
114
// Constraints that the solver has generated, rather than sourcing from the
115
// scope tree.
116
std::vector<std::unique_ptr<Constraint>> solverConstraints;
117
118
// Ticks downward toward zero each time a new constraint is pushed into
119
// solverConstraints. When this counter reaches zero, the type inference
120
// engine reports a CodeTooComplex error and aborts.
121
size_t solverConstraintLimit = 0;
122
123
// This includes every constraint that has not been fully solved.
124
// A constraint can be both blocked and unsolved, for instance.
125
std::vector<NotNull<const Constraint>> unsolvedConstraints;
126
127
// A mapping of constraint pointer to how many things the constraint is
128
// blocked on. Can be empty or 0 for constraints that are not blocked on
129
// anything.
130
std::unordered_map<NotNull<const Constraint>, size_t> blockedConstraints;
131
// A mapping of type/pack pointers to the constraints they block.
132
std::unordered_map<BlockedConstraintId, DenseHashSet<const Constraint*>, HashBlockedConstraintId> blocked;
133
// Memoized instantiations of type aliases.
134
DenseHashMap<InstantiationSignature, TypeId, HashInstantiationSignature> instantiatedAliases{{}};
135
// Breadcrumbs for where a free type's upper bound was expanded. We use
136
// these to provide more helpful error messages when a free type is solved
137
// as never unexpectedly.
138
DenseHashMap<TypeId, std::vector<std::pair<Location, TypeId>>> upperBoundContributors{nullptr};
139
140
// A mapping from free types to the number of unresolved constraints that mention them.
141
DenseHashMap<TypeId, size_t> DEPRECATED_unresolvedConstraints{{}};
142
143
std::unordered_map<NotNull<const Constraint>, TypeIds> DEPRECATED_maybeMutatedFreeTypes;
144
std::unordered_map<TypeId, OrderedSet<const Constraint*>> DEPRECATED_mutatedFreeTypeToConstraint;
145
146
/**
147
* A mapping from reference counted types (blocked types, free types,
148
* unsealed table types, etc.) to the constraints that may mutate them.
149
* When this set is empty, we can eagerly generalize the respective key.
150
*
151
* NOTE: Preferrably this would be a DenseHashMap rather than an
152
* unordered_map, but DenseHashMaps require that their elements are
153
* trivially constructable.
154
*/
155
std::unordered_map<TypeId, Set<const Constraint*>> typeToConstraintSet;
156
157
158
/**
159
* A mapping from constraints to the types that they mutate. We
160
* use this set to keep track of what constraints to remove
161
* from the values in the typeToConstraintSet.
162
*/
163
DenseHashMap<const Constraint*, TypeIds> constraintToMutatedTypes{nullptr};
164
165
// Irreducible/uninhabited type functions or type pack functions.
166
DenseHashSet<const void*> uninhabitedTypeFunctions{{}};
167
168
DenseHashMap<SubtypeConstraintRecord, Constraint*, HashSubtypeConstraintRecord> seenConstraints{{}};
169
170
// The set of types that will definitely be unchanged by generalization.
171
DenseHashSet<TypeId> generalizedTypes_{nullptr};
172
const NotNull<DenseHashSet<TypeId>> generalizedTypes{&generalizedTypes_};
173
174
// Recorded errors that take place within the solver.
175
ErrorVec errors;
176
177
NotNull<ModuleResolver> moduleResolver;
178
std::vector<RequireCycle> requireCycles;
179
180
DcrLogger* logger;
181
TypeCheckLimits limits;
182
183
DenseHashMap<TypeId, const Constraint*> typeFunctionsToFinalize{nullptr};
184
185
explicit ConstraintSolver(
186
NotNull<Normalizer> normalizer,
187
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
188
ModulePtr module,
189
NotNull<ModuleResolver> moduleResolver,
190
std::vector<RequireCycle> requireCycles,
191
DcrLogger* logger,
192
NotNull<const DataFlowGraph> dfg,
193
TypeCheckLimits limits,
194
ConstraintSet constraintSet
195
);
196
197
// TODO CLI-169086: Replace all uses of this constructor with the ConstraintSet constructor, above.
198
explicit ConstraintSolver(
199
NotNull<Normalizer> normalizer,
200
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
201
NotNull<Scope> rootScope,
202
std::vector<NotNull<Constraint>> constraints,
203
NotNull<DenseHashMap<Scope*, TypeId>> scopeToFunction,
204
ModulePtr module,
205
NotNull<ModuleResolver> moduleResolver,
206
std::vector<RequireCycle> requireCycles,
207
DcrLogger* logger,
208
NotNull<const DataFlowGraph> dfg,
209
TypeCheckLimits limits
210
);
211
212
// Randomize the order in which to dispatch constraints
213
void randomize(unsigned seed);
214
215
/**
216
* Attempts to dispatch all pending constraints and reach a type solution
217
* that satisfies all of the constraints.
218
**/
219
void run();
220
221
222
/**
223
* Attempts to perform one final reduction on type functions after every constraint has been completed
224
*
225
**/
226
void finalizeTypeFunctions();
227
228
bool isDone() const;
229
230
private:
231
/// A helper that does most of the setup work that is shared between the two constructors.
232
void initFreeTypeTracking();
233
234
void generalizeOneType(TypeId ty);
235
236
template<typename T, typename... Args>
237
void emplace(NotNull<const Constraint> constraint, TypeId ty, Args&&... args);
238
239
template<typename T, typename... Args>
240
void emplace(NotNull<const Constraint> constraint, TypePackId tp, Args&&... args);
241
242
public:
243
/** Attempt to dispatch a constraint. Returns true if it was successful. If
244
* tryDispatch() returns false, the constraint remains in the unsolved set
245
* and will be retried later.
246
*/
247
bool tryDispatch(NotNull<const Constraint> c, bool force);
248
249
bool tryDispatch(const SubtypeConstraint& c, NotNull<const Constraint> constraint);
250
bool tryDispatch(const PackSubtypeConstraint& c, NotNull<const Constraint> constraint);
251
bool tryDispatch(const GeneralizationConstraint& c, NotNull<const Constraint> constraint);
252
bool tryDispatch(const IterableConstraint& c, NotNull<const Constraint> constraint, bool force);
253
bool tryDispatch(const NameConstraint& c, NotNull<const Constraint> constraint);
254
bool tryDispatch(const TypeAliasExpansionConstraint& c, NotNull<const Constraint> constraint);
255
bool tryDispatch(const FunctionCallConstraint& c, NotNull<const Constraint> constraint, bool force);
256
bool tryDispatch(const FunctionCheckConstraint& c, NotNull<const Constraint> constraint, bool force);
257
bool tryDispatch(const PrimitiveTypeConstraint& c, NotNull<const Constraint> constraint);
258
bool tryDispatch(const HasPropConstraint& c, NotNull<const Constraint> constraint);
259
bool tryDispatch(const TypeInstantiationConstraint& c, NotNull<const Constraint> constraint);
260
261
bool tryDispatchHasIndexer(
262
int& recursionDepth,
263
NotNull<const Constraint> constraint,
264
TypeId subjectType,
265
TypeId indexType,
266
TypeId resultType,
267
Set<TypeId>& seen
268
);
269
bool tryDispatch(const HasIndexerConstraint& c, NotNull<const Constraint> constraint);
270
271
bool tryDispatch(const AssignPropConstraint& c, NotNull<const Constraint> constraint);
272
bool tryDispatch(const AssignIndexConstraint& c, NotNull<const Constraint> constraint);
273
bool tryDispatch(const UnpackConstraint& c, NotNull<const Constraint> constraint);
274
bool tryDispatch(const ReduceConstraint& c, NotNull<const Constraint> constraint, bool force);
275
bool tryDispatch(const ReducePackConstraint& c, NotNull<const Constraint> constraint, bool force);
276
bool tryDispatch(const EqualityConstraint& c, NotNull<const Constraint> constraint);
277
278
bool tryDispatch(const SimplifyConstraint& c, NotNull<const Constraint> constraint, bool force);
279
280
bool tryDispatch(const PushFunctionTypeConstraint& c, NotNull<const Constraint> constraint);
281
bool tryDispatch(const PushTypeConstraint& c, NotNull<const Constraint> constraint, bool force);
282
283
// for a, ... in some_table do
284
// also handles __iter metamethod
285
bool tryDispatchIterableTable(TypeId iteratorTy, const IterableConstraint& c, NotNull<const Constraint> constraint, bool force);
286
287
// for a, ... in next_function, t, ... do
288
bool tryDispatchIterableFunction(TypeId nextTy, TypeId tableTy, const IterableConstraint& c, NotNull<const Constraint> constraint);
289
290
TablePropLookupResult lookupTableProp(
291
NotNull<const Constraint> constraint,
292
TypeId subjectType,
293
const std::string& propName,
294
ValueContext context,
295
bool inConditional = false,
296
bool suppressSimplification = false
297
);
298
299
TablePropLookupResult lookupTableProp(
300
NotNull<const Constraint> constraint,
301
TypeId subjectType,
302
const std::string& propName,
303
ValueContext context,
304
bool inConditional,
305
bool suppressSimplification,
306
Set<TypeId>& seen
307
);
308
309
/**
310
* Generate constraints to unpack the types of srcTypes and assign each
311
* value to the corresponding BlockedType in destTypes.
312
*
313
* This function also overwrites the owners of each BlockedType. This is
314
* okay because this function is only used to decompose IterableConstraint
315
* into an UnpackConstraint.
316
*
317
* @param destTypes A vector of types comprised of BlockedTypes.
318
* @param srcTypes A TypePack that represents rvalues to be assigned.
319
* @returns The underlying UnpackConstraint. There's a bit of code in
320
* iteration that needs to pass blocks on to this constraint.
321
*/
322
NotNull<const Constraint> unpackAndAssign(const std::vector<TypeId> destTypes, TypePackId srcTypes, NotNull<const Constraint> constraint);
323
324
void block(NotNull<const Constraint> target, NotNull<const Constraint> constraint);
325
/**
326
* Block a constraint on the resolution of a Type.
327
* @returns false always. This is just to allow tryDispatch to return the result of block()
328
*/
329
bool block(TypeId target, NotNull<const Constraint> constraint);
330
bool block(TypePackId target, NotNull<const Constraint> constraint);
331
332
// Block on every target
333
template<typename T>
334
bool block(const T& targets, NotNull<const Constraint> constraint)
335
{
336
for (TypeId target : targets)
337
block(target, constraint);
338
339
return false;
340
}
341
342
/**
343
* For all constraints that are blocked on one constraint, make them block
344
* on a new constraint.
345
* @param source the constraint to copy blocks from.
346
* @param addition the constraint that other constraints should now block on.
347
*/
348
void inheritBlocks(NotNull<const Constraint> source, NotNull<const Constraint> addition);
349
350
// Traverse the type. If any pending types are found, block the constraint
351
// on them.
352
//
353
// Returns false if a type blocks the constraint.
354
//
355
// FIXME: This use of a boolean for the return result is an appalling
356
// interface.
357
bool blockOnPendingTypes(TypeId target, NotNull<const Constraint> constraint);
358
bool blockOnPendingTypes(TypePackId targetPack, NotNull<const Constraint> constraint);
359
360
void unblock(NotNull<const Constraint> progressed);
361
void unblock(TypeId ty, Location location);
362
void unblock(TypePackId progressed, Location location);
363
void unblock(const std::vector<TypeId>& types, Location location);
364
void unblock(const std::vector<TypePackId>& packs, Location location);
365
366
/**
367
* @returns true if the TypeId is in a blocked state.
368
*/
369
bool isBlocked(TypeId ty) const;
370
371
/**
372
* @returns true if the TypePackId is in a blocked state.
373
*/
374
bool isBlocked(TypePackId tp) const;
375
376
/**
377
* Returns whether the constraint is blocked on anything.
378
* @param constraint the constraint to check.
379
*/
380
bool isBlocked(NotNull<const Constraint> constraint) const;
381
382
/** Pushes a new solver constraint to the solver.
383
* @param cv the body of the constraint.
384
**/
385
NotNull<Constraint> pushConstraint(NotNull<Scope> scope, const Location& location, ConstraintV cv);
386
387
/**
388
* Attempts to resolve a module from its module information. Returns the
389
* module-level return type of the module, or the error type if one cannot
390
* be found. Reports errors to the solver if the module cannot be found or
391
* the require is illegal.
392
* @param module the module information to look up.
393
* @param location the location where the require is taking place; used for
394
* error locations.
395
**/
396
TypeId resolveModule(const ModuleInfo& info, const Location& location);
397
398
void reportError(TypeErrorData&& data, const Location& location);
399
void reportError(TypeError e);
400
401
/**
402
* Shifts the count of references from `source` to `target`. This should be paired
403
* with any instance of binding a free type in order to maintain accurate refcounts.
404
* If `target` is not a free type, this is a noop.
405
* @param source the free type which is being bound
406
* @param target the type which the free type is being bound to
407
*/
408
void shiftReferences(TypeId source, TypeId target);
409
410
/**
411
* Bind a type variable to another type.
412
*
413
* A constraint is required and will validate that blockedTy is owned by this
414
* constraint. This prevents one constraint from interfering with another's
415
* blocked types.
416
*
417
* Bind will also unblock the type variable for you.
418
*/
419
void bind(NotNull<const Constraint> constraint, TypeId ty, TypeId boundTo);
420
void bind(NotNull<const Constraint> constraint, TypePackId tp, TypePackId boundTo);
421
422
/**
423
* Checks the existing set of constraints to see if there exist any that contain
424
* the provided free type, indicating that it is not yet ready to be replaced by
425
* one of its bounds.
426
* @param ty the free type that to check for related constraints
427
* @returns whether or not it is unsafe to replace the free type by one of its bounds
428
*/
429
bool hasUnresolvedConstraints(TypeId ty);
430
431
/** Attempts to unify subTy with superTy. If doing so would require unifying
432
* BlockedTypes, fail and block the constraint on those BlockedTypes.
433
*
434
* Note: TID can only be TypeId or TypePackId.
435
*
436
* If unification fails, replace all free types with errorType.
437
*
438
* If unification succeeds, unblock every type changed by the unification.
439
*
440
* @returns true if the unification succeeded. False if the unification was
441
* too complex.
442
*/
443
template<typename TID>
444
bool unify(NotNull<const Constraint> constraint, TID subTy, TID superTy);
445
446
/**
447
* Marks a constraint as being blocked on a type or type pack. The constraint
448
* solver will not attempt to dispatch blocked constraints until their
449
* dependencies have made progress.
450
* @param target the type or type pack pointer that the constraint is blocked on.
451
* @param constraint the constraint to block.
452
**/
453
bool block_(BlockedConstraintId target, NotNull<const Constraint> constraint);
454
455
/**
456
* Informs the solver that progress has been made on a type or type pack. The
457
* solver will wake up all constraints that are blocked on the type or type pack,
458
* and will resume attempting to dispatch them.
459
* @param progressed the type or type pack pointer that has progressed.
460
**/
461
void unblock_(BlockedConstraintId progressed);
462
463
/**
464
* Reproduces any constraints necessary for new types that are copied when applying a substitution.
465
* At the time of writing, this pertains only to type functions.
466
* @param subst the substitution that was applied
467
**/
468
void reproduceConstraints(NotNull<Scope> scope, const Location& location, const Substitution& subst);
469
470
TypeId simplifyIntersection(NotNull<Scope> scope, Location location, TypeId left, TypeId right);
471
472
TypeId simplifyIntersection(NotNull<Scope> scope, Location location, TypeIds parts);
473
474
TypeId simplifyUnion(NotNull<Scope> scope, Location location, TypeId left, TypeId right);
475
476
TypeId instantiateFunctionType(
477
TypeId functionTypeId,
478
const std::vector<TypeId>& typeArguments,
479
const std::vector<TypePackId>& typePackArguments,
480
NotNull<Scope> scope,
481
const Location& location
482
);
483
484
TypePackId anyifyModuleReturnTypePackGenerics(TypePackId tp);
485
486
void throwTimeLimitError() const;
487
void throwUserCancelError() const;
488
489
ToStringOptions opts;
490
491
void fillInDiscriminantTypes(NotNull<const Constraint> constraint, const std::vector<std::optional<TypeId>>& discriminantTypes);
492
};
493
494
/** Borrow a vector of pointers from a vector of owning pointers to constraints.
495
*/
496
std::vector<NotNull<Constraint>> borrowConstraints(const std::vector<ConstraintPtr>& constraints);
497
498
std::pair<std::vector<TypeId>, std::vector<TypePackId>> saturateArguments(
499
TypeArena* arena,
500
NotNull<BuiltinTypes> builtinTypes,
501
const TypeFun& fn,
502
const std::vector<TypeId>& rawTypeArguments,
503
const std::vector<TypePackId>& rawPackArguments
504
);
505
506
void dump(NotNull<Scope> rootScope, struct ToStringOptions& opts);
507
508
} // namespace Luau
509
510