Skip to content

Commit 23e3f32

Browse files
committed
Auto merge of #119610 - Nadrieril:never_pattern_bindings, r=<try>
never patterns: Check bindings wrt never patterns Never patterns: - Shouldn't contain bindings since they never match anything; - Don't count when checking that or-patterns have consistent bindings. r? `@compiler-errors`
2 parents 5113ed2 + 997103a commit 23e3f32

File tree

11 files changed

+150
-134
lines changed

11 files changed

+150
-134
lines changed

compiler/rustc_resolve/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ resolve_attempt_to_use_non_constant_value_in_constant_with_suggestion =
3636
resolve_attempt_to_use_non_constant_value_in_constant_without_suggestion =
3737
this would need to be a `{$suggestion}`
3838
39+
resolve_binding_in_never_pattern =
40+
never patterns cannot contain variable bindings
41+
.suggestion = use a wildcard `_` instead
42+
3943
resolve_binding_shadows_something_unacceptable =
4044
{$shadowing_binding}s cannot shadow {$shadowed_binding}s
4145
.label = cannot be named the same as {$article} {$shadowed_binding}

compiler/rustc_resolve/src/diagnostics.rs

+3
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
958958
.create_err(errs::TraitImplDuplicate { span, name, trait_item_span, old_span }),
959959
ResolutionError::InvalidAsmSym => self.dcx().create_err(errs::InvalidAsmSym { span }),
960960
ResolutionError::LowercaseSelf => self.dcx().create_err(errs::LowercaseSelf { span }),
961+
ResolutionError::BindingInNeverPattern => {
962+
self.dcx().create_err(errs::BindingInNeverPattern { span })
963+
}
961964
}
962965
}
963966

compiler/rustc_resolve/src/errors.rs

+9
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,15 @@ pub(crate) struct LowercaseSelf {
486486
pub(crate) span: Span,
487487
}
488488

489+
#[derive(Debug)]
490+
#[derive(Diagnostic)]
491+
#[diag(resolve_binding_in_never_pattern)]
492+
pub(crate) struct BindingInNeverPattern {
493+
#[primary_span]
494+
#[suggestion(code = "_", applicability = "machine-applicable", style = "short")]
495+
pub(crate) span: Span,
496+
}
497+
489498
#[derive(Diagnostic)]
490499
#[diag(resolve_trait_impl_duplicate, code = "E0201")]
491500
pub(crate) struct TraitImplDuplicate {

compiler/rustc_resolve/src/late.rs

+54-37
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ enum IsRepeatExpr {
6565
Yes,
6666
}
6767

68+
struct IsNeverPattern;
69+
6870
/// Describes whether an `AnonConst` is a type level const arg or
6971
/// some other form of anon const (i.e. inline consts or enum discriminants)
7072
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -3190,12 +3192,16 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
31903192
self.resolve_pattern_top(&local.pat, PatternSource::Let);
31913193
}
31923194

3193-
/// build a map from pattern identifiers to binding-info's.
3194-
/// this is done hygienically. This could arise for a macro
3195-
/// that expands into an or-pattern where one 'x' was from the
3196-
/// user and one 'x' came from the macro.
3197-
fn binding_mode_map(&mut self, pat: &Pat) -> FxIndexMap<Ident, BindingInfo> {
3195+
/// Build a map from pattern identifiers to binding-info's, and check the bindings are
3196+
/// consistent when encountering or-patterns and never patterns.
3197+
/// This is done hygienically: this could arise for a macro that expands into an or-pattern
3198+
/// where one 'x' was from the user and one 'x' came from the macro.
3199+
fn compute_and_check_binding_map(
3200+
&mut self,
3201+
pat: &Pat,
3202+
) -> Result<FxIndexMap<Ident, BindingInfo>, IsNeverPattern> {
31983203
let mut binding_map = FxIndexMap::default();
3204+
let mut is_never_pat = false;
31993205

32003206
pat.walk(&mut |pat| {
32013207
match pat.kind {
@@ -3207,18 +3213,26 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
32073213
PatKind::Or(ref ps) => {
32083214
// Check the consistency of this or-pattern and
32093215
// then add all bindings to the larger map.
3210-
for bm in self.check_consistent_bindings(ps) {
3211-
binding_map.extend(bm);
3212-
}
3216+
let (bm, np) = self.compute_and_check_or_pat_binding_map(ps);
3217+
binding_map.extend(bm);
3218+
is_never_pat |= np;
32133219
return false;
32143220
}
3221+
PatKind::Never => is_never_pat = true,
32153222
_ => {}
32163223
}
32173224

32183225
true
32193226
});
32203227

3221-
binding_map
3228+
if is_never_pat {
3229+
for (_, binding) in binding_map {
3230+
self.report_error(binding.span, ResolutionError::BindingInNeverPattern);
3231+
}
3232+
Err(IsNeverPattern)
3233+
} else {
3234+
Ok(binding_map)
3235+
}
32223236
}
32233237

32243238
fn is_base_res_local(&self, nid: NodeId) -> bool {
@@ -3228,33 +3242,37 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
32283242
)
32293243
}
32303244

3231-
/// Checks that all of the arms in an or-pattern have exactly the
3232-
/// same set of bindings, with the same binding modes for each.
3233-
fn check_consistent_bindings(
3245+
/// Compute the binding map for an or-pattern. Checks that all of the arms in the or-pattern
3246+
/// have exactly the same set of bindings, with the same binding modes for each.
3247+
/// Returns the computed binding map and a boolean indicating whether the pattern is a never
3248+
/// pattern.
3249+
fn compute_and_check_or_pat_binding_map(
32343250
&mut self,
32353251
pats: &[P<Pat>],
3236-
) -> Vec<FxIndexMap<Ident, BindingInfo>> {
3237-
// pats is consistent.
3252+
) -> (FxIndexMap<Ident, BindingInfo>, bool) {
32383253
let mut missing_vars = FxIndexMap::default();
32393254
let mut inconsistent_vars = FxIndexMap::default();
32403255

3241-
// 1) Compute the binding maps of all arms.
3242-
let maps = pats.iter().map(|pat| self.binding_mode_map(pat)).collect::<Vec<_>>();
3256+
// 1) Compute the binding maps of all arms; never patterns don't participate in this.
3257+
let not_never_pats = pats
3258+
.iter()
3259+
.filter_map(|pat| {
3260+
let binding_map = self.compute_and_check_binding_map(pat).ok()?;
3261+
Some((binding_map, pat))
3262+
})
3263+
.collect::<Vec<_>>();
32433264

32443265
// 2) Record any missing bindings or binding mode inconsistencies.
3245-
for (map_outer, pat_outer) in maps.iter().zip(pats.iter()) {
3266+
for (map_outer, pat_outer) in not_never_pats.iter() {
32463267
// Check against all arms except for the same pattern which is always self-consistent.
3247-
let inners = maps
3268+
let inners = not_never_pats
32483269
.iter()
3249-
.zip(pats.iter())
32503270
.filter(|(_, pat)| pat.id != pat_outer.id)
3251-
.flat_map(|(map, _)| map)
3252-
.map(|(key, binding)| (key.name, map_outer.get(key), binding));
3253-
3254-
let inners = inners.collect::<Vec<_>>();
3271+
.flat_map(|(map, _)| map);
32553272

3256-
for (name, info, &binding_inner) in inners {
3257-
match info {
3273+
for (key, binding_inner) in inners {
3274+
let name = key.name;
3275+
match map_outer.get(key) {
32583276
None => {
32593277
// The inner binding is missing in the outer.
32603278
let binding_error =
@@ -3295,19 +3313,18 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
32953313
self.report_error(v.0, ResolutionError::VariableBoundWithDifferentMode(name, v.1));
32963314
}
32973315

3298-
// 5) Finally bubble up all the binding maps.
3299-
maps
3316+
// 5) Bubble up the final binding map.
3317+
let is_never_pat = not_never_pats.is_empty();
3318+
let mut binding_map = FxIndexMap::default();
3319+
for (bm, _) in not_never_pats {
3320+
binding_map.extend(bm);
3321+
}
3322+
(binding_map, is_never_pat)
33003323
}
33013324

3302-
/// Check the consistency of the outermost or-patterns.
3303-
fn check_consistent_bindings_top(&mut self, pat: &'ast Pat) {
3304-
pat.walk(&mut |pat| match pat.kind {
3305-
PatKind::Or(ref ps) => {
3306-
self.check_consistent_bindings(ps);
3307-
false
3308-
}
3309-
_ => true,
3310-
})
3325+
/// Check the consistency of bindings wrt or-patterns and never patterns.
3326+
fn check_consistent_bindings(&mut self, pat: &'ast Pat) {
3327+
let _ = self.compute_and_check_binding_map(pat);
33113328
}
33123329

33133330
fn resolve_arm(&mut self, arm: &'ast Arm) {
@@ -3336,7 +3353,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
33363353
visit::walk_pat(self, pat);
33373354
self.resolve_pattern_inner(pat, pat_src, bindings);
33383355
// This has to happen *after* we determine which pat_idents are variants:
3339-
self.check_consistent_bindings_top(pat);
3356+
self.check_consistent_bindings(pat);
33403357
}
33413358

33423359
/// Resolve bindings in a pattern. This is a helper to `resolve_pattern`.

compiler/rustc_resolve/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,8 @@ enum ResolutionError<'a> {
265265
InvalidAsmSym,
266266
/// `self` used instead of `Self` in a generic parameter
267267
LowercaseSelf,
268+
/// A never pattern has a binding.
269+
BindingInNeverPattern,
268270
}
269271

270272
enum VisResolutionError<'a> {

tests/ui/feature-gates/feature-gate-never_patterns.rs

-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ fn main() {
77
let res: Result<u32, Void> = Ok(0);
88
let (Ok(_x) | Err(&!)) = res.as_ref();
99
//~^ ERROR `!` patterns are experimental
10-
//~| ERROR: is not bound in all patterns
1110

1211
unsafe {
1312
let ptr: *const Void = NonNull::dangling().as_ptr();
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: unexpected `,` in pattern
2-
--> $DIR/feature-gate-never_patterns.rs:34:16
2+
--> $DIR/feature-gate-never_patterns.rs:33:16
33
|
44
LL | Some(_),
55
| ^
@@ -13,14 +13,6 @@ help: ...or a vertical bar to match on multiple alternatives
1313
LL | Some(_) |
1414
|
1515

16-
error[E0408]: variable `_x` is not bound in all patterns
17-
--> $DIR/feature-gate-never_patterns.rs:8:19
18-
|
19-
LL | let (Ok(_x) | Err(&!)) = res.as_ref();
20-
| -- ^^^^^^^ pattern doesn't bind `_x`
21-
| |
22-
| variable not in all patterns
23-
2416
error[E0658]: `!` patterns are experimental
2517
--> $DIR/feature-gate-never_patterns.rs:8:24
2618
|
@@ -31,7 +23,7 @@ LL | let (Ok(_x) | Err(&!)) = res.as_ref();
3123
= help: add `#![feature(never_patterns)]` to the crate attributes to enable
3224

3325
error[E0658]: `!` patterns are experimental
34-
--> $DIR/feature-gate-never_patterns.rs:15:13
26+
--> $DIR/feature-gate-never_patterns.rs:14:13
3527
|
3628
LL | !
3729
| ^
@@ -40,7 +32,7 @@ LL | !
4032
= help: add `#![feature(never_patterns)]` to the crate attributes to enable
4133

4234
error[E0658]: `!` patterns are experimental
43-
--> $DIR/feature-gate-never_patterns.rs:21:13
35+
--> $DIR/feature-gate-never_patterns.rs:20:13
4436
|
4537
LL | !
4638
| ^
@@ -49,7 +41,7 @@ LL | !
4941
= help: add `#![feature(never_patterns)]` to the crate attributes to enable
5042

5143
error[E0658]: `!` patterns are experimental
52-
--> $DIR/feature-gate-never_patterns.rs:26:13
44+
--> $DIR/feature-gate-never_patterns.rs:25:13
5345
|
5446
LL | ! => {}
5547
| ^
@@ -58,25 +50,25 @@ LL | ! => {}
5850
= help: add `#![feature(never_patterns)]` to the crate attributes to enable
5951

6052
error: `match` arm with no body
61-
--> $DIR/feature-gate-never_patterns.rs:39:9
53+
--> $DIR/feature-gate-never_patterns.rs:38:9
6254
|
6355
LL | Some(_)
6456
| ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
6557

6658
error: `match` arm with no body
67-
--> $DIR/feature-gate-never_patterns.rs:44:9
59+
--> $DIR/feature-gate-never_patterns.rs:43:9
6860
|
6961
LL | Some(_) if false,
7062
| ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
7163

7264
error: `match` arm with no body
73-
--> $DIR/feature-gate-never_patterns.rs:46:9
65+
--> $DIR/feature-gate-never_patterns.rs:45:9
7466
|
7567
LL | Some(_) if false
7668
| ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
7769

7870
error[E0658]: `!` patterns are experimental
79-
--> $DIR/feature-gate-never_patterns.rs:51:13
71+
--> $DIR/feature-gate-never_patterns.rs:50:13
8072
|
8173
LL | Err(!),
8274
| ^
@@ -85,7 +77,7 @@ LL | Err(!),
8577
= help: add `#![feature(never_patterns)]` to the crate attributes to enable
8678

8779
error[E0658]: `!` patterns are experimental
88-
--> $DIR/feature-gate-never_patterns.rs:55:13
80+
--> $DIR/feature-gate-never_patterns.rs:54:13
8981
|
9082
LL | Err(!) if false,
9183
| ^
@@ -94,24 +86,23 @@ LL | Err(!) if false,
9486
= help: add `#![feature(never_patterns)]` to the crate attributes to enable
9587

9688
error: `match` arm with no body
97-
--> $DIR/feature-gate-never_patterns.rs:65:9
89+
--> $DIR/feature-gate-never_patterns.rs:64:9
9890
|
9991
LL | Some(_)
10092
| ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
10193

10294
error: `match` arm with no body
103-
--> $DIR/feature-gate-never_patterns.rs:71:9
95+
--> $DIR/feature-gate-never_patterns.rs:70:9
10496
|
10597
LL | Some(_) if false
10698
| ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
10799

108100
error: a guard on a never pattern will never be run
109-
--> $DIR/feature-gate-never_patterns.rs:55:19
101+
--> $DIR/feature-gate-never_patterns.rs:54:19
110102
|
111103
LL | Err(!) if false,
112104
| ^^^^^ help: remove this guard
113105

114-
error: aborting due to 14 previous errors
106+
error: aborting due to 13 previous errors
115107

116-
Some errors have detailed explanations: E0408, E0658.
117-
For more information about an error, try `rustc --explain E0408`.
108+
For more information about this error, try `rustc --explain E0658`.

tests/ui/pattern/never_patterns.rs

+1-27
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,9 @@ fn main() {}
77

88
// The classic use for empty types.
99
fn safe_unwrap_result<T>(res: Result<T, Void>) {
10-
let Ok(_x) = res;
11-
// FIXME(never_patterns): These should be allowed
10+
let Ok(_x) = res; //~ ERROR refutable pattern in local binding
1211
let (Ok(_x) | Err(!)) = &res;
13-
//~^ ERROR: is not bound in all patterns
1412
let (Ok(_x) | Err(&!)) = res.as_ref();
15-
//~^ ERROR: is not bound in all patterns
1613
}
1714

1815
// Check we only accept `!` where we want to.
@@ -74,26 +71,3 @@ fn never_pattern_location(void: Void) {
7471
Some(&(_, !)),
7572
}
7673
}
77-
78-
fn never_and_bindings() {
79-
let x: Result<bool, &(u32, Void)> = Ok(false);
80-
81-
// FIXME(never_patterns): Never patterns in or-patterns don't need to share the same bindings.
82-
match x {
83-
Ok(_x) | Err(&!) => {}
84-
//~^ ERROR: is not bound in all patterns
85-
}
86-
let (Ok(_x) | Err(&!)) = x;
87-
//~^ ERROR: is not bound in all patterns
88-
89-
// FIXME(never_patterns): A never pattern mustn't have bindings.
90-
match x {
91-
Ok(_) => {}
92-
Err(&(_b, !)),
93-
}
94-
match x {
95-
Ok(_a) | Err(&(_b, !)) => {}
96-
//~^ ERROR: is not bound in all patterns
97-
//~| ERROR: is not bound in all patterns
98-
}
99-
}

0 commit comments

Comments
 (0)