Skip to content

Stabilize if let guards (feature(if_let_guard)) #141295

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 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion compiler/rustc_ast/src/lib.rs
Copy link
Member

@jieyouxu jieyouxu May 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question (forcing inline comment): could you provide more details on the test coverage of this language feature? The current description is quite vague, which makes it really hard for FCP reviewers (lang or compiler) to properly determine if this language feature has sufficient test coverage esp. surrounding temporary drop order, scoping, interactions with potentially other parts of the language (like name res, attributes, macros, you name it).

Implementation and Testing

The if let guard feature has undergone extensive implementation and testing to ensure its stability, correctness, and consistent behavior across all Rust editions. This process has involved collaborative efforts, notably with @est31, who has provided invaluable insights and thorough testing, confirming identical functionality across editions.

Key aspects verified during testing include:

  • Variable Scope: Variables bound in the main pattern are accessible within the guard expression, and variables bound in the guard are accessible within the match arm body.
  • Chaining with &&: Multiple let bindings can be chained within a single guard using the && operator (only in 2024 edition, since let chains were stabilized only in 2024 edition).
  • Refutable Patterns: The patterns within the if let guard can be refutable, allowing for concise conditional logic based on the success or failure of the pattern match.
  • MIR Generation: The Mid-level Intermediate Representation (MIR) generated for if let guards has been carefully reviewed to ensure it aligns with the expected runtime behavior, particularly concerning variable lifetimes and drop order.

That's all well and good, but could you please correlate which tests actually exercises those aspects? I'm thinking implementation and language aspects like:

  • Parsing: Precedence? Diagnostics? (Potentially) any parser recoveries?
  • Scoping and name resolution? Any name shadowing interactions?
  • Interactions with exhaustiveness checking (if any)?
  • Does it have any interesting type system interactions? Be it inference or coercions or whatever?
  • Temporary drop order?
  • Is it represented reasonably well in debuginfo?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like it was made in #132833
tests section?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes please. That makes it much easier to determine what is and is not exercised.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure I will explain existed tests! But... How do I check debuginfo

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I'd hold off on debuginfo for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I've added tests section that covers most part of tests and explain what are they testing, about the debuginfo, all that came to my mind to test is check is check if local variables corretly shows inside match arm

I guess it's fine, is there anything we can check in debuginfo please let me know

Process 70246 stopped
* thread #1, name = 'main', queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x00000001000034b0 main`main::main::h2c2906a3c5883264 at main.rs:9:13
   6   	    match x {
   7   	        Some(x) if let Some(y) = y && let Some(z) = z => {
   8   	            let _bp = ();
-> 9   	            println!("meow");
   10  	        }
   11  	        _ => println!("angry meow"),
   12  	    }
Target 0: (main) stopped.
(lldb) fr v
(core::option::Option<i32>) x = {}
(core::option::Option<i32>) y = {}
(core::option::Option<i32>) z = {}
(int *) x = 0x000000016fdfebcc
(int) y = 1
(int) z = 4
(int) x = 3
(void) _bp = <Unable to determine byte size.>

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

// tidy-alphabetical-start
#![allow(internal_features)]
#![cfg_attr(bootstrap, feature(if_let_guard))]
#![doc(
html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
test(attr(deny(warnings)))
Expand All @@ -14,7 +15,6 @@
#![feature(array_windows)]
#![feature(associated_type_defaults)]
#![feature(box_patterns)]
#![feature(if_let_guard)]
#![feature(macro_metavar_expr)]
#![feature(negative_impls)]
#![feature(never_type)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast_lowering/src/lib.rs
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(similarly using a random file to get an inline comment)

Could you distill the contents of the "Edition Considerations" section? This section needs to say which editions this will be stable in and why, which should be concise. I'm having a very tough time following wording like "...designed to integrate seamlessly across all Rust editions" when there isn't a simple statement like "x will be stable in [all editions][edition xxxx and later]".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, i just later realise that we didnt tested this in 2015 and 2018, i will update this block sure

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(from the top post)

The primary blocker identified during pre-stabilization testing was a set of subtle interactions related to drop order and variable scoping within match guards with let chains, comprehensively addressed and validated by the tests introduced in #140981. With this crucial aspect resolved, no other significant issues remain, and the feature is considered ready for stabilization.

Testing the current design is necessary, but it's much more important that you convince us why the current design is correct and why we don't need to iterate further - which no test in the world can do :).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be honest, I’m still trying to get a better grasp of what exactly counts as the “current design” in this context — whether it refers mostly to syntax, semantics, implementation details (like MIR/lowering), or a combination of all of these. I’ve mainly been focusing on making sure things work and are well-tested, but I now realize that’s not the same as being able to clearly explain why the design itself is correct and stable

I’d really appreciate any pointers or examples of what kind of justification would be most helpful for the team here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The things that matter here are mostly syntax and semantics, since that is the part we won't be able to change in the future. Lowering can come a bit into play here since drop order interacts with MIR.

As some twocents, the things you may want to consider mentioning are things like consistency with existing language behavior and anything that might be a "gotcha" for users. For example, can if_let_guards (if let on LHS of the =>) always be thought of as if let within a match arm's expression (if let on the RHS of the =>)? What order do things get dropped in? Are there any behavior differences across editions that will be user-visible?

Copy link
Contributor Author

@Kivooeo Kivooeo May 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clarification — that helps a lot

Regarding the syntax and semantics: the final form of the syntax was actually proposed back in the original RFC from 2018, so in terms of syntax, things have been stable and intentional from the start

As for semantics as far as I concerned, the semantics between using if let guards on the LHS and an if let inside the match arm body (RHS) are intended to be equivalent in terms of behavior visible to the user. Both forms bind variables and control flow similarly, and they should produce the same observable effects

I've tested drop order for LHS and RHS cases and they are identical

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I don't expect syntax to change, it's fine to say that it is a logical extension to if guards that hasn't had any bikeshed. The other two are a bit trickier since it's about identifying possible edge cases. E.g. if this is to be stable in all editions, it would be useful to see examples showing how the changes to if let temporary scope come into play.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, yes they are identical, if we change

match ... {
    <pat> if <guard> => { <arm> }
    _ => {}
}

in tests to

match ... {
    <pat> => if <guard> {
        <arm>
    },
    _ => {}
}

nothing will change in drop order

Copy link
Contributor

@tgross35 tgross35 May 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Nandi may be asking for a more first principals approach, e.g. does the compiler source use the same paths for both so we have a better idea that the results will always be identical? Or at least pointing to an existing test (or a new one) that shows the two patterns generate the same MIR in all editions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I will add test that tests drop order between if let guard and if let in match arm across all editions, that shows that they are the same, I'm not sure how can I make test for MIR but make a test that just shows that they have the same drop order

Copy link
Contributor Author

@Kivooeo Kivooeo May 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please take a look at this test, if this is ok
compare-drop-order.rs

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, yes they are identical

Nice! That's all I wanted to make sure, this test looks ok to me.

Actually that can't be fully true because 1 | 1 if guard() can run the guard twice whereas 1 | 1 => if guard() { ... } will only run the guard once, but the basic idea is there.

Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@

// tidy-alphabetical-start
#![allow(internal_features)]
#![cfg_attr(bootstrap, feature(if_let_guard))]
#![doc(rust_logo)]
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(exact_size_is_empty)]
#![feature(if_let_guard)]
#![feature(rustdoc_internals)]
// tidy-alphabetical-end

Expand Down
5 changes: 0 additions & 5 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -454,11 +454,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
}
};
}
gate_all!(
if_let_guard,
"`if let` guards are experimental",
"you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`"
);
gate_all!(let_chains, "`let` expressions in this position are unstable");
gate_all!(
async_trait_bounds,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast_passes/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

// tidy-alphabetical-start
#![allow(internal_features)]
#![cfg_attr(bootstrap, feature(if_let_guard))]
#![doc(rust_logo)]
#![feature(box_patterns)]
#![feature(if_let_guard)]
#![feature(iter_is_partitioned)]
#![feature(rustdoc_internals)]
// tidy-alphabetical-end
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
// tidy-alphabetical-start
#![allow(internal_features)]
#![cfg_attr(bootstrap, feature(if_let_guard))]
#![doc(rust_logo)]
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(file_buffered)]
#![feature(if_let_guard)]
#![feature(negative_impls)]
#![feature(never_type)]
#![feature(rustc_attrs)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_builtin_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
#![allow(internal_features)]
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
#![cfg_attr(bootstrap, feature(if_let_guard))]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(assert_matches)]
#![feature(autodiff)]
#![feature(box_patterns)]
#![feature(decl_macro)]
#![feature(if_let_guard)]
#![feature(proc_macro_internals)]
#![feature(proc_macro_quote)]
#![feature(rustdoc_internals)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_llvm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@

// tidy-alphabetical-start
#![allow(internal_features)]
#![cfg_attr(bootstrap, feature(if_let_guard))]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(assert_matches)]
#![feature(exact_size_is_empty)]
#![feature(extern_types)]
#![feature(file_buffered)]
#![feature(if_let_guard)]
#![feature(impl_trait_in_assoc_type)]
#![feature(iter_intersperse)]
#![feature(rustdoc_internals)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
#![allow(internal_features)]
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
#![cfg_attr(bootstrap, feature(if_let_guard))]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(file_buffered)]
#![feature(if_let_guard)]
#![feature(negative_impls)]
#![feature(rustdoc_internals)]
#![feature(string_from_utf8_lossy_owned)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_const_eval/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// tidy-alphabetical-start
#![allow(internal_features)]
#![allow(rustc::diagnostic_outside_of_impl)]
#![cfg_attr(bootstrap, feature(if_let_guard))]
#![doc(rust_logo)]
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(decl_macro)]
#![feature(if_let_guard)]
#![feature(never_type)]
#![feature(rustdoc_internals)]
#![feature(slice_ptr_get)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#![allow(internal_features)]
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
#![cfg_attr(bootstrap, feature(if_let_guard))]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(array_windows)]
Expand All @@ -16,7 +17,6 @@
#![feature(box_patterns)]
#![feature(default_field_values)]
#![feature(error_reporter)]
#![feature(if_let_guard)]
#![feature(negative_impls)]
#![feature(never_type)]
#![feature(rustc_attrs)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_expand/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// tidy-alphabetical-start
#![allow(internal_features)]
#![allow(rustc::diagnostic_outside_of_impl)]
#![cfg_attr(bootstrap, feature(if_let_guard))]
#![doc(rust_logo)]
#![feature(array_windows)]
#![feature(associated_type_defaults)]
#![feature(if_let_guard)]
#![feature(macro_metavar_expr)]
#![feature(map_try_insert)]
#![feature(proc_macro_diagnostic)]
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/accepted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ declare_features! (
(accepted, i128_type, "1.26.0", Some(35118)),
/// Allows the use of `if let` expressions.
(accepted, if_let, "1.0.0", None),
/// Allows `if let` guard in match arms.
(accepted, if_let_guard, "CURRENT_RUSTC_VERSION", Some(51114)),
/// Rescoping temporaries in `if let` to align with Rust 2024.
(accepted, if_let_rescope, "1.84.0", Some(124085)),
/// Allows top level or-patterns (`p | q`) in `if let` and `while let`.
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -530,8 +530,6 @@ declare_features! (
(incomplete, guard_patterns, "1.85.0", Some(129967)),
/// Allows using `..=X` as a patterns in slices.
(unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)),
/// Allows `if let` guard in match arms.
(unstable, if_let_guard, "1.47.0", Some(51114)),
/// Allows `impl Trait` to be used inside associated types (RFC 2515).
(unstable, impl_trait_in_assoc_type, "1.70.0", Some(63063)),
/// Allows `impl Trait` in bindings (`let`).
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ This API is completely unstable and subject to change.
#![allow(internal_features)]
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
#![cfg_attr(bootstrap, feature(if_let_guard))]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(assert_matches)]
#![feature(coroutines)]
#![feature(debug_closure_helpers)]
#![feature(if_let_guard)]
#![feature(iter_from_coroutine)]
#![feature(iter_intersperse)]
#![feature(never_type)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// tidy-alphabetical-start
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
#![cfg_attr(bootstrap, feature(if_let_guard))]
#![feature(array_windows)]
#![feature(box_patterns)]
#![feature(if_let_guard)]
#![feature(iter_intersperse)]
#![feature(never_type)]
#![feature(try_blocks)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@

// tidy-alphabetical-start
#![allow(internal_features)]
#![cfg_attr(bootstrap, feature(if_let_guard))]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(array_windows)]
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(if_let_guard)]
#![feature(iter_order_by)]
#![feature(rustc_attrs)]
#![feature(rustdoc_internals)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// tidy-alphabetical-start
#![allow(rustc::default_hash_types)]
#![feature(if_let_guard)]
#![cfg_attr(bootstrap, feature(if_let_guard))]
#![feature(never_type)]
#![feature(proc_macro_diagnostic)]
#![feature(proc_macro_span)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_metadata/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// tidy-alphabetical-start
#![allow(internal_features)]
#![cfg_attr(bootstrap, feature(if_let_guard))]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(coroutines)]
#![feature(decl_macro)]
#![feature(error_iter)]
#![feature(file_buffered)]
#![feature(if_let_guard)]
#![feature(iter_from_coroutine)]
#![feature(macro_metavar_expr)]
#![feature(min_specialization)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#![allow(internal_features)]
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
#![cfg_attr(bootstrap, feature(if_let_guard))]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(allocator_api)]
Expand All @@ -44,7 +45,6 @@
#![feature(discriminant_kind)]
#![feature(extern_types)]
#![feature(file_buffered)]
#![feature(if_let_guard)]
#![feature(intra_doc_pointers)]
#![feature(iter_from_coroutine)]
#![feature(min_specialization)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
// tidy-alphabetical-start
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
#![cfg_attr(bootstrap, feature(if_let_guard))]
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(if_let_guard)]
#![feature(try_blocks)]
// tidy-alphabetical-end

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_transform/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// tidy-alphabetical-start
#![cfg_attr(bootstrap, feature(if_let_guard))]
#![feature(array_windows)]
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(const_type_name)]
#![feature(cow_is_borrowed)]
#![feature(file_buffered)]
#![feature(if_let_guard)]
#![feature(impl_trait_in_assoc_type)]
#![feature(map_try_insert)]
#![feature(never_type)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_monomorphize/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// tidy-alphabetical-start
#![cfg_attr(bootstrap, feature(if_let_guard))]
#![feature(array_windows)]
#![feature(file_buffered)]
#![feature(if_let_guard)]
#![feature(impl_trait_in_assoc_type)]
#![feature(once_cell_get_mut)]
// tidy-alphabetical-end
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_parse/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
#![allow(internal_features)]
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
#![cfg_attr(bootstrap, feature(if_let_guard))]
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(debug_closure_helpers)]
#![feature(if_let_guard)]
#![feature(iter_intersperse)]
#![feature(string_from_utf8_lossy_owned)]
#![recursion_limit = "256"]
Expand Down
22 changes: 5 additions & 17 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3407,31 +3407,19 @@ impl<'a> Parser<'a> {
fn parse_match_arm_guard(&mut self) -> PResult<'a, Option<P<Expr>>> {
// Used to check the `if_let_guard` feature mostly by scanning
// `&&` tokens.
fn has_let_expr(expr: &Expr) -> bool {
match &expr.kind {
ExprKind::Binary(BinOp { node: BinOpKind::And, .. }, lhs, rhs) => {
let lhs_rslt = has_let_expr(lhs);
let rhs_rslt = has_let_expr(rhs);
lhs_rslt || rhs_rslt
}
ExprKind::Let(..) => true,
_ => false,
}
}
if !self.eat_keyword(exp!(If)) {
// No match arm guard present.
return Ok(None);
}

let if_span = self.prev_token.span;
let mut cond = self.parse_match_guard_condition()?;

CondChecker::new(self, LetChainsPolicy::AlwaysAllowed).visit_expr(&mut cond);
CondChecker::new(
self,
LetChainsPolicy::EditionDependent { current_edition: Edition::Edition2024 },
)
.visit_expr(&mut cond);

if has_let_expr(&cond) {
let span = if_span.to(cond.span);
self.psess.gated_spans.gate(sym::if_let_guard, span);
}
Ok(Some(cond))
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_resolve/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
#![allow(internal_features)]
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
#![cfg_attr(bootstrap, feature(if_let_guard))]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(if_let_guard)]
#![feature(iter_intersperse)]
#![feature(rustc_attrs)]
#![feature(rustdoc_internals)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_span/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@

// tidy-alphabetical-start
#![allow(internal_features)]
#![cfg_attr(bootstrap, feature(if_let_guard))]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(array_windows)]
#![feature(cfg_match)]
#![feature(core_io_borrowed_buf)]
#![feature(hash_set_entry)]
#![feature(if_let_guard)]
#![feature(map_try_insert)]
#![feature(negative_impls)]
#![feature(read_buf)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_trait_selection/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@
#![allow(internal_features)]
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
#![cfg_attr(bootstrap, feature(if_let_guard))]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(assert_matches)]
#![feature(associated_type_defaults)]
#![feature(box_patterns)]
#![feature(cfg_version)]
#![feature(if_let_guard)]
#![feature(iter_intersperse)]
#![feature(iterator_try_reduce)]
#![feature(never_type)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ty_utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@

// tidy-alphabetical-start
#![allow(internal_features)]
#![cfg_attr(bootstrap, feature(if_let_guard))]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(assert_matches)]
#![feature(associated_type_defaults)]
#![feature(box_patterns)]
#![feature(if_let_guard)]
#![feature(iterator_try_collect)]
#![feature(never_type)]
#![feature(rustdoc_internals)]
Expand Down
Loading
Loading