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

Non-exhaustive structs may be empty #128934

Merged
merged 1 commit into from
Sep 5, 2024
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
4 changes: 0 additions & 4 deletions compiler/rustc_middle/src/ty/inhabitedness/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,6 @@ impl<'tcx> VariantDef {
adt: ty::AdtDef<'_>,
) -> InhabitedPredicate<'tcx> {
debug_assert!(!adt.is_union());
if self.is_field_list_non_exhaustive() && !self.def_id.is_local() {
// Non-exhaustive variants from other crates are always considered inhabited.
return InhabitedPredicate::True;
}
InhabitedPredicate::all(
tcx,
self.fields.iter().map(|field| {
Expand Down
8 changes: 1 addition & 7 deletions compiler/rustc_pattern_analysis/src/rustc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,17 +229,11 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
} else {
let variant =
&adt.variant(RustcPatCtxt::variant_index_for_adt(&ctor, *adt));

// In the cases of either a `#[non_exhaustive]` field list or a non-public
// field, we skip uninhabited fields in order not to reveal the
// uninhabitedness of the whole variant.
let is_non_exhaustive =
variant.is_field_list_non_exhaustive() && !adt.did().is_local();
let tys = cx.variant_sub_tys(ty, variant).map(|(field, ty)| {
let is_visible =
adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx);
let is_uninhabited = cx.is_uninhabited(*ty);
let skip = is_uninhabited && (!is_visible || is_non_exhaustive);
let skip = is_uninhabited && !is_visible;
(ty, PrivateUninhabitedField(skip))
});
cx.dropless_arena.alloc_from_iter(tys)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@ pub enum UninhabitedEnum {

#[non_exhaustive]
pub struct UninhabitedStruct {
_priv: !,
pub never: !,
_priv: (),
}

#[non_exhaustive]
pub struct UninhabitedTupleStruct(!);
pub struct PrivatelyUninhabitedStruct {
never: !,
}

#[non_exhaustive]
pub struct UninhabitedTupleStruct(pub !);

pub enum UninhabitedVariants {
#[non_exhaustive] Tuple(!),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ LL | match x {}
| ^
|
note: `IndirectUninhabitedEnum` defined here
--> $DIR/auxiliary/uninhabited.rs:26:1
--> $DIR/auxiliary/uninhabited.rs:32:1
|
LL | pub struct IndirectUninhabitedEnum(UninhabitedEnum);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -24,7 +24,7 @@ LL | match x {}
| ^
|
note: `IndirectUninhabitedStruct` defined here
--> $DIR/auxiliary/uninhabited.rs:28:1
--> $DIR/auxiliary/uninhabited.rs:34:1
|
LL | pub struct IndirectUninhabitedStruct(UninhabitedStruct);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -43,7 +43,7 @@ LL | match x {}
| ^
|
note: `IndirectUninhabitedTupleStruct` defined here
--> $DIR/auxiliary/uninhabited.rs:30:1
--> $DIR/auxiliary/uninhabited.rs:36:1
|
LL | pub struct IndirectUninhabitedTupleStruct(UninhabitedTupleStruct);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -62,7 +62,7 @@ LL | match x {}
| ^
|
note: `IndirectUninhabitedVariants` defined here
--> $DIR/auxiliary/uninhabited.rs:32:1
--> $DIR/auxiliary/uninhabited.rs:38:1
|
LL | pub struct IndirectUninhabitedVariants(UninhabitedVariants);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ LL | match x {}
| ^
|
note: `IndirectUninhabitedEnum` defined here
--> $DIR/auxiliary/uninhabited.rs:26:1
--> $DIR/auxiliary/uninhabited.rs:32:1
|
LL | pub struct IndirectUninhabitedEnum(UninhabitedEnum);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -24,7 +24,7 @@ LL | match x {}
| ^
|
note: `IndirectUninhabitedStruct` defined here
--> $DIR/auxiliary/uninhabited.rs:28:1
--> $DIR/auxiliary/uninhabited.rs:34:1
|
LL | pub struct IndirectUninhabitedStruct(UninhabitedStruct);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -43,7 +43,7 @@ LL | match x {}
| ^
|
note: `IndirectUninhabitedTupleStruct` defined here
--> $DIR/auxiliary/uninhabited.rs:30:1
--> $DIR/auxiliary/uninhabited.rs:36:1
|
LL | pub struct IndirectUninhabitedTupleStruct(UninhabitedTupleStruct);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -62,7 +62,7 @@ LL | match x {}
| ^
|
note: `IndirectUninhabitedVariants` defined here
--> $DIR/auxiliary/uninhabited.rs:32:1
--> $DIR/auxiliary/uninhabited.rs:38:1
|
LL | pub struct IndirectUninhabitedVariants(UninhabitedVariants);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ use uninhabited::PartiallyInhabitedVariants;

pub fn foo(x: PartiallyInhabitedVariants) {
match x {
PartiallyInhabitedVariants::Struct { .. } => {},
PartiallyInhabitedVariants::Struct { .. } => {},
PartiallyInhabitedVariants::Struct { .. } => {}
//~^ ERROR unreachable pattern
_ => {},
PartiallyInhabitedVariants::Struct { .. } => {}
//~^ ERROR unreachable pattern
_ => {}
}
}

fn main() { }
fn main() {}
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
error: unreachable pattern
--> $DIR/issue-65157-repeated-match-arm.rs:15:9
--> $DIR/issue-65157-repeated-match-arm.rs:14:9
|
LL | PartiallyInhabitedVariants::Struct { .. } => {},
| ----------------------------------------- matches all the relevant values
LL | PartiallyInhabitedVariants::Struct { .. } => {},
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no value can reach this
LL | PartiallyInhabitedVariants::Struct { .. } => {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ matches no values because `PartiallyInhabitedVariants` is uninhabited
|
= note: to learn more about uninhabited types, see https://doc.rust-lang.org/nomicon/exotic-sizes.html#empty-types
note: the lint level is defined here
--> $DIR/issue-65157-repeated-match-arm.rs:2:9
|
LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^

error: aborting due to 1 previous error
error: unreachable pattern
--> $DIR/issue-65157-repeated-match-arm.rs:16:9
|
LL | PartiallyInhabitedVariants::Struct { .. } => {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ matches no values because `PartiallyInhabitedVariants` is uninhabited
|
= note: to learn more about uninhabited types, see https://doc.rust-lang.org/nomicon/exotic-sizes.html#empty-types

error: aborting due to 2 previous errors

21 changes: 10 additions & 11 deletions tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@

extern crate uninhabited;

use uninhabited::{
UninhabitedEnum,
UninhabitedStruct,
UninhabitedTupleStruct,
UninhabitedVariants,
};
use uninhabited::*;

struct A;

Expand All @@ -19,16 +14,20 @@ fn cannot_empty_match_on_empty_enum_to_anything(x: UninhabitedEnum) -> A {
match x {} //~ ERROR non-exhaustive patterns
}

fn cannot_empty_match_on_empty_struct_to_anything(x: UninhabitedStruct) -> A {
match x {} //~ ERROR non-exhaustive patterns
fn empty_match_on_empty_struct(x: UninhabitedStruct) -> A {
match x {}
}

fn cannot_empty_match_on_empty_tuple_struct_to_anything(x: UninhabitedTupleStruct) -> A {
fn cannot_empty_match_on_privately_empty_struct(x: PrivatelyUninhabitedStruct) -> A {
match x {} //~ ERROR non-exhaustive patterns
}

fn cannot_empty_match_on_enum_with_empty_variants_struct_to_anything(x: UninhabitedVariants) -> A {
match x {} //~ ERROR non-exhaustive patterns
fn empty_match_on_empty_tuple_struct(x: UninhabitedTupleStruct) -> A {
match x {}
}

fn empty_match_on_enum_with_empty_variants_struct(x: UninhabitedVariants) -> A {
match x {}
}

fn main() {}
66 changes: 12 additions & 54 deletions tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match.stderr
Original file line number Diff line number Diff line change
@@ -1,83 +1,41 @@
error[E0004]: non-exhaustive patterns: type `UninhabitedEnum` is non-empty
--> $DIR/match.rs:19:11
error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedEnum` is non-empty
--> $DIR/match.rs:14:11
|
LL | match x {}
| ^
|
note: `UninhabitedEnum` defined here
note: `uninhabited::UninhabitedEnum` defined here
--> $DIR/auxiliary/uninhabited.rs:5:1
|
LL | pub enum UninhabitedEnum {
| ^^^^^^^^^^^^^^^^^^^^^^^^
= note: the matched value is of type `UninhabitedEnum`, which is marked as non-exhaustive
= note: the matched value is of type `uninhabited::UninhabitedEnum`, which is marked as non-exhaustive
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
|
LL ~ match x {
LL + _ => todo!(),
LL ~ }
|

error[E0004]: non-exhaustive patterns: type `UninhabitedStruct` is non-empty
--> $DIR/match.rs:23:11
error[E0004]: non-exhaustive patterns: type `uninhabited::PrivatelyUninhabitedStruct` is non-empty
--> $DIR/match.rs:22:11
|
LL | match x {}
| ^
|
note: `UninhabitedStruct` defined here
--> $DIR/auxiliary/uninhabited.rs:9:1
note: `uninhabited::PrivatelyUninhabitedStruct` defined here
--> $DIR/auxiliary/uninhabited.rs:15:1
|
LL | pub struct UninhabitedStruct {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: the matched value is of type `UninhabitedStruct`
LL | pub struct PrivatelyUninhabitedStruct {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: the matched value is of type `uninhabited::PrivatelyUninhabitedStruct`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
|
LL ~ match x {
LL + _ => todo!(),
LL ~ }
|

error[E0004]: non-exhaustive patterns: type `UninhabitedTupleStruct` is non-empty
--> $DIR/match.rs:27:11
|
LL | match x {}
| ^
|
note: `UninhabitedTupleStruct` defined here
--> $DIR/auxiliary/uninhabited.rs:14:1
|
LL | pub struct UninhabitedTupleStruct(!);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: the matched value is of type `UninhabitedTupleStruct`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
|
LL ~ match x {
LL + _ => todo!(),
LL ~ }
|

error[E0004]: non-exhaustive patterns: `UninhabitedVariants::Tuple(_)` and `UninhabitedVariants::Struct { .. }` not covered
--> $DIR/match.rs:31:11
|
LL | match x {}
| ^ patterns `UninhabitedVariants::Tuple(_)` and `UninhabitedVariants::Struct { .. }` not covered
|
note: `UninhabitedVariants` defined here
--> $DIR/auxiliary/uninhabited.rs:16:1
|
LL | pub enum UninhabitedVariants {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | #[non_exhaustive] Tuple(!),
| ----- not covered
LL | #[non_exhaustive] Struct { x: ! }
| ------ not covered
= note: the matched value is of type `UninhabitedVariants`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
|
LL ~ match x {
LL + UninhabitedVariants::Tuple(_) | UninhabitedVariants::Struct { .. } => todo!(),
LL ~ }
|

error: aborting due to 4 previous errors
error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0004`.
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,28 @@
extern crate uninhabited;

use uninhabited::{
UninhabitedEnum,
UninhabitedStruct,
UninhabitedTupleStruct,
UninhabitedVariants,
UninhabitedEnum, UninhabitedStruct, UninhabitedTupleStruct, UninhabitedVariants,
};

struct A;

// This test checks that an empty match on a non-exhaustive uninhabited type from an extern crate
// will not compile. In particular, this enables the `exhaustive_patterns` feature as this can
// change the branch used in the compiler to determine this.

fn cannot_empty_match_on_empty_enum_to_anything(x: UninhabitedEnum) -> A {
// This test checks that non-exhaustive enums are never considered uninhabited outside their
// defining crate, and non-exhaustive structs are considered uninhabited the same way as normal
// ones.
fn cannot_empty_match_on_non_exhaustive_empty_enum(x: UninhabitedEnum) -> A {
match x {} //~ ERROR non-exhaustive patterns
}

fn cannot_empty_match_on_empty_struct_to_anything(x: UninhabitedStruct) -> A {
match x {} //~ ERROR non-exhaustive patterns
fn empty_match_on_empty_struct(x: UninhabitedStruct) -> A {
match x {}
}

fn cannot_empty_match_on_empty_tuple_struct_to_anything(x: UninhabitedTupleStruct) -> A {
match x {} //~ ERROR non-exhaustive patterns
fn empty_match_on_empty_tuple_struct(x: UninhabitedTupleStruct) -> A {
match x {}
}

fn cannot_empty_match_on_enum_with_empty_variants_struct_to_anything(x: UninhabitedVariants) -> A {
match x {} //~ ERROR non-exhaustive patterns
fn empty_match_on_enum_with_empty_variants_struct(x: UninhabitedVariants) -> A {
match x {}
}

fn main() {}
Loading
Loading