Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Analysis/src/Clone.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/Clone.h"
3
4
#include "Luau/Common.h"
5
#include "Luau/NotNull.h"
6
#include "Luau/Type.h"
7
#include "Luau/TypeOrPack.h"
8
#include "Luau/TypePack.h"
9
#include "Luau/TypeUtils.h"
10
#include "Luau/Unifiable.h"
11
#include "Luau/VisitType.h"
12
13
LUAU_FASTFLAG(LuauSolverV2)
14
15
// For each `Luau::clone` call, we will clone only up to N amount of types _and_ packs, as controlled by this limit.
16
LUAU_FASTINTVARIABLE(LuauTypeCloneIterationLimit, 100'000)
17
18
namespace Luau
19
{
20
21
namespace
22
{
23
24
class TypeCloner
25
{
26
27
protected:
28
NotNull<TypeArena> arena;
29
NotNull<BuiltinTypes> builtinTypes;
30
31
// A queue of kinds where we cloned it, but whose interior types hasn't
32
// been updated to point to new clones. Once all of its interior types
33
// has been updated, it gets removed from the queue.
34
std::vector<TypeOrPack> queue;
35
36
NotNull<SeenTypes> types;
37
NotNull<SeenTypePacks> packs;
38
39
TypeId forceTy = nullptr;
40
TypePackId forceTp = nullptr;
41
42
int steps = 0;
43
44
public:
45
TypeCloner(
46
NotNull<TypeArena> arena,
47
NotNull<BuiltinTypes> builtinTypes,
48
NotNull<SeenTypes> types,
49
NotNull<SeenTypePacks> packs,
50
TypeId forceTy,
51
TypePackId forceTp
52
)
53
: arena(arena)
54
, builtinTypes(builtinTypes)
55
, types(types)
56
, packs(packs)
57
, forceTy(forceTy)
58
, forceTp(forceTp)
59
{
60
}
61
62
virtual ~TypeCloner() = default;
63
64
TypeId clone(TypeId ty)
65
{
66
shallowClone(ty);
67
run();
68
69
if (hasExceededIterationLimit())
70
{
71
TypeId error = builtinTypes->errorType;
72
(*types)[ty] = error;
73
return error;
74
}
75
76
return find(ty).value_or(builtinTypes->errorType);
77
}
78
79
TypePackId clone(TypePackId tp)
80
{
81
shallowClone(tp);
82
run();
83
84
if (hasExceededIterationLimit())
85
{
86
TypePackId error = builtinTypes->errorTypePack;
87
(*packs)[tp] = error;
88
return error;
89
}
90
91
return find(tp).value_or(builtinTypes->errorTypePack);
92
}
93
94
private:
95
bool hasExceededIterationLimit() const
96
{
97
if (FInt::LuauTypeCloneIterationLimit == 0)
98
return false;
99
100
return steps + queue.size() >= size_t(FInt::LuauTypeCloneIterationLimit);
101
}
102
103
void run()
104
{
105
while (!queue.empty())
106
{
107
++steps;
108
109
if (hasExceededIterationLimit())
110
break;
111
112
TypeOrPack kind = queue.back();
113
queue.pop_back();
114
115
if (find(kind))
116
continue;
117
118
cloneChildren(kind);
119
}
120
}
121
122
protected:
123
std::optional<TypeId> find(TypeId ty) const
124
{
125
ty = follow(ty, FollowOption::DisableLazyTypeThunks);
126
if (auto it = types->find(ty); it != types->end())
127
return it->second;
128
else if (ty->persistent && ty != forceTy)
129
return ty;
130
return std::nullopt;
131
}
132
133
std::optional<TypePackId> find(TypePackId tp) const
134
{
135
tp = follow(tp);
136
if (auto it = packs->find(tp); it != packs->end())
137
return it->second;
138
else if (tp->persistent && tp != forceTp)
139
return tp;
140
return std::nullopt;
141
}
142
143
std::optional<TypeOrPack> find(TypeOrPack kind) const
144
{
145
if (auto ty = get<TypeId>(kind))
146
return find(*ty);
147
else if (auto tp = get<TypePackId>(kind))
148
return find(*tp);
149
else
150
{
151
LUAU_ASSERT(!"Unknown kind?");
152
return std::nullopt;
153
}
154
}
155
156
public:
157
virtual TypeId shallowClone(TypeId ty)
158
{
159
// We want to [`Luau::follow`] but without forcing the expansion of [`LazyType`]s.
160
ty = follow(ty, FollowOption::DisableLazyTypeThunks);
161
162
if (auto clone = find(ty))
163
return *clone;
164
else if (ty->persistent && ty != forceTy)
165
return ty;
166
167
TypeId target = arena->addType(ty->ty);
168
asMutable(target)->documentationSymbol = ty->documentationSymbol;
169
170
if (auto generic = getMutable<GenericType>(target))
171
generic->scope = nullptr;
172
else if (auto free = getMutable<FreeType>(target))
173
free->scope = nullptr;
174
else if (auto table = getMutable<TableType>(target))
175
table->scope = nullptr;
176
177
(*types)[ty] = target;
178
queue.push_back(target);
179
return target;
180
}
181
182
virtual TypePackId shallowClone(TypePackId tp)
183
{
184
tp = follow(tp);
185
186
if (auto clone = find(tp))
187
return *clone;
188
else if (tp->persistent && tp != forceTp)
189
return tp;
190
191
TypePackId target = arena->addTypePack(tp->ty);
192
193
if (auto generic = getMutable<GenericTypePack>(target))
194
generic->scope = nullptr;
195
else if (auto free = getMutable<FreeTypePack>(target))
196
free->scope = nullptr;
197
198
(*packs)[tp] = target;
199
queue.push_back(target);
200
return target;
201
}
202
203
private:
204
Property shallowClone(const Property& p)
205
{
206
std::optional<TypeId> cloneReadTy;
207
if (auto ty = p.readTy)
208
cloneReadTy = shallowClone(*ty);
209
210
std::optional<TypeId> cloneWriteTy;
211
if (auto ty = p.writeTy)
212
cloneWriteTy = shallowClone(*ty);
213
214
Property cloned = Property::create(cloneReadTy, cloneWriteTy);
215
cloned.deprecated = p.deprecated;
216
cloned.deprecatedSuggestion = p.deprecatedSuggestion;
217
cloned.location = p.location;
218
cloned.tags = p.tags;
219
cloned.documentationSymbol = p.documentationSymbol;
220
cloned.typeLocation = p.typeLocation;
221
return cloned;
222
}
223
224
void cloneChildren(TypeId ty)
225
{
226
return visit(
227
[&](auto&& t)
228
{
229
return cloneChildren(&t);
230
},
231
asMutable(ty)->ty
232
);
233
}
234
235
void cloneChildren(TypePackId tp)
236
{
237
return visit(
238
[&](auto&& t)
239
{
240
return cloneChildren(&t);
241
},
242
asMutable(tp)->ty
243
);
244
}
245
246
void cloneChildren(TypeOrPack kind)
247
{
248
if (auto ty = get<TypeId>(kind))
249
return cloneChildren(*ty);
250
else if (auto tp = get<TypePackId>(kind))
251
return cloneChildren(*tp);
252
else
253
LUAU_ASSERT(!"Item holds neither TypeId nor TypePackId when enqueuing its children?");
254
}
255
256
void cloneChildren(ErrorType* t)
257
{
258
// noop.
259
}
260
261
void cloneChildren(BoundType* t)
262
{
263
t->boundTo = shallowClone(t->boundTo);
264
}
265
266
void cloneChildren(FreeType* t)
267
{
268
if (t->lowerBound)
269
t->lowerBound = shallowClone(t->lowerBound);
270
if (t->upperBound)
271
t->upperBound = shallowClone(t->upperBound);
272
}
273
274
void cloneChildren(GenericType* t)
275
{
276
// TODO: clone upper bounds.
277
}
278
279
void cloneChildren(PrimitiveType* t)
280
{
281
// noop.
282
}
283
284
void cloneChildren(BlockedType* t)
285
{
286
// TODO: In the new solver, we should ice.
287
}
288
289
void cloneChildren(PendingExpansionType* t)
290
{
291
// TODO: In the new solver, we should ice.
292
}
293
294
void cloneChildren(SingletonType* t)
295
{
296
// noop.
297
}
298
299
void cloneChildren(FunctionType* t)
300
{
301
for (TypeId& g : t->generics)
302
g = shallowClone(g);
303
304
for (TypePackId& gp : t->genericPacks)
305
gp = shallowClone(gp);
306
307
t->argTypes = shallowClone(t->argTypes);
308
t->retTypes = shallowClone(t->retTypes);
309
}
310
311
void cloneChildren(TableType* t)
312
{
313
if (t->indexer)
314
{
315
t->indexer->indexType = shallowClone(t->indexer->indexType);
316
t->indexer->indexResultType = shallowClone(t->indexer->indexResultType);
317
}
318
319
for (auto& [_, p] : t->props)
320
p = shallowClone(p);
321
322
for (TypeId& ty : t->instantiatedTypeParams)
323
ty = shallowClone(ty);
324
325
for (TypePackId& tp : t->instantiatedTypePackParams)
326
tp = shallowClone(tp);
327
}
328
329
void cloneChildren(MetatableType* t)
330
{
331
t->table = shallowClone(t->table);
332
t->metatable = shallowClone(t->metatable);
333
}
334
335
void cloneChildren(ExternType* t)
336
{
337
for (auto& [_, p] : t->props)
338
p = shallowClone(p);
339
340
if (t->parent)
341
t->parent = shallowClone(*t->parent);
342
343
if (t->metatable)
344
t->metatable = shallowClone(*t->metatable);
345
346
if (t->indexer)
347
{
348
t->indexer->indexType = shallowClone(t->indexer->indexType);
349
t->indexer->indexResultType = shallowClone(t->indexer->indexResultType);
350
}
351
}
352
353
void cloneChildren(AnyType* t)
354
{
355
// noop.
356
}
357
358
void cloneChildren(NoRefineType* t)
359
{
360
// noop.
361
}
362
363
void cloneChildren(UnionType* t)
364
{
365
for (TypeId& ty : t->options)
366
ty = shallowClone(ty);
367
}
368
369
void cloneChildren(IntersectionType* t)
370
{
371
for (TypeId& ty : t->parts)
372
ty = shallowClone(ty);
373
}
374
375
virtual void cloneChildren(LazyType* t)
376
{
377
if (auto unwrapped = t->unwrapped.load())
378
t->unwrapped.store(shallowClone(unwrapped));
379
}
380
381
void cloneChildren(UnknownType* t)
382
{
383
// noop.
384
}
385
386
void cloneChildren(NeverType* t)
387
{
388
// noop.
389
}
390
391
void cloneChildren(NegationType* t)
392
{
393
t->ty = shallowClone(t->ty);
394
}
395
396
void cloneChildren(TypeFunctionInstanceType* t)
397
{
398
for (TypeId& ty : t->typeArguments)
399
ty = shallowClone(ty);
400
401
for (TypePackId& tp : t->packArguments)
402
tp = shallowClone(tp);
403
}
404
405
void cloneChildren(FreeTypePack* t)
406
{
407
// TODO: clone lower and upper bounds.
408
// TODO: In the new solver, we should ice.
409
}
410
411
void cloneChildren(GenericTypePack* t)
412
{
413
// TODO: clone upper bounds.
414
}
415
416
void cloneChildren(BlockedTypePack* t)
417
{
418
// TODO: In the new solver, we should ice.
419
}
420
421
void cloneChildren(BoundTypePack* t)
422
{
423
t->boundTo = shallowClone(t->boundTo);
424
}
425
426
void cloneChildren(ErrorTypePack* t)
427
{
428
// noop.
429
}
430
431
void cloneChildren(VariadicTypePack* t)
432
{
433
t->ty = shallowClone(t->ty);
434
}
435
436
void cloneChildren(TypePack* t)
437
{
438
for (TypeId& ty : t->head)
439
ty = shallowClone(ty);
440
441
if (t->tail)
442
t->tail = shallowClone(*t->tail);
443
}
444
445
void cloneChildren(TypeFunctionInstanceTypePack* t)
446
{
447
for (TypeId& ty : t->typeArguments)
448
ty = shallowClone(ty);
449
450
for (TypePackId& tp : t->packArguments)
451
tp = shallowClone(tp);
452
}
453
};
454
455
class FragmentAutocompleteTypeCloner final : public TypeCloner
456
{
457
Scope* replacementForNullScope = nullptr;
458
459
public:
460
FragmentAutocompleteTypeCloner(
461
NotNull<TypeArena> arena,
462
NotNull<BuiltinTypes> builtinTypes,
463
NotNull<SeenTypes> types,
464
NotNull<SeenTypePacks> packs,
465
TypeId forceTy,
466
TypePackId forceTp,
467
Scope* replacementForNullScope
468
)
469
: TypeCloner(arena, builtinTypes, types, packs, forceTy, forceTp)
470
, replacementForNullScope(replacementForNullScope)
471
{
472
LUAU_ASSERT(replacementForNullScope);
473
}
474
475
TypeId shallowClone(TypeId ty) override
476
{
477
// We want to [`Luau::follow`] but without forcing the expansion of [`LazyType`]s.
478
ty = follow(ty, FollowOption::DisableLazyTypeThunks);
479
480
if (auto clone = find(ty))
481
return *clone;
482
else if (ty->persistent && ty != forceTy)
483
return ty;
484
485
TypeId target = arena->addType(ty->ty);
486
asMutable(target)->documentationSymbol = ty->documentationSymbol;
487
488
if (auto generic = getMutable<GenericType>(target))
489
generic->scope = nullptr;
490
else if (auto free = getMutable<FreeType>(target))
491
{
492
free->scope = replacementForNullScope;
493
}
494
else if (auto tt = getMutable<TableType>(target))
495
tt->scope = replacementForNullScope;
496
497
(*types)[ty] = target;
498
queue.emplace_back(target);
499
return target;
500
}
501
502
TypePackId shallowClone(TypePackId tp) override
503
{
504
tp = follow(tp);
505
506
if (auto clone = find(tp))
507
return *clone;
508
else if (tp->persistent && tp != forceTp)
509
return tp;
510
511
TypePackId target = arena->addTypePack(tp->ty);
512
513
if (auto generic = getMutable<GenericTypePack>(target))
514
generic->scope = nullptr;
515
else if (auto free = getMutable<FreeTypePack>(target))
516
free->scope = replacementForNullScope;
517
518
(*packs)[tp] = target;
519
queue.emplace_back(target);
520
return target;
521
}
522
523
void cloneChildren(LazyType* t) override
524
{
525
// Do not clone lazy types
526
}
527
};
528
529
530
} // namespace
531
532
TypePackId shallowClone(TypePackId tp, TypeArena& dest, CloneState& cloneState, bool clonePersistentTypes)
533
{
534
if (tp->persistent && !clonePersistentTypes)
535
return tp;
536
537
TypeCloner cloner{
538
NotNull{&dest},
539
cloneState.builtinTypes,
540
NotNull{&cloneState.seenTypes},
541
NotNull{&cloneState.seenTypePacks},
542
nullptr,
543
clonePersistentTypes ? tp : nullptr
544
};
545
546
return cloner.shallowClone(tp);
547
}
548
549
TypeId shallowClone(TypeId typeId, TypeArena& dest, CloneState& cloneState, bool clonePersistentTypes)
550
{
551
if (typeId->persistent && !clonePersistentTypes)
552
return typeId;
553
554
TypeCloner cloner{
555
NotNull{&dest},
556
cloneState.builtinTypes,
557
NotNull{&cloneState.seenTypes},
558
NotNull{&cloneState.seenTypePacks},
559
clonePersistentTypes ? typeId : nullptr,
560
nullptr
561
};
562
563
return cloner.shallowClone(typeId);
564
}
565
566
TypePackId clone(TypePackId tp, TypeArena& dest, CloneState& cloneState)
567
{
568
if (tp->persistent)
569
return tp;
570
571
TypeCloner cloner{NotNull{&dest}, cloneState.builtinTypes, NotNull{&cloneState.seenTypes}, NotNull{&cloneState.seenTypePacks}, nullptr, nullptr};
572
return cloner.clone(tp);
573
}
574
575
TypeId clone(TypeId typeId, TypeArena& dest, CloneState& cloneState)
576
{
577
if (typeId->persistent)
578
return typeId;
579
580
TypeCloner cloner{NotNull{&dest}, cloneState.builtinTypes, NotNull{&cloneState.seenTypes}, NotNull{&cloneState.seenTypePacks}, nullptr, nullptr};
581
return cloner.clone(typeId);
582
}
583
584
TypeFun clone(const TypeFun& typeFun, TypeArena& dest, CloneState& cloneState)
585
{
586
TypeCloner cloner{NotNull{&dest}, cloneState.builtinTypes, NotNull{&cloneState.seenTypes}, NotNull{&cloneState.seenTypePacks}, nullptr, nullptr};
587
588
TypeFun copy = typeFun;
589
590
for (auto& param : copy.typeParams)
591
{
592
param.ty = cloner.clone(param.ty);
593
594
if (param.defaultValue)
595
param.defaultValue = cloner.clone(*param.defaultValue);
596
}
597
598
for (auto& param : copy.typePackParams)
599
{
600
param.tp = cloner.clone(param.tp);
601
602
if (param.defaultValue)
603
param.defaultValue = cloner.clone(*param.defaultValue);
604
}
605
606
copy.type = cloner.clone(copy.type);
607
608
return copy;
609
}
610
611
Binding clone(const Binding& binding, TypeArena& dest, CloneState& cloneState)
612
{
613
TypeCloner cloner{NotNull{&dest}, cloneState.builtinTypes, NotNull{&cloneState.seenTypes}, NotNull{&cloneState.seenTypePacks}, nullptr, nullptr};
614
615
Binding b;
616
b.deprecated = binding.deprecated;
617
b.deprecatedSuggestion = binding.deprecatedSuggestion;
618
b.documentationSymbol = binding.documentationSymbol;
619
b.location = binding.location;
620
b.typeId = cloner.clone(binding.typeId);
621
622
return b;
623
}
624
625
TypePackId cloneIncremental(TypePackId tp, TypeArena& dest, CloneState& cloneState, Scope* freshScopeForFreeTypes)
626
{
627
if (tp->persistent)
628
return tp;
629
630
FragmentAutocompleteTypeCloner cloner{
631
NotNull{&dest},
632
cloneState.builtinTypes,
633
NotNull{&cloneState.seenTypes},
634
NotNull{&cloneState.seenTypePacks},
635
nullptr,
636
nullptr,
637
freshScopeForFreeTypes
638
};
639
return cloner.clone(tp);
640
}
641
642
TypeId cloneIncremental(TypeId typeId, TypeArena& dest, CloneState& cloneState, Scope* freshScopeForFreeTypes)
643
{
644
if (typeId->persistent)
645
return typeId;
646
647
FragmentAutocompleteTypeCloner cloner{
648
NotNull{&dest},
649
cloneState.builtinTypes,
650
NotNull{&cloneState.seenTypes},
651
NotNull{&cloneState.seenTypePacks},
652
nullptr,
653
nullptr,
654
freshScopeForFreeTypes
655
};
656
return cloner.clone(typeId);
657
}
658
659
TypeFun cloneIncremental(const TypeFun& typeFun, TypeArena& dest, CloneState& cloneState, Scope* freshScopeForFreeTypes)
660
{
661
FragmentAutocompleteTypeCloner cloner{
662
NotNull{&dest},
663
cloneState.builtinTypes,
664
NotNull{&cloneState.seenTypes},
665
NotNull{&cloneState.seenTypePacks},
666
nullptr,
667
nullptr,
668
freshScopeForFreeTypes
669
};
670
671
TypeFun copy = typeFun;
672
673
for (auto& param : copy.typeParams)
674
{
675
param.ty = cloner.clone(param.ty);
676
677
if (param.defaultValue)
678
param.defaultValue = cloner.clone(*param.defaultValue);
679
}
680
681
for (auto& param : copy.typePackParams)
682
{
683
param.tp = cloner.clone(param.tp);
684
685
if (param.defaultValue)
686
param.defaultValue = cloner.clone(*param.defaultValue);
687
}
688
689
copy.type = cloner.clone(copy.type);
690
691
return copy;
692
}
693
694
Binding cloneIncremental(const Binding& binding, TypeArena& dest, CloneState& cloneState, Scope* freshScopeForFreeTypes)
695
{
696
FragmentAutocompleteTypeCloner cloner{
697
NotNull{&dest},
698
cloneState.builtinTypes,
699
NotNull{&cloneState.seenTypes},
700
NotNull{&cloneState.seenTypePacks},
701
nullptr,
702
nullptr,
703
freshScopeForFreeTypes
704
};
705
706
Binding b;
707
b.deprecated = binding.deprecated;
708
b.deprecatedSuggestion = binding.deprecatedSuggestion;
709
b.documentationSymbol = binding.documentationSymbol;
710
b.location = binding.location;
711
b.typeId = binding.typeId->persistent ? binding.typeId : cloner.clone(binding.typeId);
712
713
return b;
714
}
715
716
717
} // namespace Luau
718
719