Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/tests/DataFlowGraph.test.cpp
2723 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
#include "Fixture.h"
4
#include "Luau/Def.h"
5
#include "Luau/Error.h"
6
#include "Luau/Parser.h"
7
8
#include "AstQueryDsl.h"
9
#include "ScopedFlags.h"
10
11
#include "doctest.h"
12
13
using namespace Luau;
14
15
LUAU_FASTFLAG(DebugLuauForceOldSolver);
16
17
struct DataFlowGraphFixture
18
{
19
// Only needed to fix the operator== reflexivity of an empty Symbol.
20
ScopedFastFlag dcr{FFlag::DebugLuauForceOldSolver, false};
21
22
DefArena defArena;
23
RefinementKeyArena keyArena;
24
InternalErrorReporter handle;
25
26
Allocator allocator;
27
AstNameTable names{allocator};
28
AstStatBlock* module;
29
30
std::optional<DataFlowGraph> graph;
31
32
void dfg(const std::string& code)
33
{
34
ParseResult parseResult = Parser::parse(code.c_str(), code.size(), names, allocator);
35
if (!parseResult.errors.empty())
36
throw ParseErrors(std::move(parseResult.errors));
37
module = parseResult.root;
38
graph = DataFlowGraphBuilder::build(module, NotNull{&defArena}, NotNull{&keyArena}, NotNull{&handle});
39
}
40
41
template<typename T, int N>
42
DefId getDef(const std::vector<Nth>& nths = {nth<T>(N)})
43
{
44
T* node = query<T, N>(module, nths);
45
REQUIRE(node);
46
return graph->getDef(node);
47
}
48
49
void checkOperands(const Phi* phi, std::vector<DefId> operands)
50
{
51
Set<const Def*> operandSet{nullptr};
52
for (auto o : operands)
53
operandSet.insert(o.get());
54
CHECK(phi->operands.size() == operandSet.size());
55
for (auto o : phi->operands)
56
CHECK(operandSet.contains(o.get()));
57
}
58
};
59
60
TEST_SUITE_BEGIN("DataFlowGraphBuilder");
61
62
TEST_CASE_FIXTURE(DataFlowGraphFixture, "define_locals_in_local_stat")
63
{
64
dfg(R"(
65
local x = 5
66
local y = x
67
)");
68
69
(void)getDef<AstExprLocal, 1>();
70
}
71
72
TEST_CASE_FIXTURE(DataFlowGraphFixture, "define_parameters_in_functions")
73
{
74
dfg(R"(
75
local function f(x)
76
local y = x
77
end
78
)");
79
80
(void)getDef<AstExprLocal, 1>();
81
}
82
83
TEST_CASE_FIXTURE(DataFlowGraphFixture, "find_aliases")
84
{
85
dfg(R"(
86
local x = 5
87
local y = x
88
local z = y
89
)");
90
91
DefId x = getDef<AstExprLocal, 1>();
92
DefId y = getDef<AstExprLocal, 2>();
93
REQUIRE(x != y);
94
}
95
96
TEST_CASE_FIXTURE(DataFlowGraphFixture, "independent_locals")
97
{
98
dfg(R"(
99
local x = 5
100
local y = 5
101
102
local a = x
103
local b = y
104
)");
105
106
DefId x = getDef<AstExprLocal, 1>();
107
DefId y = getDef<AstExprLocal, 2>();
108
REQUIRE(x != y);
109
}
110
111
TEST_CASE_FIXTURE(DataFlowGraphFixture, "phi")
112
{
113
dfg(R"(
114
local x
115
116
if a then
117
x = true
118
end
119
120
local y = x
121
)");
122
123
DefId y = getDef<AstExprLocal, 2>();
124
125
const Phi* phi = get<Phi>(y);
126
CHECK(phi);
127
}
128
129
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_not_owned_by_while")
130
{
131
dfg(R"(
132
local x
133
134
while cond() do
135
x = true
136
end
137
138
local y = x
139
)");
140
141
DefId x0 = graph->getDef(query<AstStatLocal>(module)->vars.data[0]);
142
DefId x1 = getDef<AstExprLocal, 1>(); // x = true
143
DefId x2 = getDef<AstExprLocal, 2>(); // local y = x
144
145
auto phi = get<Phi>(x2);
146
REQUIRE(phi);
147
checkOperands(phi, {x0, x1});
148
}
149
150
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_owned_by_while")
151
{
152
dfg(R"(
153
while cond() do
154
local x
155
x = true
156
x = 5
157
end
158
)");
159
160
DefId x0 = graph->getDef(query<AstStatLocal>(module)->vars.data[0]);
161
DefId x1 = getDef<AstExprLocal, 1>(); // x = true
162
DefId x2 = getDef<AstExprLocal, 2>(); // x = 5
163
164
CHECK(x0 != x1);
165
CHECK(x1 != x2);
166
}
167
168
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_not_owned_by_repeat")
169
{
170
dfg(R"(
171
local x
172
173
repeat
174
x = true
175
until cond()
176
177
local y = x
178
)");
179
180
DefId x0 = graph->getDef(query<AstStatLocal>(module)->vars.data[0]);
181
DefId x1 = getDef<AstExprLocal, 1>(); // x = true
182
DefId x2 = getDef<AstExprLocal, 2>(); // local y = x
183
184
CHECK(x0 != x1);
185
CHECK(x1 == x2);
186
}
187
188
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_owned_by_repeat")
189
{
190
dfg(R"(
191
repeat
192
local x
193
x = true
194
x = 5
195
until cond()
196
)");
197
198
DefId x0 = graph->getDef(query<AstStatLocal>(module)->vars.data[0]);
199
DefId x1 = getDef<AstExprLocal, 1>(); // x = true
200
DefId x2 = getDef<AstExprLocal, 2>(); // x = 5
201
202
CHECK(x0 != x1);
203
CHECK(x1 != x2);
204
}
205
206
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_not_owned_by_for")
207
{
208
dfg(R"(
209
local x
210
211
for i = 0, 5 do
212
x = true
213
end
214
215
local y = x
216
)");
217
218
DefId x0 = graph->getDef(query<AstStatLocal>(module)->vars.data[0]);
219
DefId x1 = getDef<AstExprLocal, 1>(); // x = true
220
DefId x2 = getDef<AstExprLocal, 2>(); // local y = x
221
222
auto phi = get<Phi>(x2);
223
REQUIRE(phi);
224
checkOperands(phi, {x0, x1});
225
}
226
227
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_owned_by_for")
228
{
229
dfg(R"(
230
for i = 0, 5 do
231
local x
232
x = true
233
x = 5
234
end
235
)");
236
237
DefId x0 = graph->getDef(query<AstStatLocal>(module)->vars.data[0]);
238
DefId x1 = getDef<AstExprLocal, 1>(); // x = true
239
DefId x2 = getDef<AstExprLocal, 2>(); // x = 5
240
241
CHECK(x0 != x1);
242
CHECK(x1 != x2);
243
}
244
245
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_not_owned_by_for_in")
246
{
247
dfg(R"(
248
local x
249
250
for i, v in t do
251
x = true
252
end
253
254
local y = x
255
)");
256
257
DefId x0 = graph->getDef(query<AstStatLocal>(module)->vars.data[0]);
258
DefId x1 = getDef<AstExprLocal, 1>(); // x = true
259
DefId x2 = getDef<AstExprLocal, 2>(); // local y = x
260
261
auto phi = get<Phi>(x2);
262
REQUIRE(phi);
263
checkOperands(phi, {x0, x1});
264
}
265
266
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_owned_by_for_in")
267
{
268
dfg(R"(
269
for i, v in t do
270
local x
271
x = true
272
x = 5
273
end
274
)");
275
276
DefId x0 = graph->getDef(query<AstStatLocal>(module)->vars.data[0]);
277
DefId x1 = getDef<AstExprLocal, 1>(); // x = true
278
DefId x2 = getDef<AstExprLocal, 2>(); // x = 5
279
280
CHECK(x0 != x1);
281
CHECK(x1 != x2);
282
}
283
284
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_preexisting_property_not_owned_by_while")
285
{
286
dfg(R"(
287
local t = {}
288
t.x = 5
289
290
while cond() do
291
t.x = true
292
end
293
294
local y = t.x
295
)");
296
297
DefId x1 = getDef<AstExprIndexName, 1>(); // t.x = 5
298
DefId x2 = getDef<AstExprIndexName, 2>(); // t.x = true
299
DefId x3 = getDef<AstExprIndexName, 3>(); // local y = t.x
300
301
auto phi = get<Phi>(x3);
302
REQUIRE(phi);
303
checkOperands(phi, {x1, x2});
304
}
305
306
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_non_preexisting_property_not_owned_by_while")
307
{
308
dfg(R"(
309
local t = {}
310
311
while cond() do
312
t.x = true
313
end
314
315
local y = t.x
316
)");
317
318
DefId x1 = getDef<AstExprIndexName, 1>(); // t.x = true
319
DefId x2 = getDef<AstExprIndexName, 2>(); // local y = t.x
320
321
CHECK(x1 == x2);
322
}
323
324
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_property_of_table_owned_by_while")
325
{
326
dfg(R"(
327
while cond() do
328
local t = {}
329
t.x = true
330
t.x = 5
331
end
332
)");
333
334
DefId x1 = getDef<AstExprIndexName, 1>(); // t.x = true
335
DefId x2 = getDef<AstExprIndexName, 2>(); // t.x = 5
336
337
CHECK(x1 != x2);
338
}
339
340
TEST_CASE_FIXTURE(DataFlowGraphFixture, "property_lookup_on_a_phi_node")
341
{
342
dfg(R"(
343
local t = {}
344
t.x = 5
345
346
if cond() then
347
t.x = 7
348
end
349
350
print(t.x)
351
)");
352
353
DefId x1 = getDef<AstExprIndexName, 1>(); // t.x = 5
354
DefId x2 = getDef<AstExprIndexName, 2>(); // t.x = 7
355
DefId x3 = getDef<AstExprIndexName, 3>(); // print(t.x)
356
357
CHECK(x1 != x2);
358
CHECK(x2 != x3);
359
360
const Phi* phi = get<Phi>(x3);
361
REQUIRE(phi);
362
REQUIRE(phi->operands.size() == 2);
363
CHECK(phi->operands.at(0) == x1);
364
CHECK(phi->operands.at(1) == x2);
365
}
366
367
TEST_CASE_FIXTURE(DataFlowGraphFixture, "property_lookup_on_a_phi_node_2")
368
{
369
dfg(R"(
370
local t = {}
371
372
if cond() then
373
t.x = 5
374
else
375
t.x = 7
376
end
377
378
print(t.x)
379
)");
380
381
DefId x1 = getDef<AstExprIndexName, 1>(); // t.x = 5
382
DefId x2 = getDef<AstExprIndexName, 2>(); // t.x = 7
383
DefId x3 = getDef<AstExprIndexName, 3>(); // print(t.x)
384
385
CHECK(x1 != x2);
386
CHECK(x2 != x3);
387
388
const Phi* phi = get<Phi>(x3);
389
REQUIRE(phi);
390
REQUIRE(phi->operands.size() == 2);
391
CHECK(phi->operands.at(0) == x2);
392
CHECK(phi->operands.at(1) == x1);
393
}
394
395
TEST_CASE_FIXTURE(DataFlowGraphFixture, "property_lookup_on_a_phi_node_3")
396
{
397
dfg(R"(
398
local t = {}
399
t.x = 3
400
401
if cond() then
402
t.x = 5
403
t.y = 7
404
else
405
t.z = 42
406
end
407
408
print(t.x)
409
print(t.y)
410
print(t.z)
411
)");
412
413
DefId x1 = getDef<AstExprIndexName, 1>(); // t.x = 3
414
DefId x2 = getDef<AstExprIndexName, 2>(); // t.x = 5
415
416
DefId y1 = getDef<AstExprIndexName, 3>(); // t.y = 7
417
418
DefId z1 = getDef<AstExprIndexName, 4>(); // t.z = 42
419
420
DefId x3 = getDef<AstExprIndexName, 5>(); // print(t.x)
421
DefId y2 = getDef<AstExprIndexName, 6>(); // print(t.y)
422
DefId z2 = getDef<AstExprIndexName, 7>(); // print(t.z)
423
424
CHECK(x1 != x2);
425
CHECK(x2 != x3);
426
CHECK(y1 == y2);
427
CHECK(z1 == z2);
428
429
const Phi* phi = get<Phi>(x3);
430
REQUIRE(phi);
431
REQUIRE(phi->operands.size() == 2);
432
CHECK(phi->operands.at(0) == x1);
433
CHECK(phi->operands.at(1) == x2);
434
}
435
436
TEST_CASE_FIXTURE(DataFlowGraphFixture, "function_captures_are_phi_nodes_of_all_versions")
437
{
438
dfg(R"(
439
local x = 5
440
441
function f()
442
print(x)
443
x = nil
444
end
445
446
f()
447
x = "five"
448
)");
449
450
DefId x1 = graph->getDef(query<AstStatLocal>(module)->vars.data[0]);
451
DefId x2 = getDef<AstExprLocal, 1>(); // print(x)
452
DefId x3 = getDef<AstExprLocal, 2>(); // x = nil
453
DefId x4 = getDef<AstExprLocal, 3>(); // x = "five"
454
455
CHECK(x1 != x2);
456
CHECK(x2 == x3);
457
CHECK(x3 != x4);
458
459
const Phi* phi = get<Phi>(x2);
460
REQUIRE(phi);
461
REQUIRE(phi->operands.size() == 2);
462
CHECK(phi->operands.at(0) == x1);
463
CHECK(phi->operands.at(1) == x4);
464
}
465
466
TEST_CASE_FIXTURE(DataFlowGraphFixture, "function_captures_are_phi_nodes_of_all_versions_properties")
467
{
468
dfg(R"(
469
local t = {}
470
t.x = 5
471
472
function f()
473
print(t.x)
474
t.x = nil
475
end
476
477
f()
478
t.x = "five"
479
)");
480
481
DefId x1 = getDef<AstExprIndexName, 1>(); // t.x = 5
482
DefId x2 = getDef<AstExprIndexName, 2>(); // print(t.x)
483
DefId x3 = getDef<AstExprIndexName, 3>(); // t.x = nil
484
DefId x4 = getDef<AstExprIndexName, 4>(); // t.x = "five"
485
486
CHECK(x1 != x2);
487
CHECK(x2 != x3);
488
CHECK(x3 != x4);
489
490
// When a local is referenced within a function, it is not pointer identical.
491
// Instead, it's a phi node of all possible versions, including just one version.
492
DefId t1 = graph->getDef(query<AstStatLocal>(module)->vars.data[0]);
493
DefId t2 = getDef<AstExprLocal, 2>(); // print(t.x)
494
495
const Phi* phi = get<Phi>(t2);
496
REQUIRE(phi);
497
REQUIRE(phi->operands.size() == 1);
498
CHECK(phi->operands.at(0) == t1);
499
}
500
501
TEST_CASE_FIXTURE(DataFlowGraphFixture, "local_f_which_is_prototyped_enclosed_by_function")
502
{
503
dfg(R"(
504
local f
505
function f()
506
if cond() then
507
f()
508
end
509
end
510
)");
511
512
DefId f1 = graph->getDef(query<AstStatLocal>(module)->vars.data[0]);
513
DefId f2 = getDef<AstExprLocal, 1>(); // function f()
514
DefId f3 = getDef<AstExprLocal, 2>(); // f()
515
516
CHECK(f1 != f2);
517
CHECK(f2 != f3);
518
519
const Phi* phi = get<Phi>(f3);
520
REQUIRE(phi);
521
REQUIRE(phi->operands.size() == 1);
522
CHECK(phi->operands.at(0) == f2);
523
}
524
525
TEST_CASE_FIXTURE(DataFlowGraphFixture, "local_f_which_is_prototyped_enclosed_by_function_has_some_prior_versions")
526
{
527
dfg(R"(
528
local f
529
f = 5
530
function f()
531
if cond() then
532
f()
533
end
534
end
535
)");
536
537
DefId f1 = graph->getDef(query<AstStatLocal>(module)->vars.data[0]);
538
DefId f2 = getDef<AstExprLocal, 1>(); // f = 5
539
DefId f3 = getDef<AstExprLocal, 2>(); // function f()
540
DefId f4 = getDef<AstExprLocal, 3>(); // f()
541
542
CHECK(f1 != f2);
543
CHECK(f2 != f3);
544
CHECK(f3 != f4);
545
546
const Phi* phi = get<Phi>(f4);
547
REQUIRE(phi);
548
REQUIRE(phi->operands.size() == 1);
549
CHECK(phi->operands.at(0) == f3);
550
}
551
552
TEST_CASE_FIXTURE(DataFlowGraphFixture, "local_f_which_is_prototyped_enclosed_by_function_has_some_future_versions")
553
{
554
dfg(R"(
555
local f
556
function f()
557
if cond() then
558
f()
559
end
560
end
561
f = 5
562
)");
563
564
DefId f1 = graph->getDef(query<AstStatLocal>(module)->vars.data[0]);
565
DefId f2 = getDef<AstExprLocal, 1>(); // function f()
566
DefId f3 = getDef<AstExprLocal, 2>(); // f()
567
DefId f4 = getDef<AstExprLocal, 3>(); // f = 5
568
569
CHECK(f1 != f2);
570
CHECK(f2 != f3);
571
CHECK(f3 != f4);
572
573
const Phi* phi = get<Phi>(f3);
574
REQUIRE(phi);
575
REQUIRE(phi->operands.size() == 2);
576
CHECK(phi->operands.at(0) == f2);
577
CHECK(phi->operands.at(1) == f4);
578
}
579
580
TEST_CASE_FIXTURE(DataFlowGraphFixture, "phi_node_if_case_binding")
581
{
582
dfg(R"(
583
local x = nil
584
if true then
585
if true then
586
x = 5
587
end
588
print(x)
589
else
590
print(x)
591
end
592
)");
593
DefId x1 = graph->getDef(query<AstStatLocal>(module)->vars.data[0]);
594
DefId x2 = getDef<AstExprLocal, 1>(); // x = 5
595
DefId x3 = getDef<AstExprLocal, 2>(); // print(x)
596
597
const Phi* phi = get<Phi>(x3);
598
REQUIRE(phi);
599
CHECK(phi->operands.at(0) == x2);
600
CHECK(phi->operands.at(1) == x1);
601
}
602
603
TEST_CASE_FIXTURE(DataFlowGraphFixture, "phi_node_if_case_table_prop")
604
{
605
dfg(R"(
606
local t = {}
607
t.x = true
608
if true then
609
if true then
610
t.x = 5
611
end
612
print(t.x)
613
else
614
print(t.x)
615
end
616
)");
617
618
DefId x1 = getDef<AstExprIndexName, 1>(); // t.x = true
619
DefId x2 = getDef<AstExprIndexName, 2>(); // t.x = 5
620
621
DefId x3 = getDef<AstExprIndexName, 3>(); // print(t.x)
622
const Phi* phi = get<Phi>(x3);
623
REQUIRE(phi);
624
CHECK(phi->operands.size() == 2);
625
CHECK(phi->operands.at(0) == x1);
626
CHECK(phi->operands.at(1) == x2);
627
}
628
629
TEST_CASE_FIXTURE(DataFlowGraphFixture, "phi_node_if_case_table_prop_literal")
630
{
631
dfg(R"(
632
local t = { x = true }
633
if true then
634
t.x = 5
635
end
636
print(t.x)
637
638
)");
639
640
DefId x1 = getDef<AstExprConstantBool, 1>(); // {x = true <- }
641
DefId x2 = getDef<AstExprIndexName, 1>(); // t.x = 5
642
DefId x3 = getDef<AstExprIndexName, 2>(); // print(t.x)
643
const Phi* phi = get<Phi>(x3);
644
REQUIRE(phi);
645
CHECK(phi->operands.size() == 2);
646
CHECK(phi->operands.at(0) == x1);
647
CHECK(phi->operands.at(1) == x2);
648
}
649
650
TEST_CASE_FIXTURE(DataFlowGraphFixture, "insert_trivial_phi_nodes_inside_of_phi_nodes")
651
{
652
dfg(R"(
653
local t = {}
654
655
local function f(k: string)
656
if t[k] ~= nil then
657
return
658
end
659
660
t[k] = 5
661
end
662
)");
663
664
DefId t1 = graph->getDef(query<AstStatLocal>(module)->vars.data[0]); // local t = {}
665
DefId t2 = getDef<AstExprLocal, 1>(); // t[k] ~= nil
666
DefId t3 = getDef<AstExprLocal, 3>(); // t[k] = 5
667
668
CHECK(t1 != t2);
669
CHECK(t2 == t3);
670
671
const Phi* t2phi = get<Phi>(t2);
672
REQUIRE(t2phi);
673
CHECK(t2phi->operands.size() == 1);
674
CHECK(t2phi->operands.at(0) == t1);
675
}
676
677
TEST_CASE_FIXTURE(DataFlowGraphFixture, "dfg_function_definition_in_a_do_block")
678
{
679
dfg(R"(
680
local f
681
do
682
function f()
683
end
684
end
685
f()
686
)");
687
688
DefId x1 = graph->getDef(query<AstStatLocal>(module)->vars.data[0]);
689
DefId x2 = getDef<AstExprLocal, 1>(); // x = 5
690
DefId x3 = getDef<AstExprLocal, 2>(); // print(x)
691
692
CHECK(x1 != x2);
693
CHECK(x1 != x3);
694
CHECK(x2 == x3);
695
}
696
697
TEST_CASE_FIXTURE(DataFlowGraphFixture, "dfg_captured_local_is_assigned_a_function")
698
{
699
dfg(R"(
700
local f
701
702
local function g()
703
f()
704
end
705
706
function f()
707
end
708
)");
709
710
DefId f1 = graph->getDef(query<AstStatLocal>(module)->vars.data[0]);
711
DefId f2 = getDef<AstExprLocal, 1>();
712
DefId f3 = getDef<AstExprLocal, 2>();
713
714
CHECK(f1 != f2);
715
CHECK(f2 != f3);
716
717
const Phi* f2phi = get<Phi>(f2);
718
REQUIRE(f2phi);
719
CHECK(f2phi->operands.size() == 1);
720
CHECK(f2phi->operands.at(0) == f3);
721
}
722
723
TEST_SUITE_END();
724
725