Skip to content
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

Stabilize min_exhaustive_patterns #445

Merged
merged 1 commit into from
Aug 11, 2024

Conversation

Nadrieril
Copy link
Member

This is a documentation change to accompany this stabilization report. It should only be merged if the stabilization gets accepted.

matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Aug 6, 2024
… r=fee1-dead

Stabilize `min_exhaustive_patterns`

## Stabilisation report

I propose we stabilize the [`min_exhaustive_patterns`](rust-lang#119612) language feature.

With this feature, patterns of empty types are considered unreachable when matched by-value. This allows:
```rust
enum Void {}
fn foo() -> Result<u32, Void>;

fn main() {
  let Ok(x) = foo();
  // also
  match foo() {
    Ok(x) => ...,
  }
}
```

This is a subset of the long-unstable [`exhaustive_patterns`](rust-lang#51085) feature. That feature is blocked because omitting empty patterns is tricky when *not* matched by-value. This PR stabilizes the by-value case, which is not tricky.

The not-by-value cases (behind references, pointers, and unions) stay as they are today, e.g.
```rust
enum Void {}
fn foo() -> Result<u32, &Void>;

fn main() {
  let Ok(x) = foo(); // ERROR: missing `Err(_)`
}
```

The consequence on existing code is some extra "unreachable pattern" warnings. This is fully backwards-compatible.

### Comparison with today's rust

This proposal only affects match checking of empty types (i.e. types with no valid values). Non-empty types behave the same with or without this feature. Note that everything below is phrased in terms of `match` but applies equallly to `if let` and other pattern-matching expressions.

To be precise, a visibly empty type is:
- an enum with no variants;
- the never type `!`;
- a struct with a *visible* field of a visibly empty type (and no #[non_exhaustive] annotation);
- a tuple where one of the types is visibly empty;
- en enum with all variants visibly empty (and no `#[non_exhaustive]` annotation);
- a `[T; N]` with `N != 0` and `T` visibly empty;
- all other types are nonempty.

(An extra change was proposed below: that we ignore #[non_exhaustive] for structs since adding fields cannot turn an empty struct into a non-empty one)

For normal types, exhaustiveness checking requires that we list all variants (or use a wildcard). For empty types it's more subtle: in some cases we require a `_` pattern even though there are no valid values that can match it. This is where the difference lies regarding this feature.

#### Today's rust

Under today's rust, a `_` is required for all empty types, except specifically: if the matched expression is of type `!` (the never type) or `EmptyEnum` (where `EmptyEnum` is an enum with no variants), then the `_` is not required.

```rust
let foo: Result<u32, !> = ...;
match foo {
    Ok(x) => ...,
    Err(_) => ..., // required
}
let foo: Result<u32, &!> = ...;
match foo {
    Ok(x) => ...,
    Err(_) => ..., // required
}
let foo: &! = ...;
match foo {
    _ => ..., // required
}
fn blah(foo: (u32, !)) {
    match foo {
        _ => ..., // required
    }
}
unsafe {
    let ptr: *const ! = ...;
    match *ptr {} // allowed
    let ptr: *const (u32, !) = ...;
    match *ptr {
        (x, _) => { ... } // required
    }
    let ptr: *const Result<u32, !> = ...;
    match *ptr {
        Ok(x) => { ... }
        Err(_) => { ... } // required
    }
}
```

#### After this PR

After this PR, a pattern of an empty type can be omitted if (and only if):
- the match scrutinee expression has type  `!` or `EmptyEnum` (like before);
- *or* the empty type is matched by value (that's the new behavior).

In all other cases, a `_` is required to match on an empty type.

```rust
let foo: Result<u32, !> = ...;
match foo {
    Ok(x) => ..., // `Err` not required
}
let foo: Result<u32, &!> = ...;
match foo {
    Ok(x) => ...,
    Err(_) => ..., // required because `!` is under a dereference
}
let foo: &! = ...;
match foo {
    _ => ..., // required because `!` is under a dereference
}
fn blah(foo: (u32, !)) {
    match foo {} // allowed
}
unsafe {
    let ptr: *const ! = ...;
    match *ptr {} // allowed
    let ptr: *const (u32, !) = ...;
    match *ptr {
        (x, _) => { ... } // required because the matched place is under a (pointer) dereference
    }
    let ptr: *const Result<u32, !> = ...;
    match *ptr {
        Ok(x) => { ... }
        Err(_) => { ... } // required because the matched place is under a (pointer) dereference
    }
}
```

### Documentation

The reference does not say anything specific about exhaustiveness checking, hence there is nothing to update there. The nomicon does, I opened rust-lang/nomicon#445 to reflect the changes.

### Tests

The relevant tests are in `tests/ui/pattern/usefulness/empty-types.rs`.

### Unresolved Questions

None that I know of.
bors added a commit to rust-lang-ci/rust that referenced this pull request Aug 6, 2024
…=<try>

Stabilize `min_exhaustive_patterns`

## Stabilisation report

I propose we stabilize the [`min_exhaustive_patterns`](rust-lang#119612) language feature.

With this feature, patterns of empty types are considered unreachable when matched by-value. This allows:
```rust
enum Void {}
fn foo() -> Result<u32, Void>;

fn main() {
  let Ok(x) = foo();
  // also
  match foo() {
    Ok(x) => ...,
  }
}
```

This is a subset of the long-unstable [`exhaustive_patterns`](rust-lang#51085) feature. That feature is blocked because omitting empty patterns is tricky when *not* matched by-value. This PR stabilizes the by-value case, which is not tricky.

The not-by-value cases (behind references, pointers, and unions) stay as they are today, e.g.
```rust
enum Void {}
fn foo() -> Result<u32, &Void>;

fn main() {
  let Ok(x) = foo(); // ERROR: missing `Err(_)`
}
```

The consequence on existing code is some extra "unreachable pattern" warnings. This is fully backwards-compatible.

### Comparison with today's rust

This proposal only affects match checking of empty types (i.e. types with no valid values). Non-empty types behave the same with or without this feature. Note that everything below is phrased in terms of `match` but applies equallly to `if let` and other pattern-matching expressions.

To be precise, a visibly empty type is:
- an enum with no variants;
- the never type `!`;
- a struct with a *visible* field of a visibly empty type (and no #[non_exhaustive] annotation);
- a tuple where one of the types is visibly empty;
- en enum with all variants visibly empty (and no `#[non_exhaustive]` annotation);
- a `[T; N]` with `N != 0` and `T` visibly empty;
- all other types are nonempty.

(An extra change was proposed below: that we ignore #[non_exhaustive] for structs since adding fields cannot turn an empty struct into a non-empty one)

For normal types, exhaustiveness checking requires that we list all variants (or use a wildcard). For empty types it's more subtle: in some cases we require a `_` pattern even though there are no valid values that can match it. This is where the difference lies regarding this feature.

#### Today's rust

Under today's rust, a `_` is required for all empty types, except specifically: if the matched expression is of type `!` (the never type) or `EmptyEnum` (where `EmptyEnum` is an enum with no variants), then the `_` is not required.

```rust
let foo: Result<u32, !> = ...;
match foo {
    Ok(x) => ...,
    Err(_) => ..., // required
}
let foo: Result<u32, &!> = ...;
match foo {
    Ok(x) => ...,
    Err(_) => ..., // required
}
let foo: &! = ...;
match foo {
    _ => ..., // required
}
fn blah(foo: (u32, !)) {
    match foo {
        _ => ..., // required
    }
}
unsafe {
    let ptr: *const ! = ...;
    match *ptr {} // allowed
    let ptr: *const (u32, !) = ...;
    match *ptr {
        (x, _) => { ... } // required
    }
    let ptr: *const Result<u32, !> = ...;
    match *ptr {
        Ok(x) => { ... }
        Err(_) => { ... } // required
    }
}
```

#### After this PR

After this PR, a pattern of an empty type can be omitted if (and only if):
- the match scrutinee expression has type  `!` or `EmptyEnum` (like before);
- *or* the empty type is matched by value (that's the new behavior).

In all other cases, a `_` is required to match on an empty type.

```rust
let foo: Result<u32, !> = ...;
match foo {
    Ok(x) => ..., // `Err` not required
}
let foo: Result<u32, &!> = ...;
match foo {
    Ok(x) => ...,
    Err(_) => ..., // required because `!` is under a dereference
}
let foo: &! = ...;
match foo {
    _ => ..., // required because `!` is under a dereference
}
fn blah(foo: (u32, !)) {
    match foo {} // allowed
}
unsafe {
    let ptr: *const ! = ...;
    match *ptr {} // allowed
    let ptr: *const (u32, !) = ...;
    match *ptr {
        (x, _) => { ... } // required because the matched place is under a (pointer) dereference
    }
    let ptr: *const Result<u32, !> = ...;
    match *ptr {
        Ok(x) => { ... }
        Err(_) => { ... } // required because the matched place is under a (pointer) dereference
    }
}
```

### Documentation

The reference does not say anything specific about exhaustiveness checking, hence there is nothing to update there. The nomicon does, I opened rust-lang/nomicon#445 to reflect the changes.

### Tests

The relevant tests are in `tests/ui/pattern/usefulness/empty-types.rs`.

### Unresolved Questions

None that I know of.
bors added a commit to rust-lang-ci/rust that referenced this pull request Aug 7, 2024
…=fee1-dead

Stabilize `min_exhaustive_patterns`

## Stabilisation report

I propose we stabilize the [`min_exhaustive_patterns`](rust-lang#119612) language feature.

With this feature, patterns of empty types are considered unreachable when matched by-value. This allows:
```rust
enum Void {}
fn foo() -> Result<u32, Void>;

fn main() {
  let Ok(x) = foo();
  // also
  match foo() {
    Ok(x) => ...,
  }
}
```

This is a subset of the long-unstable [`exhaustive_patterns`](rust-lang#51085) feature. That feature is blocked because omitting empty patterns is tricky when *not* matched by-value. This PR stabilizes the by-value case, which is not tricky.

The not-by-value cases (behind references, pointers, and unions) stay as they are today, e.g.
```rust
enum Void {}
fn foo() -> Result<u32, &Void>;

fn main() {
  let Ok(x) = foo(); // ERROR: missing `Err(_)`
}
```

The consequence on existing code is some extra "unreachable pattern" warnings. This is fully backwards-compatible.

### Comparison with today's rust

This proposal only affects match checking of empty types (i.e. types with no valid values). Non-empty types behave the same with or without this feature. Note that everything below is phrased in terms of `match` but applies equallly to `if let` and other pattern-matching expressions.

To be precise, a visibly empty type is:
- an enum with no variants;
- the never type `!`;
- a struct with a *visible* field of a visibly empty type (and no #[non_exhaustive] annotation);
- a tuple where one of the types is visibly empty;
- en enum with all variants visibly empty (and no `#[non_exhaustive]` annotation);
- a `[T; N]` with `N != 0` and `T` visibly empty;
- all other types are nonempty.

(An extra change was proposed below: that we ignore #[non_exhaustive] for structs since adding fields cannot turn an empty struct into a non-empty one)

For normal types, exhaustiveness checking requires that we list all variants (or use a wildcard). For empty types it's more subtle: in some cases we require a `_` pattern even though there are no valid values that can match it. This is where the difference lies regarding this feature.

#### Today's rust

Under today's rust, a `_` is required for all empty types, except specifically: if the matched expression is of type `!` (the never type) or `EmptyEnum` (where `EmptyEnum` is an enum with no variants), then the `_` is not required.

```rust
let foo: Result<u32, !> = ...;
match foo {
    Ok(x) => ...,
    Err(_) => ..., // required
}
let foo: Result<u32, &!> = ...;
match foo {
    Ok(x) => ...,
    Err(_) => ..., // required
}
let foo: &! = ...;
match foo {
    _ => ..., // required
}
fn blah(foo: (u32, !)) {
    match foo {
        _ => ..., // required
    }
}
unsafe {
    let ptr: *const ! = ...;
    match *ptr {} // allowed
    let ptr: *const (u32, !) = ...;
    match *ptr {
        (x, _) => { ... } // required
    }
    let ptr: *const Result<u32, !> = ...;
    match *ptr {
        Ok(x) => { ... }
        Err(_) => { ... } // required
    }
}
```

#### After this PR

After this PR, a pattern of an empty type can be omitted if (and only if):
- the match scrutinee expression has type  `!` or `EmptyEnum` (like before);
- *or* the empty type is matched by value (that's the new behavior).

In all other cases, a `_` is required to match on an empty type.

```rust
let foo: Result<u32, !> = ...;
match foo {
    Ok(x) => ..., // `Err` not required
}
let foo: Result<u32, &!> = ...;
match foo {
    Ok(x) => ...,
    Err(_) => ..., // required because `!` is under a dereference
}
let foo: &! = ...;
match foo {
    _ => ..., // required because `!` is under a dereference
}
fn blah(foo: (u32, !)) {
    match foo {} // allowed
}
unsafe {
    let ptr: *const ! = ...;
    match *ptr {} // allowed
    let ptr: *const (u32, !) = ...;
    match *ptr {
        (x, _) => { ... } // required because the matched place is under a (pointer) dereference
    }
    let ptr: *const Result<u32, !> = ...;
    match *ptr {
        Ok(x) => { ... }
        Err(_) => { ... } // required because the matched place is under a (pointer) dereference
    }
}
```

### Documentation

The reference does not say anything specific about exhaustiveness checking, hence there is nothing to update there. The nomicon does, I opened rust-lang/nomicon#445 to reflect the changes.

### Tests

The relevant tests are in `tests/ui/pattern/usefulness/empty-types.rs`.

### Unresolved Questions

None that I know of.
bors added a commit to rust-lang-ci/rust that referenced this pull request Aug 10, 2024
…=<try>

Stabilize `min_exhaustive_patterns`

## Stabilisation report

I propose we stabilize the [`min_exhaustive_patterns`](rust-lang#119612) language feature.

With this feature, patterns of empty types are considered unreachable when matched by-value. This allows:
```rust
enum Void {}
fn foo() -> Result<u32, Void>;

fn main() {
  let Ok(x) = foo();
  // also
  match foo() {
    Ok(x) => ...,
  }
}
```

This is a subset of the long-unstable [`exhaustive_patterns`](rust-lang#51085) feature. That feature is blocked because omitting empty patterns is tricky when *not* matched by-value. This PR stabilizes the by-value case, which is not tricky.

The not-by-value cases (behind references, pointers, and unions) stay as they are today, e.g.
```rust
enum Void {}
fn foo() -> Result<u32, &Void>;

fn main() {
  let Ok(x) = foo(); // ERROR: missing `Err(_)`
}
```

The consequence on existing code is some extra "unreachable pattern" warnings. This is fully backwards-compatible.

### Comparison with today's rust

This proposal only affects match checking of empty types (i.e. types with no valid values). Non-empty types behave the same with or without this feature. Note that everything below is phrased in terms of `match` but applies equallly to `if let` and other pattern-matching expressions.

To be precise, a visibly empty type is:
- an enum with no variants;
- the never type `!`;
- a struct with a *visible* field of a visibly empty type (and no #[non_exhaustive] annotation);
- a tuple where one of the types is visibly empty;
- en enum with all variants visibly empty (and no `#[non_exhaustive]` annotation);
- a `[T; N]` with `N != 0` and `T` visibly empty;
- all other types are nonempty.

(An extra change was proposed below: that we ignore #[non_exhaustive] for structs since adding fields cannot turn an empty struct into a non-empty one)

For normal types, exhaustiveness checking requires that we list all variants (or use a wildcard). For empty types it's more subtle: in some cases we require a `_` pattern even though there are no valid values that can match it. This is where the difference lies regarding this feature.

#### Today's rust

Under today's rust, a `_` is required for all empty types, except specifically: if the matched expression is of type `!` (the never type) or `EmptyEnum` (where `EmptyEnum` is an enum with no variants), then the `_` is not required.

```rust
let foo: Result<u32, !> = ...;
match foo {
    Ok(x) => ...,
    Err(_) => ..., // required
}
let foo: Result<u32, &!> = ...;
match foo {
    Ok(x) => ...,
    Err(_) => ..., // required
}
let foo: &! = ...;
match foo {
    _ => ..., // required
}
fn blah(foo: (u32, !)) {
    match foo {
        _ => ..., // required
    }
}
unsafe {
    let ptr: *const ! = ...;
    match *ptr {} // allowed
    let ptr: *const (u32, !) = ...;
    match *ptr {
        (x, _) => { ... } // required
    }
    let ptr: *const Result<u32, !> = ...;
    match *ptr {
        Ok(x) => { ... }
        Err(_) => { ... } // required
    }
}
```

#### After this PR

After this PR, a pattern of an empty type can be omitted if (and only if):
- the match scrutinee expression has type  `!` or `EmptyEnum` (like before);
- *or* the empty type is matched by value (that's the new behavior).

In all other cases, a `_` is required to match on an empty type.

```rust
let foo: Result<u32, !> = ...;
match foo {
    Ok(x) => ..., // `Err` not required
}
let foo: Result<u32, &!> = ...;
match foo {
    Ok(x) => ...,
    Err(_) => ..., // required because `!` is under a dereference
}
let foo: &! = ...;
match foo {
    _ => ..., // required because `!` is under a dereference
}
fn blah(foo: (u32, !)) {
    match foo {} // allowed
}
unsafe {
    let ptr: *const ! = ...;
    match *ptr {} // allowed
    let ptr: *const (u32, !) = ...;
    match *ptr {
        (x, _) => { ... } // required because the matched place is under a (pointer) dereference
    }
    let ptr: *const Result<u32, !> = ...;
    match *ptr {
        Ok(x) => { ... }
        Err(_) => { ... } // required because the matched place is under a (pointer) dereference
    }
}
```

### Documentation

The reference does not say anything specific about exhaustiveness checking, hence there is nothing to update there. The nomicon does, I opened rust-lang/nomicon#445 to reflect the changes.

### Tests

The relevant tests are in `tests/ui/pattern/usefulness/empty-types.rs`.

### Unresolved Questions

None that I know of.

try-job: dist-aarch64-apple
bors added a commit to rust-lang-ci/rust that referenced this pull request Aug 10, 2024
…=fee1-dead

Stabilize `min_exhaustive_patterns`

## Stabilisation report

I propose we stabilize the [`min_exhaustive_patterns`](rust-lang#119612) language feature.

With this feature, patterns of empty types are considered unreachable when matched by-value. This allows:
```rust
enum Void {}
fn foo() -> Result<u32, Void>;

fn main() {
  let Ok(x) = foo();
  // also
  match foo() {
    Ok(x) => ...,
  }
}
```

This is a subset of the long-unstable [`exhaustive_patterns`](rust-lang#51085) feature. That feature is blocked because omitting empty patterns is tricky when *not* matched by-value. This PR stabilizes the by-value case, which is not tricky.

The not-by-value cases (behind references, pointers, and unions) stay as they are today, e.g.
```rust
enum Void {}
fn foo() -> Result<u32, &Void>;

fn main() {
  let Ok(x) = foo(); // ERROR: missing `Err(_)`
}
```

The consequence on existing code is some extra "unreachable pattern" warnings. This is fully backwards-compatible.

### Comparison with today's rust

This proposal only affects match checking of empty types (i.e. types with no valid values). Non-empty types behave the same with or without this feature. Note that everything below is phrased in terms of `match` but applies equallly to `if let` and other pattern-matching expressions.

To be precise, a visibly empty type is:
- an enum with no variants;
- the never type `!`;
- a struct with a *visible* field of a visibly empty type (and no #[non_exhaustive] annotation);
- a tuple where one of the types is visibly empty;
- en enum with all variants visibly empty (and no `#[non_exhaustive]` annotation);
- a `[T; N]` with `N != 0` and `T` visibly empty;
- all other types are nonempty.

(An extra change was proposed below: that we ignore #[non_exhaustive] for structs since adding fields cannot turn an empty struct into a non-empty one)

For normal types, exhaustiveness checking requires that we list all variants (or use a wildcard). For empty types it's more subtle: in some cases we require a `_` pattern even though there are no valid values that can match it. This is where the difference lies regarding this feature.

#### Today's rust

Under today's rust, a `_` is required for all empty types, except specifically: if the matched expression is of type `!` (the never type) or `EmptyEnum` (where `EmptyEnum` is an enum with no variants), then the `_` is not required.

```rust
let foo: Result<u32, !> = ...;
match foo {
    Ok(x) => ...,
    Err(_) => ..., // required
}
let foo: Result<u32, &!> = ...;
match foo {
    Ok(x) => ...,
    Err(_) => ..., // required
}
let foo: &! = ...;
match foo {
    _ => ..., // required
}
fn blah(foo: (u32, !)) {
    match foo {
        _ => ..., // required
    }
}
unsafe {
    let ptr: *const ! = ...;
    match *ptr {} // allowed
    let ptr: *const (u32, !) = ...;
    match *ptr {
        (x, _) => { ... } // required
    }
    let ptr: *const Result<u32, !> = ...;
    match *ptr {
        Ok(x) => { ... }
        Err(_) => { ... } // required
    }
}
```

#### After this PR

After this PR, a pattern of an empty type can be omitted if (and only if):
- the match scrutinee expression has type  `!` or `EmptyEnum` (like before);
- *or* the empty type is matched by value (that's the new behavior).

In all other cases, a `_` is required to match on an empty type.

```rust
let foo: Result<u32, !> = ...;
match foo {
    Ok(x) => ..., // `Err` not required
}
let foo: Result<u32, &!> = ...;
match foo {
    Ok(x) => ...,
    Err(_) => ..., // required because `!` is under a dereference
}
let foo: &! = ...;
match foo {
    _ => ..., // required because `!` is under a dereference
}
fn blah(foo: (u32, !)) {
    match foo {} // allowed
}
unsafe {
    let ptr: *const ! = ...;
    match *ptr {} // allowed
    let ptr: *const (u32, !) = ...;
    match *ptr {
        (x, _) => { ... } // required because the matched place is under a (pointer) dereference
    }
    let ptr: *const Result<u32, !> = ...;
    match *ptr {
        Ok(x) => { ... }
        Err(_) => { ... } // required because the matched place is under a (pointer) dereference
    }
}
```

### Documentation

The reference does not say anything specific about exhaustiveness checking, hence there is nothing to update there. The nomicon does, I opened rust-lang/nomicon#445 to reflect the changes.

### Tests

The relevant tests are in `tests/ui/pattern/usefulness/empty-types.rs`.

### Unresolved Questions

None that I know of.

try-job: dist-aarch64-apple
@Nadrieril
Copy link
Member Author

The feature is now merged rust-lang/rust#122792

Copy link
Contributor

@ehuss ehuss left a comment

Choose a reason for hiding this comment

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

Thanks!

Will merge after the next nightly is published tonight.

@ehuss ehuss merged commit 6ecf95c into rust-lang:master Aug 11, 2024
1 check passed
@Nadrieril Nadrieril deleted the Nadrieril-patch-1 branch August 11, 2024 17:36
RalfJung pushed a commit to RalfJung/miri that referenced this pull request Aug 12, 2024
Stabilize `min_exhaustive_patterns`

## Stabilisation report

I propose we stabilize the [`min_exhaustive_patterns`](rust-lang/rust#119612) language feature.

With this feature, patterns of empty types are considered unreachable when matched by-value. This allows:
```rust
enum Void {}
fn foo() -> Result<u32, Void>;

fn main() {
  let Ok(x) = foo();
  // also
  match foo() {
    Ok(x) => ...,
  }
}
```

This is a subset of the long-unstable [`exhaustive_patterns`](rust-lang/rust#51085) feature. That feature is blocked because omitting empty patterns is tricky when *not* matched by-value. This PR stabilizes the by-value case, which is not tricky.

The not-by-value cases (behind references, pointers, and unions) stay as they are today, e.g.
```rust
enum Void {}
fn foo() -> Result<u32, &Void>;

fn main() {
  let Ok(x) = foo(); // ERROR: missing `Err(_)`
}
```

The consequence on existing code is some extra "unreachable pattern" warnings. This is fully backwards-compatible.

### Comparison with today's rust

This proposal only affects match checking of empty types (i.e. types with no valid values). Non-empty types behave the same with or without this feature. Note that everything below is phrased in terms of `match` but applies equallly to `if let` and other pattern-matching expressions.

To be precise, a visibly empty type is:
- an enum with no variants;
- the never type `!`;
- a struct with a *visible* field of a visibly empty type (and no #[non_exhaustive] annotation);
- a tuple where one of the types is visibly empty;
- en enum with all variants visibly empty (and no `#[non_exhaustive]` annotation);
- a `[T; N]` with `N != 0` and `T` visibly empty;
- all other types are nonempty.

(An extra change was proposed below: that we ignore #[non_exhaustive] for structs since adding fields cannot turn an empty struct into a non-empty one)

For normal types, exhaustiveness checking requires that we list all variants (or use a wildcard). For empty types it's more subtle: in some cases we require a `_` pattern even though there are no valid values that can match it. This is where the difference lies regarding this feature.

#### Today's rust

Under today's rust, a `_` is required for all empty types, except specifically: if the matched expression is of type `!` (the never type) or `EmptyEnum` (where `EmptyEnum` is an enum with no variants), then the `_` is not required.

```rust
let foo: Result<u32, !> = ...;
match foo {
    Ok(x) => ...,
    Err(_) => ..., // required
}
let foo: Result<u32, &!> = ...;
match foo {
    Ok(x) => ...,
    Err(_) => ..., // required
}
let foo: &! = ...;
match foo {
    _ => ..., // required
}
fn blah(foo: (u32, !)) {
    match foo {
        _ => ..., // required
    }
}
unsafe {
    let ptr: *const ! = ...;
    match *ptr {} // allowed
    let ptr: *const (u32, !) = ...;
    match *ptr {
        (x, _) => { ... } // required
    }
    let ptr: *const Result<u32, !> = ...;
    match *ptr {
        Ok(x) => { ... }
        Err(_) => { ... } // required
    }
}
```

#### After this PR

After this PR, a pattern of an empty type can be omitted if (and only if):
- the match scrutinee expression has type  `!` or `EmptyEnum` (like before);
- *or* the empty type is matched by value (that's the new behavior).

In all other cases, a `_` is required to match on an empty type.

```rust
let foo: Result<u32, !> = ...;
match foo {
    Ok(x) => ..., // `Err` not required
}
let foo: Result<u32, &!> = ...;
match foo {
    Ok(x) => ...,
    Err(_) => ..., // required because `!` is under a dereference
}
let foo: &! = ...;
match foo {
    _ => ..., // required because `!` is under a dereference
}
fn blah(foo: (u32, !)) {
    match foo {} // allowed
}
unsafe {
    let ptr: *const ! = ...;
    match *ptr {} // allowed
    let ptr: *const (u32, !) = ...;
    match *ptr {
        (x, _) => { ... } // required because the matched place is under a (pointer) dereference
    }
    let ptr: *const Result<u32, !> = ...;
    match *ptr {
        Ok(x) => { ... }
        Err(_) => { ... } // required because the matched place is under a (pointer) dereference
    }
}
```

### Documentation

The reference does not say anything specific about exhaustiveness checking, hence there is nothing to update there. The nomicon does, I opened rust-lang/nomicon#445 to reflect the changes.

### Tests

The relevant tests are in `tests/ui/pattern/usefulness/empty-types.rs`.

### Unresolved Questions

None that I know of.

try-job: dist-aarch64-apple
lnicola pushed a commit to lnicola/rust-analyzer that referenced this pull request Aug 13, 2024
Stabilize `min_exhaustive_patterns`

## Stabilisation report

I propose we stabilize the [`min_exhaustive_patterns`](rust-lang/rust#119612) language feature.

With this feature, patterns of empty types are considered unreachable when matched by-value. This allows:
```rust
enum Void {}
fn foo() -> Result<u32, Void>;

fn main() {
  let Ok(x) = foo();
  // also
  match foo() {
    Ok(x) => ...,
  }
}
```

This is a subset of the long-unstable [`exhaustive_patterns`](rust-lang/rust#51085) feature. That feature is blocked because omitting empty patterns is tricky when *not* matched by-value. This PR stabilizes the by-value case, which is not tricky.

The not-by-value cases (behind references, pointers, and unions) stay as they are today, e.g.
```rust
enum Void {}
fn foo() -> Result<u32, &Void>;

fn main() {
  let Ok(x) = foo(); // ERROR: missing `Err(_)`
}
```

The consequence on existing code is some extra "unreachable pattern" warnings. This is fully backwards-compatible.

### Comparison with today's rust

This proposal only affects match checking of empty types (i.e. types with no valid values). Non-empty types behave the same with or without this feature. Note that everything below is phrased in terms of `match` but applies equallly to `if let` and other pattern-matching expressions.

To be precise, a visibly empty type is:
- an enum with no variants;
- the never type `!`;
- a struct with a *visible* field of a visibly empty type (and no #[non_exhaustive] annotation);
- a tuple where one of the types is visibly empty;
- en enum with all variants visibly empty (and no `#[non_exhaustive]` annotation);
- a `[T; N]` with `N != 0` and `T` visibly empty;
- all other types are nonempty.

(An extra change was proposed below: that we ignore #[non_exhaustive] for structs since adding fields cannot turn an empty struct into a non-empty one)

For normal types, exhaustiveness checking requires that we list all variants (or use a wildcard). For empty types it's more subtle: in some cases we require a `_` pattern even though there are no valid values that can match it. This is where the difference lies regarding this feature.

#### Today's rust

Under today's rust, a `_` is required for all empty types, except specifically: if the matched expression is of type `!` (the never type) or `EmptyEnum` (where `EmptyEnum` is an enum with no variants), then the `_` is not required.

```rust
let foo: Result<u32, !> = ...;
match foo {
    Ok(x) => ...,
    Err(_) => ..., // required
}
let foo: Result<u32, &!> = ...;
match foo {
    Ok(x) => ...,
    Err(_) => ..., // required
}
let foo: &! = ...;
match foo {
    _ => ..., // required
}
fn blah(foo: (u32, !)) {
    match foo {
        _ => ..., // required
    }
}
unsafe {
    let ptr: *const ! = ...;
    match *ptr {} // allowed
    let ptr: *const (u32, !) = ...;
    match *ptr {
        (x, _) => { ... } // required
    }
    let ptr: *const Result<u32, !> = ...;
    match *ptr {
        Ok(x) => { ... }
        Err(_) => { ... } // required
    }
}
```

#### After this PR

After this PR, a pattern of an empty type can be omitted if (and only if):
- the match scrutinee expression has type  `!` or `EmptyEnum` (like before);
- *or* the empty type is matched by value (that's the new behavior).

In all other cases, a `_` is required to match on an empty type.

```rust
let foo: Result<u32, !> = ...;
match foo {
    Ok(x) => ..., // `Err` not required
}
let foo: Result<u32, &!> = ...;
match foo {
    Ok(x) => ...,
    Err(_) => ..., // required because `!` is under a dereference
}
let foo: &! = ...;
match foo {
    _ => ..., // required because `!` is under a dereference
}
fn blah(foo: (u32, !)) {
    match foo {} // allowed
}
unsafe {
    let ptr: *const ! = ...;
    match *ptr {} // allowed
    let ptr: *const (u32, !) = ...;
    match *ptr {
        (x, _) => { ... } // required because the matched place is under a (pointer) dereference
    }
    let ptr: *const Result<u32, !> = ...;
    match *ptr {
        Ok(x) => { ... }
        Err(_) => { ... } // required because the matched place is under a (pointer) dereference
    }
}
```

### Documentation

The reference does not say anything specific about exhaustiveness checking, hence there is nothing to update there. The nomicon does, I opened rust-lang/nomicon#445 to reflect the changes.

### Tests

The relevant tests are in `tests/ui/pattern/usefulness/empty-types.rs`.

### Unresolved Questions

None that I know of.

try-job: dist-aarch64-apple
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Aug 14, 2024
Update books

## rust-lang/book

7 commits in 67fa536768013d9d5a13f3a06790521d511ef711..04bc1396bb857f35b5dda1d773c9571e1f253304
2024-07-31 13:19:44 UTC to 2024-07-16 18:18:38 UTC

- mdbook-trpl-listing: Add missing elided lifetimes (rust-lang/book#3995)
- infra: include ghp-import and git push in generate-preview script (rust-lang/book#3998)
- infra: add robots.txt for GH Pages previews (rust-lang/book#3997)
- Clarify function definitions vs. expressions (rust-lang/book#3870)
- infra: fix some shellcheck issues in CI config (rust-lang/book#3988)
- infra: support test renderer in mdbook preprocessors (rust-lang/book#3982)
- Improve handling of `<Listing>`s (rust-lang/book#3975)

## rust-lang/edition-guide

4 commits in 5454de3d12b9ccc6375b629cf7ccda8264640aac..aeeb287d41a0332c210da122bea8e0e91844ab3e
2024-08-06 21:16:24 UTC to 2024-07-29 21:41:36 UTC

- Stabilize unsafe extern blocks (rust-lang/edition-guide#313)
- Add chapter for Lifetime Capture Rules 2024 (rust-lang/edition-guide#316)
- 2024: Add page for missing_fragment_specifier (rust-lang/edition-guide#315)
- Add documentation for 2024 prelude migration. (rust-lang/edition-guide#314)

## rust-lang/nomicon

3 commits in 0ebdacadbda8ce2cd8fbf93985e15af61a7ab895..6ecf95c5f2bfa0e6314dfe282bf775fd1405f7e9
2024-08-11 16:55:29 UTC to 2024-08-09 23:25:22 UTC

- Stabilize `min_exhaustive_patterns` (rust-lang/nomicon#445)
- repr(int) enums: both size and sign matter (rust-lang/nomicon#458)
- Update what-unsafe-does.md (rust-lang/nomicon#457)

## rust-lang/reference

6 commits in 2e191814f163ee1e77e2d6094eee4dd78a289c5b..62cd0df95061ba0ac886333f5cd7f3012f149da1
2024-08-11 21:06:12 UTC to 2024-07-30 06:34:03 UTC

- Reformat (and only reformat) the inline assembly chapter (rust-lang/reference#1550)
- Changes for unsafe extern blocks (RFC 3484) (rust-lang/reference#1536)
- Stabilize Wasm relaxed SIMD (rust-lang/reference#1421)
- Remove custom blockquote styling (rust-lang/reference#1547)
- Fix std-links for generics with commas. (rust-lang/reference#1549)
- Add details on how names are introduced. (rust-lang/reference#1052)

## rust-lang/rust-by-example

3 commits in 89aecb6951b77bc746da73df8c9f2b2ceaad494a..8f94061936e492159f4f6c09c0f917a7521893ff
2024-08-06 17:25:35 UTC to 2024-07-16 20:58:25 UTC

- Update lifetime_bounds.md (rust-lang/rust-by-example#1869)
- Remove the link to Japanese translation (rust-lang/rust-by-example#1868)
- Add an example of implementing the FromStr trait for Circles. (rust-lang/rust-by-example#1865)

## rust-lang/rustc-dev-guide

12 commits in 0c4d55c..43d8378
2024-08-08 17:54:27 UTC to 2024-07-19 07:15:12 UTC

- Added 'the' in chapter "Running test" subtitle "Run unit tests on the compiler/library" (rust-lang/rustc-dev-guide#2040)
- Correct rust code block in *Dataflow Analysis* (rust-lang/rustc-dev-guide#2037)
- linkcheck: fix filtering of the source files (rust-lang/rustc-dev-guide#2019)
- chore: fix some comments (rust-lang/rustc-dev-guide#2028)
- linkcheck: fix reported broken links (part 2) (rust-lang/rustc-dev-guide#2024)
- typo (rust-lang/rustc-dev-guide#2029)
- Fix broken links in `llvm-coverage-instrumentation.md` (rust-lang/rustc-dev-guide#2027)
- Fix invalid link to toolstate documentation (rust-lang/rustc-dev-guide#2021)
- linkcheck: fix reported broken links (part 1) (rust-lang/rustc-dev-guide#2022)
- fix link (rust-lang/rustc-dev-guide#2020)
- MIR docs: fix borked links and update style (rust-lang/rustc-dev-guide#2017)
- Update adding.md (rust-lang/rustc-dev-guide#2016)
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Aug 15, 2024
Rollup merge of rust-lang#129015 - rustbot:docs-update, r=ehuss

Update books

## rust-lang/book

7 commits in 67fa536768013d9d5a13f3a06790521d511ef711..04bc1396bb857f35b5dda1d773c9571e1f253304
2024-07-31 13:19:44 UTC to 2024-07-16 18:18:38 UTC

- mdbook-trpl-listing: Add missing elided lifetimes (rust-lang/book#3995)
- infra: include ghp-import and git push in generate-preview script (rust-lang/book#3998)
- infra: add robots.txt for GH Pages previews (rust-lang/book#3997)
- Clarify function definitions vs. expressions (rust-lang/book#3870)
- infra: fix some shellcheck issues in CI config (rust-lang/book#3988)
- infra: support test renderer in mdbook preprocessors (rust-lang/book#3982)
- Improve handling of `<Listing>`s (rust-lang/book#3975)

## rust-lang/edition-guide

4 commits in 5454de3d12b9ccc6375b629cf7ccda8264640aac..aeeb287d41a0332c210da122bea8e0e91844ab3e
2024-08-06 21:16:24 UTC to 2024-07-29 21:41:36 UTC

- Stabilize unsafe extern blocks (rust-lang/edition-guide#313)
- Add chapter for Lifetime Capture Rules 2024 (rust-lang/edition-guide#316)
- 2024: Add page for missing_fragment_specifier (rust-lang/edition-guide#315)
- Add documentation for 2024 prelude migration. (rust-lang/edition-guide#314)

## rust-lang/nomicon

3 commits in 0ebdacadbda8ce2cd8fbf93985e15af61a7ab895..6ecf95c5f2bfa0e6314dfe282bf775fd1405f7e9
2024-08-11 16:55:29 UTC to 2024-08-09 23:25:22 UTC

- Stabilize `min_exhaustive_patterns` (rust-lang/nomicon#445)
- repr(int) enums: both size and sign matter (rust-lang/nomicon#458)
- Update what-unsafe-does.md (rust-lang/nomicon#457)

## rust-lang/reference

6 commits in 2e191814f163ee1e77e2d6094eee4dd78a289c5b..62cd0df95061ba0ac886333f5cd7f3012f149da1
2024-08-11 21:06:12 UTC to 2024-07-30 06:34:03 UTC

- Reformat (and only reformat) the inline assembly chapter (rust-lang/reference#1550)
- Changes for unsafe extern blocks (RFC 3484) (rust-lang/reference#1536)
- Stabilize Wasm relaxed SIMD (rust-lang/reference#1421)
- Remove custom blockquote styling (rust-lang/reference#1547)
- Fix std-links for generics with commas. (rust-lang/reference#1549)
- Add details on how names are introduced. (rust-lang/reference#1052)

## rust-lang/rust-by-example

3 commits in 89aecb6951b77bc746da73df8c9f2b2ceaad494a..8f94061936e492159f4f6c09c0f917a7521893ff
2024-08-06 17:25:35 UTC to 2024-07-16 20:58:25 UTC

- Update lifetime_bounds.md (rust-lang/rust-by-example#1869)
- Remove the link to Japanese translation (rust-lang/rust-by-example#1868)
- Add an example of implementing the FromStr trait for Circles. (rust-lang/rust-by-example#1865)

## rust-lang/rustc-dev-guide

12 commits in 0c4d55c..43d8378
2024-08-08 17:54:27 UTC to 2024-07-19 07:15:12 UTC

- Added 'the' in chapter "Running test" subtitle "Run unit tests on the compiler/library" (rust-lang/rustc-dev-guide#2040)
- Correct rust code block in *Dataflow Analysis* (rust-lang/rustc-dev-guide#2037)
- linkcheck: fix filtering of the source files (rust-lang/rustc-dev-guide#2019)
- chore: fix some comments (rust-lang/rustc-dev-guide#2028)
- linkcheck: fix reported broken links (part 2) (rust-lang/rustc-dev-guide#2024)
- typo (rust-lang/rustc-dev-guide#2029)
- Fix broken links in `llvm-coverage-instrumentation.md` (rust-lang/rustc-dev-guide#2027)
- Fix invalid link to toolstate documentation (rust-lang/rustc-dev-guide#2021)
- linkcheck: fix reported broken links (part 1) (rust-lang/rustc-dev-guide#2022)
- fix link (rust-lang/rustc-dev-guide#2020)
- MIR docs: fix borked links and update style (rust-lang/rustc-dev-guide#2017)
- Update adding.md (rust-lang/rustc-dev-guide#2016)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants