Skip to content

Stabilize let else#93628

Merged
bors merged 5 commits intorust-lang:masterfrom
est31:stabilize_let_else
Sep 17, 2022
Merged

Stabilize let else#93628
bors merged 5 commits intorust-lang:masterfrom
est31:stabilize_let_else

Conversation

@est31
Copy link
Member

@est31 est31 commented Feb 3, 2022

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

Reference PR: rust-lang/reference#1156

closes #87335 (let else tracking issue)

FCP: #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 in this thread, as well as the followup. Since that followup, further changes have happened to the desugaring, in #98574, #99518, #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 (#87688):

Added by 5b95df4 (#87688):

Added by bf7c32a (#89965):

Added by 8565419 (#89974):

Added by 9b45713:

Added by 61bcd8d (#89841):

Added by 102b912 (#89841):

Added by 2715c5f (#89841):

  • Match ergonomic tests adapted from the rfc2005 test suite.

Added by fec8a50 (#89841):

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

Added by 76ea566 (#94211):

Added by e7730dc (#94208):

  • ui/let-else/let-else-allow-in-expr.rs to test whether #[allow(unused_variables)] works in the expr, as well as its non presence, as well as putting it on the entire let else affects the expr, too. This was adding a missing test as pointed out by the stabilization report.
  • Expansion of ui/let-else/let-else-allow-unused.rs and ui/let-else/let-else-check.rs to ensure that non-presence of #[allow(unused)] does issue the unused lint. This was adding a missing test case as pointed out by the stabilization report.

Added by 5bd7106 (#94208):

Added by 5374688 (#98574):

Added by 6c529de (#98574):

Added by 9b56640 (#99518):

Added by baf9a7c (#99518):

Added by 60be2de (#99518):

Added by 47a7a91 (#100132):

Added by e3c5bd6 (#100443):

Added by 9818526 (#100443):

Added by e182d12 (#100434):

Added by e262856 (#99954):

Added by 2d8460e (#99291):

Added by 1b87ce0 (#101410):

Added by af591eb (#101410):

Added by this PR:

  • ui/let-else/let-else.rs, a simple run-pass check, similar to ui/let-else/let-else-run-pass.rs.

Things not currently tested

  • The #[allow(...)] tests check whether allow works, but they don't check whether the non-presence of allow causes a lint to fire.test added by e7730dc
  • There is no #[allow(...)] test for the expression, as there are tests for the pattern and the else block.test added by e7730dc
  • let-else-brace-before-else.rs forbids the let ... = {} else {} pattern and there is a rustfix to obtain let ... = ({}) else {}. I'm not sure whether the .fixed files are checked by the tooling that they compile. But if there is no such check, it would be neat to make sure that let ... = ({}) else {} compiles.test added by e7730dc
  • Wrong suggestion for slice patterns when using let-else statement #92069 got closed as fixed, but no regression test was added. Not sure it's worth to add one.test added by 5bd7106
  • consistency between let else and if let regarding lifetimes and drop order: Stabilize let else #93628 (comment)test added by 2d8460e

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 #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.

@rustbot rustbot added T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. labels Feb 3, 2022
@rust-highfive
Copy link
Contributor

r? @jackh726

(rust-highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Feb 3, 2022
@rust-log-analyzer

This comment has been minimized.

@est31 est31 force-pushed the stabilize_let_else branch from 9c1b8d2 to 68d46b6 Compare February 3, 2022 19:45
@rust-log-analyzer

This comment has been minimized.

@joshtriplett joshtriplett added relnotes Marks issues that should be documented in the release notes of the next release. T-lang Relevant to the language team labels Feb 3, 2022
@est31 est31 force-pushed the stabilize_let_else branch from 68d46b6 to f0a8446 Compare February 3, 2022 20:32
@joshtriplett
Copy link
Member

This looks good to me; I'll start a T-lang FCP.

@joshtriplett joshtriplett removed T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Feb 3, 2022
@joshtriplett
Copy link
Member

Shall we stabilize let else syntax? We've had many demonstrated uses, including extensively throughout rust-lang/rust, we've seen the value of it, and there aren't any known issues with it.

@rfcbot merge

@rfcbot
Copy link

rfcbot commented Feb 3, 2022

Team member @joshtriplett has proposed to merge this. The next step is review by the rest of the tagged team members:

Concerns:

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@rfcbot rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Feb 3, 2022
@rust-log-analyzer

This comment has been minimized.

@scottmcm
Copy link
Member

scottmcm commented Feb 3, 2022

@rfcbot concern not-while-rustfmt-breaks-on-it

I remembered it not formatting last I tried using it in rustc, and sure enough it still seems to fail when I try it in playground: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=350415c3adb74101bbdc002031976b70

(There's enough people using rustfmt in CI that I think adding formatting support for it after stabilization would be impolite.)

EDIT: "breaks" is not the word I should have used here. I meant "while rustfmt is unable to normalize formatting for the construct", since that's what I'd expect rustfmt to do for all stable syntax.

@scottmcm scottmcm added the needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. label Feb 3, 2022
@calebcartwright
Copy link
Member

I remembered it not formatting last I tried using it in rustc, and sure enough it still seems to fail when I try it in playground:

It's not that rustfmt is "breaking" on it, but the formatting rules aren't established yet so rustfmt is just leaving whatever you wrote in place. You have trailing whitespace in your snippet, and rustfmt is just acknowledging the presence of what was originally written; it's not modifying what was already there/inserting trailing whitespace itself.

@scottmcm
Copy link
Member

scottmcm commented Feb 3, 2022

it's not modifying what was already there

I think that's my complaint. I think it's clear that we'd want rustfmt to format it, but based on https://rust-lang.github.io/rfcs/2437-rustfmt-stability.html it'd need to be a new version to add formatting support for it if we did so after stabilization. And I don't think that dance is worth it; it should just have formatting support in the release that stabilizes it.

@est31 est31 force-pushed the stabilize_let_else branch from f0a8446 to 70a6fb4 Compare February 3, 2022 21:04
@rustbot rustbot added T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. labels Feb 3, 2022
@calebcartwright
Copy link
Member

it'd need to be a new version to add formatting support for it if we did so after stabilization. And I don't think that dance is worth it; it should just have formatting support in the release that stabilizes it

That's not the case actually.

rustfmt has always had leeway to make formatting changes for "new" syntax (we have to) until such time as the formatting has actually been stabilized. We try very hard to only do this once, which is why the "first do no harm" pattern followed for syntax that's new to rustfmt.

I've never seen a case where stabilization of a feature was held up on rustfmt, and doing so would be a big mistake IMO. Throughout my time working on rustfmt it's often been an after thought where we weren't even aware of syntax changes until long after they'd been in use, and there's plenty of cases where rustfmt exhibits this same behavior on long-standing syntax (e.g. try adding comments between trait bounds).

I'd think that blocking feature stabilization on style guide + formatting stabilization would either greatly elongate the lead time for feature availability on stable, or create large amounts of friction on the rustfmt side between expediency (not wanting to block) and formatting stability + quality/correctness.

Also, and I say this as someone that spends a large chunk of my free time trying to maintain and improve rustfmt 😄, there's a non-zero subset of the Rust population that dislikes rustfmt and doesn't use it whatsoever. If I were one of those folks I think it would be extremely frustrating to not have access to a language feature on stable simply because of an optional tool I don't leverage.

@rust-log-analyzer

This comment has been minimized.

@joshtriplett
Copy link
Member

No objections since it's been being reviewed for far longer than 10 days.

@joshtriplett
Copy link
Member

@bors r+

@bors
Copy link
Collaborator

bors commented Sep 16, 2022

📌 Commit 37dde3f has been approved by joshtriplett

It is now in the queue for this repository.

@slanterns
Copy link
Contributor

slanterns commented Sep 16, 2022

Maybe we can give it some priority if we want it to be merged before the branching.

@joshtriplett
Copy link
Member

joshtriplett commented Sep 16, 2022

@slanterns I don't think we should get in the habit of skipping the queue; if it makes it it makes it, if not it'll make the next one.

@rfcbot
Copy link

rfcbot commented Sep 16, 2022

The final comment period, with a disposition to merge, as per the review above, is now complete.

As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.

This will be merged soon.

@joshtriplett
Copy link
Member

Looks like this failed in a rollup.

@est31
Copy link
Member Author

est31 commented Sep 16, 2022

Yeah, seems the problems from earlier are plaguing us again: #101910 (comment) , which is weird as I've thought that #98147 fixed the problem. Apparently doc-tests of the rustc_macros crate are affected now. I guess those don't get the right flags passed, but need to investigate how exactly we are hitting the bug.
Screenshot_20220916_224716

@est31
Copy link
Member Author

est31 commented Sep 16, 2022

I can reproduce it locally with ./x.py test compiler/rustc_macros/. It seems that the rustdoc wrapper needs adjustments too, similar to how #98147 adjusted the rustc wrapper. Also, ./x.py test --stage 0 compiler/rustc_macros/ is broken as well. It complains about a missing feature(let_else), which occurs when no --cfg=bootstrap is passed. This is probably because cargo doesn't pass RUSTDOCFLAGS to proc-macro crate rustdoc invocations similar to how it doesn't pass RUSTFLAGS to proc-macro rustc invocations for $reasons (the PR i linked links to the cargo issue for that). The rustc wrapper has code to deal with this for rustc, but probably the rustdoc wrapper needs similar logic.

@est31
Copy link
Member Author

est31 commented Sep 16, 2022

I've filed #101921 to fix the issue. I've locally applied that PR to this one, the issue is resolved.

@Fishrock123
Copy link
Contributor

@est31 Big thanks for carrying this over the finish line!!

@est31
Copy link
Member Author

est31 commented Sep 17, 2022

🎉 the stabilization PR has been merged 🎉

Getting let else into the language was very much a team effort, and I would like to thank everyone who was involved in their introduction and stabilization. In particular I would like to thank:

Authors of the RFCs 1303 and 3137:

@Fishrock123 @mbrubeck

PR authors for the implementation in the compiler:

@camsteffen (lowering, tests) @compiler-errors (diagnostics, ICE fixes) @cormacrelf (initial implementation, lowering, tests) @dingxiangfei2009 (lowering, tests) @est31 (stabilization, diagnostics, tests) @JohnTitor (ICE fixes)

Reviewers of those PRs:

@cjgillot @petrochenkov @nagisa @matthewjasper @oli-obk @Mark-Simulacrum @pnkfelix @joshtriplett @jackh726

Everyone who tested out let-else, filed issues/bug reports, provided guidance and feedback in the RFCs, PRs and issues, including team members who approved FCPs.

@Florob Florob mentioned this pull request Oct 11, 2022
8 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-query-system Area: The rustc query system (https://rustc-dev-guide.rust-lang.org/query.html) disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this PR / Issue. needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. relnotes Marks issues that should be documented in the release notes of the next release. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. S-waiting-on-fcp Status: PR is in FCP and is awaiting for FCP to complete. T-lang Relevant to the language team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tracking Issue for RFC 3137: let-else statements