Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,472 changes: 871 additions & 601 deletions src/interpret.c

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions src/pending_changes/gh-401-statement-expression-break.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[GH #401]
break and continue in GNU statement expressions in constant evaluation

In GNU modes, a break or continue statement inside a statement expression
did not correctly transfer control out of the enclosing full-expression
during constant evaluation: the evaluation of the enclosing expression
continued with an indeterminate value for the statement expression, and the
enclosing loop or switch statement was not exited. For example, with
--c++20 --gnu_version 130000:

constexpr int f(int v) {
int n = 0;
for (int i = 0; i < 5; ++i) {
n += ({ if (i == v) break; i; });
}
return n;
}
static_assert(f(3) == 3); // Previously failed.

This is now fixed. To that end, the interpretation of control transfers
(break, continue, and return statements, and the entry of a switch statement
at a case label) and of loops was reorganized.
23 changes: 23 additions & 0 deletions src/pending_changes/gh-402-switch-case-variable.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[GH #402]
Variables declared before a case label in constant evaluation

During constant evaluation, a variable declared (without initializer) after
one case label of a switch statement and used after a subsequent case label
was treated as inaccessible when the switch statement transferred control
to the subsequent label. The same happened for a variable declared without
initializer in the init-statement of an if- or for-statement whose
substatement contains the case label. For example, with --c++20:

constexpr int f() {
switch (1) {
for (int x; ;) {
case 1:
x = 3;
return x;
}
}
return 0;
}
static_assert(f() == 3); // Previously failed.

This is now fixed.
23 changes: 23 additions & 0 deletions src/pending_changes/gh-403-condition-variable-lifetime.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[GH #403]
Lifetime of condition variables in constant evaluation

During constant evaluation, a variable declared in the condition of an if-,
switch-, while-, or for-statement was destroyed before the substatement
(and, for a for-statement, the increment expression) was evaluated, instead
of at the end of the statement (or, for a loop, of the iteration). The same
was true for a variable declared in the init-statement of an if- or
switch-statement. For example, with --c++20:

struct S {
int* p;
constexpr ~S() { *p = *p * 10 + 2; }
constexpr explicit operator bool() const { return true; }
};
constexpr int f() {
int n = 0;
if (S s{&n}) n = n * 10 + 1;
return n;
}
static_assert(f() == 12); // Previously failed.

This is now fixed.
17 changes: 17 additions & 0 deletions src/pending_changes/gh-404-range-for-variable-lifetime.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[GH #404]
Lifetime of the variable of a range-based for-statement in constant evaluation

During constant evaluation, the variable declared by the for-range-declaration
of a range-based for-statement was not destroyed at the end of each
iteration. For example, with --c++20:

struct R { int* p; constexpr ~R() { ++*p; } };
constexpr int g() {
int n = 0;
R a[2] = {{&n}, {&n}};
for (R r : a) {}
return n;
}
static_assert(g() == 2); // Previously failed.

This is now fixed.
25 changes: 25 additions & 0 deletions src/pending_changes/gh-411-loop-entered-through-case-label.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[GH #411]
For loops entered through a case label in constant evaluation

During constant evaluation, when a switch statement transferred control to a
case label in the body of a for loop that was followed by other statements,
the loop was not continued after the body completed: The loop condition was
not evaluated again. For example, with --c++20:

constexpr int f(int k) {
bool b = false;
int z = 0;
switch (k) {
for (; b;) {
return 5;
case 1:
b = true;
z = 9;
}
return z;
}
return -1;
}
static_assert(f(1) == 5); // Previously failed.

This is now fixed.
1 change: 1 addition & 0 deletions tests/tests/changes/.gh-401.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=1 --c++20 --gnu_version 130000 Test_name.c
5 changes: 5 additions & 0 deletions tests/tests/changes/.gh-402.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fe_only -DTEST_NUMBER=1 --c++20 Test_name.c
"Test_name.c", line 45: warning: statement is unreachable
if (int x; false) {
^

1 change: 1 addition & 0 deletions tests/tests/changes/.gh-403.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=1 --c++20 Test_name.c
1 change: 1 addition & 0 deletions tests/tests/changes/.gh-404.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=1 --c++20 Test_name.c
1 change: 1 addition & 0 deletions tests/tests/changes/.gh-411.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=1 --c++20 Test_name.c
97 changes: 97 additions & 0 deletions tests/tests/changes/gh-401.sft.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//type:fp
//options:--c++20 --gnu_version 130000
//remark:Constant evaluation of break/continue in a GNU statement expression

constexpr int brk(int v) {
int n = 0;
for (int i = 0; i < 5; ++i) {
n += ({ if (i == v) break; i; });
}
return n;
}
static_assert(brk(3) == 3); // Previously failed.
static_assert(brk(0) == 0); // Previously failed.
static_assert(brk(9) == 10);

constexpr int cont(int v) {
int n = 0;
for (int i = 0; i < 5; ++i) {
n += ({ if (i == v) continue; i; });
}
return n;
}
static_assert(cont(3) == 7); // Previously failed.

constexpr int sw(int v) {
int n = 0;
switch (v) {
case 1:
n = ({ if (v) break; 5; });
n += 10;
}
return n;
}
static_assert(sw(1) == 0); // Previously failed.

constexpr int ret(int v) {
int m = ({ if (v) return 100; 7; });
return m;
}
static_assert(ret(1) == 100);
static_assert(ret(0) == 7);

struct D {
int* p;
constexpr ~D() { ++*p; }
};
constexpr int dtors() {
int n = 0;
while (true) {
D d{&n};
int k = ({ D e{&n}; if (n == 0) break; 1; });
n += k;
}
return n;
}
static_assert(dtors() == 2); // Previously failed.

// Pointer variables (here with union member addresses) that a break leaves
// without a value, or that belong to an outer recursive activation, must not
// be cleaned up when the block is left.
union U { int a; float b; };
constexpr int ptr_in_stmt_expr(int k) {
U u{2};
int r = 0;
for (int i = 0; i < 4; ++i) {
int *p = ({ if (i == k) break; &u.a; });
r += *p;
}
return r;
}
static_assert(ptr_in_stmt_expr(2) == 4);

constexpr int ptr_assigned_later(int k) {
U u{3};
int r = 0;
for (int i = 0; i < 4; ++i) {
int *p;
if (i == k) break;
p = &u.a;
r += *p;
}
return r;
}
static_assert(ptr_assigned_later(2) == 6);

constexpr int ptr_recursive(int n) {
U u{1};
int r = 0;
for (int i = 0; i < 1; ++i) {
if (n == 0) break;
int *p = &u.a;
r = ptr_recursive(n - 1);
r += *p;
}
return r;
}
static_assert(ptr_recursive(3) == 3);
54 changes: 54 additions & 0 deletions tests/tests/changes/gh-402.sft.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//type:fp
//options:--c++20
//remark:Constant evaluation: use of a variable after a jump to a case label

constexpr int after_label(int n) {
switch (n) {
case 0:
int x;
case 1:
x = 4;
return x;
}
return 0;
}
static_assert(after_label(1) == 4); // Previously failed.
static_assert(after_label(0) == 4); // Previously failed.

constexpr int into_for() {
switch (1) {
for (int x; ;) {
case 1:
x = 3;
return x;
}
}
return 0;
}
static_assert(into_for() == 3); // Previously failed.

constexpr int into_for_loop(bool b) {
switch (1) {
for (int x; b;) {
return x;
case 1:
x = 43;
b = true;
}
}
return 0;
}
static_assert(into_for_loop(false) == 43); // Previously failed.

constexpr int into_if() {
switch (1) {
if (int x; false) {
} else {
case 1:
x = 42;
return x;
}
}
return 0;
}
static_assert(into_if() == 42); // Previously failed.
77 changes: 77 additions & 0 deletions tests/tests/changes/gh-403.sft.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//type:fp
//options:--c++20
//remark:Constant evaluation: destruction point of condition variables

struct S {
int* p;
int* cnt;
constexpr S(int* p, int* cnt) : p(p), cnt(cnt) { *p = *p * 10 + 1; }
constexpr S(const S& s) : p(s.p), cnt(s.cnt) { *p = *p * 10 + 1; }
constexpr ~S() { *p = *p * 10 + 2; }
constexpr explicit operator bool() const { return (*cnt)-- > 0; }
constexpr operator int() const { return *cnt; }
};

constexpr int if_cond() {
int n = 0, cnt = 1;
if (S s{&n, &cnt}) n = n * 10 + 3;
return n;
}
static_assert(if_cond() == 132); // Previously failed.

constexpr int if_cond_else() {
int n = 0, cnt = 0;
if (S s{&n, &cnt}) n = n * 10 + 3; else n = n * 10 + 4;
return n;
}
static_assert(if_cond_else() == 142); // Previously failed.

constexpr int if_init() {
int n = 0, cnt = 0;
if (S s{&n, &cnt}; true) n = n * 10 + 3;
return n;
}
static_assert(if_init() == 132); // Previously failed.

constexpr int switch_cond() {
int n = 0, cnt = 4;
switch (S s{&n, &cnt}) { case 4: n = n * 10 + 3; break; }
return n;
}
static_assert(switch_cond() == 132); // Previously failed.

constexpr int while_cond() {
int n = 0, cnt = 1;
while (S s{&n, &cnt}) n = n * 10 + 3;
return n;
}
static_assert(while_cond() == 13212); // Previously failed.

constexpr int for_cond() {
int n = 0, cnt = 2;
for (int i = 0; S s{&n, &cnt}; n = n * 10 + 4) {
if (i++ == 0) continue;
n = n * 10 + 3;
}
return n;
}
static_assert(for_cond() == 142134212); // Previously failed.

constexpr int while_break() {
int n = 0, cnt = 5;
while (S s{&n, &cnt}) { n = n * 10 + 3; break; }
return n;
}
static_assert(while_break() == 132); // Previously failed.

constexpr int while_return(int* p) {
int cnt = 5;
while (S s{p, &cnt}) return *p;
return -1;
}
constexpr int while_return() {
int n = 0;
int r = while_return(&n);
return r * 100 + n;
}
static_assert(while_return() == 112); // Previously failed.
Loading
Loading