Skip to content

Stabilize let chains in the 2024 edition #132833

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

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 48 additions & 14 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@ use rustc_macros::Subdiagnostic;
use rustc_session::errors::{ExprParenthesesNeeded, report_lit_error};
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP;
use rustc_span::edition::Edition;
use rustc_span::source_map::{self, Spanned};
use rustc_span::{BytePos, ErrorGuaranteed, Ident, Pos, Span, Symbol, kw, sym};
use thin_vec::{ThinVec, thin_vec};
@@ -2594,7 +2595,7 @@ impl<'a> Parser<'a> {
/// Parses an `if` expression (`if` token already eaten).
fn parse_expr_if(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.prev_token.span;
let cond = self.parse_expr_cond()?;
let cond = self.parse_expr_cond(lo.edition())?;
self.parse_if_after_cond(lo, cond)
}

@@ -2703,8 +2704,11 @@ impl<'a> Parser<'a> {
}

/// Parses the condition of a `if` or `while` expression.
///
/// The specified `edition` should be that of the whole `if` or `while` construct: the same
/// span that we later decide the drop behaviour on (editions ..=2021 vs 2024..)
// Public because it is used in rustfmt forks such as https://github.com/tucant/rustfmt/blob/30c83df9e1db10007bdd16dafce8a86b404329b2/src/parse/macros/html.rs#L57 for custom if expressions.
pub fn parse_expr_cond(&mut self) -> PResult<'a, P<Expr>> {
pub fn parse_expr_cond(&mut self, edition: Edition) -> PResult<'a, P<Expr>> {
let attrs = self.parse_outer_attributes()?;
let (mut cond, _) =
self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, attrs)?;
@@ -2714,6 +2718,27 @@ impl<'a> Parser<'a> {
if let ExprKind::Let(_, _, _, Recovered::No) = cond.kind {
// Remove the last feature gating of a `let` expression since it's stable.
self.psess.gated_spans.ungate_last(sym::let_chains, cond.span);
} else {
fn ungate_let_exprs(this: &mut Parser<'_>, expr: &Expr) {
if !expr.span.at_least_rust_2024() {
return;
}
match &expr.kind {
ExprKind::Binary(BinOp { node: BinOpKind::And, .. }, lhs, rhs) => {
ungate_let_exprs(this, rhs);
ungate_let_exprs(this, lhs);
}
ExprKind::Let(..) => {
this.psess.gated_spans.ungate_last(sym::let_chains, expr.span)
}
_ => (),
}
}
if edition.at_least_rust_2024() {
// Scoping code checks the top level edition of the `if`: let's match it here.
// Also check all editions in between, just to make sure.
ungate_let_exprs(self, &cond);
}
}

Ok(cond)
@@ -3009,7 +3034,7 @@ impl<'a> Parser<'a> {

/// Parses a `while` or `while let` expression (`while` token already eaten).
fn parse_expr_while(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> {
let cond = self.parse_expr_cond().map_err(|mut err| {
let cond = self.parse_expr_cond(lo.edition()).map_err(|mut err| {
err.span_label(lo, "while parsing the condition of this `while` expression");
err
})?;
@@ -3395,15 +3420,15 @@ impl<'a> Parser<'a> {
fn parse_match_arm_guard(&mut self) -> PResult<'a, Option<P<Expr>>> {
// Used to check the `let_chains` and `if_let_guard` features mostly by scanning
// `&&` tokens.
fn check_let_expr(expr: &Expr) -> (bool, bool) {
fn has_let_expr(expr: &Expr) -> bool {
match &expr.kind {
ExprKind::Binary(BinOp { node: BinOpKind::And, .. }, lhs, rhs) => {
let lhs_rslt = check_let_expr(lhs);
let rhs_rslt = check_let_expr(rhs);
(lhs_rslt.0 || rhs_rslt.0, false)
let lhs_rslt = has_let_expr(lhs);
let rhs_rslt = has_let_expr(rhs);
lhs_rslt || rhs_rslt
}
ExprKind::Let(..) => (true, true),
_ => (false, true),
ExprKind::Let(..) => true,
_ => false,
}
}
if !self.eat_keyword(exp!(If)) {
@@ -3416,12 +3441,21 @@ impl<'a> Parser<'a> {

CondChecker::new(self).visit_expr(&mut cond);

let (has_let_expr, does_not_have_bin_op) = check_let_expr(&cond);
if has_let_expr {
if does_not_have_bin_op {
// Remove the last feature gating of a `let` expression since it's stable.
self.psess.gated_spans.ungate_last(sym::let_chains, cond.span);
if has_let_expr(&cond) {
// Let chains are allowed in match guards, but only there
fn ungate_let_exprs(this: &mut Parser<'_>, expr: &Expr) {
match &expr.kind {
ExprKind::Binary(BinOp { node: BinOpKind::And, .. }, lhs, rhs) => {
ungate_let_exprs(this, rhs);
ungate_let_exprs(this, lhs);
}
ExprKind::Let(..) => {
this.psess.gated_spans.ungate_last(sym::let_chains, expr.span)
}
_ => (),
}
}
ungate_let_exprs(self, &cond);
let span = if_span.to(cond.span);
self.psess.gated_spans.gate(sym::if_let_guard, span);
}
2 changes: 1 addition & 1 deletion tests/mir-opt/building/logical_or_in_conditional.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// skip-filecheck
//@ compile-flags: -Z validate-mir
#![feature(let_chains)]
//@ edition: 2024
struct Droppy(u8);
impl Drop for Droppy {
fn drop(&mut self) {
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ fn test_complex() -> () {
bb0: {
StorageLive(_1);
StorageLive(_2);
_2 = E::f() -> [return: bb1, unwind: bb34];
_2 = E::f() -> [return: bb1, unwind: bb35];
}

bb1: {
@@ -42,7 +42,7 @@ fn test_complex() -> () {

bb5: {
StorageLive(_4);
_4 = always_true() -> [return: bb6, unwind: bb34];
_4 = always_true() -> [return: bb6, unwind: bb35];
}

bb6: {
@@ -64,7 +64,7 @@ fn test_complex() -> () {
}

bb9: {
drop(_7) -> [return: bb11, unwind: bb34];
drop(_7) -> [return: bb11, unwind: bb35];
}

bb10: {
@@ -78,7 +78,7 @@ fn test_complex() -> () {
}

bb12: {
drop(_7) -> [return: bb13, unwind: bb34];
drop(_7) -> [return: bb13, unwind: bb35];
}

bb13: {
@@ -98,7 +98,7 @@ fn test_complex() -> () {
}

bb15: {
drop(_10) -> [return: bb17, unwind: bb34];
drop(_10) -> [return: bb17, unwind: bb35];
}

bb16: {
@@ -113,11 +113,12 @@ fn test_complex() -> () {

bb18: {
_1 = const ();
StorageDead(_2);
goto -> bb22;
}

bb19: {
drop(_10) -> [return: bb20, unwind: bb34];
drop(_10) -> [return: bb20, unwind: bb35];
}

bb20: {
@@ -127,6 +128,7 @@ fn test_complex() -> () {
}

bb21: {
StorageDead(_2);
_1 = const ();
goto -> bb22;
}
@@ -135,18 +137,17 @@ fn test_complex() -> () {
StorageDead(_8);
StorageDead(_5);
StorageDead(_4);
StorageDead(_2);
StorageDead(_1);
StorageLive(_11);
_11 = always_true() -> [return: bb23, unwind: bb34];
_11 = always_true() -> [return: bb23, unwind: bb35];
}

bb23: {
switchInt(move _11) -> [0: bb25, otherwise: bb24];
}

bb24: {
goto -> bb32;
goto -> bb33;
}

bb25: {
@@ -155,7 +156,7 @@ fn test_complex() -> () {

bb26: {
StorageLive(_12);
_12 = E::f() -> [return: bb27, unwind: bb34];
_12 = E::f() -> [return: bb27, unwind: bb35];
}

bb27: {
@@ -178,21 +179,26 @@ fn test_complex() -> () {

bb31: {
_0 = const ();
goto -> bb33;
StorageDead(_12);
goto -> bb34;
}

bb32: {
_0 = const ();
StorageDead(_12);
goto -> bb33;
}

bb33: {
_0 = const ();
goto -> bb34;
}

bb34: {
StorageDead(_11);
StorageDead(_12);
return;
}

bb34 (cleanup): {
bb35 (cleanup): {
resume;
}
}
2 changes: 1 addition & 1 deletion tests/ui/deriving/auxiliary/malicious-macro.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![feature(let_chains)]
//@ edition: 2024

extern crate proc_macro;

2 changes: 1 addition & 1 deletion tests/ui/drop/drop-order-comparisons.e2021.fixed
Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@
//@ [e2024] edition: 2024
//@ run-pass

#![feature(let_chains)]
#![cfg_attr(e2021, feature(let_chains))]
#![cfg_attr(e2021, warn(rust_2024_compatibility))]

fn t_bindings() {
2 changes: 1 addition & 1 deletion tests/ui/drop/drop-order-comparisons.rs
Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@
//@ [e2024] edition: 2024
//@ run-pass

#![feature(let_chains)]
#![cfg_attr(e2021, feature(let_chains))]
#![cfg_attr(e2021, warn(rust_2024_compatibility))]

fn t_bindings() {
3 changes: 2 additions & 1 deletion tests/ui/drop/drop_order.rs
Original file line number Diff line number Diff line change
@@ -2,9 +2,10 @@
//@ compile-flags: -Z validate-mir
//@ revisions: edition2021 edition2024
//@ [edition2021] edition: 2021
//@ [edition2024] compile-flags: -Z lint-mir
//@ [edition2024] edition: 2024

#![feature(let_chains)]
#![cfg_attr(edition2021, feature(let_chains))]

use std::cell::RefCell;
use std::convert::TryInto;
4 changes: 1 addition & 3 deletions tests/ui/drop/drop_order_if_let_rescope.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
//@ run-pass
//@ edition:2024
//@ compile-flags: -Z validate-mir

#![feature(let_chains)]
//@ compile-flags: -Z validate-mir -Z lint-mir

use std::cell::RefCell;
use std::convert::TryInto;
7 changes: 6 additions & 1 deletion tests/ui/drop/issue-100276.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
//@ check-pass
//@ compile-flags: -Z validate-mir
#![feature(let_chains)]
//@ revisions: edition2021 edition2024
//@ [edition2021] edition: 2021
//@ [edition2024] compile-flags: -Z lint-mir
//@ [edition2024] edition: 2024

#![cfg_attr(edition2021, feature(let_chains))]

fn let_chains(entry: std::io::Result<std::fs::DirEntry>) {
if let Ok(entry) = entry
3 changes: 1 addition & 2 deletions tests/ui/expr/if/attrs/let-chains-attr.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//@ check-pass

#![feature(let_chains)]
//@ edition:2024

#[cfg(FALSE)]
fn foo() {
2 changes: 1 addition & 1 deletion tests/ui/lint/issue-121070-let-range.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//@ check-pass
//@ edition:2024

#![feature(let_chains)]
#![allow(irrefutable_let_patterns)]
fn main() {
let _a = 0..1;
2 changes: 1 addition & 1 deletion tests/ui/mir/issue-99852.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//@ check-pass
//@ compile-flags: -Z validate-mir
#![feature(let_chains)]
//@ edition: 2024

fn lambda<T, U>() -> U
where
2 changes: 1 addition & 1 deletion tests/ui/mir/mir_let_chains_drop_order.rs
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@

// See `mir_drop_order.rs` for more information

#![feature(let_chains)]
#![cfg_attr(edition2021, feature(let_chains))]
#![allow(irrefutable_let_patterns)]

use std::cell::RefCell;
2 changes: 1 addition & 1 deletion tests/ui/parser/brace-in-let-chain.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// issue #117766
//@ edition: 2024

#![feature(let_chains)]
fn main() {
if let () = ()
&& let () = () {
2 changes: 1 addition & 1 deletion tests/ui/parser/deli-ident-issue-1.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![feature(let_chains)]
//@ edition: 2024
trait Demo {}

impl dyn Demo {
2 changes: 1 addition & 1 deletion tests/ui/parser/issues/issue-103381.fixed
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//@ edition: 2024
//@ run-rustfix

#![feature(let_chains)]
#![allow(unused_variables)]
#![allow(dead_code)]
#![allow(irrefutable_let_patterns)]
2 changes: 1 addition & 1 deletion tests/ui/parser/issues/issue-103381.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//@ edition: 2024
//@ run-rustfix

#![feature(let_chains)]
#![allow(unused_variables)]
#![allow(dead_code)]
#![allow(irrefutable_let_patterns)]
3 changes: 1 addition & 2 deletions tests/ui/parser/semi-in-let-chain.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Issue #117720

#![feature(let_chains)]
//@ edition: 2024

fn main() {
if let () = ()
Loading