Lower let-else in MIR by dingxiangfei2009 · Pull Request #98574 · rust-lang/rust (original) (raw)

Conversation

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

[ Show hidden characters]({{ revealButtonHref }})

dingxiangfei2009

@rustbot rustbot added the T-compiler

Relevant to the compiler team, which will review and decide on the PR/issue.

label

Jun 27, 2022

flip1995

flip1995

dingxiangfei2009

dingxiangfei2009

@bors bors added S-waiting-on-bors

Status: Waiting on bors to run and complete tests. Bors will change the label on completion.

and removed S-waiting-on-review

Status: Awaiting review from the assignee but also interested parties.

labels

Jul 12, 2022

Dylan-DPC added a commit to Dylan-DPC/rust that referenced this pull request

Jul 13, 2022

@Dylan-DPC

…li-obk

Lower let-else in MIR

This MR will switch to lower let-else statements in MIR building instead.

To lower let-else in MIR, we build a mini-switch two branches. One branch leads to the matching case, and the other leads to the else block. This arrangement will allow temporary lifetime analysis running as-is so that the temporaries are properly extended according to the same rule applied to regular let statements.

cc rust-lang#87335

Fix rust-lang#98672

Dylan-DPC added a commit to Dylan-DPC/rust that referenced this pull request

Jul 13, 2022

@Dylan-DPC

…li-obk

Lower let-else in MIR

This MR will switch to lower let-else statements in MIR building instead.

To lower let-else in MIR, we build a mini-switch two branches. One branch leads to the matching case, and the other leads to the else block. This arrangement will allow temporary lifetime analysis running as-is so that the temporaries are properly extended according to the same rule applied to regular let statements.

cc rust-lang#87335

Fix rust-lang#98672

This was referenced

Jul 13, 2022

@est31 est31 mentioned this pull request

Jul 13, 2022

bors added a commit to rust-lang-ci/rust that referenced this pull request

Jul 13, 2022

@bors

Rollup of 5 pull requests

Successful merges:

Failed merges:

r? @ghost @rustbot modify labels: rollup

This was referenced

Jul 14, 2022

flip1995 pushed a commit to flip1995/rust that referenced this pull request

Jul 18, 2022

@Dylan-DPC

…li-obk

Lower let-else in MIR

This MR will switch to lower let-else statements in MIR building instead.

To lower let-else in MIR, we build a mini-switch two branches. One branch leads to the matching case, and the other leads to the else block. This arrangement will allow temporary lifetime analysis running as-is so that the temporaries are properly extended according to the same rule applied to regular let statements.

cc rust-lang#87335

Fix rust-lang#98672

flip1995 pushed a commit to flip1995/rust that referenced this pull request

Jul 18, 2022

@bors

Rollup of 5 pull requests

Successful merges:

Failed merges:

r? @ghost @rustbot modify labels: rollup

JohnTitor pushed a commit to JohnTitor/rust that referenced this pull request

Jul 26, 2022

@bors

Rollup of 5 pull requests

Successful merges:

Failed merges:

r? @ghost @rustbot modify labels: rollup

@est31 est31 mentioned this pull request

Aug 12, 2022

Dylan-DPC added a commit to Dylan-DPC/rust that referenced this pull request

Aug 12, 2022

@Dylan-DPC

@est31 est31 mentioned this pull request

Aug 31, 2022

GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this pull request

Sep 16, 2022

@GuillaumeGomez

…plett

Stabilize let else

🎉 **Stabilizes the let else feature, added by RFC 3137 🎉

Reference PR: rust-lang/reference#1156

closes rust-lang#87335 (let else tracking issue)

FCP: rust-lang#93628 (comment)


Stabilization report

Summary

The feature allows refutable patterns in let statements if the expression is followed by a diverging else:

fn get_count_item(s: &str) -> (u64, &str) {
    let mut it = s.split(' ');
    let (Some(count_str), Some(item)) = (it.next(), it.next()) else {
        panic!("Can't segment count item pair: '{s}'");
    };
    let Ok(count) = u64::from_str(count_str) else {
        panic!("Can't parse integer: '{count_str}'");
    };
    (count, item)
}
assert_eq!(get_count_item("3 chairs"), (3, "chairs"));

Differences from the RFC / Desugaring

Outside of desugaring I'm not aware of any differences between the implementation and the RFC. The chosen desugaring has been changed from the RFC's original. You can read a detailed discussion of the implementation history of it in @cormacrelf 's [summary](rust-lang#93628 (comment)) in this thread, as well as the [followup](rust-lang#93628 (comment)). Since that followup, further changes have happened to the desugaring, in rust-lang#98574, rust-lang#99518, rust-lang#99954. The later changes were mostly about the drop order: On match, temporaries drop in the same order as they would for a let declaration. On mismatch, temporaries drop before the else block.

Test cases

In chronological order as they were merged.

Added by df9a2e0 (rust-lang#87688):

Added by 5b95df4 (rust-lang#87688):

Added by bf7c32a (rust-lang#89965):

Added by 8565419 (rust-lang#89974):

Added by 9b45713:

Added by 61bcd8d (rust-lang#89841):

Added by 102b912 (rust-lang#89841):

Added by 2715c5f (rust-lang#89841):

Added by fec8a50 (rust-lang#89841):

Added since this stabilization report was originally written (2022-02-09)

Added by 76ea566 (rust-lang#94211):

Added by e7730dc (rust-lang#94208):

Added by 5bd7106 (rust-lang#94208):

Added by 5374688 (rust-lang#98574):

Added by 6c529de (rust-lang#98574):

Added by 9b56640 (rust-lang#99518):

Added by baf9a7c (rust-lang#99518):

Added by 60be2de (rust-lang#99518):

Added by 47a7a91 (rust-lang#100132):

Added by e3c5bd6 (rust-lang#100443):

Added by 9818526 (rust-lang#100443):

Added by e182d12 (rust-lang#100434):

Added by e262856 (rust-lang#99954):

Added by 2d8460e (rust-lang#99291):

Added by 1b87ce0 (rust-lang#101410):

Added by af591eb (rust-lang#101410):

Added by this PR:

Things not currently tested

Edit: they are all tested now.

Possible future work / Refutable destructuring assignments

RFC 2909 specifies destructuring assignment, allowing statements like FooBar { a, b, c } = foo();. As it was stabilized, destructuring assignment only allows irrefutable patterns, which before the advent of let else were the only patterns that let supported. So the combination of let else and destructuring assignments gives reason to think about extensions of the destructuring assignments feature that allow refutable patterns, discussed in rust-lang#93995.

A naive mapping of let else to destructuring assignments in the form of Some(v) = foo() else { ... }; might not be the ideal way. let else needs a diverging else clause as it introduces new bindings, while assignments have a default behaviour to fall back to if the pattern does not match, in the form of not performing the assignment. Thus, there is no good case to require divergence, or even an else clause at all, beyond the need for having some introducer syntax so that it is clear to readers that the assignment is not a given (enums and structs look similar). There are better candidates for introducer syntax however than an empty else {} clause, like maybe which could be added as a keyword on an edition boundary:

let mut v = 0;
maybe Some(v) = foo(&v);
maybe Some(v) = foo(&v) else { bar() };

Further design discussion is left to an RFC, or the linked issue.

Dylan-DPC added a commit to Dylan-DPC/rust that referenced this pull request

Sep 17, 2022

@Dylan-DPC

…plett

Stabilize let else

🎉 **Stabilizes the let else feature, added by RFC 3137 🎉

Reference PR: rust-lang/reference#1156

closes rust-lang#87335 (let else tracking issue)

FCP: rust-lang#93628 (comment)


Stabilization report

Summary

The feature allows refutable patterns in let statements if the expression is followed by a diverging else:

fn get_count_item(s: &str) -> (u64, &str) {
    let mut it = s.split(' ');
    let (Some(count_str), Some(item)) = (it.next(), it.next()) else {
        panic!("Can't segment count item pair: '{s}'");
    };
    let Ok(count) = u64::from_str(count_str) else {
        panic!("Can't parse integer: '{count_str}'");
    };
    (count, item)
}
assert_eq!(get_count_item("3 chairs"), (3, "chairs"));

Differences from the RFC / Desugaring

Outside of desugaring I'm not aware of any differences between the implementation and the RFC. The chosen desugaring has been changed from the RFC's original. You can read a detailed discussion of the implementation history of it in @cormacrelf 's [summary](rust-lang#93628 (comment)) in this thread, as well as the [followup](rust-lang#93628 (comment)). Since that followup, further changes have happened to the desugaring, in rust-lang#98574, rust-lang#99518, rust-lang#99954. The later changes were mostly about the drop order: On match, temporaries drop in the same order as they would for a let declaration. On mismatch, temporaries drop before the else block.

Test cases

In chronological order as they were merged.

Added by df9a2e0 (rust-lang#87688):

Added by 5b95df4 (rust-lang#87688):

Added by bf7c32a (rust-lang#89965):

Added by 8565419 (rust-lang#89974):

Added by 9b45713:

Added by 61bcd8d (rust-lang#89841):

Added by 102b912 (rust-lang#89841):

Added by 2715c5f (rust-lang#89841):

Added by fec8a50 (rust-lang#89841):

Added since this stabilization report was originally written (2022-02-09)

Added by 76ea566 (rust-lang#94211):

Added by e7730dc (rust-lang#94208):

Added by 5bd7106 (rust-lang#94208):

Added by 5374688 (rust-lang#98574):

Added by 6c529de (rust-lang#98574):

Added by 9b56640 (rust-lang#99518):

Added by baf9a7c (rust-lang#99518):

Added by 60be2de (rust-lang#99518):

Added by 47a7a91 (rust-lang#100132):

Added by e3c5bd6 (rust-lang#100443):

Added by 9818526 (rust-lang#100443):

Added by e182d12 (rust-lang#100434):

Added by e262856 (rust-lang#99954):

Added by 2d8460e (rust-lang#99291):

Added by 1b87ce0 (rust-lang#101410):

Added by af591eb (rust-lang#101410):

Added by this PR:

Things not currently tested

Edit: they are all tested now.

Possible future work / Refutable destructuring assignments

RFC 2909 specifies destructuring assignment, allowing statements like FooBar { a, b, c } = foo();. As it was stabilized, destructuring assignment only allows irrefutable patterns, which before the advent of let else were the only patterns that let supported. So the combination of let else and destructuring assignments gives reason to think about extensions of the destructuring assignments feature that allow refutable patterns, discussed in rust-lang#93995.

A naive mapping of let else to destructuring assignments in the form of Some(v) = foo() else { ... }; might not be the ideal way. let else needs a diverging else clause as it introduces new bindings, while assignments have a default behaviour to fall back to if the pattern does not match, in the form of not performing the assignment. Thus, there is no good case to require divergence, or even an else clause at all, beyond the need for having some introducer syntax so that it is clear to readers that the assignment is not a given (enums and structs look similar). There are better candidates for introducer syntax however than an empty else {} clause, like maybe which could be added as a keyword on an edition boundary:

let mut v = 0;
maybe Some(v) = foo(&v);
maybe Some(v) = foo(&v) else { bar() };

Further design discussion is left to an RFC, or the linked issue.

matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request

Nov 30, 2022

@matthiaskrgr

…low, r=oli-obk

Restore control flow on error in EUV

cc @Nilstrieb

Fix rust-lang#104649

Since rust-lang#98574 refactored a piece of scrutinee memory categorization out as a subroutine, there is a subtle change in handling match arms especially when the categorization process faults and bails. In the correct case, it is not supposed to continue to process the arms any more. This PR restores the original control flow in EUV.

I promise to add a compile-fail test to demonstrate that this indeed fixes the issue after coming back from a nap.