Skip to content

Commit e0cb264

Browse files
committed
Auto merge of #141295 - Kivooeo:if-let-guard-stable, r=fee1-dead,est31
Stabilize `if let` guards (`feature(if_let_guard)`) ## Summary This proposes the stabilization of `if let` guards (tracking issue: #51114, RFC: rust-lang/rfcs#2294). This feature allows `if let` expressions to be used directly within match arm guards, enabling conditional pattern matching within guard clauses. ## What is being stabilized The ability to use `if let` expressions within match arm guards. Example: ```rust enum Command { Run(String), Stop, Pause, } fn process_command(cmd: Command, state: &mut String) { match cmd { Command::Run(name) if let Some(first_char) = name.chars().next() && first_char.is_ascii_alphabetic() => { // Both `name` and `first_char` are available here println!("Running command: {} (starts with '{}')", name, first_char); state.push_str(&format!("Running {}", name)); } Command::Run(name) => { println!("Cannot run command '{}'. Invalid name.", name); } Command::Stop if state.contains("running") => { println!("Stopping current process."); state.clear(); } _ => { println!("Unhandled command or state."); } } } ``` ## Motivation The primary motivation for `if let` guards is to reduce nesting and improve readability when conditional logic depends on pattern matching. Without this feature, such logic requires nested `if let` statements within match arms: ```rust // Without if let guards match value { Some(x) => { if let Ok(y) = compute(x) { // Both `x` and `y` are available here println!("{}, {}", x, y); } } _ => {} } // With if let guards match value { Some(x) if let Ok(y) = compute(x) => { // Both `x` and `y` are available here println!("{}, {}", x, y); } _ => {} } ``` ## Implementation and Testing The feature has been implemented and tested comprehensively across different scenarios: ### Core Functionality Tests **Scoping and variable binding:** - [`scope.rs`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/tests/ui/rfcs/rfc-2294-if-let-guard/scope.rs) - Verifies that bindings created in `if let` guards are properly scoped and available in match arms - [`shadowing.rs`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/tests/ui/rfcs/rfc-2294-if-let-guard/shadowing.rs) - Tests that variable shadowing works correctly within guards - [`scoping-consistency.rs`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/tests/ui/rfcs/rfc-2294-if-let-guard/scoping-consistency.rs) - Ensures temporaries in guards remain valid for the duration of their match arms **Type system integration:** - [`type-inference.rs`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/tests/ui/rfcs/rfc-2294-if-let-guard/type-inference.rs) - Confirms type inference works correctly in `if let` guards - [`typeck.rs`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/tests/ui/rfcs/rfc-2294-if-let-guard/typeck.rs) - Verifies type mismatches are caught appropriately **Pattern matching semantics:** - [`exhaustive.rs`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/tests/ui/rfcs/rfc-2294-if-let-guard/exhaustive.rs) - Validates that `if let` guards are correctly handled in exhaustiveness analysis - [`move-guard-if-let.rs`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let.rs) and [`move-guard-if-let-chain.rs`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let-chain.rs) - Test that conditional moves in guards are tracked correctly by the borrow checker ### Error Handling and Diagnostics - [`warns.rs`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/tests/ui/rfcs/rfc-2294-if-let-guard/warns.rs) - Tests warnings for irrefutable patterns and unreachable code in guards - [`parens.rs`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/tests/ui/rfcs/rfc-2294-if-let-guard/parens.rs) - Ensures parentheses around `let` expressions are properly rejected - [`macro-expanded.rs`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/tests/ui/rfcs/rfc-2294-if-let-guard/macro-expanded.rs) - Verifies macro expansions that produce invalid constructs are caught - [`guard-mutability-2.rs`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/tests/ui/rfcs/rfc-2294-if-let-guard/guard-mutability-2.rs) - Tests mutability and ownership violations in guards - [`ast-validate-guards.rs`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs) - Validates AST-level syntax restrictions ### Drop Order and Temporaries **Key insight:** Unlike `let_chains` in regular `if` expressions, `if let` guards do not have drop order inconsistencies because: 1. Match guards are clearly scoped to their arms 2. There is no "else block" equivalent that could cause temporal confusion - [`drop-order.rs`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/tests/ui/rfcs/rfc-2294-if-let-guard/drop-order.rs) - Check drop order of temporaries create in match guards - [`compare-drop-order.rs`](https://github.com/rust-lang/rust/blob/aef3f5fdf052fbbc16e174aef5da6d50832ca316/tests/ui/rfcs/rfc-2294-if-let-guard/compare-drop-order.rs) - Compares drop order between `if let` guards and nested `if let` in match arms, confirming they behave identically across all editions - #140981 - A complicated drop order test involved `let chain` was made by @est31 - [`drop-order-comparisons-let-chains.rs`](https://github.com/rust-lang/rust/blob/902b4d28783e03e231d8513082cc30c4fcce5d95/tests/ui/drop/drop-order-comparisons-let-chains.rs) - Compares drop order between `let chains` in `if let guard` and regular `if` expressions - [`if-let-guards.rs`](https://github.com/rust-lang/rust/blob/5650d716e0589e2e145ce9027f35bd534e5f862a/tests/ui/drop/if-let-guards.rs) - Test correctness of drop order for bindings and temporaries - [`if-let-guards-2`](https://github.com/rust-lang/rust/blob/3a6c8c8f3d7ae654fdb6ce1255182bda21680655/tests/ui/drop/if-let-guards-2.rs) - The same test as above but more comprehensive and tests more interactions between different features and their drop order, checking that drop order is correct, created by @traviscross ## Edition Compatibility This feature stabilizes on all editions, unlike `let chains` which was limited to edition 2024. This is safe because: 1. `if let` guards don't suffer from the drop order issues that affected `let chains` in regular `if` expressions 2. The scoping is unambiguous - guards are clearly tied to their match arms 3. Extensive testing confirms identical behavior across all editions ## Interactions with Future Features The lang team has reviewed potential interactions with planned "guard patterns" and determined that stabilizing `if let` guards now does not create obstacles for future work. The scoping and evaluation semantics established here align with what guard patterns will need. ## Unresolved Issues - [x] - #140981 - [x] - added tests description by @jieyouxu request - [x] - Concers from @scottmcm about stabilizing this across all editions - [x] - check if drop order in all edition when using `let chains` inside `if let` guard is the same - [x] - interactions with guard patters - [x] - pattern bindings drops before guard bindings #143376 - [x] - documentaion (rust-lang/reference#1957) - [ ] (non-blocking) add tests for [this](#145237) and [this](#141295 (comment)) --- **Related:** - Tracking Issue: #51114 - RFC: rust-lang/rfcs#2294 - Documentation PR: rust-lang/reference#1957
2 parents c043085 + 964b63f commit e0cb264

148 files changed

Lines changed: 746 additions & 731 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

compiler/rustc_ast/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
//! This API is completely unstable and subject to change.
66
77
// tidy-alphabetical-start
8+
#![cfg_attr(bootstrap, feature(if_let_guard))]
89
#![doc(test(attr(deny(warnings), allow(internal_features))))]
910
#![feature(associated_type_defaults)]
1011
#![feature(box_patterns)]
11-
#![feature(if_let_guard)]
1212
#![feature(iter_order_by)]
1313
#![feature(macro_metavar_expr)]
1414
#![recursion_limit = "256"]

compiler/rustc_ast_lowering/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
3131
//! in the HIR, especially for multiple identifiers.
3232
3333
// tidy-alphabetical-start
34+
#![cfg_attr(bootstrap, feature(if_let_guard))]
3435
#![feature(box_patterns)]
35-
#![feature(if_let_guard)]
3636
// tidy-alphabetical-end
3737

3838
use std::mem;

compiler/rustc_ast_passes/src/feature_gate.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -481,11 +481,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
481481
}
482482
};
483483
}
484-
gate_all!(
485-
if_let_guard,
486-
"`if let` guards are experimental",
487-
"you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`"
488-
);
489484
gate_all!(
490485
async_trait_bounds,
491486
"`async` trait bounds are unstable",

compiler/rustc_ast_passes/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
//! by `rustc_ast_lowering`.
44
55
// tidy-alphabetical-start
6+
#![cfg_attr(bootstrap, feature(if_let_guard))]
67
#![feature(box_patterns)]
7-
#![feature(if_let_guard)]
88
#![feature(iter_intersperse)]
99
#![feature(iter_is_partitioned)]
1010
// tidy-alphabetical-end

compiler/rustc_attr_parsing/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@
7777
//! containing both `C` and `packed` annotations.
7878
7979
// tidy-alphabetical-start
80+
#![cfg_attr(bootstrap, feature(if_let_guard))]
8081
#![feature(decl_macro)]
81-
#![feature(if_let_guard)]
8282
#![feature(iter_intersperse)]
8383
#![recursion_limit = "256"]
8484
// tidy-alphabetical-end

compiler/rustc_borrowck/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
// tidy-alphabetical-start
44
#![allow(internal_features)]
55
#![cfg_attr(bootstrap, feature(assert_matches))]
6+
#![cfg_attr(bootstrap, feature(if_let_guard))]
67
#![feature(box_patterns)]
78
#![feature(default_field_values)]
89
#![feature(file_buffered)]
9-
#![feature(if_let_guard)]
1010
#![feature(negative_impls)]
1111
#![feature(never_type)]
1212
#![feature(rustc_attrs)]

compiler/rustc_builtin_macros/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
// tidy-alphabetical-start
55
#![allow(internal_features)]
66
#![cfg_attr(bootstrap, feature(assert_matches))]
7+
#![cfg_attr(bootstrap, feature(if_let_guard))]
78
#![feature(box_patterns)]
89
#![feature(decl_macro)]
9-
#![feature(if_let_guard)]
1010
#![feature(iter_order_by)]
1111
#![feature(proc_macro_internals)]
1212
#![feature(proc_macro_quote)]

compiler/rustc_codegen_llvm/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
77
// tidy-alphabetical-start
88
#![cfg_attr(bootstrap, feature(assert_matches))]
9+
#![cfg_attr(bootstrap, feature(if_let_guard))]
910
#![feature(extern_types)]
1011
#![feature(file_buffered)]
11-
#![feature(if_let_guard)]
1212
#![feature(impl_trait_in_assoc_type)]
1313
#![feature(iter_intersperse)]
1414
#![feature(macro_derive)]

compiler/rustc_codegen_ssa/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// tidy-alphabetical-start
22
#![cfg_attr(bootstrap, feature(assert_matches))]
3+
#![cfg_attr(bootstrap, feature(if_let_guard))]
34
#![feature(box_patterns)]
45
#![feature(file_buffered)]
5-
#![feature(if_let_guard)]
66
#![feature(negative_impls)]
77
#![feature(string_from_utf8_lossy_owned)]
88
#![feature(trait_alias)]

compiler/rustc_const_eval/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// tidy-alphabetical-start
22
#![cfg_attr(bootstrap, feature(assert_matches))]
3+
#![cfg_attr(bootstrap, feature(if_let_guard))]
34
#![feature(array_try_map)]
45
#![feature(box_patterns)]
56
#![feature(decl_macro)]
6-
#![feature(if_let_guard)]
77
#![feature(never_type)]
88
#![feature(slice_ptr_get)]
99
#![feature(trait_alias)]

0 commit comments

Comments
 (0)