Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Analysis/include/Luau/Normalize.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/NotNull.h"
5
#include "Luau/Set.h"
6
#include "Luau/TypeFwd.h"
7
#include "Luau/TypeIds.h"
8
#include "Luau/UnifierSharedState.h"
9
10
#include <map>
11
#include <memory>
12
#include <unordered_map>
13
#include <vector>
14
15
namespace Luau
16
{
17
18
struct InternalErrorReporter;
19
struct Module;
20
struct Scope;
21
struct TypeFunctionRuntime;
22
23
using ModulePtr = std::shared_ptr<Module>;
24
25
bool isSubtype_DEPRECATED(
26
TypeId subTy,
27
TypeId superTy,
28
NotNull<Scope> scope,
29
NotNull<BuiltinTypes> builtinTypes,
30
InternalErrorReporter& ice,
31
SolverMode solverMode
32
);
33
34
} // namespace Luau
35
36
template<>
37
struct std::hash<Luau::TypeIds>
38
{
39
std::size_t operator()(const Luau::TypeIds& tys) const
40
{
41
return tys.getHash();
42
}
43
};
44
45
template<>
46
struct std::hash<const Luau::TypeIds*>
47
{
48
std::size_t operator()(const Luau::TypeIds* tys) const
49
{
50
return tys->getHash();
51
}
52
};
53
54
template<>
55
struct std::equal_to<Luau::TypeIds>
56
{
57
bool operator()(const Luau::TypeIds& here, const Luau::TypeIds& there) const
58
{
59
return here == there;
60
}
61
};
62
63
template<>
64
struct std::equal_to<const Luau::TypeIds*>
65
{
66
bool operator()(const Luau::TypeIds* here, const Luau::TypeIds* there) const
67
{
68
return *here == *there;
69
}
70
};
71
72
namespace Luau
73
{
74
75
/** A normalized string type is either `string` (represented by `nullopt`) or a
76
* union of string singletons.
77
*
78
* The representation is as follows:
79
*
80
* * A union of string singletons is finite and includes the singletons named by
81
* the `singletons` field.
82
* * An intersection of negated string singletons is cofinite and includes the
83
* singletons excluded by the `singletons` field. It is implied that cofinite
84
* values are exclusions from `string` itself.
85
* * The `string` data type is a cofinite set minus zero elements.
86
* * The `never` data type is a finite set plus zero elements.
87
*/
88
struct NormalizedStringType
89
{
90
// When false, this type represents a union of singleton string types.
91
// eg "a" | "b" | "c"
92
//
93
// When true, this type represents string intersected with negated string
94
// singleton types.
95
// eg string & ~"a" & ~"b" & ...
96
bool isCofinite = false;
97
98
std::map<std::string, TypeId> singletons;
99
100
void resetToString();
101
void resetToNever();
102
103
bool isNever() const;
104
bool isString() const;
105
106
/// Returns true if the string has finite domain.
107
///
108
/// Important subtlety: This method returns true for `never`. The empty set
109
/// is indeed an empty set.
110
bool isUnion() const;
111
112
/// Returns true if the string has infinite domain.
113
bool isIntersection() const;
114
115
bool includes(const std::string& str) const;
116
117
static const NormalizedStringType never;
118
119
NormalizedStringType();
120
NormalizedStringType(bool isCofinite, std::map<std::string, TypeId> singletons);
121
};
122
123
bool isSubtype(const NormalizedStringType& subStr, const NormalizedStringType& superStr);
124
125
struct NormalizedExternType
126
{
127
/** Has the following structure:
128
*
129
* (C1 & ~N11 & ... & ~Nn) | (C2 & ~N21 & ... & ~N2n) | ...
130
*
131
* C2 is either not a subtype of any other Cm, or it is and is also a
132
* subtype of one of Nmn types within the same cluster.
133
*
134
* Each TypeId is a class type.
135
*/
136
std::unordered_map<TypeId, TypeIds> externTypes;
137
138
/*
139
* We track an overall collection of shapes that extend this extern type.
140
* This should be interpreted as a big intersection of the given types.
141
*/
142
TypeIds shapeExtensions;
143
144
/**
145
* In order to maintain a consistent insertion order, we use this vector to
146
* keep track of it. An ordered std::map will sort by pointer identity,
147
* which is undesirable.
148
*/
149
std::vector<TypeId> ordering;
150
151
void pushPair(TypeId ty, TypeIds negations);
152
153
void resetToNever();
154
bool isNever() const;
155
};
156
157
// A normalized function type can be `never`, the top function type `function`,
158
// or an intersection of function types.
159
//
160
// NOTE: type normalization can fail on function types with generics (e.g.
161
// because we do not support unions and intersections of generic type packs), so
162
// this type may contain `error`.
163
struct NormalizedFunctionType
164
{
165
bool isTop = false;
166
TypeIds parts;
167
168
void resetToNever();
169
void resetToTop();
170
171
bool isNever() const;
172
};
173
174
// A normalized generic/free type is a union, where each option is of the form (X & T) where
175
// * X is either a free type, a generic or a blocked type.
176
// * T is a normalized type.
177
struct NormalizedType;
178
using NormalizedTyvars = std::unordered_map<TypeId, std::unique_ptr<NormalizedType>>;
179
180
// Operations provided by `Normalizer` can have ternary results:
181
// 1. The operation returned true.
182
// 2. The operation returned false.
183
// 3. They can hit resource limitations, which invalidates _all normalized types_.
184
enum class NormalizationResult
185
{
186
// The operation returned true or succeeded.
187
True,
188
// The operation returned false or failed.
189
False,
190
// Resource limits were hit, invalidating all normalized types.
191
HitLimits,
192
};
193
194
// A normalized type is either any, unknown, or one of the form P | T | F | G where
195
// * P is a union of primitive types (including singletons, extern types and the error type)
196
// * T is a union of table types
197
// * F is a union of an intersection of function types
198
// * G is a union of generic/free/blocked types, intersected with a normalized type
199
struct NormalizedType
200
{
201
NotNull<BuiltinTypes> builtinTypes;
202
203
// The top part of the type.
204
// This type is either never, unknown, or any.
205
// If this type is not never, all the other fields are null.
206
TypeId tops;
207
208
// The boolean part of the type.
209
// This type is either never, boolean type, or a boolean singleton.
210
TypeId booleans;
211
212
NormalizedExternType externTypes;
213
214
// The error part of the type.
215
// This type is either never or the error type.
216
TypeId errors;
217
218
// The nil part of the type.
219
// This type is either never or nil.
220
TypeId nils;
221
222
// The number part of the type.
223
// This type is either never or number.
224
TypeId numbers;
225
226
// The integer part of the type.
227
// This type is either never or integer.
228
TypeId integers;
229
230
// The string part of the type.
231
// This may be the `string` type, or a union of singletons.
232
NormalizedStringType strings;
233
234
// The thread part of the type.
235
// This type is either never or thread.
236
TypeId threads;
237
238
// The buffer part of the type.
239
// This type is either never or buffer.
240
TypeId buffers;
241
242
// The (meta)table part of the type.
243
// Each element of this set is a (meta)table type, or the top `table` type.
244
// An empty set denotes never.
245
TypeIds tables;
246
247
// The function part of the type.
248
NormalizedFunctionType functions;
249
250
// The generic/free part of the type.
251
NormalizedTyvars tyvars;
252
253
// Free types, blocked types, and certain other types change shape as type
254
// inference is done. If we were to cache the normalization of these types,
255
// we'd be reusing bad, stale data.
256
bool isCacheable = true;
257
258
explicit NormalizedType(NotNull<BuiltinTypes> builtinTypes);
259
260
NormalizedType() = delete;
261
~NormalizedType() = default;
262
263
NormalizedType(const NormalizedType&) = delete;
264
NormalizedType& operator=(const NormalizedType&) = delete;
265
266
NormalizedType(NormalizedType&&) = default;
267
NormalizedType& operator=(NormalizedType&&) = default;
268
269
// IsType functions
270
bool isUnknown() const;
271
/// Returns true if the type is exactly a number. Behaves like Type::isNumber()
272
bool isExactlyNumber() const;
273
274
/// Returns true if the type is a subtype of string(it could be a singleton). Behaves like Type::isString()
275
bool isSubtypeOfString() const;
276
277
/// Returns true if the type is a subtype of boolean(it could be a singleton). Behaves like Type::isBoolean()
278
bool isSubtypeOfBooleans() const;
279
280
/// Returns true if this type should result in error suppressing behavior.
281
bool shouldSuppressErrors() const;
282
283
/// Returns true if this type contains the primitive top table type, `table`.
284
bool hasTopTable() const;
285
286
/// Returns true if this type is `nil` or `nil | *error-type*`
287
bool isNil() const;
288
289
// Helpers that improve readability of the above (they just say if the component is present)
290
bool hasTops() const;
291
bool hasBooleans() const;
292
bool hasExternTypes() const;
293
bool hasErrors() const;
294
bool hasNils() const;
295
bool hasNumbers() const;
296
bool hasIntegers() const;
297
bool hasStrings() const;
298
bool hasThreads() const;
299
bool hasBuffers() const;
300
bool hasTables() const;
301
bool hasFunctions() const;
302
bool hasTyvars() const;
303
304
bool isFalsy() const;
305
bool isTruthy() const;
306
};
307
308
309
using SeenTablePropPairs = Set<std::pair<TypeId, TypeId>, TypeIdPairHash>;
310
311
class Normalizer
312
{
313
std::unordered_map<TypeId, std::shared_ptr<NormalizedType>> cachedNormals;
314
std::unordered_map<const TypeIds*, TypeId> cachedIntersections;
315
std::unordered_map<const TypeIds*, TypeId> cachedUnions;
316
std::unordered_map<const TypeIds*, std::unique_ptr<TypeIds>> cachedTypeIds;
317
318
DenseHashMap<TypeId, bool> cachedIsInhabited{nullptr};
319
DenseHashMap<std::pair<TypeId, TypeId>, bool, TypeIdPairHash> cachedIsInhabitedIntersection{{nullptr, nullptr}};
320
321
std::optional<int> fuel{std::nullopt};
322
323
bool withinResourceLimits();
324
bool useNewLuauSolver() const;
325
326
public:
327
TypeArena* arena;
328
NotNull<BuiltinTypes> builtinTypes;
329
NotNull<UnifierSharedState> sharedState;
330
bool cacheInhabitance = false;
331
SolverMode solverMode;
332
Normalizer(
333
TypeArena* arena,
334
NotNull<BuiltinTypes> builtinTypes,
335
NotNull<UnifierSharedState> sharedState,
336
SolverMode solver,
337
bool cacheInhabitance = false
338
);
339
Normalizer(const Normalizer&) = delete;
340
Normalizer(Normalizer&&) = delete;
341
Normalizer() = delete;
342
~Normalizer() = default;
343
Normalizer& operator=(Normalizer&&) = delete;
344
Normalizer& operator=(Normalizer&) = delete;
345
346
// If this returns null, the typechecker should emit a "too complex" error
347
std::shared_ptr<const NormalizedType> normalize(TypeId ty);
348
349
void clearCaches();
350
351
NormalizationResult isIntersectionInhabited(TypeId left, TypeId right);
352
353
// Check for inhabitance
354
NormalizationResult isInhabited(TypeId ty);
355
NormalizationResult isInhabited(const NormalizedType* norm);
356
357
// -------- Convert back from a normalized type to a type
358
TypeId typeFromNormal(const NormalizedType& norm);
359
360
std::optional<TypePackId> intersectionOfTypePacks(TypePackId here, TypePackId there);
361
362
private:
363
std::optional<TypePackId> intersectionOfTypePacks_INTERNAL(TypePackId here, TypePackId there);
364
365
// ------- Cached TypeIds
366
TypeId unionType(TypeId here, TypeId there);
367
TypeId intersectionType(TypeId here, TypeId there);
368
const TypeIds* cacheTypeIds(TypeIds tys);
369
void clearNormal(NormalizedType& norm);
370
371
// ------- Normalizing unions
372
void unionTysWithTy(TypeIds& here, TypeId there);
373
TypeId unionOfTops(TypeId here, TypeId there);
374
TypeId unionOfBools(TypeId here, TypeId there);
375
void unionExternTypesWithExternType(TypeIds& heres, TypeId there);
376
void unionExternTypes(TypeIds& heres, const TypeIds& theres);
377
void unionExternTypesWithExternType(NormalizedExternType& heres, TypeId there);
378
void unionExternTypes(NormalizedExternType& heres, const NormalizedExternType& theres);
379
void unionStrings(NormalizedStringType& here, const NormalizedStringType& there);
380
std::optional<TypePackId> unionOfTypePacks(TypePackId here, TypePackId there);
381
std::optional<TypeId> unionOfFunctions(TypeId here, TypeId there);
382
std::optional<TypeId> unionSaturatedFunctions(TypeId here, TypeId there);
383
void unionFunctionsWithFunction(NormalizedFunctionType& heress, TypeId there);
384
void unionFunctions(NormalizedFunctionType& heress, const NormalizedFunctionType& theress);
385
void unionTablesWithTable(TypeIds& heres, TypeId there);
386
void unionTables(TypeIds& heres, const TypeIds& theres);
387
NormalizationResult unionNormals(NormalizedType& here, const NormalizedType& there, int ignoreSmallerTyvars = -1);
388
NormalizationResult unionNormalWithTy(
389
NormalizedType& here,
390
TypeId there,
391
SeenTablePropPairs& seenTablePropPairs,
392
Set<TypeId>& seenSetTypes,
393
int ignoreSmallerTyvars = -1
394
);
395
396
// ------- Negations
397
std::optional<NormalizedType> negateNormal(const NormalizedType& here);
398
TypeIds negateAll(const TypeIds& theres);
399
TypeId negate(TypeId there);
400
void subtractPrimitive(NormalizedType& here, TypeId ty);
401
void subtractSingleton(NormalizedType& here, TypeId ty);
402
NormalizationResult intersectNormalWithNegationTy(TypeId toNegate, NormalizedType& intersect);
403
404
// ------- Normalizing intersections
405
TypeId intersectionOfTops(TypeId here, TypeId there);
406
TypeId intersectionOfBools(TypeId here, TypeId there);
407
void intersectExternTypes(NormalizedExternType& heres, const NormalizedExternType& theres);
408
void intersectExternTypesWithExternType(NormalizedExternType& heres, TypeId there);
409
void intersectExternTypesWithShape(NormalizedExternType& heres, TypeId there);
410
void intersectStrings(NormalizedStringType& here, const NormalizedStringType& there);
411
std::optional<TypeId> intersectionOfTables(TypeId here, TypeId there, SeenTablePropPairs& seenTablePropPairs, Set<TypeId>& seenSet);
412
void intersectTablesWithTable(TypeIds& heres, TypeId there, SeenTablePropPairs& seenTablePropPairs, Set<TypeId>& seenSetTypes);
413
void intersectTables(TypeIds& heres, const TypeIds& theres);
414
std::optional<TypeId> intersectionOfFunctions(TypeId here, TypeId there);
415
void intersectFunctionsWithFunction(NormalizedFunctionType& heress, TypeId there);
416
void intersectFunctions(NormalizedFunctionType& heress, const NormalizedFunctionType& theress);
417
NormalizationResult intersectTyvarsWithTy(
418
NormalizedTyvars& here,
419
TypeId there,
420
SeenTablePropPairs& seenTablePropPairs,
421
Set<TypeId>& seenSetTypes
422
);
423
NormalizationResult intersectNormals(NormalizedType& here, const NormalizedType& there, int ignoreSmallerTyvars = -1);
424
NormalizationResult intersectNormalWithTy(NormalizedType& here, TypeId there, SeenTablePropPairs& seenTablePropPairs, Set<TypeId>& seenSetTypes);
425
NormalizationResult normalizeIntersections(
426
const std::vector<TypeId>& intersections,
427
NormalizedType& outType,
428
SeenTablePropPairs& seenTablePropPairs,
429
Set<TypeId>& seenSet
430
);
431
432
NormalizationResult isInhabited(TypeId ty, Set<TypeId>& seen);
433
NormalizationResult isInhabited(const NormalizedType* norm, Set<TypeId>& seen);
434
435
// Check for intersections being inhabited
436
NormalizationResult isIntersectionInhabited(TypeId left, TypeId right, SeenTablePropPairs& seenTablePropPairs, Set<TypeId>& seenSet);
437
438
439
// Fuel setup
440
441
bool initializeFuel();
442
void clearFuel();
443
void consumeFuel();
444
445
friend struct FuelInitializer;
446
};
447
448
bool isSubtype(
449
TypeId subTy,
450
TypeId superTy,
451
NotNull<TypeArena> arena,
452
NotNull<BuiltinTypes> builtinTypes,
453
NotNull<Scope> scope,
454
NotNull<Normalizer> normalizer,
455
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
456
NotNull<InternalErrorReporter> reporter
457
);
458
459
460
} // namespace Luau
461
462