Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Analysis/src/Substitution.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/Substitution.h"
3
4
#include "Luau/Common.h"
5
#include "Luau/TxnLog.h"
6
#include "Luau/Type.h"
7
8
#include <algorithm>
9
10
LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000)
11
LUAU_FASTFLAG(LuauSolverV2)
12
LUAU_FASTINTVARIABLE(LuauTarjanPreallocationSize, 256)
13
LUAU_FASTFLAG(LuauAnalysisUsesSolverMode)
14
15
namespace Luau
16
{
17
18
static TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log)
19
{
20
auto go = [ty, &dest](auto&& a)
21
{
22
using T = std::decay_t<decltype(a)>;
23
24
// The pointer identities of free and local types is very important.
25
// We decline to copy them.
26
if constexpr (std::is_same_v<T, FreeType>)
27
return ty;
28
else if constexpr (std::is_same_v<T, BoundType>)
29
{
30
// This should never happen, but visit() cannot see it.
31
LUAU_ASSERT(!"shallowClone didn't follow its argument!");
32
return dest.addType(BoundType{a.boundTo});
33
}
34
else if constexpr (std::is_same_v<T, GenericType>)
35
return dest.addType(a);
36
else if constexpr (std::is_same_v<T, BlockedType>)
37
return dest.addType(a);
38
else if constexpr (std::is_same_v<T, PrimitiveType>)
39
{
40
LUAU_ASSERT(ty->persistent);
41
return ty;
42
}
43
else if constexpr (std::is_same_v<T, PendingExpansionType>)
44
{
45
PendingExpansionType clone = PendingExpansionType{a.prefix, a.name, a.typeArguments, a.packArguments};
46
return dest.addType(std::move(clone));
47
}
48
else if constexpr (std::is_same_v<T, AnyType>)
49
{
50
LUAU_ASSERT(ty->persistent);
51
return ty;
52
}
53
else if constexpr (std::is_same_v<T, NoRefineType>)
54
{
55
LUAU_ASSERT(ty->persistent);
56
return ty;
57
}
58
else if constexpr (std::is_same_v<T, ErrorType>)
59
{
60
LUAU_ASSERT(ty->persistent || a.synthetic);
61
62
if (ty->persistent)
63
return ty;
64
65
// While this code intentionally works (and clones) even if `a.synthetic` is `std::nullopt`,
66
// we still assert above because we consider it a bug to have a non-persistent error type
67
// without any associated metadata. We should always use the persistent version in such cases.
68
ErrorType clone = ErrorType{};
69
clone.synthetic = a.synthetic;
70
return dest.addType(clone);
71
}
72
else if constexpr (std::is_same_v<T, UnknownType>)
73
{
74
LUAU_ASSERT(ty->persistent);
75
return ty;
76
}
77
else if constexpr (std::is_same_v<T, NeverType>)
78
{
79
LUAU_ASSERT(ty->persistent);
80
return ty;
81
}
82
else if constexpr (std::is_same_v<T, LazyType>)
83
return ty;
84
else if constexpr (std::is_same_v<T, SingletonType>)
85
return dest.addType(a);
86
else if constexpr (std::is_same_v<T, FunctionType>)
87
{
88
FunctionType clone = FunctionType{a.level, a.argTypes, a.retTypes, a.definition, a.hasSelf};
89
clone.generics = a.generics;
90
clone.genericPacks = a.genericPacks;
91
clone.magic = a.magic;
92
clone.tags = a.tags;
93
clone.argNames = a.argNames;
94
clone.isCheckedFunction = a.isCheckedFunction;
95
clone.isDeprecatedFunction = a.isDeprecatedFunction;
96
clone.deprecatedInfo = a.deprecatedInfo;
97
return dest.addType(std::move(clone));
98
}
99
else if constexpr (std::is_same_v<T, TableType>)
100
{
101
LUAU_ASSERT(!a.boundTo);
102
TableType clone = TableType{a.props, a.indexer, a.level, a.scope, a.state};
103
clone.definitionModuleName = a.definitionModuleName;
104
clone.definitionLocation = a.definitionLocation;
105
clone.name = a.name;
106
clone.syntheticName = a.syntheticName;
107
clone.instantiatedTypeParams = a.instantiatedTypeParams;
108
clone.instantiatedTypePackParams = a.instantiatedTypePackParams;
109
clone.tags = a.tags;
110
return dest.addType(std::move(clone));
111
}
112
else if constexpr (std::is_same_v<T, MetatableType>)
113
{
114
MetatableType clone = MetatableType{a.table, a.metatable};
115
clone.syntheticName = a.syntheticName;
116
return dest.addType(std::move(clone));
117
}
118
else if constexpr (std::is_same_v<T, UnionType>)
119
{
120
UnionType clone;
121
clone.options = a.options;
122
return dest.addType(std::move(clone));
123
}
124
else if constexpr (std::is_same_v<T, IntersectionType>)
125
{
126
IntersectionType clone;
127
clone.parts = a.parts;
128
return dest.addType(std::move(clone));
129
}
130
else if constexpr (std::is_same_v<T, ExternType>)
131
{
132
ExternType clone{a.name, a.props, a.parent, a.metatable, a.tags, a.userData, a.definitionModuleName, a.definitionLocation, a.indexer};
133
return dest.addType(std::move(clone));
134
}
135
else if constexpr (std::is_same_v<T, NegationType>)
136
return dest.addType(NegationType{a.ty});
137
else if constexpr (std::is_same_v<T, TypeFunctionInstanceType>)
138
{
139
TypeFunctionInstanceType clone{a.function, a.typeArguments, a.packArguments, a.userFuncName, a.userFuncData};
140
return dest.addType(std::move(clone));
141
}
142
else
143
static_assert(always_false_v<T>, "Non-exhaustive shallowClone switch");
144
};
145
146
ty = log->follow(ty);
147
148
if (auto pty = log->pending(ty))
149
ty = &pty->pending;
150
151
TypeId resTy = visit(go, ty->ty);
152
if (resTy != ty)
153
asMutable(resTy)->documentationSymbol = ty->documentationSymbol;
154
155
return resTy;
156
}
157
158
Tarjan::Tarjan()
159
: typeToIndex(nullptr, FInt::LuauTarjanPreallocationSize)
160
, packToIndex(nullptr, FInt::LuauTarjanPreallocationSize)
161
{
162
nodes.reserve(FInt::LuauTarjanPreallocationSize);
163
stack.reserve(FInt::LuauTarjanPreallocationSize);
164
edgesTy.reserve(FInt::LuauTarjanPreallocationSize);
165
edgesTp.reserve(FInt::LuauTarjanPreallocationSize);
166
worklist.reserve(FInt::LuauTarjanPreallocationSize);
167
}
168
169
void Tarjan::visitChildren(TypeId ty, int index)
170
{
171
LUAU_ASSERT(ty == log->follow(ty));
172
173
if (ignoreChildrenVisit(ty))
174
return;
175
176
if (auto pty = log->pending(ty))
177
ty = &pty->pending;
178
179
if (const FunctionType* ftv = get<FunctionType>(ty))
180
{
181
for (TypeId generic : ftv->generics)
182
visitChild(generic);
183
for (TypePackId genericPack : ftv->genericPacks)
184
visitChild(genericPack);
185
186
visitChild(ftv->argTypes);
187
visitChild(ftv->retTypes);
188
}
189
else if (const TableType* ttv = get<TableType>(ty))
190
{
191
LUAU_ASSERT(!ttv->boundTo);
192
for (const auto& [name, prop] : ttv->props)
193
{
194
visitChild(prop.readTy);
195
visitChild(prop.writeTy);
196
}
197
198
if (ttv->indexer)
199
{
200
visitChild(ttv->indexer->indexType);
201
visitChild(ttv->indexer->indexResultType);
202
}
203
204
for (TypeId itp : ttv->instantiatedTypeParams)
205
visitChild(itp);
206
207
for (TypePackId itp : ttv->instantiatedTypePackParams)
208
visitChild(itp);
209
}
210
else if (const MetatableType* mtv = get<MetatableType>(ty))
211
{
212
visitChild(mtv->table);
213
visitChild(mtv->metatable);
214
}
215
else if (const UnionType* utv = get<UnionType>(ty))
216
{
217
for (TypeId opt : utv->options)
218
visitChild(opt);
219
}
220
else if (const IntersectionType* itv = get<IntersectionType>(ty))
221
{
222
for (TypeId part : itv->parts)
223
visitChild(part);
224
}
225
else if (const PendingExpansionType* petv = get<PendingExpansionType>(ty))
226
{
227
for (TypeId a : petv->typeArguments)
228
visitChild(a);
229
230
for (TypePackId a : petv->packArguments)
231
visitChild(a);
232
}
233
else if (const TypeFunctionInstanceType* tfit = get<TypeFunctionInstanceType>(ty))
234
{
235
for (TypeId a : tfit->typeArguments)
236
visitChild(a);
237
238
for (TypePackId a : tfit->packArguments)
239
visitChild(a);
240
}
241
else if (const ExternType* etv = get<ExternType>(ty))
242
{
243
for (const auto& [name, prop] : etv->props)
244
{
245
if (prop.readTy)
246
visitChild(prop.readTy);
247
if (prop.writeTy)
248
visitChild(prop.writeTy);
249
}
250
251
if (etv->parent)
252
visitChild(*etv->parent);
253
254
if (etv->metatable)
255
visitChild(*etv->metatable);
256
257
if (etv->indexer)
258
{
259
visitChild(etv->indexer->indexType);
260
visitChild(etv->indexer->indexResultType);
261
}
262
}
263
else if (const NegationType* ntv = get<NegationType>(ty))
264
{
265
visitChild(ntv->ty);
266
}
267
}
268
269
void Tarjan::visitChildren(TypePackId tp, int index)
270
{
271
LUAU_ASSERT(tp == log->follow(tp));
272
273
if (ignoreChildrenVisit(tp))
274
return;
275
276
if (auto ptp = log->pending(tp))
277
tp = &ptp->pending;
278
279
if (const TypePack* tpp = get<TypePack>(tp))
280
{
281
for (TypeId tv : tpp->head)
282
visitChild(tv);
283
if (tpp->tail)
284
visitChild(*tpp->tail);
285
}
286
else if (const VariadicTypePack* vtp = get<VariadicTypePack>(tp))
287
{
288
visitChild(vtp->ty);
289
}
290
}
291
292
std::pair<int, bool> Tarjan::indexify(TypeId ty)
293
{
294
ty = log->follow(ty);
295
296
auto [index, fresh] = typeToIndex.try_insert(ty, false);
297
298
if (fresh)
299
{
300
index = int(nodes.size());
301
nodes.emplace_back(ty, nullptr, false, false, index);
302
}
303
304
return {index, fresh};
305
}
306
307
std::pair<int, bool> Tarjan::indexify(TypePackId tp)
308
{
309
tp = log->follow(tp);
310
311
auto [index, fresh] = packToIndex.try_insert(tp, false);
312
313
if (fresh)
314
{
315
index = int(nodes.size());
316
nodes.emplace_back(nullptr, tp, false, false, index);
317
}
318
319
return {index, fresh};
320
}
321
322
void Tarjan::visitChild(TypeId ty)
323
{
324
ty = log->follow(ty);
325
326
edgesTy.push_back(ty);
327
edgesTp.push_back(nullptr);
328
}
329
330
void Tarjan::visitChild(TypePackId tp)
331
{
332
tp = log->follow(tp);
333
334
edgesTy.push_back(nullptr);
335
edgesTp.push_back(tp);
336
}
337
338
TarjanResult Tarjan::loop()
339
{
340
// Normally Tarjan is presented recursively, but this is a hot loop, so worth optimizing
341
while (!worklist.empty())
342
{
343
auto [index, currEdge, lastEdge] = worklist.back();
344
345
// First visit
346
if (currEdge == -1)
347
{
348
++childCount;
349
if (childLimit > 0 && childLimit <= childCount)
350
return TarjanResult::TooManyChildren;
351
352
stack.push_back(index);
353
354
nodes[index].onStack = true;
355
356
currEdge = int(edgesTy.size());
357
358
// Fill in edge list of this vertex
359
if (TypeId ty = nodes[index].ty)
360
visitChildren(ty, index);
361
else if (TypePackId tp = nodes[index].tp)
362
visitChildren(tp, index);
363
364
lastEdge = int(edgesTy.size());
365
}
366
367
// Visit children
368
bool foundFresh = false;
369
370
for (; currEdge < lastEdge; currEdge++)
371
{
372
int childIndex = -1;
373
bool fresh = false;
374
375
if (auto ty = edgesTy[currEdge])
376
std::tie(childIndex, fresh) = indexify(ty);
377
else if (auto tp = edgesTp[currEdge])
378
std::tie(childIndex, fresh) = indexify(tp);
379
else
380
LUAU_ASSERT(false);
381
382
if (fresh)
383
{
384
// Original recursion point, update the parent continuation point and start the new element
385
worklist.back() = {index, currEdge + 1, lastEdge};
386
worklist.emplace_back(childIndex, -1, -1); // We need to continue the top-level loop from the start with the new worklist element
387
foundFresh = true;
388
break;
389
}
390
else if (nodes[childIndex].onStack)
391
{
392
nodes[index].lowlink = std::min(nodes[index].lowlink, childIndex);
393
}
394
395
visitEdge(childIndex, index);
396
}
397
398
if (foundFresh)
399
continue;
400
401
if (nodes[index].lowlink == index)
402
{
403
visitSCC(index);
404
while (!stack.empty())
405
{
406
int popped = stack.back();
407
stack.pop_back();
408
nodes[popped].onStack = false;
409
if (popped == index)
410
break;
411
}
412
}
413
414
worklist.pop_back();
415
416
// Original return from recursion into a child
417
if (!worklist.empty())
418
{
419
auto [parentIndex, _, parentEndEdge] = worklist.back();
420
421
// No need to keep child edges around
422
edgesTy.resize(parentEndEdge);
423
edgesTp.resize(parentEndEdge);
424
425
nodes[parentIndex].lowlink = std::min(nodes[parentIndex].lowlink, nodes[index].lowlink);
426
visitEdge(index, parentIndex);
427
}
428
}
429
430
return TarjanResult::Ok;
431
}
432
433
TarjanResult Tarjan::visitRoot(TypeId ty)
434
{
435
childCount = 0;
436
if (childLimit == 0)
437
childLimit = FInt::LuauTarjanChildLimit;
438
439
ty = log->follow(ty);
440
441
auto [index, fresh] = indexify(ty);
442
worklist.emplace_back(index, -1, -1);
443
return loop();
444
}
445
446
TarjanResult Tarjan::visitRoot(TypePackId tp)
447
{
448
childCount = 0;
449
if (childLimit == 0)
450
childLimit = FInt::LuauTarjanChildLimit;
451
452
tp = log->follow(tp);
453
454
auto [index, fresh] = indexify(tp);
455
worklist.emplace_back(index, -1, -1);
456
return loop();
457
}
458
459
void Tarjan::clearTarjan(const TxnLog* log)
460
{
461
typeToIndex.clear(~0u);
462
packToIndex.clear(~0u);
463
464
nodes.clear();
465
466
stack.clear();
467
468
childCount = 0;
469
// childLimit setting stays the same
470
471
this->log = log;
472
473
edgesTy.clear();
474
edgesTp.clear();
475
worklist.clear();
476
}
477
478
bool Tarjan::getDirty(int index)
479
{
480
LUAU_ASSERT(size_t(index) < nodes.size());
481
return nodes[index].dirty;
482
}
483
484
void Tarjan::setDirty(int index, bool d)
485
{
486
LUAU_ASSERT(size_t(index) < nodes.size());
487
nodes[index].dirty = d;
488
}
489
490
void Tarjan::visitEdge(int index, int parentIndex)
491
{
492
if (getDirty(index))
493
setDirty(parentIndex, true);
494
}
495
496
void Tarjan::visitSCC(int index)
497
{
498
bool d = getDirty(index);
499
500
for (auto it = stack.rbegin(); !d && it != stack.rend(); it++)
501
{
502
TarjanNode& node = nodes[*it];
503
504
if (TypeId ty = node.ty)
505
d = isDirty(ty);
506
else if (TypePackId tp = node.tp)
507
d = isDirty(tp);
508
509
if (*it == index)
510
break;
511
}
512
513
if (!d)
514
return;
515
516
for (auto it = stack.rbegin(); it != stack.rend(); it++)
517
{
518
setDirty(*it, true);
519
520
TarjanNode& node = nodes[*it];
521
522
if (TypeId ty = node.ty)
523
foundDirty(ty);
524
else if (TypePackId tp = node.tp)
525
foundDirty(tp);
526
527
if (*it == index)
528
return;
529
}
530
}
531
532
bool Tarjan::ignoreChildren(TypeId ty)
533
{
534
return false;
535
}
536
537
bool Tarjan::ignoreChildren(TypePackId ty)
538
{
539
return false;
540
}
541
542
// Some subclasses might ignore children visit, but not other actions like replacing the children
543
bool Tarjan::ignoreChildrenVisit(TypeId ty)
544
{
545
return ignoreChildren(ty);
546
}
547
548
bool Tarjan::ignoreChildrenVisit(TypePackId ty)
549
{
550
return ignoreChildren(ty);
551
}
552
553
TarjanResult Tarjan::findDirty(TypeId ty)
554
{
555
return visitRoot(ty);
556
}
557
558
TarjanResult Tarjan::findDirty(TypePackId tp)
559
{
560
return visitRoot(tp);
561
}
562
563
Substitution::Substitution(TypeArena* arena)
564
: Substitution(TxnLog::empty(), arena)
565
{
566
}
567
568
Substitution::Substitution(const TxnLog* log_, TypeArena* arena)
569
: arena(arena)
570
{
571
log = log_;
572
LUAU_ASSERT(log);
573
}
574
575
void Substitution::dontTraverseInto(TypeId ty)
576
{
577
noTraverseTypes.insert(ty);
578
}
579
580
void Substitution::dontTraverseInto(TypePackId tp)
581
{
582
noTraverseTypePacks.insert(tp);
583
}
584
585
std::optional<TypeId> Substitution::substitute(TypeId ty)
586
{
587
ty = log->follow(ty);
588
589
// clear algorithm state for reentrancy
590
clearTarjan(log);
591
592
auto result = findDirty(ty);
593
if (result != TarjanResult::Ok)
594
return std::nullopt;
595
596
for (auto [oldTy, newTy] : newTypes)
597
{
598
if (!ignoreChildren(oldTy) && !replacedTypes.contains(newTy))
599
{
600
if (!noTraverseTypes.contains(newTy))
601
replaceChildren(newTy);
602
replacedTypes.insert(newTy);
603
}
604
}
605
for (auto [oldTp, newTp] : newPacks)
606
{
607
if (!ignoreChildren(oldTp) && !replacedTypePacks.contains(newTp))
608
{
609
if (!noTraverseTypePacks.contains(newTp))
610
replaceChildren(newTp);
611
replacedTypePacks.insert(newTp);
612
}
613
}
614
TypeId newTy = replace(ty);
615
return newTy;
616
}
617
618
std::optional<TypePackId> Substitution::substitute(TypePackId tp)
619
{
620
tp = log->follow(tp);
621
622
// clear algorithm state for reentrancy
623
clearTarjan(log);
624
625
auto result = findDirty(tp);
626
if (result != TarjanResult::Ok)
627
return std::nullopt;
628
629
for (auto [oldTy, newTy] : newTypes)
630
{
631
if (!ignoreChildren(oldTy) && !replacedTypes.contains(newTy))
632
{
633
if (!noTraverseTypes.contains(newTy))
634
replaceChildren(newTy);
635
replacedTypes.insert(newTy);
636
}
637
}
638
for (auto [oldTp, newTp] : newPacks)
639
{
640
if (!ignoreChildren(oldTp) && !replacedTypePacks.contains(newTp))
641
{
642
if (!noTraverseTypePacks.contains(newTp))
643
replaceChildren(newTp);
644
replacedTypePacks.insert(newTp);
645
}
646
}
647
TypePackId newTp = replace(tp);
648
return newTp;
649
}
650
651
void Substitution::resetState(const TxnLog* log, TypeArena* arena)
652
{
653
clearTarjan(log);
654
655
this->arena = arena;
656
657
newTypes.clear();
658
newPacks.clear();
659
replacedTypes.clear();
660
replacedTypePacks.clear();
661
662
noTraverseTypes.clear();
663
noTraverseTypePacks.clear();
664
}
665
666
TypeId Substitution::clone(TypeId ty)
667
{
668
return shallowClone(ty, *arena, log);
669
}
670
671
TypePackId Substitution::clone(TypePackId tp)
672
{
673
tp = log->follow(tp);
674
675
if (auto ptp = log->pending(tp))
676
tp = &ptp->pending;
677
678
if (const TypePack* tpp = get<TypePack>(tp))
679
{
680
TypePack clone;
681
clone.head = tpp->head;
682
clone.tail = tpp->tail;
683
return addTypePack(std::move(clone));
684
}
685
else if (const VariadicTypePack* vtp = get<VariadicTypePack>(tp))
686
{
687
VariadicTypePack clone;
688
clone.ty = vtp->ty;
689
clone.hidden = vtp->hidden;
690
return addTypePack(std::move(clone));
691
}
692
else if (const TypeFunctionInstanceTypePack* tfitp = get<TypeFunctionInstanceTypePack>(tp))
693
{
694
TypeFunctionInstanceTypePack clone{
695
tfitp->function, std::vector<TypeId>(tfitp->typeArguments.size()), std::vector<TypePackId>(tfitp->packArguments.size())
696
};
697
clone.typeArguments.assign(tfitp->typeArguments.begin(), tfitp->typeArguments.end());
698
clone.packArguments.assign(tfitp->packArguments.begin(), tfitp->packArguments.end());
699
return addTypePack(std::move(clone));
700
}
701
else
702
return addTypePack(*tp);
703
}
704
705
void Substitution::foundDirty(TypeId ty)
706
{
707
ty = log->follow(ty);
708
709
if (newTypes.contains(ty))
710
return;
711
712
if (isDirty(ty))
713
newTypes[ty] = follow(clean(ty));
714
else
715
newTypes[ty] = follow(clone(ty));
716
}
717
718
void Substitution::foundDirty(TypePackId tp)
719
{
720
tp = log->follow(tp);
721
722
if (newPacks.contains(tp))
723
return;
724
725
if (isDirty(tp))
726
newPacks[tp] = follow(clean(tp));
727
else
728
newPacks[tp] = follow(clone(tp));
729
}
730
731
TypeId Substitution::replace(TypeId ty)
732
{
733
ty = log->follow(ty);
734
735
if (TypeId* prevTy = newTypes.find(ty))
736
return *prevTy;
737
else
738
return ty;
739
}
740
741
TypePackId Substitution::replace(TypePackId tp)
742
{
743
tp = log->follow(tp);
744
745
if (TypePackId* prevTp = newPacks.find(tp))
746
return *prevTp;
747
else
748
return tp;
749
}
750
751
void Substitution::replaceChildren(TypeId ty)
752
{
753
LUAU_ASSERT(ty == log->follow(ty));
754
755
if (ignoreChildren(ty))
756
return;
757
758
if (ty->owningArena != arena)
759
return;
760
761
if (FunctionType* ftv = getMutable<FunctionType>(ty))
762
{
763
for (TypeId& generic : ftv->generics)
764
generic = replace(generic);
765
for (TypePackId& genericPack : ftv->genericPacks)
766
genericPack = replace(genericPack);
767
768
ftv->argTypes = replace(ftv->argTypes);
769
ftv->retTypes = replace(ftv->retTypes);
770
}
771
else if (TableType* ttv = getMutable<TableType>(ty))
772
{
773
LUAU_ASSERT(!ttv->boundTo);
774
for (auto& [name, prop] : ttv->props)
775
{
776
if (prop.readTy)
777
prop.readTy = replace(prop.readTy);
778
if (prop.writeTy)
779
prop.writeTy = replace(prop.writeTy);
780
}
781
782
if (ttv->indexer)
783
{
784
ttv->indexer->indexType = replace(ttv->indexer->indexType);
785
ttv->indexer->indexResultType = replace(ttv->indexer->indexResultType);
786
}
787
788
for (TypeId& itp : ttv->instantiatedTypeParams)
789
itp = replace(itp);
790
791
for (TypePackId& itp : ttv->instantiatedTypePackParams)
792
itp = replace(itp);
793
}
794
else if (MetatableType* mtv = getMutable<MetatableType>(ty))
795
{
796
mtv->table = replace(mtv->table);
797
mtv->metatable = replace(mtv->metatable);
798
}
799
else if (UnionType* utv = getMutable<UnionType>(ty))
800
{
801
for (TypeId& opt : utv->options)
802
opt = replace(opt);
803
}
804
else if (IntersectionType* itv = getMutable<IntersectionType>(ty))
805
{
806
for (TypeId& part : itv->parts)
807
part = replace(part);
808
}
809
else if (PendingExpansionType* petv = getMutable<PendingExpansionType>(ty))
810
{
811
for (TypeId& a : petv->typeArguments)
812
a = replace(a);
813
814
for (TypePackId& a : petv->packArguments)
815
a = replace(a);
816
}
817
else if (TypeFunctionInstanceType* tfit = getMutable<TypeFunctionInstanceType>(ty))
818
{
819
for (TypeId& a : tfit->typeArguments)
820
a = replace(a);
821
822
for (TypePackId& a : tfit->packArguments)
823
a = replace(a);
824
}
825
else if (ExternType* etv = getMutable<ExternType>(ty))
826
{
827
for (auto& [name, prop] : etv->props)
828
{
829
if (prop.readTy)
830
prop.readTy = replace(prop.readTy);
831
if (prop.writeTy)
832
prop.writeTy = replace(prop.writeTy);
833
}
834
835
if (etv->parent)
836
etv->parent = replace(*etv->parent);
837
838
if (etv->metatable)
839
etv->metatable = replace(*etv->metatable);
840
841
if (etv->indexer)
842
{
843
etv->indexer->indexType = replace(etv->indexer->indexType);
844
etv->indexer->indexResultType = replace(etv->indexer->indexResultType);
845
}
846
}
847
else if (NegationType* ntv = getMutable<NegationType>(ty))
848
{
849
ntv->ty = replace(ntv->ty);
850
}
851
}
852
853
void Substitution::replaceChildren(TypePackId tp)
854
{
855
LUAU_ASSERT(tp == log->follow(tp));
856
857
if (ignoreChildren(tp))
858
return;
859
860
if (tp->owningArena != arena)
861
return;
862
863
if (TypePack* tpp = getMutable<TypePack>(tp))
864
{
865
for (TypeId& tv : tpp->head)
866
tv = replace(tv);
867
if (tpp->tail)
868
tpp->tail = replace(*tpp->tail);
869
}
870
else if (VariadicTypePack* vtp = getMutable<VariadicTypePack>(tp))
871
{
872
vtp->ty = replace(vtp->ty);
873
}
874
else if (TypeFunctionInstanceTypePack* tfitp = getMutable<TypeFunctionInstanceTypePack>(tp))
875
{
876
for (TypeId& t : tfitp->typeArguments)
877
t = replace(t);
878
879
for (TypePackId& t : tfitp->packArguments)
880
t = replace(t);
881
}
882
}
883
884
template<typename Ty>
885
std::optional<Ty> Substitution::replace(std::optional<Ty> ty)
886
{
887
if (ty)
888
return replace(*ty);
889
else
890
return std::nullopt;
891
}
892
893
} // namespace Luau
894
895