Repository navigation
break in a statement expression in a switch condition [GH #434] #436
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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
23
src/pending_changes/gh-403-condition-variable-lifetime.txt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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
17
src/pending_changes/gh-404-range-for-variable-lifetime.txt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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
25
src/pending_changes/gh-411-loop-entered-through-case-label.txt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| [GH #434] | ||
| GNU statement expressions and switch statements | ||
|
|
||
| In GNU modes, a break statement inside a statement expression that appears as | ||
| the value a switch statement switches on was bound to that switch statement | ||
| instead of (as with GCC and Clang) to the enclosing loop or switch statement. | ||
| Similarly, a case or default label in such a statement expression was | ||
| associated with that switch statement. For example, with --g++ --c++20: | ||
|
|
||
| int f(int y, bool c) { | ||
| switch (y) { | ||
| case 0: | ||
| switch (({ if (c) break; 1; })) { | ||
| case 1: | ||
| return 10; | ||
| } | ||
| return 1; | ||
| } | ||
| return 2; | ||
| } | ||
|
|
||
| f(0, true) returned 1 instead of 2. Constant evaluation had the same | ||
| problem, and also let a switch statement complete a break statement executed | ||
| while computing the value it switches on. This is now fixed. Such a break | ||
| statement is now diagnosed if there is no enclosing loop or switch | ||
| statement, and such a case or default label now applies to an enclosing | ||
| switch statement (and is diagnosed as a transfer of control into a statement | ||
| expression). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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) { | ||
| ^ | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| fe_only -DTEST_NUMBER=1 --c++20 Test_name.c |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| fe_only -DTEST_NUMBER=1 --c++20 Test_name.c |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| fe_only -DTEST_NUMBER=1 --c++20 Test_name.c |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| eccp -DTEST_NUMBER=1 --g++ --c++20 Test_name.c |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ./a.out |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Script executed:
Repository: edgcpp/compiler
Length of output: 2342
Script executed:
Repository: edgcpp/compiler
Length of output: 15173
Script executed:
Repository: edgcpp/compiler
Length of output: 17242
Script executed:
Repository: edgcpp/compiler
Length of output: 41566
Script executed:
Repository: edgcpp/compiler
Length of output: 40163
Script executed:
Repository: edgcpp/compiler
Length of output: 10868
Preserve switch visibility for
in_switch_statement.A plain condition declaration does not invoke this statement-only handler. However, a GNU statement expression in the switch condition can contain an empty statement with
[[fallthrough]].apply_fallthrough_attrthen seesin_switch_statement() == FALSEand reportsec_fallthrough_not_in_switch.Keep condition skipping for
break,case, anddefaultlookup, but use a separate predicate forapply_fallthrough_attrthat does not skip the active switch.