-
Notifications
You must be signed in to change notification settings - Fork 258
[BUG] Initialization vs assignment in a loop #1049
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
Comments
This should be rejected, just like https://cpp2.godbolt.org/z/8ssxKs4YT is:
|
For consistency, yes. But in both cases it would be nice to only error if the variable is actually used after the branch. If it is only used in the branch where it is initialized, that could be allowed & would be useful e.g. when a pointer (declared in the same scope as the variable) is set to point at the variable after the variable is initialized. BTW the main readme mentions P1179, perhaps that will allow this and #440 as well. |
Yes, I agree this is a bug, lazy initialization shouldn't be allowed in a loop (loops can be entered zero times). |
It would be possible to make this work, but wouldn't that be equivalent to suppressing an unused variable warning? |
OK, fixed. The following errors are now diagnosed: error1: () = {
i: int;
while true {
i = 42; // ERROR: can't initialize i in a loop
}
i = 42;
}
error2: () = {
i: int;
if true {
while true {
i = 42; // ERROR: can't initialize i in a loop
}
i = 42;
}
else {
i = 42;
}
i = 42;
} And the following is allowed: ok: () = {
i: int;
if true {
i = 42;
while true { // OK: in-branch loop is after initialization
i = 42;
}
}
else {
i = 42;
}
i = 42;
} Also, uses like this one already in protected parse_statement: ( /*...*/ )
-> (ret: std::unique_ptr<statement_node>) // LAZILY INITIALIZED
= {
/*... code that doesn't mention ret ... */
if /*...*/ {
while /*... code that doesn't mention ret ... */ { // OK: this loop doesn't matter
/*... code that doesn't mention ret ... */
}
}
/*... code that doesn't mention ret ... */
ret = parser.parse_one_declaration( // OK: definite first use of ret is a construction
tokens*.get_map().begin()*.second,
generated_tokens*
);
/*...*/
} |
So this is invalid C++2? error1: () = {
i: int;
while true {
s = input_from_user(); // string
if s.is_int() {
i = s.to_int(); // ERROR: can't initialize i in a loop??
break;
}
// ask again
}
// Is this point flagged as an error too?
} |
Oh, is the reasoning here that the user can initialize |
Correct, and is now diagnosed with the above recent commit:
No because you get the earlier error. But if you had code that didn't have the error but then didn't use a the variable you would get an error from the Cpp1 compiler if "unused variable" warnings are on.
The latter isn't just harder, I think it's not possible for The primary reason is that a If somehow we guaranteed at-least-once loop semantics (such as |
Not if it is initialized and used in the branch. So why not declare the variable in the branch? The example shows why - though in real code the type of the variable might be more complex - some complicated template instantiation. main: () = {
i: int;
if e1 {
// other code
if e2 {
// initialize and use i
}
// no use of i
}
else if e2 {
// initialize and use i
}
// no use of i
} |
I agree with both parts, including "why not declare the variable in the branch?" That is, in a case like this why wouldn't it be better to write it as follows? main: () = {
if e1 {
// other code
if e2 {
// initialize and use i
i := 42; std::cout << i;
}
// no use of i
}
else if e2 {
// initialize and use i
i := 42; std::cout << i;
}
// no use of i
} Now it's correct by construction; you can't make the mistake of using The purpose of declaring a local in a larger scope is so that it can be used later in the scope (in both Cpp2 and Cpp1), and allowing it to have no initializer allows initializing it in a nested branch scope first including to use different constructors etc. (in Cpp2). If it's only going to be used in the branch scope, shouldn't it be declared there? |
Well you can alias that type and still declare the variables in the innermost scope. |
I agree with @jcanizales but I now see I had missed @ntrel 's point originally... thanks Jorge for fixing my reply with a better shorter one, and my apologies Nick that I missed your clearly stated point the first time! |
To Reproduce
The
p = new<int>
line generates a call top.construct
, which works for the first iteration to initializep
. Then on the second iteration, an assignment was intended, butp.construct
is called again, which causes a contract violation.git cppfront, g++ (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
The text was updated successfully, but these errors were encountered: