Skip to content

Commit 893c9aa

Browse files
committed
Enforce that definite first initialization cannot happen inside loops
Closes #1049
1 parent 5ac5df9 commit 893c9aa

7 files changed

+125
-12
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
f: () = {
3+
i: int;
4+
if true {
5+
while true {
6+
i = 42; // ERROR: can't initialize i in a loop
7+
}
8+
i = 42;
9+
}
10+
else {
11+
i = 42;
12+
}
13+
i = 42;
14+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
f: () = {
3+
i: int;
4+
while true {
5+
i = 42; // ERROR: can't initialize i in a loop
6+
}
7+
i = 42;
8+
}

regression-tests/pure2-initialization-safety-with-else-if.cpp2

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,18 @@ main: (args) = {
2323

2424
std::cout << p* << std::endl;
2525
}
26+
27+
28+
ok: () = {
29+
i: int;
30+
if true {
31+
i = 42;
32+
while true { // OK: in-branch loop is after initialization
33+
i = 42;
34+
}
35+
}
36+
else {
37+
i = 42;
38+
}
39+
i = 42;
40+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pure2-initialization-loop-2-error.cpp2...
2+
pure2-initialization-loop-2-error.cpp2(6,13): error: local variable i cannot be initialized inside a loop
3+
==> program violates initialization safety guarantee - see previous errors
4+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pure2-initialization-loop-error.cpp2...
2+
pure2-initialization-loop-error.cpp2(5,9): error: local variable i cannot be initialized inside a loop
3+
==> program violates initialization safety guarantee - see previous errors
4+

regression-tests/test-results/pure2-initialization-safety-with-else-if.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
#line 1 "pure2-initialization-safety-with-else-if.cpp2"
1515
auto main(int const argc_, char** argv_) -> int;
1616

17+
#line 28 "pure2-initialization-safety-with-else-if.cpp2"
18+
auto ok() -> void;
19+
1720
//=== Cpp2 function definitions =================================================
1821

1922
#line 1 "pure2-initialization-safety-with-else-if.cpp2"
@@ -45,3 +48,18 @@ auto main(int const argc_, char** argv_) -> int{
4548
std::cout << *cpp2::impl::assert_not_null(cpp2::move(p.value())) << std::endl;
4649
}
4750

51+
#line 28 "pure2-initialization-safety-with-else-if.cpp2"
52+
auto ok() -> void{
53+
cpp2::impl::deferred_init<int> i;
54+
if (true) {
55+
i.construct(42);
56+
while( true ) { // OK: in-branch loop is after initialization
57+
i.value() = 42;
58+
}
59+
}
60+
else {
61+
i.construct(42);
62+
}
63+
i.value() = 42;
64+
}
65+

source/sema.h

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ struct selection_sym {
174174
struct compound_sym {
175175
bool start = false;
176176
compound_statement_node const* compound = {};
177-
enum kind { is_scope, is_true, is_false } kind_ = is_scope;
177+
enum kind { is_scope, is_true, is_false, is_loop } kind_ = is_scope;
178178

179179
compound_sym(
180180
bool s,
@@ -373,6 +373,7 @@ class sema
373373
index_t global_token_counter = 1;
374374

375375
std::vector<selection_statement_node const*> active_selections;
376+
std::vector<iteration_statement_node const*> active_iterations;
376377
std::vector<declaration_sym const*> current_declarations;
377378

378379
struct declaration_of_t {
@@ -648,10 +649,12 @@ class sema
648649
else if (sym.kind_ == sym.is_false) {
649650
o << "false branch";
650651
}
652+
else if (sym.kind_ == sym.is_loop) {
653+
o << "loop";
654+
}
651655
else {
652656
o << "scope";
653657
}
654-
655658
}
656659

657660
break;default:
@@ -1164,7 +1167,7 @@ class sema
11641167
{
11651168
auto name = decl->identifier->to_string();
11661169

1167-
struct stack_entry{
1170+
struct selection_stack_entry{
11681171
int pos; // start of this selection statement
11691172

11701173
struct branch {
@@ -1175,17 +1178,34 @@ class sema
11751178
};
11761179
std::vector<branch> branches;
11771180

1178-
stack_entry(int p) : pos{p} { }
1181+
selection_stack_entry(int p) : pos{p} { }
11791182

11801183
auto debug_print(std::ostream& o) const -> void
11811184
{
1182-
o << "Stack entry: " << pos << "\n";
1185+
o << "Selection stack entry: " << pos << "\n";
11831186
for (auto const& e : branches) {
11841187
o << " ( " << e.start << " , " << e.result << " )\n";
11851188
}
11861189
}
11871190
};
1188-
std::vector<stack_entry> selection_stack;
1191+
std::vector<selection_stack_entry> selection_stack;
1192+
1193+
std::vector<source_position> loop_stack; // start of each active loop
1194+
1195+
1196+
auto record_result = [&](token const* t) -> bool {
1197+
if (!loop_stack.empty()) {
1198+
errors.emplace_back(
1199+
t->position(),
1200+
"local variable " + name
1201+
+ " cannot be initialized inside a loop"
1202+
);
1203+
return false;
1204+
}
1205+
definite_initializations.push_back(t);
1206+
return true;
1207+
};
1208+
11891209

11901210
for (
11911211
;
@@ -1236,7 +1256,9 @@ class sema
12361256
// just return true if it's an assignment to it, else return false
12371257
if (std::ssize(selection_stack) == 0) {
12381258
if (sym.standalone_assignment_to) {
1239-
definite_initializations.push_back( sym.identifier );
1259+
if (!record_result(sym.identifier)) {
1260+
return false;
1261+
}
12401262
}
12411263
else {
12421264
errors.emplace_back(
@@ -1254,7 +1276,9 @@ class sema
12541276
// if we weren't an a selection statement
12551277
if (std::ssize(selection_stack) == 1) {
12561278
if (sym.standalone_assignment_to) {
1257-
definite_initializations.push_back( sym.identifier );
1279+
if (!record_result(sym.identifier)) {
1280+
return false;
1281+
}
12581282
}
12591283
else {
12601284
errors.emplace_back(
@@ -1282,7 +1306,9 @@ class sema
12821306
// and record this as the result for the current branch
12831307
else {
12841308
if (sym.standalone_assignment_to) {
1285-
definite_initializations.push_back( sym.identifier );
1309+
if (!record_result(sym.identifier)) {
1310+
return false;
1311+
}
12861312
}
12871313
else {
12881314
errors.emplace_back(
@@ -1390,8 +1416,20 @@ class sema
13901416
break;case symbol::active::compound: {
13911417
auto const& sym = std::get<symbol::active::compound>(symbols[pos].sym);
13921418

1393-
// If we're in a selection
1394-
if (std::ssize(selection_stack) > 0) {
1419+
// Track loop entries/exits
1420+
if (sym.kind_ == sym.is_loop)
1421+
{
1422+
if (sym.start) {
1423+
loop_stack.push_back( sym.position() );
1424+
}
1425+
else {
1426+
assert (!loop_stack.empty());
1427+
loop_stack.pop_back();
1428+
}
1429+
}
1430+
1431+
// Else if we're at a selection
1432+
else if (std::ssize(selection_stack) > 0) {
13951433
// If this is a compound start with the current selection's depth
13961434
// plus one, it's the start of one of the branches of that selection
13971435
if (
@@ -2316,6 +2354,7 @@ class sema
23162354

23172355
auto start(iteration_statement_node const& n, int) -> void
23182356
{
2357+
active_iterations.push_back(&n);
23192358
if (*n.identifier != "for") {
23202359
symbols.emplace_back( scope_depth, identifier_sym( false, n.identifier ) );
23212360
}
@@ -2324,6 +2363,7 @@ class sema
23242363
auto end(iteration_statement_node const& n, int) -> void
23252364
{
23262365
symbols.emplace_back( scope_depth, identifier_sym( false, n.identifier, identifier_sym::deactivation ) );
2366+
active_iterations.pop_back();
23272367
}
23282368

23292369
auto start(loop_body_tag const& n, int) -> void
@@ -2688,7 +2728,16 @@ class sema
26882728
-> compound_sym::kind
26892729
{
26902730
auto kind = compound_sym::is_scope;
2691-
if (!active_selections.empty())
2731+
2732+
if (
2733+
!active_iterations.empty()
2734+
&& active_iterations.back()->statements.get() == &n
2735+
)
2736+
{
2737+
kind = compound_sym::is_loop;
2738+
}
2739+
2740+
else if (!active_selections.empty())
26922741
{
26932742
assert(active_selections.back()->true_branch);
26942743
if (active_selections.back()->true_branch.get() == &n)
@@ -2703,6 +2752,7 @@ class sema
27032752
kind = compound_sym::is_false;
27042753
}
27052754
}
2755+
27062756
return kind;
27072757
}
27082758

0 commit comments

Comments
 (0)