-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
let_chains desugaring is wrong #100513
Comments
Yeah, that definitely looks wrong; we will have to roll this back and try again for 1.65. |
In rust-lang#100513 it was shown that the drop behavior of let_chains is not correct currently. Since drop behavior is something pretty subtle, this adds explicit tests for the drop behavior of `if`, `if let` and `match` to make sure that it does not regress in the future. The `println!`s were left in to make debugging easier in case something goes wrong, but they are not required for the test.
Add tests for the drop behavior of some control flow constructs In rust-lang#100513 it was shown that the drop behaviour of let_chains is not correct currently. Since drop behaviour is something pretty subtle, this adds explicit tests for the drop behavior of `if`, `if let` and `match` to make sure that it does not regress in the future. The `println!`s were left in to make debugging easier in case something goes wrong, but they are not required for the test.
I've opened a zulip thread about fixing them. cc @c410-f3r, I wasn't able to find you on zulip to ping you there |
That was a misclick 😆 |
@joshtriplett given rust-lang/rustfmt#5203 (comment) , can we use this opportunity to also ensure that there is a style decision put forth by the lang team in place before restabilizing so that rustfmt is able to merge formatting for let_chains? |
…compiler-errors Revert let_chains stabilization This reverts commit 3266460. It was discovered in rust-lang#100513 that they are not implemented correctly, which does not make them ready for stabilization. The merge in the let parsing had a few conflicts, cc `@compiler-errors` and `@c410-f3r` to make sure I did it correctly (alternatively I could also revert `@compiler-errors'` let diagnostic improvement PR as well if a simpler revert is desired). r? `@Mark-Simulacrum`
I'm not sure if this is the right place to provide feedback, but has there been any consideration on disabling 'irrefutable pattern' warnings for cases like |
This isn't the proper place to discuss the feature, to my knowledge. That would probably be an IRLO thread (internals.rust-lang.org), or a Zulip one |
Note that one doesn't have to follow the RFC, what gets stabilized in the end can sometimes deviate from the things that an RFC decides upon. I'm not saying that the current drop order is good for stabilization though, it isn't. One could e.g. drop temporaries more early on, as in, before the |
The irrefutable pattern lint was specifically extended to let chains in #94951 , after my comment on the tracking issue received only positive responses (see the thumbs up reactions). As for the right place to provide feedback, it is let chains related I would say, but despite that, this specific thread is the wrong place to discuss it, as it is about the desugaring and not about let chains in general. Personally I don't think it should be changed because the lint was made in a way that if it fires, there exists a way to move the irrefutable pattern out of the let chain. Your particular code would work with Again, so far this lint has only received positive feedback. |
Ah yes, the black swan theory of RFCs. Well, consider this to be your first black swan. |
The third option is do what was suggested: open a zulip thread, github issue, or internals thread, or such where you can make your case. You could have made that instead of making that comment to complain on how I have the last word. I didn't open a thread because that's your responsibility: you are the person who has the complaint, not me, and so far you haven't delivered convincing points why a change would make sense. I replied to you because that did feel like my responsibility, as I was the person who implemented the lint. |
Discussed in lang-team meeting. We are removing the nomination. The current behavior seems clearly surprising and also not in accordance with the RFC. |
@rustbot claim |
Now that the first nightly with #102998 is out, I've tried running again the snippet from above. The snippet now prints:
This is not 100% what the desugaring from the RFC prints (should be "first", "1.5", "last"). It's closer than what is seen at the top of the issue's thread, but not quite it. Over at #102998 , @cjgillot has brought up similar concerns. So maybe this issue should be reopened? |
I agree, I think this issue should only be closed once T-lang decided on a desired drop order and the compiler implements it. |
Although actually I have to say that my desugaring might be be fully correct. I split up a normal |
Yeah it doesn't have to match it 100%, one always deviate in the end from the RFC. What is important is that the details are well understood and approved. #102998 has greatly improved the state but we aren't there yet I think. What is currently implementedSo in order to figure out what the current desugaring is doing, I've extended the drop_order.rs test that you added: if self.option_loud_drop(4).is_some()
&& self.option_loud_drop(1).is_some()
&& self.option_loud_drop(2).is_some()
&& self.option_loud_drop(3).is_some()
&& let Some(_d) = self.option_loud_drop(19)
&& let Some(_e) = self.option_loud_drop(18)
&& let Some(_e) = self.option_loud_drop(17)
&& self.option_loud_drop(5).is_some()
&& self.option_loud_drop(6).is_some()
&& self.option_loud_drop(7).is_some()
&& self.option_loud_drop(8).is_some()
&& let Some(_e) = self.option_loud_drop(16)
&& let Some(_e) = self.option_loud_drop(15)
&& let Some(_e) = self.option_loud_drop(14)
&& self.option_loud_drop(9).is_some()
&& self.option_loud_drop(10).is_some()
&& self.option_loud_drop(11).is_some()
&& self.option_loud_drop(12).is_some() {
self.print(13);
} And: if let Some(_d) = self.option_loud_drop(8)
&& let Some(_e) = self.option_loud_drop(7)
&& let Some(_e) = self.option_loud_drop(6)
&& self.option_loud_drop(1).is_some()
&& self.option_loud_drop(2).is_some()
&& self.option_loud_drop(3).is_some()
&& self.option_loud_drop(4).is_some() {
self.print(5);
} Compare this with normal if chains: if self.option_loud_drop(5).is_some()
&& self.option_loud_drop(1).is_some()
&& self.option_loud_drop(2).is_some()
&& self.option_loud_drop(3).is_some()
&& self.option_loud_drop(4).is_some() {
self.print(6);
} And nested ifs: if let Some(_d) = self.option_loud_drop(4) {
if let Some(_e) = self.option_loud_drop(3) {
if let Some(_e) = self.option_loud_drop(2) {
self.print(1);
}
}
} So nested if let's drop in reverse definition order after their body. On the other hand, let-free ifs with && drop in reading order, except for the first condition, which gets droped last (and all conditions drop before their body). Let's call this the "twist". For let chains, the current implementation seems to:
What behaviour should look likePersonally, I agree a lot with the current implementation, except for that weird exception in rule number 4. Rules 1 and 3 make sense because you need to keep the bindings around for the stuff that possibly uses them, which can be either the subsequent let conditions or the body. Ideally however we would deviate from if let in that we drop before the Rule 2 makes sense because ideally you drop stuff as quickly as possible. Same goes for rule 4, except the twist. My proposal would be to:
Changes required from the current implementation on master (as of 2022-10-20):
What do you think? An alternative proposal would drop everything in reverse order, after the body. This rule would be simpler, but you would keep around the temporaries for way too long. (Side note: Ideally the twist would also be eliminated from normal if's but I'm not sure if we can do this due to backwards compatibly. This step would make bool-only && chains them associative wrt drop order though, and also if let chains associative, at least when we consider a hypothetical extension that allows ()s in let chains. |
One note:
This bit isn't quite accurate. In the current implementation it's inconsistent -- if the This is the result of the same bug that causes #100276. After #103034, |
@nathanwhit thanks! I've edited my comment. Regarding #103034, I wonder if it's possible to change it so that it always drops before |
I've filed an issue for let chains else dropping behaviour: #103248 |
PR filed to get rid of the && drop order twist in all Rust: #103293 |
I tried this code: playground
This prints
According to RFC 2497, it should desugar to this: playground
But this prints
Meta
Wrong on the current beta
The text was updated successfully, but these errors were encountered: