-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of #116821 - Nadrieril:fix-opaque-ice, r=compiler-errors
Exhaustiveness: reveal opaque types properly Previously, exhaustiveness had no clear policy around opaque types. In this PR I propose the following policy: within the body of an item that defines the hidden type of some opaque type, exhaustiveness checking on a value of that opaque type is performed using the concrete hidden type inferred in this body. I'm not sure how consistent this is with other operations allowed on opaque types; I believe this will require FCP. From what I can tell, this doesn't change anything for non-empty types. The observable changes are: - when the real type is uninhabited, matches within the defining scopes can now rely on that for exhaustiveness, e.g.: ```rust #[derive(Copy, Clone)] enum Void {} fn return_never_rpit(x: Void) -> impl Copy { if false { match return_never_rpit(x) {} } x } ``` - this properly fixes ICEs like #117100 that occurred because a same match could have some patterns where the type is revealed and some where it is not. Bonus subtle point: if `x` is opaque, a match like `match x { ("", "") => {} ... }` will constrain its type ([playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=901d715330eac40339b4016ac566d6c3)). This is not the case for `match x {}`: this will not constain the type, and will only compile if something else constrains the type to be empty. Fixes #117100 r? `@oli-obk` Edited for precision of the wording [Included](#116821 (comment)) in the FCP on this PR is this rule: > Within the body of an item that defines the hidden type of some opaque type, exhaustiveness checking on a value of that opaque type is performed using the concrete hidden type inferred in this body.
- Loading branch information
Showing
7 changed files
with
284 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
#![feature(never_type)] | ||
#![feature(exhaustive_patterns)] | ||
#![feature(type_alias_impl_trait)] | ||
#![feature(non_exhaustive_omitted_patterns_lint)] | ||
#![deny(unreachable_patterns)] | ||
// Test that the lint traversal handles opaques correctly | ||
#![deny(non_exhaustive_omitted_patterns)] | ||
|
||
fn main() {} | ||
|
||
#[derive(Copy, Clone)] | ||
enum Void {} | ||
|
||
fn return_never_rpit(x: Void) -> impl Copy { | ||
if false { | ||
match return_never_rpit(x) { | ||
_ => {} //~ ERROR unreachable | ||
} | ||
} | ||
x | ||
} | ||
fn friend_of_return_never_rpit(x: Void) { | ||
match return_never_rpit(x) {} | ||
//~^ ERROR non-empty | ||
} | ||
|
||
type T = impl Copy; | ||
fn return_never_tait(x: Void) -> T { | ||
if false { | ||
match return_never_tait(x) { | ||
_ => {} //~ ERROR unreachable | ||
} | ||
} | ||
x | ||
} | ||
fn friend_of_return_never_tait(x: Void) { | ||
match return_never_tait(x) {} | ||
//~^ ERROR non-empty | ||
} | ||
|
||
fn option_never(x: Void) -> Option<impl Copy> { | ||
if false { | ||
match option_never(x) { | ||
None => {} | ||
Some(_) => {} //~ ERROR unreachable | ||
} | ||
match option_never(x) { | ||
None => {} | ||
// FIXME: Unreachable not detected because `is_uninhabited` does not look into opaque | ||
// types. | ||
_ => {} | ||
} | ||
} | ||
Some(x) | ||
} | ||
|
||
fn option_never2(x: Void) -> impl Copy { | ||
if false { | ||
match option_never2(x) { | ||
None => {} | ||
Some(_) => {} //~ ERROR unreachable | ||
} | ||
match option_never2(x) { | ||
None => {} | ||
_ => {} //~ ERROR unreachable | ||
} | ||
match option_never2(x) { | ||
None => {} | ||
} | ||
} | ||
Some(x) | ||
} | ||
|
||
fn inner_never(x: Void) { | ||
type T = impl Copy; | ||
let y: T = x; | ||
match y { | ||
_ => {} //~ ERROR unreachable | ||
} | ||
} | ||
|
||
// This one caused ICE https://github.com/rust-lang/rust/issues/117100. | ||
fn inner_tuple() { | ||
type T = impl Copy; | ||
let foo: T = Some((1u32, 2u32)); | ||
match foo { | ||
_ => {} | ||
Some((a, b)) => {} //~ ERROR unreachable | ||
} | ||
} | ||
|
||
type U = impl Copy; | ||
fn unify_never(x: Void, u: U) -> U { | ||
if false { | ||
match u { | ||
_ => {} //~ ERROR unreachable | ||
} | ||
} | ||
x | ||
} | ||
|
||
type V = impl Copy; | ||
fn infer_in_match(x: Option<V>) { | ||
match x { | ||
None => {} | ||
Some((a, b)) => {} | ||
Some((mut x, mut y)) => { | ||
//~^ ERROR unreachable | ||
x = 42; | ||
y = "foo"; | ||
} | ||
} | ||
} | ||
|
||
type W = impl Copy; | ||
#[derive(Copy, Clone)] | ||
struct Rec<'a> { | ||
n: u32, | ||
w: Option<&'a W>, | ||
} | ||
fn recursive_opaque() -> W { | ||
if false { | ||
match recursive_opaque() { | ||
// Check for the ol' ICE when the type is recursively opaque. | ||
_ => {} | ||
Rec { n: 0, w: Some(Rec { n: 0, w: _ }) } => {} //~ ERROR unreachable | ||
} | ||
} | ||
let w: Option<&'static W> = None; | ||
Rec { n: 0, w } | ||
} | ||
|
||
type X = impl Copy; | ||
struct SecretelyVoid(X); | ||
fn nested_empty_opaque(x: Void) -> X { | ||
if false { | ||
let opaque_void = nested_empty_opaque(x); | ||
let secretely_void = SecretelyVoid(opaque_void); | ||
match secretely_void { | ||
// FIXME: Unreachable not detected because `is_uninhabited` does not look into opaque | ||
// types. | ||
_ => {} | ||
} | ||
} | ||
x | ||
} |
Oops, something went wrong.