Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Analysis/include/Luau/Constraint.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
#pragma once
3
4
#include "Luau/Ast.h" // Used for some of the enumerations
5
#include "Luau/DenseHash.h"
6
#include "Luau/NotNull.h"
7
#include "Luau/Variant.h"
8
#include "Luau/TypeFwd.h"
9
#include "Luau/TypeIds.h"
10
11
#include <string>
12
#include <memory>
13
#include <vector>
14
15
namespace Luau
16
{
17
18
enum class ValueContext;
19
struct Scope;
20
21
// if resultType is a freeType, assignmentType <: freeType <: resultType bounds
22
struct EqualityConstraint
23
{
24
TypeId resultType;
25
TypeId assignmentType;
26
};
27
28
// subType <: superType
29
struct SubtypeConstraint
30
{
31
TypeId subType;
32
TypeId superType;
33
};
34
35
// subPack <: superPack
36
struct PackSubtypeConstraint
37
{
38
TypePackId subPack;
39
TypePackId superPack;
40
41
// HACK!! TODO clip.
42
// We need to know which of `PackSubtypeConstraint` are emitted from `AstStatReturn` vs any others.
43
// Then we force these specific `PackSubtypeConstraint` to only dispatch in the order of the `return`s.
44
bool returns = false;
45
};
46
47
// generalizedType ~ gen sourceType
48
struct GeneralizationConstraint
49
{
50
TypeId generalizedType;
51
TypeId sourceType;
52
53
std::vector<TypeId> interiorTypes;
54
bool hasDeprecatedAttribute = false;
55
AstAttr::DeprecatedInfo deprecatedInfo;
56
57
/// If true, never introduce generics. Always replace free types by their
58
/// bounds or unknown. Presently used only to generalize the whole module.
59
bool noGenerics = false;
60
};
61
62
// variables ~ iterate iterator
63
// Unpack the iterator, figure out what types it iterates over, and bind those types to variables.
64
struct IterableConstraint
65
{
66
TypePackId iterator;
67
std::vector<TypeId> variables;
68
69
const AstNode* nextAstFragment;
70
DenseHashMap<const AstNode*, TypeId>* astForInNextTypes;
71
};
72
73
// name(namedType) = name
74
struct NameConstraint
75
{
76
TypeId namedType;
77
std::string name;
78
bool synthetic = false;
79
std::vector<TypeId> typeParameters;
80
std::vector<TypePackId> typePackParameters;
81
};
82
83
// target ~ inst target
84
struct TypeAliasExpansionConstraint
85
{
86
// Must be a PendingExpansionType.
87
TypeId target;
88
};
89
90
struct FunctionCallConstraint
91
{
92
TypeId fn;
93
TypePackId argsPack;
94
TypePackId result;
95
96
// callSite can be nullptr in the case that this constraint was
97
// synthetically generated from some other constraint. eg
98
// IterableConstraint.
99
class AstExprCall* callSite = nullptr;
100
std::vector<std::optional<TypeId>> discriminantTypes;
101
102
std::vector<TypeId> typeArguments;
103
std::vector<TypePackId> typePackArguments;
104
105
// When we dispatch this constraint, we update the key at this map to record
106
// the overload that we selected.
107
DenseHashMap<const AstNode*, TypeId>* astOverloadResolvedTypes = nullptr;
108
};
109
110
// function_check fn argsPack
111
//
112
// If fn is a function type and argsPack is a partially solved
113
// pack of arguments to be supplied to the function, propagate the argument
114
// types of fn into the types of argsPack. This is used to implement
115
// bidirectional inference of lambda arguments.
116
struct FunctionCheckConstraint
117
{
118
TypeId fn;
119
TypePackId argsPack;
120
121
class AstExprCall* callSite = nullptr;
122
NotNull<DenseHashMap<const AstExpr*, TypeId>> astTypes;
123
NotNull<DenseHashMap<const AstExpr*, TypeId>> astExpectedTypes;
124
};
125
126
// prim FreeType ExpectedType PrimitiveType
127
//
128
// FreeType is bounded below by the singleton type and above by PrimitiveType
129
// initially. When this constraint is resolved, it will check that the bounds
130
// of the free type are well-formed by subtyping.
131
//
132
// If they are not well-formed, then FreeType is replaced by its lower bound
133
//
134
// If they are well-formed and ExpectedType is potentially a singleton (an
135
// actual singleton or a union that contains a singleton),
136
// then FreeType is replaced by its lower bound
137
//
138
// else FreeType is replaced by PrimitiveType
139
struct PrimitiveTypeConstraint
140
{
141
TypeId freeType;
142
143
// potentially gets used to force the lower bound?
144
std::optional<TypeId> expectedType;
145
146
// the primitive type to check against
147
TypeId primitiveType;
148
};
149
150
// result ~ hasProp type "prop_name"
151
//
152
// If the subject is a table, bind the result to the named prop. If the table
153
// has an indexer, bind it to the index result type. If the subject is a union,
154
// bind the result to the union of its constituents' properties.
155
//
156
// It would be nice to get rid of this constraint and someday replace it with
157
//
158
// T <: {p: X}
159
//
160
// Where {} describes an inexact shape type.
161
struct HasPropConstraint
162
{
163
TypeId resultType;
164
TypeId subjectType;
165
std::string prop;
166
ValueContext context;
167
168
// We want to track if this `HasPropConstraint` comes from a conditional.
169
// If it does, we're going to change the behavior of property look-up a bit.
170
// In particular, we're going to return `unknownType` for property lookups
171
// on `table` or inexact table types where the property is not present.
172
//
173
// This allows us to refine table types to have additional properties
174
// without reporting errors in typechecking on the property tests.
175
bool inConditional = false;
176
177
// HACK: We presently need types like true|false or string|"hello" when
178
// deciding whether a particular literal expression should have a singleton
179
// type. This boolean is set to true when extracting the property type of a
180
// value that may be a union of tables.
181
//
182
// For example, in the following code fragment, we want the lookup of the
183
// success property to yield true|false when extracting an expectedType in
184
// this expression:
185
//
186
// type Result<T, E> = {success:true, result: T} | {success:false, error: E}
187
//
188
// local r: Result<number, string> = {success=true, result=9}
189
//
190
// If we naively simplify the expectedType to boolean, we will erroneously
191
// compute the type boolean for the success property of the table literal.
192
// This causes type checking to fail.
193
bool suppressSimplification = false;
194
};
195
196
// resultType ~ hasIndexer subjectType indexType
197
//
198
// If the subject type is a table or table-like thing that supports indexing,
199
// populate the type result with the result type of such an index operation.
200
//
201
// If the subject is not indexable, resultType is bound to errorType.
202
struct HasIndexerConstraint
203
{
204
TypeId resultType;
205
TypeId subjectType;
206
TypeId indexType;
207
};
208
209
// assignProp lhsType propName rhsType
210
//
211
// Assign a value of type rhsType into the named property of lhsType.
212
213
struct AssignPropConstraint
214
{
215
TypeId lhsType;
216
std::string propName;
217
TypeId rhsType;
218
219
/// If a new property is to be inserted into a table type, it will be
220
/// ascribed this location.
221
std::optional<Location> propLocation;
222
223
/// The canonical write type of the property. It is _solely_ used to
224
/// populate astTypes during constraint resolution. Nothing should ever
225
/// block on it.
226
TypeId propType;
227
228
// When we generate constraints, we increment the remaining prop count on
229
// the table if we are able. This flag informs the solver as to whether or
230
// not it should in turn decrement the prop count when this constraint is
231
// dispatched.
232
bool decrementPropCount = false;
233
};
234
235
struct AssignIndexConstraint
236
{
237
TypeId lhsType;
238
TypeId indexType;
239
TypeId rhsType;
240
241
/// The canonical write type of the property. It is _solely_ used to
242
/// populate astTypes during constraint resolution. Nothing should ever
243
/// block on it.
244
TypeId propType;
245
};
246
247
// resultTypes ~ unpack sourceTypePack
248
//
249
// Similar to PackSubtypeConstraint, but with one important difference: If the
250
// sourcePack is blocked, this constraint blocks.
251
struct UnpackConstraint
252
{
253
std::vector<TypeId> resultPack;
254
TypePackId sourcePack;
255
};
256
257
// ty ~ reduce ty
258
//
259
// Try to reduce ty, if it is a TypeFunctionInstanceType. Otherwise, do nothing.
260
struct ReduceConstraint
261
{
262
TypeId ty;
263
};
264
265
// tp ~ reduce tp
266
//
267
// Analogous to ReduceConstraint, but for type packs.
268
struct ReducePackConstraint
269
{
270
TypePackId tp;
271
};
272
273
// simplify ty
274
struct SimplifyConstraint
275
{
276
TypeId ty;
277
};
278
279
// push_function_type_constraint expectedFunctionType => functionType
280
//
281
// Attempt to "push" the types of `expectedFunctionType` into `functionType`,
282
// assuming that `expr` is a lambda who's un-generalized type is `functionType`.
283
// Similar to `FunctionCheckConstraint`. For example:
284
//
285
// local Foo = {} :: { bar : (number) -> () }
286
//
287
// function Foo.bar(x) end
288
//
289
// This will force `x` to be inferred as `number`.
290
struct PushFunctionTypeConstraint
291
{
292
TypeId expectedFunctionType;
293
TypeId functionType;
294
NotNull<AstExprFunction> expr;
295
bool isSelf;
296
};
297
298
// Binds the function to a set of explicitly specified types,
299
// for f<<T>>.
300
struct TypeInstantiationConstraint
301
{
302
TypeId functionType;
303
TypeId placeholderType;
304
std::vector<TypeId> typeArguments;
305
std::vector<TypePackId> typePackArguments;
306
};
307
308
struct PushTypeConstraint
309
{
310
TypeId expectedType;
311
TypeId targetType;
312
NotNull<DenseHashMap<const AstExpr*, TypeId>> astTypes;
313
NotNull<DenseHashMap<const AstExpr*, TypeId>> astExpectedTypes;
314
NotNull<const AstExpr> expr;
315
};
316
317
using ConstraintV = Variant<
318
SubtypeConstraint,
319
PackSubtypeConstraint,
320
GeneralizationConstraint,
321
IterableConstraint,
322
NameConstraint,
323
TypeAliasExpansionConstraint,
324
FunctionCallConstraint,
325
FunctionCheckConstraint,
326
PrimitiveTypeConstraint,
327
HasPropConstraint,
328
HasIndexerConstraint,
329
AssignPropConstraint,
330
AssignIndexConstraint,
331
UnpackConstraint,
332
ReduceConstraint,
333
ReducePackConstraint,
334
EqualityConstraint,
335
SimplifyConstraint,
336
PushFunctionTypeConstraint,
337
PushTypeConstraint,
338
TypeInstantiationConstraint>;
339
340
struct Constraint
341
{
342
Constraint(NotNull<Scope> scope, const Location& location, ConstraintV&& c);
343
344
Constraint(const Constraint&) = delete;
345
Constraint& operator=(const Constraint&) = delete;
346
347
NotNull<Scope> scope;
348
Location location;
349
ConstraintV c;
350
351
std::vector<NotNull<Constraint>> dependencies;
352
353
TypeIds DEPRECATED_getMaybeMutatedFreeTypes() const;
354
355
/**
356
* Return the types and type packs that may be mutated by this constraint.
357
* Currently we do not do anything with type packs.
358
*/
359
std::pair<TypeIds, TypePackIds> getMaybeMutatedTypes() const;
360
361
};
362
363
using ConstraintPtr = std::unique_ptr<Constraint>;
364
365
bool isReferenceCountedType(TypePackId tp);
366
bool isReferenceCountedType(const TypeId typ);
367
368
inline Constraint& asMutable(const Constraint& c)
369
{
370
return const_cast<Constraint&>(c);
371
}
372
373
template<typename T>
374
T* getMutable(Constraint& c)
375
{
376
return ::Luau::get_if<T>(&c.c);
377
}
378
379
template<typename T>
380
const T* get(const Constraint& c)
381
{
382
return getMutable<T>(asMutable(c));
383
}
384
385
} // namespace Luau
386
387