Skip to content

Commit a20d0d5

Browse files
authored
Rollup merge of #134989 - max-niederman:guard-patterns-hir, r=oli-obk
Lower Guard Patterns to HIR. Implements lowering of [guard patterns](https://rust-lang.github.io/rfcs/3637-guard-patterns.html) (see the [tracking issue](#129967)) to HIR.
2 parents c371a94 + 8cefc0a commit a20d0d5

File tree

19 files changed

+72
-41
lines changed

19 files changed

+72
-41
lines changed

compiler/rustc_ast_lowering/src/pat.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
120120
self.lower_range_end(end, e2.is_some()),
121121
);
122122
}
123-
// FIXME(guard_patterns): lower pattern guards to HIR
124-
PatKind::Guard(inner, _) => pattern = inner,
123+
PatKind::Guard(inner, cond) => {
124+
break hir::PatKind::Guard(self.lower_pat(inner), self.lower_expr(cond));
125+
}
125126
PatKind::Slice(pats) => break self.lower_pat_slice(pats),
126127
PatKind::Rest => {
127128
// If we reach here the `..` pattern is not semantically allowed.

compiler/rustc_hir/src/hir.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -1387,7 +1387,7 @@ impl<'hir> Pat<'hir> {
13871387
use PatKind::*;
13881388
match self.kind {
13891389
Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => true,
1390-
Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_short_(it),
1390+
Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_short_(it),
13911391
Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)),
13921392
TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().all(|p| p.walk_short_(it)),
13931393
Slice(before, slice, after) => {
@@ -1414,7 +1414,7 @@ impl<'hir> Pat<'hir> {
14141414
use PatKind::*;
14151415
match self.kind {
14161416
Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => {}
1417-
Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_(it),
1417+
Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_(it),
14181418
Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)),
14191419
TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)),
14201420
Slice(before, slice, after) => {
@@ -1566,6 +1566,9 @@ pub enum PatKind<'hir> {
15661566
/// A literal.
15671567
Lit(&'hir Expr<'hir>),
15681568

1569+
/// A guard pattern (e.g., `x if guard(x)`).
1570+
Guard(&'hir Pat<'hir>, &'hir Expr<'hir>),
1571+
15691572
/// A range pattern (e.g., `1..=2` or `1..2`).
15701573
Range(Option<&'hir Expr<'hir>>, Option<&'hir Expr<'hir>>, RangeEnd),
15711574

compiler/rustc_hir/src/intravisit.rs

+4
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,10 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) -> V:
696696
visit_opt!(visitor, visit_pat, slice_pattern);
697697
walk_list!(visitor, visit_pat, postpatterns);
698698
}
699+
PatKind::Guard(subpat, condition) => {
700+
try_visit!(visitor.visit_pat(subpat));
701+
try_visit!(visitor.visit_expr(condition));
702+
}
699703
}
700704
V::Result::output()
701705
}

compiler/rustc_hir_analysis/src/check/region.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,7 @@ fn resolve_local<'tcx>(
654654
/// | ( ..., P&, ... )
655655
/// | ... "|" P& "|" ...
656656
/// | box P&
657+
/// | P& if ...
657658
/// ```
658659
fn is_binding_pat(pat: &hir::Pat<'_>) -> bool {
659660
// Note that the code below looks for *explicit* refs only, that is, it won't
@@ -694,7 +695,9 @@ fn resolve_local<'tcx>(
694695
| PatKind::TupleStruct(_, subpats, _)
695696
| PatKind::Tuple(subpats, _) => subpats.iter().any(|p| is_binding_pat(p)),
696697

697-
PatKind::Box(subpat) | PatKind::Deref(subpat) => is_binding_pat(subpat),
698+
PatKind::Box(subpat) | PatKind::Deref(subpat) | PatKind::Guard(subpat, _) => {
699+
is_binding_pat(subpat)
700+
}
698701

699702
PatKind::Ref(_, _)
700703
| PatKind::Binding(hir::BindingMode(hir::ByRef::No, _), ..)

compiler/rustc_hir_pretty/src/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1999,6 +1999,12 @@ impl<'a> State<'a> {
19991999
self.commasep(Inconsistent, after, |s, p| s.print_pat(p));
20002000
self.word("]");
20012001
}
2002+
PatKind::Guard(inner, cond) => {
2003+
self.print_pat(inner);
2004+
self.space();
2005+
self.word_space("if");
2006+
self.print_expr(cond);
2007+
}
20022008
PatKind::Err(_) => {
20032009
self.popen();
20042010
self.word("/*ERROR*/");

compiler/rustc_hir_typeck/src/expr.rs

+3
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
456456
// Does not constitute a read.
457457
hir::PatKind::Wild => false,
458458

459+
// Might not constitute a read, since the condition might be false.
460+
hir::PatKind::Guard(_, _) => true,
461+
459462
// This is unnecessarily restrictive when the pattern that doesn't
460463
// constitute a read is unreachable.
461464
//

compiler/rustc_hir_typeck/src/expr_use_visitor.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
615615
| PatKind::Box(_)
616616
| PatKind::Deref(_)
617617
| PatKind::Ref(..)
618+
| PatKind::Guard(..)
618619
| PatKind::Wild
619620
| PatKind::Err(_) => {
620621
// If the PatKind is Or, Box, or Ref, the decision is made later
@@ -1737,7 +1738,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
17371738
}
17381739
}
17391740

1740-
PatKind::Binding(.., Some(subpat)) => {
1741+
PatKind::Binding(.., Some(subpat)) | PatKind::Guard(subpat, _) => {
17411742
self.cat_pattern(place_with_id, subpat, op)?;
17421743
}
17431744

compiler/rustc_hir_typeck/src/pat.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
284284
PatKind::Struct(ref qpath, fields, has_rest_pat) => {
285285
self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, pat_info)
286286
}
287+
PatKind::Guard(pat, cond) => {
288+
self.check_pat(pat, expected, pat_info);
289+
self.check_expr_has_type_or_error(cond, self.tcx.types.bool, |_| {});
290+
expected
291+
}
287292
PatKind::Or(pats) => {
288293
for pat in pats {
289294
self.check_pat(pat, expected, pat_info);
@@ -422,7 +427,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
422427
// An OR-pattern just propagates to each individual alternative.
423428
// This is maximally flexible, allowing e.g., `Some(mut x) | &Some(mut x)`.
424429
// In that example, `Some(mut x)` results in `Peel` whereas `&Some(mut x)` in `Reset`.
425-
| PatKind::Or(_) => AdjustMode::Pass,
430+
| PatKind::Or(_)
431+
// Like or-patterns, guard patterns just propogate to their subpatterns.
432+
| PatKind::Guard(..) => AdjustMode::Pass,
426433
}
427434
}
428435

@@ -901,6 +908,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
901908
PatKind::Struct(..)
902909
| PatKind::TupleStruct(..)
903910
| PatKind::Or(..)
911+
| PatKind::Guard(..)
904912
| PatKind::Tuple(..)
905913
| PatKind::Slice(..) => "binding",
906914

compiler/rustc_mir_build/src/thir/pattern/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
435435

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

438+
// FIXME(guard_patterns): implement guard pattern lowering
439+
hir::PatKind::Guard(pat, _) => self.lower_pattern(pat).kind,
440+
438441
hir::PatKind::Err(guar) => PatKind::Error(guar),
439442
};
440443

compiler/rustc_passes/src/input_stats.rs

+1
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
305305
Deref,
306306
Ref,
307307
Lit,
308+
Guard,
308309
Range,
309310
Slice,
310311
Err

src/librustdoc/clean/utils.rs

+1
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol {
320320
);
321321
return Symbol::intern("()");
322322
}
323+
PatKind::Guard(p, _) => return name_from_pat(&*p),
323324
PatKind::Range(..) => return kw::Underscore,
324325
PatKind::Slice(begin, ref mid, end) => {
325326
let begin = begin.iter().map(|p| name_from_pat(p).to_string());

src/tools/clippy/clippy_lints/src/equatable_if_let.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool {
5555
| PatKind::Err(_) => false,
5656
PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)),
5757
PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a),
58-
PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) => unary_pattern(x),
58+
PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x),
5959
PatKind::Path(_) | PatKind::Lit(_) => true,
6060
}
6161
}

src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -254,9 +254,11 @@ impl<'a> NormalizedPat<'a> {
254254
fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> Self {
255255
match pat.kind {
256256
PatKind::Wild | PatKind::Binding(.., None) => Self::Wild,
257-
PatKind::Binding(.., Some(pat)) | PatKind::Box(pat) | PatKind::Deref(pat) | PatKind::Ref(pat, _) => {
258-
Self::from_pat(cx, arena, pat)
259-
},
257+
PatKind::Binding(.., Some(pat))
258+
| PatKind::Box(pat)
259+
| PatKind::Deref(pat)
260+
| PatKind::Ref(pat, _)
261+
| PatKind::Guard(pat, _) => Self::from_pat(cx, arena, pat),
260262
PatKind::Never => Self::Never,
261263
PatKind::Struct(ref path, fields, _) => {
262264
let fields =

src/tools/clippy/clippy_lints/src/matches/single_match.rs

+4
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,10 @@ impl<'a> PatState<'a> {
343343
matches!(self, Self::Wild)
344344
},
345345

346+
PatKind::Guard(..) => {
347+
matches!(self, Self::Wild)
348+
}
349+
346350
// Patterns for things which can only contain a single sub-pattern.
347351
PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) | PatKind::Box(pat) | PatKind::Deref(pat) => {
348352
self.add_pat(cx, pat)

src/tools/clippy/clippy_lints/src/utils/author.rs

+6
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,12 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
712712
kind!("Ref({pat}, Mutability::{muta:?})");
713713
self.pat(pat);
714714
},
715+
PatKind::Guard(pat, cond) => {
716+
bind!(self, pat, cond);
717+
kind!("Guard({pat}, {cond})");
718+
self.pat(pat);
719+
self.expr(cond);
720+
}
715721
PatKind::Lit(lit_expr) => {
716722
bind!(self, lit_expr);
717723
kind!("Lit({lit_expr})");

src/tools/clippy/clippy_utils/src/hir_utils.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1104,6 +1104,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
11041104
self.hash_pat(pat);
11051105
std::mem::discriminant(&mu).hash(&mut self.s);
11061106
},
1107+
PatKind::Guard(pat, guard) => {
1108+
self.hash_pat(pat);
1109+
self.hash_expr(guard);
1110+
},
11071111
PatKind::Slice(l, m, r) => {
11081112
for pat in l {
11091113
self.hash_pat(pat);

src/tools/clippy/clippy_utils/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1777,7 +1777,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
17771777
},
17781778
}
17791779
},
1780-
PatKind::Lit(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) => true,
1780+
PatKind::Lit(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,
17811781
}
17821782
}
17831783

tests/ui/feature-gates/feature-gate-guard-patterns.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(irrefutable_let_patterns)]
2+
13
fn match_guards_still_work() {
24
match 0 {
35
0 if guard(0) => {},
@@ -24,11 +26,9 @@ fn other_guards_dont() {
2426

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

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

3333
#[cfg(FALSE)]
3434
while let (x if guard(x)) = 0 {}

tests/ui/feature-gates/feature-gate-guard-patterns.stderr

+8-27
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: unexpected parentheses surrounding `match` arm pattern
2-
--> $DIR/feature-gate-guard-patterns.rs:10:9
2+
--> $DIR/feature-gate-guard-patterns.rs:12:9
33
|
44
LL | (0 if guard(0)) => {},
55
| ^ ^
@@ -11,7 +11,7 @@ LL + 0 if guard(0) => {},
1111
|
1212

1313
error[E0425]: cannot find value `x` in this scope
14-
--> $DIR/feature-gate-guard-patterns.rs:21:22
14+
--> $DIR/feature-gate-guard-patterns.rs:23:22
1515
|
1616
LL | let ((x if guard(x)) | x) = 0;
1717
| ^ not found in this scope
@@ -23,13 +23,13 @@ LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {
2323
| ^
2424
|
2525
help: the binding `x` is available in a different scope in the same function
26-
--> $DIR/feature-gate-guard-patterns.rs:21:11
26+
--> $DIR/feature-gate-guard-patterns.rs:23:11
2727
|
2828
LL | let ((x if guard(x)) | x) = 0;
2929
| ^
3030

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

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

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

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

97-
warning: irrefutable `if let` pattern
98-
--> $DIR/feature-gate-guard-patterns.rs:25:8
99-
|
100-
LL | if let (x if guard(x)) = 0 {}
101-
| ^^^^^^^^^^^^^^^^^^^^^^^
102-
|
103-
= note: this pattern will always match, so the `if let` is useless
104-
= help: consider replacing the `if let` with a `let`
105-
= note: `#[warn(irrefutable_let_patterns)]` on by default
106-
107-
warning: irrefutable `while let` pattern
108-
--> $DIR/feature-gate-guard-patterns.rs:29:11
109-
|
110-
LL | while let (x if guard(x)) = 0 {}
111-
| ^^^^^^^^^^^^^^^^^^^^^^^
112-
|
113-
= note: this pattern will always match, so the loop will never exit
114-
= help: consider instead using a `loop { ... }` with a `let` inside it
115-
116-
error: aborting due to 9 previous errors; 2 warnings emitted
97+
error: aborting due to 9 previous errors
11798

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

0 commit comments

Comments
 (0)