Skip to content

Commit

Permalink
Auto merge of rust-lang#119610 - Nadrieril:never_pattern_bindings, r=…
Browse files Browse the repository at this point in the history
…<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`
  • Loading branch information
bors committed Jan 5, 2024
2 parents 8d39ec1 + ad1bf5d commit 1a3eeaf
Show file tree
Hide file tree
Showing 11 changed files with 156 additions and 130 deletions.
4 changes: 4 additions & 0 deletions compiler/rustc_resolve/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ resolve_attempt_to_use_non_constant_value_in_constant_with_suggestion =
resolve_attempt_to_use_non_constant_value_in_constant_without_suggestion =
this would need to be a `{$suggestion}`
resolve_binding_in_never_pattern =
never patterns cannot contain variable bindings
.suggestion = use a wildcard `_` instead
resolve_binding_shadows_something_unacceptable =
{$shadowing_binding}s cannot shadow {$shadowed_binding}s
.label = cannot be named the same as {$article} {$shadowed_binding}
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_resolve/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
.create_err(errs::TraitImplDuplicate { span, name, trait_item_span, old_span }),
ResolutionError::InvalidAsmSym => self.dcx().create_err(errs::InvalidAsmSym { span }),
ResolutionError::LowercaseSelf => self.dcx().create_err(errs::LowercaseSelf { span }),
ResolutionError::BindingInNeverPattern => {
self.dcx().create_err(errs::BindingInNeverPattern { span })
}
}
}

Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_resolve/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,15 @@ pub(crate) struct LowercaseSelf {
pub(crate) span: Span,
}

#[derive(Debug)]
#[derive(Diagnostic)]
#[diag(resolve_binding_in_never_pattern)]
pub(crate) struct BindingInNeverPattern {
#[primary_span]
#[suggestion(code = "_", applicability = "machine-applicable", style = "short")]
pub(crate) span: Span,
}

#[derive(Diagnostic)]
#[diag(resolve_trait_impl_duplicate, code = "E0201")]
pub(crate) struct TraitImplDuplicate {
Expand Down
93 changes: 60 additions & 33 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ enum IsRepeatExpr {
Yes,
}

struct IsNeverPattern;

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

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

pat.walk(&mut |pat| {
match pat.kind {
Expand All @@ -3207,18 +3213,26 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
PatKind::Or(ref ps) => {
// Check the consistency of this or-pattern and
// then add all bindings to the larger map.
for bm in self.check_consistent_bindings(ps) {
binding_map.extend(bm);
}
let (bm, np) = self.compute_and_check_or_pat_binding_map(ps);
binding_map.extend(bm);
is_never_pat |= np;
return false;
}
PatKind::Never => is_never_pat = true,
_ => {}
}

true
});

binding_map
if is_never_pat {
for (_, binding) in binding_map {
self.report_error(binding.span, ResolutionError::BindingInNeverPattern);
}
Err(IsNeverPattern)
} else {
Ok(binding_map)
}
}

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

/// Checks that all of the arms in an or-pattern have exactly the
/// same set of bindings, with the same binding modes for each.
fn check_consistent_bindings(
/// Compute the binding map for an or-pattern. Checks that all of the arms in the or-pattern
/// have exactly the same set of bindings, with the same binding modes for each.
/// Returns the computed binding map and a boolean indicating whether the pattern is a never
/// pattern.
fn compute_and_check_or_pat_binding_map(
&mut self,
pats: &[P<Pat>],
) -> Vec<FxIndexMap<Ident, BindingInfo>> {
// pats is consistent.
) -> (FxIndexMap<Ident, BindingInfo>, bool) {
let mut missing_vars = FxIndexMap::default();
let mut inconsistent_vars = FxIndexMap::default();

// 1) Compute the binding maps of all arms.
let maps = pats.iter().map(|pat| self.binding_mode_map(pat)).collect::<Vec<_>>();
// 1) Compute the binding maps of all arms; never patterns don't participate in this.
let not_never_pats = pats
.iter()
.filter_map(|pat| {
let binding_map = self.compute_and_check_binding_map(pat).ok()?;
Some((binding_map, pat))
})
.collect::<Vec<_>>();

// 2) Record any missing bindings or binding mode inconsistencies.
for (map_outer, pat_outer) in maps.iter().zip(pats.iter()) {
for (map_outer, pat_outer) in not_never_pats.iter() {
// Check against all arms except for the same pattern which is always self-consistent.
let inners = maps
let inners = not_never_pats
.iter()
.zip(pats.iter())
.filter(|(_, pat)| pat.id != pat_outer.id)
.flat_map(|(map, _)| map)
.map(|(key, binding)| (key.name, map_outer.get(key), binding));

let inners = inners.collect::<Vec<_>>();
.flat_map(|(map, _)| map);

for (name, info, &binding_inner) in inners {
match info {
for (key, binding_inner) in inners {
let name = key.name;
match map_outer.get(key) {
None => {
// The inner binding is missing in the outer.
let binding_error =
Expand Down Expand Up @@ -3295,19 +3313,28 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
self.report_error(v.0, ResolutionError::VariableBoundWithDifferentMode(name, v.1));
}

// 5) Finally bubble up all the binding maps.
maps
// 5) Bubble up the final binding map.
let is_never_pat = not_never_pats.is_empty();
let mut binding_map = FxIndexMap::default();
for (bm, _) in not_never_pats {
binding_map.extend(bm);
}
(binding_map, is_never_pat)
}

/// Check the consistency of the outermost or-patterns.
fn check_consistent_bindings_top(&mut self, pat: &'ast Pat) {
/// Check the consistency of bindings wrt or-patterns and never patterns.
fn check_consistent_bindings(&mut self, pat: &'ast Pat) {
let mut is_or_or_never = false;
pat.walk(&mut |pat| match pat.kind {
PatKind::Or(ref ps) => {
self.check_consistent_bindings(ps);
PatKind::Or(..) | PatKind::Never => {
is_or_or_never = true;
false
}
_ => true,
})
});
if is_or_or_never {
let _ = self.compute_and_check_binding_map(pat);
}
}

fn resolve_arm(&mut self, arm: &'ast Arm) {
Expand Down Expand Up @@ -3336,7 +3363,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
visit::walk_pat(self, pat);
self.resolve_pattern_inner(pat, pat_src, bindings);
// This has to happen *after* we determine which pat_idents are variants:
self.check_consistent_bindings_top(pat);
self.check_consistent_bindings(pat);
}

/// Resolve bindings in a pattern. This is a helper to `resolve_pattern`.
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_resolve/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,8 @@ enum ResolutionError<'a> {
InvalidAsmSym,
/// `self` used instead of `Self` in a generic parameter
LowercaseSelf,
/// A never pattern has a binding.
BindingInNeverPattern,
}

enum VisResolutionError<'a> {
Expand Down
1 change: 0 additions & 1 deletion tests/ui/feature-gates/feature-gate-never_patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ fn main() {
let res: Result<u32, Void> = Ok(0);
let (Ok(_x) | Err(&!)) = res.as_ref();
//~^ ERROR `!` patterns are experimental
//~| ERROR: is not bound in all patterns

unsafe {
let ptr: *const Void = NonNull::dangling().as_ptr();
Expand Down
37 changes: 14 additions & 23 deletions tests/ui/feature-gates/feature-gate-never_patterns.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: unexpected `,` in pattern
--> $DIR/feature-gate-never_patterns.rs:34:16
--> $DIR/feature-gate-never_patterns.rs:33:16
|
LL | Some(_),
| ^
Expand All @@ -13,14 +13,6 @@ help: ...or a vertical bar to match on multiple alternatives
LL | Some(_) |
|

error[E0408]: variable `_x` is not bound in all patterns
--> $DIR/feature-gate-never_patterns.rs:8:19
|
LL | let (Ok(_x) | Err(&!)) = res.as_ref();
| -- ^^^^^^^ pattern doesn't bind `_x`
| |
| variable not in all patterns

error[E0658]: `!` patterns are experimental
--> $DIR/feature-gate-never_patterns.rs:8:24
|
Expand All @@ -31,7 +23,7 @@ LL | let (Ok(_x) | Err(&!)) = res.as_ref();
= help: add `#![feature(never_patterns)]` to the crate attributes to enable

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

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

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

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

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

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

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

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

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

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

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

error: aborting due to 14 previous errors
error: aborting due to 13 previous errors

Some errors have detailed explanations: E0408, E0658.
For more information about an error, try `rustc --explain E0408`.
For more information about this error, try `rustc --explain E0658`.
28 changes: 1 addition & 27 deletions tests/ui/pattern/never_patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@ fn main() {}

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

// Check we only accept `!` where we want to.
Expand Down Expand Up @@ -74,26 +71,3 @@ fn never_pattern_location(void: Void) {
Some(&(_, !)),
}
}

fn never_and_bindings() {
let x: Result<bool, &(u32, Void)> = Ok(false);

// FIXME(never_patterns): Never patterns in or-patterns don't need to share the same bindings.
match x {
Ok(_x) | Err(&!) => {}
//~^ ERROR: is not bound in all patterns
}
let (Ok(_x) | Err(&!)) = x;
//~^ ERROR: is not bound in all patterns

// FIXME(never_patterns): A never pattern mustn't have bindings.
match x {
Ok(_) => {}
Err(&(_b, !)),
}
match x {
Ok(_a) | Err(&(_b, !)) => {}
//~^ ERROR: is not bound in all patterns
//~| ERROR: is not bound in all patterns
}
}
Loading

0 comments on commit 1a3eeaf

Please sign in to comment.