Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Analysis/src/DataFlowGraph.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/DataFlowGraph.h"
3
4
#include "Luau/Ast.h"
5
#include "Luau/BuiltinDefinitions.h"
6
#include "Luau/Def.h"
7
#include "Luau/Common.h"
8
#include "Luau/Error.h"
9
#include "Luau/TimeTrace.h"
10
11
#include <memory>
12
#include <optional>
13
14
LUAU_FASTFLAG(DebugLuauFreezeArena)
15
LUAU_FASTFLAG(LuauSolverV2)
16
LUAU_FASTFLAG(LuauExplicitTypeInstantiationSupport)
17
LUAU_FASTFLAGVARIABLE(LuauCaptureRecursiveCallsForTablesAndGlobals2)
18
19
namespace Luau
20
{
21
22
bool doesCallError(const AstExprCall* call); // TypeInfer.cpp
23
24
struct PushScope
25
{
26
ScopeStack& stack;
27
size_t previousSize;
28
29
PushScope(ScopeStack& stack, DfgScope* scope)
30
: stack(stack)
31
, previousSize(stack.size())
32
{
33
// `scope` should never be `nullptr` here.
34
LUAU_ASSERT(scope);
35
stack.push_back(scope);
36
}
37
38
~PushScope()
39
{
40
// If somehow this stack has _shrunk_ to be smaller than we expect,
41
// something very strange has happened.
42
LUAU_ASSERT(stack.size() > previousSize);
43
while (stack.size() > previousSize)
44
stack.pop_back();
45
}
46
};
47
48
const RefinementKey* RefinementKeyArena::leaf(DefId def)
49
{
50
return allocator.allocate(RefinementKey{nullptr, def, std::nullopt});
51
}
52
53
const RefinementKey* RefinementKeyArena::node(const RefinementKey* parent, DefId def, const std::string& propName)
54
{
55
return allocator.allocate(RefinementKey{parent, def, propName});
56
}
57
58
DataFlowGraph::DataFlowGraph(NotNull<DefArena> defArena, NotNull<RefinementKeyArena> keyArena)
59
: defArena{defArena}
60
, keyArena{keyArena}
61
{
62
}
63
64
DefId DataFlowGraph::getDef(const AstExpr* expr) const
65
{
66
auto def = astDefs.find(expr);
67
LUAU_ASSERT(def);
68
return NotNull{*def};
69
}
70
71
std::optional<DefId> DataFlowGraph::getDefOptional(const AstExpr* expr) const
72
{
73
auto def = astDefs.find(expr);
74
if (!def)
75
return std::nullopt;
76
return NotNull{*def};
77
}
78
79
DefId DataFlowGraph::getDef(const AstLocal* local) const
80
{
81
auto def = localDefs.find(local);
82
LUAU_ASSERT(def);
83
return NotNull{*def};
84
}
85
86
DefId DataFlowGraph::getDef(const AstStatDeclareGlobal* global) const
87
{
88
auto def = declaredDefs.find(global);
89
LUAU_ASSERT(def);
90
return NotNull{*def};
91
}
92
93
DefId DataFlowGraph::getDef(const AstStatDeclareFunction* func) const
94
{
95
auto def = declaredDefs.find(func);
96
LUAU_ASSERT(def);
97
return NotNull{*def};
98
}
99
100
const RefinementKey* DataFlowGraph::getRefinementKey(const AstExpr* expr) const
101
{
102
if (auto key = astRefinementKeys.find(expr))
103
return *key;
104
105
return nullptr;
106
}
107
108
std::optional<Symbol> DataFlowGraph::getSymbolFromDef(const Def* def) const
109
{
110
if (auto ref = defToSymbol.find(def))
111
return *ref;
112
113
return std::nullopt;
114
}
115
116
std::optional<DefId> DfgScope::lookup(Symbol symbol) const
117
{
118
for (const DfgScope* current = this; current; current = current->parent)
119
{
120
if (auto def = current->bindings.find(symbol))
121
return NotNull{*def};
122
}
123
124
return std::nullopt;
125
}
126
127
std::optional<DefId> DfgScope::lookup(DefId def, const std::string& key) const
128
{
129
for (const DfgScope* current = this; current; current = current->parent)
130
{
131
if (auto props = current->props.find(def))
132
{
133
if (auto it = props->find(key); it != props->end())
134
return NotNull{it->second};
135
}
136
}
137
138
return std::nullopt;
139
}
140
141
void DfgScope::inherit(const DfgScope* childScope)
142
{
143
for (const auto& [k, a] : childScope->bindings)
144
{
145
if (lookup(k))
146
bindings[k] = a;
147
}
148
149
for (const auto& [k1, a1] : childScope->props)
150
{
151
for (const auto& [k2, a2] : a1)
152
props[k1][k2] = a2;
153
}
154
}
155
156
DataFlowGraphBuilder::DataFlowGraphBuilder(NotNull<DefArena> defArena, NotNull<RefinementKeyArena> keyArena)
157
: graph{defArena, keyArena}
158
, defArena{defArena}
159
, keyArena{keyArena}
160
{
161
}
162
163
DataFlowGraph DataFlowGraphBuilder::build(
164
AstStatBlock* block,
165
NotNull<DefArena> defArena,
166
NotNull<RefinementKeyArena> keyArena,
167
NotNull<struct InternalErrorReporter> handle
168
)
169
{
170
LUAU_TIMETRACE_SCOPE("DataFlowGraphBuilder::build", "Typechecking");
171
172
DataFlowGraphBuilder builder(defArena, keyArena);
173
builder.handle = handle;
174
175
DfgScope* moduleScope = builder.scopes.emplace_back(new DfgScope{nullptr, DfgScope::ScopeType::Linear}).get();
176
PushScope ps{builder.scopeStack, moduleScope};
177
builder.visitBlockWithoutChildScope(block);
178
builder.resolveCaptures();
179
180
if (FFlag::DebugLuauFreezeArena)
181
{
182
builder.defArena->allocator.freeze();
183
builder.keyArena->allocator.freeze();
184
}
185
186
return std::move(builder.graph);
187
}
188
189
DataFlowGraph DataFlowGraphBuilder::empty(NotNull<DefArena> defArena, NotNull<RefinementKeyArena> keyArena)
190
{
191
return DataFlowGraph{defArena, keyArena};
192
}
193
194
void DataFlowGraphBuilder::resolveCaptures()
195
{
196
for (const auto& [_, capture] : captures)
197
{
198
std::vector<DefId> operands;
199
for (size_t i = capture.versionOffset; i < capture.allVersions.size(); ++i)
200
collectOperands(capture.allVersions[i], &operands);
201
202
for (DefId captureDef : capture.captureDefs)
203
{
204
Phi* phi = const_cast<Phi*>(get<Phi>(captureDef));
205
LUAU_ASSERT(phi);
206
LUAU_ASSERT(phi->operands.empty());
207
phi->operands = operands;
208
}
209
}
210
}
211
212
NotNull<DfgScope> DataFlowGraphBuilder::currentScope()
213
{
214
LUAU_ASSERT(!scopeStack.empty());
215
return NotNull{scopeStack.back()};
216
}
217
218
DfgScope* DataFlowGraphBuilder::makeChildScope(DfgScope::ScopeType scopeType)
219
{
220
return scopes.emplace_back(new DfgScope{currentScope(), scopeType}).get();
221
}
222
223
void DataFlowGraphBuilder::join(DfgScope* p, DfgScope* a, DfgScope* b)
224
{
225
joinBindings(p, *a, *b);
226
joinProps(p, *a, *b);
227
}
228
229
void DataFlowGraphBuilder::joinBindings(DfgScope* p, const DfgScope& a, const DfgScope& b)
230
{
231
for (const auto& [sym, def1] : a.bindings)
232
{
233
if (auto def2 = b.bindings.find(sym))
234
p->bindings[sym] = defArena->phi(NotNull{def1}, NotNull{*def2});
235
else if (auto def2 = p->lookup(sym))
236
p->bindings[sym] = defArena->phi(NotNull{def1}, NotNull{*def2});
237
}
238
239
for (const auto& [sym, def1] : b.bindings)
240
{
241
if (auto def2 = p->lookup(sym))
242
p->bindings[sym] = defArena->phi(NotNull{def1}, NotNull{*def2});
243
}
244
}
245
246
void DataFlowGraphBuilder::joinProps(DfgScope* result, const DfgScope& a, const DfgScope& b)
247
{
248
auto phinodify = [this](DfgScope* scope, const auto& a, const auto& b, DefId parent) mutable
249
{
250
auto& p = scope->props[parent];
251
for (const auto& [k, defA] : a)
252
{
253
if (auto it = b.find(k); it != b.end())
254
p[k] = defArena->phi(NotNull{it->second}, NotNull{defA});
255
else if (auto it = p.find(k); it != p.end())
256
p[k] = defArena->phi(NotNull{it->second}, NotNull{defA});
257
else if (auto def2 = scope->lookup(parent, k))
258
p[k] = defArena->phi(*def2, NotNull{defA});
259
else
260
p[k] = defA;
261
}
262
263
for (const auto& [k, defB] : b)
264
{
265
if (auto it = a.find(k); it != a.end())
266
continue;
267
else if (auto it = p.find(k); it != p.end())
268
p[k] = defArena->phi(NotNull{it->second}, NotNull{defB});
269
else if (auto def2 = scope->lookup(parent, k))
270
p[k] = defArena->phi(*def2, NotNull{defB});
271
else
272
p[k] = defB;
273
}
274
};
275
276
for (const auto& [def, a1] : a.props)
277
{
278
result->props.try_insert(def, {});
279
if (auto a2 = b.props.find(def))
280
phinodify(result, a1, *a2, NotNull{def});
281
else if (auto a2 = result->props.find(def))
282
phinodify(result, a1, *a2, NotNull{def});
283
}
284
285
for (const auto& [def, a1] : b.props)
286
{
287
result->props.try_insert(def, {});
288
if (a.props.find(def))
289
continue;
290
else if (auto a2 = result->props.find(def))
291
phinodify(result, a1, *a2, NotNull{def});
292
}
293
}
294
295
DefId DataFlowGraphBuilder::lookup(Symbol symbol, Location location)
296
{
297
DfgScope* scope = currentScope();
298
299
// true if any of the considered scopes are a loop.
300
bool outsideLoopScope = false;
301
for (DfgScope* current = scope; current; current = current->parent)
302
{
303
outsideLoopScope = outsideLoopScope || current->scopeType == DfgScope::Loop;
304
305
if (auto found = current->bindings.find(symbol))
306
return NotNull{*found};
307
else if (current->scopeType == DfgScope::Function)
308
{
309
FunctionCapture& capture = captures[symbol];
310
DefId captureDef = defArena->phi({});
311
capture.captureDefs.push_back(captureDef);
312
313
// If we are outside of a loop scope, then we don't want to actually bind
314
// uses of `symbol` to this new phi node since it will not get populated.
315
if (!outsideLoopScope)
316
scope->bindings[symbol] = captureDef;
317
318
return NotNull{captureDef};
319
}
320
}
321
322
DefId result = defArena->freshCell(symbol, location);
323
scope->bindings[symbol] = result;
324
captures[symbol].allVersions.push_back(result);
325
return result;
326
}
327
328
DefId DataFlowGraphBuilder::lookup(DefId def, const std::string& key, Location location)
329
{
330
DfgScope* scope = currentScope();
331
for (DfgScope* current = scope; current; current = current->parent)
332
{
333
if (auto props = current->props.find(def))
334
{
335
if (auto it = props->find(key); it != props->end())
336
return NotNull{it->second};
337
}
338
else if (auto phi = get<Phi>(def);
339
phi && phi->operands.empty() && (!FFlag::LuauCaptureRecursiveCallsForTablesAndGlobals2 || current->scopeType == DfgScope::Function))
340
{
341
DefId result = defArena->freshCell(def->name, location);
342
scope->props[def][key] = result;
343
return result;
344
}
345
}
346
347
if (auto phi = get<Phi>(def))
348
{
349
std::vector<DefId> defs;
350
for (DefId operand : phi->operands)
351
defs.push_back(lookup(operand, key, location));
352
353
DefId result = defArena->phi(defs);
354
scope->props[def][key] = result;
355
return result;
356
}
357
else if (get<Cell>(def))
358
{
359
DefId result = defArena->freshCell(def->name, location);
360
scope->props[def][key] = result;
361
return result;
362
}
363
else
364
handle->ice("Inexhaustive lookup cases in DataFlowGraphBuilder::lookup");
365
}
366
367
ControlFlow DataFlowGraphBuilder::visit(AstStatBlock* b)
368
{
369
DfgScope* child = makeChildScope();
370
371
ControlFlow cf;
372
{
373
PushScope ps{scopeStack, child};
374
cf = visitBlockWithoutChildScope(b);
375
}
376
377
currentScope()->inherit(child);
378
return cf;
379
}
380
381
ControlFlow DataFlowGraphBuilder::visitBlockWithoutChildScope(AstStatBlock* b)
382
{
383
std::optional<ControlFlow> firstControlFlow;
384
for (AstStat* stat : b->body)
385
{
386
ControlFlow cf = visit(stat);
387
if (cf != ControlFlow::None && !firstControlFlow)
388
firstControlFlow = cf;
389
}
390
391
return firstControlFlow.value_or(ControlFlow::None);
392
}
393
394
ControlFlow DataFlowGraphBuilder::visit(AstStat* s)
395
{
396
if (auto b = s->as<AstStatBlock>())
397
return visit(b);
398
else if (auto i = s->as<AstStatIf>())
399
return visit(i);
400
else if (auto w = s->as<AstStatWhile>())
401
return visit(w);
402
else if (auto r = s->as<AstStatRepeat>())
403
return visit(r);
404
else if (auto b = s->as<AstStatBreak>())
405
return visit(b);
406
else if (auto c = s->as<AstStatContinue>())
407
return visit(c);
408
else if (auto r = s->as<AstStatReturn>())
409
return visit(r);
410
else if (auto e = s->as<AstStatExpr>())
411
return visit(e);
412
else if (auto l = s->as<AstStatLocal>())
413
return visit(l);
414
else if (auto f = s->as<AstStatFor>())
415
return visit(f);
416
else if (auto f = s->as<AstStatForIn>())
417
return visit(f);
418
else if (auto a = s->as<AstStatAssign>())
419
return visit(a);
420
else if (auto c = s->as<AstStatCompoundAssign>())
421
return visit(c);
422
else if (auto f = s->as<AstStatFunction>())
423
return visit(f);
424
else if (auto l = s->as<AstStatLocalFunction>())
425
return visit(l);
426
else if (auto t = s->as<AstStatTypeAlias>())
427
return visit(t);
428
else if (auto f = s->as<AstStatTypeFunction>())
429
return visit(f);
430
else if (auto d = s->as<AstStatDeclareGlobal>())
431
return visit(d);
432
else if (auto d = s->as<AstStatDeclareFunction>())
433
return visit(d);
434
else if (auto d = s->as<AstStatDeclareExternType>())
435
return visit(d);
436
else if (auto error = s->as<AstStatError>())
437
return visit(error);
438
else
439
handle->ice("Unknown AstStat in DataFlowGraphBuilder::visit");
440
}
441
442
ControlFlow DataFlowGraphBuilder::visit(AstStatIf* i)
443
{
444
visitExpr(i->condition);
445
446
DfgScope* thenScope = makeChildScope();
447
DfgScope* elseScope = makeChildScope();
448
449
ControlFlow thencf;
450
{
451
PushScope ps{scopeStack, thenScope};
452
thencf = visit(i->thenbody);
453
}
454
455
ControlFlow elsecf = ControlFlow::None;
456
if (i->elsebody)
457
{
458
PushScope ps{scopeStack, elseScope};
459
elsecf = visit(i->elsebody);
460
}
461
462
DfgScope* scope = currentScope();
463
// If the control flow from the `if` or `else` block is non-linear,
464
// then we should assume that the _other_ branch is the one taken.
465
if (thencf != ControlFlow::None && elsecf == ControlFlow::None)
466
scope->inherit(elseScope);
467
else if (thencf == ControlFlow::None && elsecf != ControlFlow::None)
468
scope->inherit(thenScope);
469
else if ((thencf | elsecf) == ControlFlow::None)
470
join(scope, thenScope, elseScope);
471
472
if (thencf == elsecf)
473
return thencf;
474
else if (matches(thencf, ControlFlow::Returns | ControlFlow::Throws) && matches(elsecf, ControlFlow::Returns | ControlFlow::Throws))
475
return ControlFlow::Returns;
476
else
477
return ControlFlow::None;
478
}
479
480
ControlFlow DataFlowGraphBuilder::visit(AstStatWhile* w)
481
{
482
// FIXME: This is unsound, as it does not consider the _second_ loop
483
// iteration. Consider something like:
484
//
485
// local function f(_: number) end
486
// local x = 42
487
// while math.random () > 0.5 do
488
// f(x)
489
// x = ""
490
// end
491
//
492
// While the first iteration is fine, the second iteration would
493
// allow a string to flow into a position that expects.
494
DfgScope* whileScope = makeChildScope(DfgScope::Loop);
495
496
ControlFlow cf;
497
{
498
PushScope ps{scopeStack, whileScope};
499
visitExpr(w->condition);
500
cf = visit(w->body);
501
}
502
503
auto scope = currentScope();
504
// If the inner loop unconditionally returns or throws we shouldn't
505
// consume any type state from the loop body.
506
if (!matches(cf, ControlFlow::Returns | ControlFlow::Throws))
507
join(scope, scope, whileScope);
508
509
return ControlFlow::None;
510
}
511
512
ControlFlow DataFlowGraphBuilder::visit(AstStatRepeat* r)
513
{
514
// See comment in visit(AstStatWhile*): this is unsound as it
515
// does not consider the _second_ loop iteration.
516
DfgScope* repeatScope = makeChildScope(DfgScope::Loop);
517
518
ControlFlow cf;
519
520
{
521
PushScope ps{scopeStack, repeatScope};
522
cf = visitBlockWithoutChildScope(r->body);
523
visitExpr(r->condition);
524
}
525
526
// Ultimately: the options for a repeat-until loop are more
527
// straightforward.
528
currentScope()->inherit(repeatScope);
529
530
// `repeat` loops will unconditionally fire: if the internal control
531
// flow is unconditionally a break or continue, then we have linear
532
// control flow, but if it's throws or returns, then we need to
533
// return _that_ to the parent.
534
return matches(cf, ControlFlow::Breaks | ControlFlow::Continues) ? ControlFlow::None : cf;
535
}
536
537
ControlFlow DataFlowGraphBuilder::visit(AstStatBreak* b)
538
{
539
return ControlFlow::Breaks;
540
}
541
542
ControlFlow DataFlowGraphBuilder::visit(AstStatContinue* c)
543
{
544
return ControlFlow::Continues;
545
}
546
547
ControlFlow DataFlowGraphBuilder::visit(AstStatReturn* r)
548
{
549
for (AstExpr* e : r->list)
550
visitExpr(e);
551
552
return ControlFlow::Returns;
553
}
554
555
ControlFlow DataFlowGraphBuilder::visit(AstStatExpr* e)
556
{
557
visitExpr(e->expr);
558
if (auto call = e->expr->as<AstExprCall>(); call && doesCallError(call))
559
return ControlFlow::Throws;
560
else
561
return ControlFlow::None;
562
}
563
564
ControlFlow DataFlowGraphBuilder::visit(AstStatLocal* l)
565
{
566
// We're gonna need a `visitExprList` and `visitVariadicExpr` (function calls and `...`)
567
std::vector<DefId> defs;
568
defs.reserve(l->values.size);
569
for (AstExpr* e : l->values)
570
defs.push_back(visitExpr(e).def);
571
572
for (size_t i = 0; i < l->vars.size; ++i)
573
{
574
AstLocal* local = l->vars.data[i];
575
if (local->annotation)
576
visitType(local->annotation);
577
578
// We need to create a new def to intentionally avoid alias tracking, but we'd like to
579
// make sure that the non-aliased defs are also marked as a subscript for refinements.
580
bool subscripted = i < defs.size() && containsSubscriptedDefinition(defs[i]);
581
DefId def = defArena->freshCell(local, local->location, subscripted);
582
if (i < l->values.size)
583
{
584
AstExpr* e = l->values.data[i];
585
if (e->is<AstExprTable>())
586
{
587
def = defs[i];
588
}
589
}
590
graph.localDefs[local] = def;
591
currentScope()->bindings[local] = def;
592
captures[local].allVersions.push_back(def);
593
}
594
595
return ControlFlow::None;
596
}
597
598
ControlFlow DataFlowGraphBuilder::visit(AstStatFor* f)
599
{
600
// See comment in visit(AstStatWhile*): this is unsound as it
601
// does not consider the _second_ loop iteration.
602
DfgScope* forScope = makeChildScope(DfgScope::Loop);
603
604
visitExpr(f->from);
605
visitExpr(f->to);
606
if (f->step)
607
visitExpr(f->step);
608
609
ControlFlow cf;
610
{
611
PushScope ps{scopeStack, forScope};
612
613
if (f->var->annotation)
614
visitType(f->var->annotation);
615
616
DefId def = defArena->freshCell(f->var, f->var->location);
617
graph.localDefs[f->var] = def;
618
currentScope()->bindings[f->var] = def;
619
captures[f->var].allVersions.push_back(def);
620
621
cf = visit(f->body);
622
}
623
624
auto scope = currentScope();
625
// If the inner loop unconditionally returns or throws we shouldn't
626
// consume any type state from the loop body.
627
if (!matches(cf, ControlFlow::Returns | ControlFlow::Throws))
628
join(scope, scope, forScope);
629
630
return ControlFlow::None;
631
}
632
633
ControlFlow DataFlowGraphBuilder::visit(AstStatForIn* f)
634
{
635
DfgScope* forScope = makeChildScope(DfgScope::Loop);
636
637
ControlFlow cf;
638
{
639
PushScope ps{scopeStack, forScope};
640
641
for (AstLocal* local : f->vars)
642
{
643
if (local->annotation)
644
visitType(local->annotation);
645
646
DefId def = defArena->freshCell(local, local->location);
647
graph.localDefs[local] = def;
648
currentScope()->bindings[local] = def;
649
captures[local].allVersions.push_back(def);
650
}
651
652
// TODO(controlflow): entry point has a back edge from exit point
653
// We're gonna need a `visitExprList` and `visitVariadicExpr` (function calls and `...`)
654
for (AstExpr* e : f->values)
655
visitExpr(e);
656
657
cf = visit(f->body);
658
}
659
660
auto scope = currentScope();
661
// If the inner loop unconditionally returns or throws we shouldn't
662
// consume any type state from the loop body.
663
if (!matches(cf, ControlFlow::Returns | ControlFlow::Throws))
664
join(scope, scope, forScope);
665
666
return ControlFlow::None;
667
}
668
669
ControlFlow DataFlowGraphBuilder::visit(AstStatAssign* a)
670
{
671
std::vector<DefId> defs;
672
defs.reserve(a->values.size);
673
for (AstExpr* e : a->values)
674
defs.push_back(visitExpr(e).def);
675
676
for (size_t i = 0; i < a->vars.size; ++i)
677
{
678
AstExpr* v = a->vars.data[i];
679
visitLValue(v, i < defs.size() ? defs[i] : defArena->freshCell(Symbol{}, v->location));
680
}
681
682
return ControlFlow::None;
683
}
684
685
ControlFlow DataFlowGraphBuilder::visit(AstStatCompoundAssign* c)
686
{
687
(void)visitExpr(c->value);
688
(void)visitExpr(c->var);
689
690
return ControlFlow::None;
691
}
692
693
ControlFlow DataFlowGraphBuilder::visit(AstStatFunction* f)
694
{
695
// In the old solver, we assumed that the name of the function is always a function in the body
696
// but this isn't true, e.g. the following example will print `5`, not a function address.
697
//
698
// local function f() print(f) end
699
// local g = f
700
// f = 5
701
// g() --> 5
702
//
703
// which is evidence that references to variables must be a phi node of all possible definitions,
704
// but for bug compatibility, we'll assume the same thing here.
705
visitLValue(f->name, defArena->freshCell(Symbol{}, f->name->location));
706
707
if (FFlag::LuauCaptureRecursiveCallsForTablesAndGlobals2)
708
{
709
// This logic is for supporting:
710
//
711
// local coolmath = {}
712
// function coolmath.factorial(n: number)
713
// if n <= 1 then
714
// return 1
715
// else
716
// return coolmath.factorial(n - 1) * n
717
// end
718
// end
719
//
720
// We want to ensure that the `coolmath.factorial` inside the function
721
// statement uses the ungeneralized function type. Without any
722
// intervention we would use the version from the captured `coolmath`
723
// upvalue, which would be the generalized type. That would cause
724
// the above snippet to _always_ force a constraint, as there is a
725
// cycle between the generalization constraint of the function and
726
// the constraints related to resolving the recursive call. We add
727
// a similar case for global functions, as in:
728
//
729
// function walk(n)
730
// if n.tag == "leaf" then
731
// print(n.value)
732
// else
733
// walk(n.left)
734
// print(n.value)
735
// walk(n.right)
736
// end
737
// end
738
//
739
// NOTE: It is not immediately obvious to me, in DataFlowGraph, if this
740
// can be extended to any arbitrary assignment, such as:
741
//
742
// function foo.bar.baz.bing()
743
// local _ = foo.bar.baz.bing()
744
// end
745
//
746
// ... hence us only handling the common case of a single property deep.
747
DfgScope* signatureScope = makeChildScope(DfgScope::Function);
748
PushScope ps{scopeStack, signatureScope};
749
if (auto global = f->name->as<AstExprGlobal>())
750
{
751
signatureScope->bindings[global->name] = graph.getDef(f->name);
752
}
753
else if (auto name = f->name->as<AstExprIndexName>(); name && name->expr->is<AstExprLocal>())
754
{
755
auto receiver = name->expr->as<AstExprLocal>()->local;
756
signatureScope->props[lookup(receiver, f->func->location)][name->index.value] = graph.getDef(f->name);
757
}
758
visitFunction(f->func, NotNull{signatureScope});
759
}
760
else
761
{
762
visitExpr(f->func);
763
}
764
765
if (auto local = f->name->as<AstExprLocal>())
766
{
767
// local f
768
// function f()
769
// if cond() then
770
// f() -- should reference only the function version and other future version, and nothing prior
771
// end
772
// end
773
FunctionCapture& capture = captures[local->local];
774
capture.versionOffset = capture.allVersions.size() - 1;
775
}
776
777
return ControlFlow::None;
778
}
779
780
ControlFlow DataFlowGraphBuilder::visit(AstStatLocalFunction* l)
781
{
782
DefId def = defArena->freshCell(l->name, l->location);
783
graph.localDefs[l->name] = def;
784
currentScope()->bindings[l->name] = def;
785
captures[l->name].allVersions.push_back(def);
786
visitExpr(l->func);
787
788
return ControlFlow::None;
789
}
790
791
ControlFlow DataFlowGraphBuilder::visit(AstStatTypeAlias* t)
792
{
793
DfgScope* unreachable = makeChildScope();
794
PushScope ps{scopeStack, unreachable};
795
796
visitGenerics(t->generics);
797
visitGenericPacks(t->genericPacks);
798
visitType(t->type);
799
800
return ControlFlow::None;
801
}
802
803
ControlFlow DataFlowGraphBuilder::visit(AstStatTypeFunction* f)
804
{
805
DfgScope* unreachable = makeChildScope();
806
PushScope ps{scopeStack, unreachable};
807
808
visitExpr(f->body);
809
810
return ControlFlow::None;
811
}
812
813
ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareGlobal* d)
814
{
815
DefId def = defArena->freshCell(d->name, d->nameLocation);
816
graph.declaredDefs[d] = def;
817
currentScope()->bindings[d->name] = def;
818
captures[d->name].allVersions.push_back(def);
819
820
visitType(d->type);
821
822
return ControlFlow::None;
823
}
824
825
ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareFunction* d)
826
{
827
DefId def = defArena->freshCell(d->name, d->nameLocation);
828
graph.declaredDefs[d] = def;
829
currentScope()->bindings[d->name] = def;
830
captures[d->name].allVersions.push_back(def);
831
832
DfgScope* unreachable = makeChildScope();
833
PushScope ps{scopeStack, unreachable};
834
835
visitGenerics(d->generics);
836
visitGenericPacks(d->genericPacks);
837
visitTypeList(d->params);
838
visitTypePack(d->retTypes);
839
840
return ControlFlow::None;
841
}
842
843
ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareExternType* d)
844
{
845
// This declaration does not "introduce" any bindings in value namespace,
846
// so there's no symbolic value to begin with. We'll traverse the properties
847
// because their type annotations may depend on something in the value namespace.
848
DfgScope* unreachable = makeChildScope();
849
PushScope ps{scopeStack, unreachable};
850
851
for (AstDeclaredExternTypeProperty prop : d->props)
852
visitType(prop.ty);
853
854
return ControlFlow::None;
855
}
856
857
ControlFlow DataFlowGraphBuilder::visit(AstStatError* error)
858
{
859
DfgScope* unreachable = makeChildScope();
860
PushScope ps{scopeStack, unreachable};
861
862
for (AstStat* s : error->statements)
863
visit(s);
864
for (AstExpr* e : error->expressions)
865
visitExpr(e);
866
867
return ControlFlow::None;
868
}
869
870
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExpr* e)
871
{
872
// Some subexpressions could be visited two times. If we've already seen it, just extract it.
873
if (auto def = graph.astDefs.find(e))
874
{
875
auto key = graph.astRefinementKeys.find(e);
876
return {NotNull{*def}, key ? *key : nullptr};
877
}
878
879
auto go = [&]() -> DataFlowResult
880
{
881
if (auto g = e->as<AstExprGroup>())
882
return visitExpr(g);
883
else if (auto c = e->as<AstExprConstantNil>())
884
return {defArena->freshCell(Symbol{}, c->location), nullptr}; // ok
885
else if (auto c = e->as<AstExprConstantBool>())
886
return {defArena->freshCell(Symbol{}, c->location), nullptr}; // ok
887
else if (auto c = e->as<AstExprConstantNumber>())
888
return {defArena->freshCell(Symbol{}, c->location), nullptr}; // ok
889
else if (auto c = e->as<AstExprConstantInteger>())
890
return {defArena->freshCell(Symbol{}, c->location), nullptr}; // ok
891
else if (auto c = e->as<AstExprConstantString>())
892
return {defArena->freshCell(Symbol{}, c->location), nullptr}; // ok
893
else if (auto l = e->as<AstExprLocal>())
894
return visitExpr(l);
895
else if (auto g = e->as<AstExprGlobal>())
896
return visitExpr(g);
897
else if (auto v = e->as<AstExprVarargs>())
898
return {defArena->freshCell(Symbol{}, v->location), nullptr}; // ok
899
else if (auto c = e->as<AstExprCall>())
900
return visitExpr(c);
901
else if (auto i = e->as<AstExprIndexName>())
902
return visitExpr(i);
903
else if (auto i = e->as<AstExprIndexExpr>())
904
return visitExpr(i);
905
else if (auto f = e->as<AstExprFunction>())
906
return visitExpr(f);
907
else if (auto t = e->as<AstExprTable>())
908
return visitExpr(t);
909
else if (auto u = e->as<AstExprUnary>())
910
return visitExpr(u);
911
else if (auto b = e->as<AstExprBinary>())
912
return visitExpr(b);
913
else if (auto t = e->as<AstExprTypeAssertion>())
914
return visitExpr(t);
915
else if (auto i = e->as<AstExprIfElse>())
916
return visitExpr(i);
917
else if (auto i = e->as<AstExprInterpString>())
918
return visitExpr(i);
919
else if (auto i = e->as<AstExprInstantiate>())
920
return visitExpr(i);
921
else if (auto error = e->as<AstExprError>())
922
return visitExpr(error);
923
else
924
handle->ice("Unknown AstExpr in DataFlowGraphBuilder::visitExpr");
925
};
926
927
auto [def, key] = go();
928
graph.astDefs[e] = def;
929
if (key)
930
graph.astRefinementKeys[e] = key;
931
return {def, key};
932
}
933
934
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprGroup* group)
935
{
936
return visitExpr(group->expr);
937
}
938
939
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprLocal* l)
940
{
941
DefId def = lookup(l->local, l->local->location);
942
const RefinementKey* key = keyArena->leaf(def);
943
graph.defToSymbol[def] = l->local;
944
return {def, key};
945
}
946
947
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprGlobal* g)
948
{
949
DefId def = lookup(g->name, g->location);
950
graph.defToSymbol[def] = g->name;
951
return {def, keyArena->leaf(def)};
952
}
953
954
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprCall* c)
955
{
956
visitExpr(c->func);
957
958
for (AstExpr* arg : c->args)
959
visitExpr(arg);
960
961
if (shouldTypestateForFirstArgument(*c) && c->args.size > 1 && isLValue(*c->args.begin()))
962
{
963
AstExpr* firstArg = *c->args.begin();
964
965
// this logic has to handle the name-like subset of expressions.
966
std::optional<DataFlowResult> result;
967
if (auto l = firstArg->as<AstExprLocal>())
968
result = visitExpr(l);
969
else if (auto g = firstArg->as<AstExprGlobal>())
970
result = visitExpr(g);
971
else if (auto i = firstArg->as<AstExprIndexName>())
972
result = visitExpr(i);
973
else if (auto i = firstArg->as<AstExprIndexExpr>())
974
result = visitExpr(i);
975
else
976
LUAU_UNREACHABLE(); // This is unreachable because the whole thing is guarded by `isLValue`.
977
978
LUAU_ASSERT(result);
979
980
DfgScope* child = makeChildScope();
981
scopeStack.push_back(child);
982
983
auto [def, key] = *result;
984
graph.astDefs[firstArg] = def;
985
if (key)
986
graph.astRefinementKeys[firstArg] = key;
987
988
visitLValue(firstArg, def);
989
}
990
991
// We treat function calls as "subscripted" as they could potentially
992
// return a subscripted value, consider:
993
//
994
// local function foo(tbl: {[string]: woof)
995
// return tbl["foobarbaz"]
996
// end
997
//
998
// local v = foo({})
999
//
1000
// We want to consider `v` to be subscripted here.
1001
return {defArena->freshCell(Symbol{}, c->location, /*subscripted=*/true)};
1002
}
1003
1004
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprIndexName* i)
1005
{
1006
auto [parentDef, parentKey] = visitExpr(i->expr);
1007
std::string index = i->index.value;
1008
1009
DefId def = lookup(parentDef, index, i->location);
1010
return {def, keyArena->node(parentKey, def, index)};
1011
}
1012
1013
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprIndexExpr* i)
1014
{
1015
auto [parentDef, parentKey] = visitExpr(i->expr);
1016
visitExpr(i->index);
1017
1018
if (auto string = i->index->as<AstExprConstantString>())
1019
{
1020
std::string index{string->value.data, string->value.size};
1021
1022
DefId def = lookup(parentDef, index, i->location);
1023
return {def, keyArena->node(parentKey, def, index)};
1024
}
1025
1026
return {defArena->freshCell(Symbol{}, i->location, /* subscripted= */ true), nullptr};
1027
}
1028
DataFlowResult DataFlowGraphBuilder::visitFunction(AstExprFunction* f, NotNull<DfgScope> signatureScope)
1029
{
1030
if (AstLocal* self = f->self)
1031
{
1032
// There's no syntax for `self` to have an annotation if using `function t:m()`
1033
LUAU_ASSERT(!self->annotation);
1034
1035
DefId def = defArena->freshCell(f->debugname, f->location);
1036
graph.localDefs[self] = def;
1037
signatureScope->bindings[self] = def;
1038
captures[self].allVersions.push_back(def);
1039
}
1040
1041
for (AstLocal* param : f->args)
1042
{
1043
if (param->annotation)
1044
visitType(param->annotation);
1045
1046
DefId def = defArena->freshCell(param, param->location);
1047
graph.localDefs[param] = def;
1048
signatureScope->bindings[param] = def;
1049
captures[param].allVersions.push_back(def);
1050
}
1051
1052
if (f->varargAnnotation)
1053
visitTypePack(f->varargAnnotation);
1054
1055
if (f->returnAnnotation)
1056
visitTypePack(f->returnAnnotation);
1057
1058
// TODO: function body can be re-entrant, as in mutations that occurs at the end of the function can also be
1059
// visible to the beginning of the function, so statically speaking, the body of the function has an exit point
1060
// that points back to itself, e.g.
1061
//
1062
// local function f() print(f) f = 5 end
1063
// local g = f
1064
// g() --> function: address
1065
// g() --> 5
1066
visit(f->body);
1067
1068
return {defArena->freshCell(f->debugname, f->location), nullptr};
1069
}
1070
1071
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprFunction* f)
1072
{
1073
DfgScope* signatureScope = makeChildScope(DfgScope::Function);
1074
PushScope ps{scopeStack, signatureScope};
1075
1076
if (FFlag::LuauCaptureRecursiveCallsForTablesAndGlobals2)
1077
{
1078
return visitFunction(f, NotNull{signatureScope});
1079
}
1080
else
1081
{
1082
1083
if (AstLocal* self = f->self)
1084
{
1085
// There's no syntax for `self` to have an annotation if using `function t:m()`
1086
LUAU_ASSERT(!self->annotation);
1087
1088
DefId def = defArena->freshCell(f->debugname, f->location);
1089
graph.localDefs[self] = def;
1090
signatureScope->bindings[self] = def;
1091
captures[self].allVersions.push_back(def);
1092
}
1093
1094
for (AstLocal* param : f->args)
1095
{
1096
if (param->annotation)
1097
visitType(param->annotation);
1098
1099
DefId def = defArena->freshCell(param, param->location);
1100
graph.localDefs[param] = def;
1101
signatureScope->bindings[param] = def;
1102
captures[param].allVersions.push_back(def);
1103
}
1104
1105
if (f->varargAnnotation)
1106
visitTypePack(f->varargAnnotation);
1107
1108
if (f->returnAnnotation)
1109
visitTypePack(f->returnAnnotation);
1110
1111
// TODO: function body can be re-entrant, as in mutations that occurs at the end of the function can also be
1112
// visible to the beginning of the function, so statically speaking, the body of the function has an exit point
1113
// that points back to itself, e.g.
1114
//
1115
// local function f() print(f) f = 5 end
1116
// local g = f
1117
// g() --> function: address
1118
// g() --> 5
1119
visit(f->body);
1120
1121
return {defArena->freshCell(f->debugname, f->location), nullptr};
1122
}
1123
}
1124
1125
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTable* t)
1126
{
1127
DefId tableCell = defArena->freshCell(Symbol{}, t->location);
1128
currentScope()->props[tableCell] = {};
1129
for (AstExprTable::Item item : t->items)
1130
{
1131
DataFlowResult result = visitExpr(item.value);
1132
if (item.key)
1133
{
1134
visitExpr(item.key);
1135
if (auto string = item.key->as<AstExprConstantString>())
1136
{
1137
currentScope()->props[tableCell][string->value.data] = result.def;
1138
}
1139
}
1140
}
1141
1142
return {tableCell, nullptr};
1143
}
1144
1145
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprUnary* u)
1146
{
1147
visitExpr(u->expr);
1148
1149
return {defArena->freshCell(Symbol{}, u->location), nullptr};
1150
}
1151
1152
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprBinary* b)
1153
{
1154
auto left = visitExpr(b->left);
1155
auto right = visitExpr(b->right);
1156
// I think there's some subtlety here. There are probably cases where
1157
// X or Y / X and Y can _never_ "be subscripted."
1158
auto subscripted = (b->op == AstExprBinary::And || b->op == AstExprBinary::Or) &&
1159
(containsSubscriptedDefinition(left.def) || containsSubscriptedDefinition(right.def));
1160
return {defArena->freshCell(Symbol{}, b->location, subscripted), nullptr};
1161
}
1162
1163
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTypeAssertion* t)
1164
{
1165
auto [def, key] = visitExpr(t->expr);
1166
visitType(t->annotation);
1167
1168
return {def, key};
1169
}
1170
1171
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprIfElse* i)
1172
{
1173
visitExpr(i->condition);
1174
visitExpr(i->trueExpr);
1175
visitExpr(i->falseExpr);
1176
1177
return {defArena->freshCell(Symbol{}, i->location), nullptr};
1178
}
1179
1180
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprInterpString* i)
1181
{
1182
for (AstExpr* e : i->expressions)
1183
visitExpr(e);
1184
1185
return {defArena->freshCell(Symbol{}, i->location), nullptr};
1186
}
1187
1188
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprInstantiate* i)
1189
{
1190
if (FFlag::LuauExplicitTypeInstantiationSupport)
1191
{
1192
for (const AstTypeOrPack& typeOrPack : i->typeArguments)
1193
{
1194
if (typeOrPack.type)
1195
{
1196
visitType(typeOrPack.type);
1197
}
1198
else
1199
{
1200
LUAU_ASSERT(typeOrPack.typePack);
1201
visitTypePack(typeOrPack.typePack);
1202
}
1203
}
1204
}
1205
1206
return visitExpr(i->expr);
1207
}
1208
1209
1210
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprError* error)
1211
{
1212
DfgScope* unreachable = makeChildScope();
1213
PushScope ps{scopeStack, unreachable};
1214
1215
for (AstExpr* e : error->expressions)
1216
visitExpr(e);
1217
1218
return {defArena->freshCell(Symbol{}, error->location), nullptr};
1219
}
1220
1221
void DataFlowGraphBuilder::visitLValue(AstExpr* e, DefId incomingDef)
1222
{
1223
auto go = [&]()
1224
{
1225
if (auto l = e->as<AstExprLocal>())
1226
return visitLValue(l, incomingDef);
1227
else if (auto g = e->as<AstExprGlobal>())
1228
return visitLValue(g, incomingDef);
1229
else if (auto i = e->as<AstExprIndexName>())
1230
return visitLValue(i, incomingDef);
1231
else if (auto i = e->as<AstExprIndexExpr>())
1232
return visitLValue(i, incomingDef);
1233
else if (auto error = e->as<AstExprError>())
1234
return visitLValue(error, incomingDef);
1235
else
1236
handle->ice("Unknown AstExpr in DataFlowGraphBuilder::visitLValue");
1237
};
1238
1239
graph.astDefs[e] = go();
1240
}
1241
1242
DefId DataFlowGraphBuilder::visitLValue(AstExprLocal* l, DefId incomingDef)
1243
{
1244
DfgScope* scope = currentScope();
1245
1246
// In order to avoid alias tracking, we need to clip the reference to the parent def.
1247
if (!l->upvalue)
1248
{
1249
DefId updated = defArena->freshCell(l->local, l->location, containsSubscriptedDefinition(incomingDef));
1250
scope->bindings[l->local] = updated;
1251
captures[l->local].allVersions.push_back(updated);
1252
return updated;
1253
}
1254
else
1255
return visitExpr(static_cast<AstExpr*>(l)).def;
1256
}
1257
1258
DefId DataFlowGraphBuilder::visitLValue(AstExprGlobal* g, DefId incomingDef)
1259
{
1260
DfgScope* scope = currentScope();
1261
1262
DefId updated = defArena->freshCell(g->name, g->location, containsSubscriptedDefinition(incomingDef));
1263
scope->bindings[g->name] = updated;
1264
captures[g->name].allVersions.push_back(updated);
1265
return updated;
1266
}
1267
1268
DefId DataFlowGraphBuilder::visitLValue(AstExprIndexName* i, DefId incomingDef)
1269
{
1270
DefId parentDef = visitExpr(i->expr).def;
1271
1272
DfgScope* scope = currentScope();
1273
DefId updated = defArena->freshCell(i->index, i->location, containsSubscriptedDefinition(incomingDef));
1274
scope->props[parentDef][i->index.value] = updated;
1275
return updated;
1276
}
1277
1278
DefId DataFlowGraphBuilder::visitLValue(AstExprIndexExpr* i, DefId incomingDef)
1279
{
1280
DefId parentDef = visitExpr(i->expr).def;
1281
visitExpr(i->index);
1282
1283
DfgScope* scope = currentScope();
1284
if (auto string = i->index->as<AstExprConstantString>())
1285
{
1286
DefId updated = defArena->freshCell(Symbol{}, i->location, containsSubscriptedDefinition(incomingDef));
1287
scope->props[parentDef][string->value.data] = updated;
1288
return updated;
1289
}
1290
else
1291
return defArena->freshCell(Symbol{}, i->location, /*subscripted=*/true);
1292
}
1293
1294
DefId DataFlowGraphBuilder::visitLValue(AstExprError* error, DefId incomingDef)
1295
{
1296
return visitExpr(error).def;
1297
}
1298
1299
void DataFlowGraphBuilder::visitType(AstType* t)
1300
{
1301
if (auto r = t->as<AstTypeReference>())
1302
return visitType(r);
1303
else if (auto table = t->as<AstTypeTable>())
1304
return visitType(table);
1305
else if (auto f = t->as<AstTypeFunction>())
1306
return visitType(f);
1307
else if (auto tyof = t->as<AstTypeTypeof>())
1308
return visitType(tyof);
1309
else if (t->is<AstTypeOptional>())
1310
return;
1311
else if (auto u = t->as<AstTypeUnion>())
1312
return visitType(u);
1313
else if (auto i = t->as<AstTypeIntersection>())
1314
return visitType(i);
1315
else if (auto e = t->as<AstTypeError>())
1316
return visitType(e);
1317
else if (t->is<AstTypeSingletonBool>())
1318
return; // ok
1319
else if (t->is<AstTypeSingletonString>())
1320
return; // ok
1321
else if (auto g = t->as<AstTypeGroup>())
1322
return visitType(g->type);
1323
else
1324
handle->ice("Unknown AstType in DataFlowGraphBuilder::visitType");
1325
}
1326
1327
void DataFlowGraphBuilder::visitType(AstTypeReference* r)
1328
{
1329
for (AstTypeOrPack param : r->parameters)
1330
{
1331
if (param.type)
1332
visitType(param.type);
1333
else
1334
visitTypePack(param.typePack);
1335
}
1336
}
1337
1338
void DataFlowGraphBuilder::visitType(AstTypeTable* t)
1339
{
1340
for (AstTableProp p : t->props)
1341
visitType(p.type);
1342
1343
if (t->indexer)
1344
{
1345
visitType(t->indexer->indexType);
1346
visitType(t->indexer->resultType);
1347
}
1348
}
1349
1350
void DataFlowGraphBuilder::visitType(AstTypeFunction* f)
1351
{
1352
visitGenerics(f->generics);
1353
visitGenericPacks(f->genericPacks);
1354
visitTypeList(f->argTypes);
1355
visitTypePack(f->returnTypes);
1356
}
1357
1358
void DataFlowGraphBuilder::visitType(AstTypeTypeof* t)
1359
{
1360
visitExpr(t->expr);
1361
}
1362
1363
void DataFlowGraphBuilder::visitType(AstTypeUnion* u)
1364
{
1365
for (AstType* t : u->types)
1366
visitType(t);
1367
}
1368
1369
void DataFlowGraphBuilder::visitType(AstTypeIntersection* i)
1370
{
1371
for (AstType* t : i->types)
1372
visitType(t);
1373
}
1374
1375
void DataFlowGraphBuilder::visitType(AstTypeError* error)
1376
{
1377
for (AstType* t : error->types)
1378
visitType(t);
1379
}
1380
1381
void DataFlowGraphBuilder::visitTypePack(AstTypePack* p)
1382
{
1383
if (auto e = p->as<AstTypePackExplicit>())
1384
return visitTypePack(e);
1385
else if (auto v = p->as<AstTypePackVariadic>())
1386
return visitTypePack(v);
1387
else if (p->is<AstTypePackGeneric>())
1388
return; // ok
1389
else
1390
handle->ice("Unknown AstTypePack in DataFlowGraphBuilder::visitTypePack");
1391
}
1392
1393
void DataFlowGraphBuilder::visitTypePack(AstTypePackExplicit* e)
1394
{
1395
visitTypeList(e->typeList);
1396
}
1397
1398
void DataFlowGraphBuilder::visitTypePack(AstTypePackVariadic* v)
1399
{
1400
visitType(v->variadicType);
1401
}
1402
1403
void DataFlowGraphBuilder::visitTypeList(AstTypeList l)
1404
{
1405
for (AstType* t : l.types)
1406
visitType(t);
1407
1408
if (l.tailType)
1409
visitTypePack(l.tailType);
1410
}
1411
1412
void DataFlowGraphBuilder::visitGenerics(AstArray<AstGenericType*> g)
1413
{
1414
for (AstGenericType* generic : g)
1415
{
1416
if (generic->defaultValue)
1417
visitType(generic->defaultValue);
1418
}
1419
}
1420
1421
void DataFlowGraphBuilder::visitGenericPacks(AstArray<AstGenericTypePack*> g)
1422
{
1423
for (AstGenericTypePack* generic : g)
1424
{
1425
if (generic->defaultValue)
1426
visitTypePack(generic->defaultValue);
1427
}
1428
}
1429
1430
} // namespace Luau
1431
1432