Skip to content

Lower Guard Patterns to HIR. #134989

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

Merged
merged 4 commits into from
Jan 8, 2025
Merged
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
5 changes: 3 additions & 2 deletions compiler/rustc_ast_lowering/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.lower_range_end(end, e2.is_some()),
);
}
// FIXME(guard_patterns): lower pattern guards to HIR
PatKind::Guard(inner, _) => pattern = inner,
PatKind::Guard(inner, cond) => {
break hir::PatKind::Guard(self.lower_pat(inner), self.lower_expr(cond));
}
PatKind::Slice(pats) => break self.lower_pat_slice(pats),
PatKind::Rest => {
// If we reach here the `..` pattern is not semantically allowed.
Expand Down
7 changes: 5 additions & 2 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1385,7 +1385,7 @@ impl<'hir> Pat<'hir> {
use PatKind::*;
match self.kind {
Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => true,
Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_short_(it),
Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_short_(it),
Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)),
TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().all(|p| p.walk_short_(it)),
Slice(before, slice, after) => {
Expand All @@ -1412,7 +1412,7 @@ impl<'hir> Pat<'hir> {
use PatKind::*;
match self.kind {
Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => {}
Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_(it),
Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_(it),
Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)),
TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)),
Slice(before, slice, after) => {
Expand Down Expand Up @@ -1564,6 +1564,9 @@ pub enum PatKind<'hir> {
/// A literal.
Lit(&'hir Expr<'hir>),

/// A guard pattern (e.g., `x if guard(x)`).
Guard(&'hir Pat<'hir>, &'hir Expr<'hir>),

/// A range pattern (e.g., `1..=2` or `1..2`).
Range(Option<&'hir Expr<'hir>>, Option<&'hir Expr<'hir>>, RangeEnd),

Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_hir/src/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,10 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) -> V:
visit_opt!(visitor, visit_pat, slice_pattern);
walk_list!(visitor, visit_pat, postpatterns);
}
PatKind::Guard(subpat, condition) => {
try_visit!(visitor.visit_pat(subpat));
try_visit!(visitor.visit_expr(condition));
}
}
V::Result::output()
}
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_hir_analysis/src/check/region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,7 @@ fn resolve_local<'tcx>(
/// | ( ..., P&, ... )
/// | ... "|" P& "|" ...
/// | box P&
/// | P& if ...
/// ```
fn is_binding_pat(pat: &hir::Pat<'_>) -> bool {
// Note that the code below looks for *explicit* refs only, that is, it won't
Expand Down Expand Up @@ -694,7 +695,9 @@ fn resolve_local<'tcx>(
| PatKind::TupleStruct(_, subpats, _)
| PatKind::Tuple(subpats, _) => subpats.iter().any(|p| is_binding_pat(p)),

PatKind::Box(subpat) | PatKind::Deref(subpat) => is_binding_pat(subpat),
PatKind::Box(subpat) | PatKind::Deref(subpat) | PatKind::Guard(subpat, _) => {
is_binding_pat(subpat)
}

PatKind::Ref(_, _)
| PatKind::Binding(hir::BindingMode(hir::ByRef::No, _), ..)
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_hir_pretty/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1999,6 +1999,12 @@ impl<'a> State<'a> {
self.commasep(Inconsistent, after, |s, p| s.print_pat(p));
self.word("]");
}
PatKind::Guard(inner, cond) => {
self.print_pat(inner);
self.space();
self.word_space("if");
self.print_expr(cond);
}
PatKind::Err(_) => {
self.popen();
self.word("/*ERROR*/");
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Does not constitute a read.
hir::PatKind::Wild => false,

// Might not constitute a read, since the condition might be false.
hir::PatKind::Guard(_, _) => true,

// This is unnecessarily restrictive when the pattern that doesn't
// constitute a read is unreachable.
//
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_hir_typeck/src/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
| PatKind::Box(_)
| PatKind::Deref(_)
| PatKind::Ref(..)
| PatKind::Guard(..)
| PatKind::Wild
| PatKind::Err(_) => {
// If the PatKind is Or, Box, or Ref, the decision is made later
Expand Down Expand Up @@ -1737,7 +1738,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
}
}

PatKind::Binding(.., Some(subpat)) => {
PatKind::Binding(.., Some(subpat)) | PatKind::Guard(subpat, _) => {
self.cat_pattern(place_with_id, subpat, op)?;
}

Expand Down
10 changes: 9 additions & 1 deletion compiler/rustc_hir_typeck/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
PatKind::Struct(ref qpath, fields, has_rest_pat) => {
self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, pat_info)
}
PatKind::Guard(pat, cond) => {
self.check_pat(pat, expected, pat_info);
self.check_expr_has_type_or_error(cond, self.tcx.types.bool, |_| {});
expected
}
PatKind::Or(pats) => {
for pat in pats {
self.check_pat(pat, expected, pat_info);
Expand Down Expand Up @@ -422,7 +427,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// An OR-pattern just propagates to each individual alternative.
// This is maximally flexible, allowing e.g., `Some(mut x) | &Some(mut x)`.
// In that example, `Some(mut x)` results in `Peel` whereas `&Some(mut x)` in `Reset`.
| PatKind::Or(_) => AdjustMode::Pass,
| PatKind::Or(_)
// Like or-patterns, guard patterns just propogate to their subpatterns.
| PatKind::Guard(..) => AdjustMode::Pass,
}
}

Expand Down Expand Up @@ -901,6 +908,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
PatKind::Struct(..)
| PatKind::TupleStruct(..)
| PatKind::Or(..)
| PatKind::Guard(..)
| PatKind::Tuple(..)
| PatKind::Slice(..) => "binding",

Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_mir_build/src/thir/pattern/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {

hir::PatKind::Or(pats) => PatKind::Or { pats: self.lower_patterns(pats) },

// FIXME(guard_patterns): implement guard pattern lowering
hir::PatKind::Guard(pat, _) => self.lower_pattern(pat).kind,
Copy link
Member

Choose a reason for hiding this comment

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

Maybe emit an error here instead until the correct lowering is implemented?


hir::PatKind::Err(guar) => PatKind::Error(guar),
};

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_passes/src/input_stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
Deref,
Ref,
Lit,
Guard,
Range,
Slice,
Err
Expand Down
1 change: 1 addition & 0 deletions src/librustdoc/clean/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol {
);
return Symbol::intern("()");
}
PatKind::Guard(p, _) => return name_from_pat(&*p),
PatKind::Range(..) => return kw::Underscore,
PatKind::Slice(begin, ref mid, end) => {
let begin = begin.iter().map(|p| name_from_pat(p).to_string());
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_lints/src/equatable_if_let.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool {
| PatKind::Err(_) => false,
PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)),
PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a),
PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) => unary_pattern(x),
PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x),
PatKind::Path(_) | PatKind::Lit(_) => true,
}
}
Expand Down
8 changes: 5 additions & 3 deletions src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,9 +254,11 @@ impl<'a> NormalizedPat<'a> {
fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> Self {
match pat.kind {
PatKind::Wild | PatKind::Binding(.., None) => Self::Wild,
PatKind::Binding(.., Some(pat)) | PatKind::Box(pat) | PatKind::Deref(pat) | PatKind::Ref(pat, _) => {
Self::from_pat(cx, arena, pat)
},
PatKind::Binding(.., Some(pat))
| PatKind::Box(pat)
| PatKind::Deref(pat)
| PatKind::Ref(pat, _)
| PatKind::Guard(pat, _) => Self::from_pat(cx, arena, pat),
PatKind::Never => Self::Never,
PatKind::Struct(ref path, fields, _) => {
let fields =
Expand Down
4 changes: 4 additions & 0 deletions src/tools/clippy/clippy_lints/src/matches/single_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,10 @@ impl<'a> PatState<'a> {
matches!(self, Self::Wild)
},

PatKind::Guard(..) => {
matches!(self, Self::Wild)
}

// Patterns for things which can only contain a single sub-pattern.
PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) | PatKind::Box(pat) | PatKind::Deref(pat) => {
self.add_pat(cx, pat)
Expand Down
6 changes: 6 additions & 0 deletions src/tools/clippy/clippy_lints/src/utils/author.rs
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,12 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
kind!("Ref({pat}, Mutability::{muta:?})");
self.pat(pat);
},
PatKind::Guard(pat, cond) => {
bind!(self, pat, cond);
kind!("Guard({pat}, {cond})");
self.pat(pat);
self.expr(cond);
}
PatKind::Lit(lit_expr) => {
bind!(self, lit_expr);
kind!("Lit({lit_expr})");
Expand Down
4 changes: 4 additions & 0 deletions src/tools/clippy/clippy_utils/src/hir_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_pat(pat);
std::mem::discriminant(&mu).hash(&mut self.s);
},
PatKind::Guard(pat, guard) => {
self.hash_pat(pat);
self.hash_expr(guard);
},
PatKind::Slice(l, m, r) => {
for pat in l {
self.hash_pat(pat);
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1777,7 +1777,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
},
}
},
PatKind::Lit(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) => true,
PatKind::Lit(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,
}
}

Expand Down
4 changes: 2 additions & 2 deletions tests/ui/feature-gates/feature-gate-guard-patterns.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(irrefutable_let_patterns)]

fn match_guards_still_work() {
match 0 {
0 if guard(0) => {},
Expand All @@ -24,11 +26,9 @@ fn other_guards_dont() {

if let (x if guard(x)) = 0 {}
//~^ ERROR: guard patterns are experimental
//~| WARN: irrefutable

while let (x if guard(x)) = 0 {}
//~^ ERROR: guard patterns are experimental
//~| WARN: irrefutable

#[cfg(FALSE)]
while let (x if guard(x)) = 0 {}
Expand Down
35 changes: 8 additions & 27 deletions tests/ui/feature-gates/feature-gate-guard-patterns.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: unexpected parentheses surrounding `match` arm pattern
--> $DIR/feature-gate-guard-patterns.rs:10:9
--> $DIR/feature-gate-guard-patterns.rs:12:9
|
LL | (0 if guard(0)) => {},
| ^ ^
Expand All @@ -11,7 +11,7 @@ LL + 0 if guard(0) => {},
|

error[E0425]: cannot find value `x` in this scope
--> $DIR/feature-gate-guard-patterns.rs:21:22
--> $DIR/feature-gate-guard-patterns.rs:23:22
|
LL | let ((x if guard(x)) | x) = 0;
| ^ not found in this scope
Expand All @@ -23,13 +23,13 @@ LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {
| ^
|
help: the binding `x` is available in a different scope in the same function
--> $DIR/feature-gate-guard-patterns.rs:21:11
--> $DIR/feature-gate-guard-patterns.rs:23:11
|
LL | let ((x if guard(x)) | x) = 0;
| ^

error[E0658]: guard patterns are experimental
--> $DIR/feature-gate-guard-patterns.rs:16:15
--> $DIR/feature-gate-guard-patterns.rs:18:15
|
LL | (0 if guard(0)) | 1 => {},
| ^^^^^^^^
Expand All @@ -40,7 +40,7 @@ LL | (0 if guard(0)) | 1 => {},
= help: consider using match arm guards

error[E0658]: guard patterns are experimental
--> $DIR/feature-gate-guard-patterns.rs:21:16
--> $DIR/feature-gate-guard-patterns.rs:23:16
|
LL | let ((x if guard(x)) | x) = 0;
| ^^^^^^^^
Expand All @@ -51,7 +51,7 @@ LL | let ((x if guard(x)) | x) = 0;
= help: consider using match arm guards

error[E0658]: guard patterns are experimental
--> $DIR/feature-gate-guard-patterns.rs:25:18
--> $DIR/feature-gate-guard-patterns.rs:27:18
|
LL | if let (x if guard(x)) = 0 {}
| ^^^^^^^^
Expand All @@ -62,7 +62,7 @@ LL | if let (x if guard(x)) = 0 {}
= help: consider using match arm guards

error[E0658]: guard patterns are experimental
--> $DIR/feature-gate-guard-patterns.rs:29:21
--> $DIR/feature-gate-guard-patterns.rs:30:21
|
LL | while let (x if guard(x)) = 0 {}
| ^^^^^^^^
Expand Down Expand Up @@ -94,26 +94,7 @@ LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= help: consider using match arm guards

warning: irrefutable `if let` pattern
--> $DIR/feature-gate-guard-patterns.rs:25:8
|
LL | if let (x if guard(x)) = 0 {}
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this pattern will always match, so the `if let` is useless
= help: consider replacing the `if let` with a `let`
= note: `#[warn(irrefutable_let_patterns)]` on by default

warning: irrefutable `while let` pattern
--> $DIR/feature-gate-guard-patterns.rs:29:11
|
LL | while let (x if guard(x)) = 0 {}
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this pattern will always match, so the loop will never exit
= help: consider instead using a `loop { ... }` with a `let` inside it

error: aborting due to 9 previous errors; 2 warnings emitted
error: aborting due to 9 previous errors

Some errors have detailed explanations: E0425, E0658.
For more information about an error, try `rustc --explain E0425`.
Loading