-
Notifications
You must be signed in to change notification settings - Fork 249
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
Add support for multiple else if
and if constexpr
#366
Add support for multiple else if
and if constexpr
#366
Conversation
else if
and if constexpr
else if
and if constexpr
Looking at https://godbolt.org/z/vT8rz8brb, which fails with
Wouldn't making that return an lvalue reference to the constructed object, simulating the conventional semantics of the assignment operator, have sufficed? |
Thanks for the feedback! I did not check if that makes sense. This is how it works before my change - I just make it work in multiple |
You are right - returning the lvalue reference will solve that issue. |
Currently, it will save us from the mistakes like writing |
I do wonder. The meaning of the first Cpp2 |
That can happen if it's not a definite first assignment. Opened #368 for the suggestion at #366 (comment). |
f3a87c7
to
afa6878
Compare
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.
Thanks! Definite initialization currently requires that an uninitialized variable be initialized on both branches or neither branch... did you test that this generalizes correctly to requiring that an uninitialized variable be initialized on all branches or no branch?
Yes. I have test that. That was the easy part. The thing I was strangling was the case where the initialization happened in the condition of some else-if (previously there was only one condition and it was handled as an exception). I might add some tests to this PR as a check. |
I am not sure about not "no branch case" - I will check it. |
afa6878
to
853a1e9
Compare
I have fixed nested ifs/else-ifs. The new version of the code was pushed. Now for the following code: main: (args) = {
p : *int;
a := 1;
b := 2;
c := 3;
d := 4;
if args.cout == 3 {
p = a&;
} else if p = b& {
if args.cout == 2 {
p = c&;
} else {
if b > 0 {
p = a&;
}
else {
p = d&;
}
}
} else {
p = c&;
}
std::cout << p* << std::endl;
} It generates: //=== Cpp2 type declarations ====================================================
#include "cpp2util.h"
//=== Cpp2 type definitions and function declarations ===========================
#line 1 "/Users/filipsajdak/dev/execspec/external/tests/pure2-nested-ifs.cpp2"
auto main(int const argc_, char const* const* const argv_) -> int;
//=== Cpp2 function definitions =================================================
#line 1 "/Users/filipsajdak/dev/execspec/external/tests/pure2-nested-ifs.cpp2"
auto main(int const argc_, char const* const* const argv_) -> int{
auto args = cpp2::make_args(argc_, argv_);
#line 2 "/Users/filipsajdak/dev/execspec/external/tests/pure2-nested-ifs.cpp2"
cpp2::deferred_init<int*> p;
auto a {1};
auto b {2};
auto c {3};
auto d {4};
if (args.cout==3) {
p.construct(&a);
} else if (p.construct(&b)) {
if (args.cout==2) {
p.construct(&c);
}else {
if (cpp2::cmp_greater(std::move(b),0)) {
p.construct(&a);
}
else {
p.construct(&d);
}
}
}else {
p.construct(&c);
}
std::cout << *cpp2::assert_not_null(std::move(p.value())) << std::endl;
} And creates the following symbols:
|
If one of the branches is not initializing the variable, it stops with the error on the if statements that do not provide initialization in all branches. E.g.: main: (args) = {
p : *int;
a := 1;
b := 2;
c := 3;
d := 4;
if args.cout == 3 {
p = a&;
} else if p = b& {
if args.cout == 2 {
p = c&;
} else {
if b > 0 {
p = a&;
}
else {
// p = d&;
}
}
} else {
p = c&;
}
std::cout << p* << std::endl;
} Gives the following error:
And the following: main: (args) = {
p : *int;
a := 1;
b := 2;
c := 3;
d := 4;
if args.cout == 3 {
p = a&;
} else if p = b& {
if args.cout == 2 {
p = c&;
} else {
if b > 0 {
p = a&;
}
else {
p = d&;
}
}
} else {
// p = c&;
}
std::cout << p* << std::endl;
} Gives:
|
This looks wrong. The |
@hsutter I can now confirm that this generalization correctly requires that an uninitialized variable be initialized on all branches or on no branch. |
I will add two tests, and it will be ready to merge. |
OK, tests added. PR ready |
I need to fix a few cases... it is not yet ready. |
77b48c5
to
8066ef9
Compare
Two comments after thinking about this more: Re Re |
8066ef9
to
b502bfc
Compare
Added support for else if branches. Added support for constexpr ifs Added support for definite initialization checks Added support for debug symbols (if branch, if else branch, else branch)
This change allows to initialize variables in `if` and `else if` conditions. It make possible to process the following code: ```cpp main: (args) = { p : *int; a := 1; b := 2; c := 3; d := 4; if args.cout == 3 { p = a&; } else if args.cout == 2 { p = c&; } else if p = b& { p = a&; } else { p = d&; } std::cout << p* << std::endl; } ``` And gets generated: ```cpp auto main(int const argc_, char const* const* const argv_) -> int{ auto args = cpp2::make_args(argc_, argv_); #line 2 "tests/else_if.cpp2" cpp2::deferred_init<int*> p; auto a {1}; auto b {2}; auto c {3}; auto d {4}; if (args.cout==3) { p.construct(&a); } else if (args.cout==2) { p.construct(&c); } else if (p.construct(&b)) { p.value() = &a; } else { p.value() = &d; } std::cout << *cpp2::assert_not_null(std::move(p.value())) << std::endl; } ```
This change brings support for nested `if` or `else if`. It make possible to process the following code: ```cpp main: (args) = { p : *int; a := 1; b := 2; c := 3; d := 4; if args.cout == 3 { p = a&; } else if p = b& { if args.cout == 2 { p = c&; } else { if b > 0 { p = a&; } else { p = d&; } } } else { p = c&; } std::cout << p* << std::endl; } ``` And gets generated: ```cpp auto main(int const argc_, char const* const* const argv_) -> int{ auto args = cpp2::make_args(argc_, argv_); cpp2::deferred_init<int*> p; auto a {1}; auto b {2}; auto c {3}; auto d {4}; if (args.cout==3) { p.construct(&a); } else if (p.construct(&b)) { if (args.cout==2) { p.construct(&c); }else { if (cpp2::cmp_greater(std::move(b),0)) { p.construct(&a); } else { p.construct(&d); } } }else { p.construct(&c); } std::cout << *cpp2::assert_not_null(std::move(p.value())) << std::endl; } ``` Assignements are properly marked as `DEFINITE INITIALIZATION` ``` 0 | function main 1 | scope 2 | var p *** UNINITIALIZED 2 | /var 2 | var a 2 | /var 2 | var b 2 | /var 2 | var c 2 | /var 2 | var d 2 | /var 2 | selection 3 | if branch 4 | *** (10,9) DEFINITE INITIALIZATION OF p 4 | *** use of a 4 | /if branch 3 | *** (11,15) DEFINITE INITIALIZATION OF p 3 | *** use of b 3 | if else branch 4 | selection 5 | if branch 6 | *** (13,13) DEFINITE INITIALIZATION OF p 6 | *** (13,17) DEFINITE LAST POTENTIALLY MOVING USE OF *** use of c 6 | /if branch 5 | else branch 6 | selection 7 | *** (15,16) DEFINITE LAST POTENTIALLY MOVING USE OF *** use of b 7 | if branch 8 | *** (16,17) DEFINITE INITIALIZATION OF p 8 | *** (16,21) DEFINITE LAST POTENTIALLY MOVING USE OF *** use of a 8 | /if branch 7 | else branch 8 | *** (19,17) DEFINITE INITIALIZATION OF p 8 | *** (19,21) DEFINITE LAST POTENTIALLY MOVING USE OF *** use of d 8 | /else branch 7 | /selection 6 | /else branch 5 | /selection 4 | /if else branch 3 | else branch 4 | *** (23,9) DEFINITE INITIALIZATION OF p 4 | *** (23,13) DEFINITE LAST POTENTIALLY MOVING USE OF *** use of c 4 | /else branch 3 | /selection 2 | *** (26,18) DEFINITE LAST POTENTIALLY MOVING USE OF *** use of p 2 | /scope 1 | /function ```
b502bfc
to
8fddc94
Compare
@hsutter I have completed the change. Please check the tests that are included. I have also covered "support for constructing variables in conditions" even though |
I see that I was late ~15min - nice sync. I was doing the last checks and tests. I was thinking about spelling I drop that idea for some reason. I will think why as I don't recall it now. |
Thanks again for this suggestion and all the work on this. I'm sorry I have to be the bearer of sad news: I've decided to try out the simpler implementation approach, and coded it up just now. It seems to work well so I'm going to go ahead and check it in. But your work on this has not gone to waste, you raised the issue that we needed Also, your example made me remember that I had forgotten to disallow Also, please check that what I coded up actually resolves this, and reopen this or a new issue if I got it wrong and you spot bugs. Thanks again! |
Also, reject `=` assignment expressions in `if` conditions. They're already disallowed in loop conditions, and it's an oversight that they were not already disallowed in `if` conditions... the `if` condition is supposed to take *logical-or-expression* (the conditional expression production) like the loop conditions do.
Add support for else if branches and constexpr ifs
The current implementation of cppfront (f83ca9) supports only
if
with one condition:This change makes it possible to construct ifs with multiple
else if
checks. That makes the following code:Generates:
The change also brings support for debug symbols:
All regression tests pass.