Auto merge of #56110 - varkor:inhabitedness-union-enum, r=cramertj
Consider references and unions potentially inhabited during privacy-respecting inhabitedness checks It isn't settled exactly how references to uninhabited types and unions of uninhabited types should act, but we should be more conservative here, as it's likely it will be permitted to soundly have values of such types. This will also be more important in light of the changes at https://github.com/rust-lang/rust/pull/54125. cc @RalfJung
This commit is contained in:
commit
b817d0b651
@ -167,23 +167,16 @@ impl<'a, 'gcx, 'tcx> VariantDef {
|
||||
substs: &'tcx Substs<'tcx>,
|
||||
adt_kind: AdtKind) -> DefIdForest
|
||||
{
|
||||
match adt_kind {
|
||||
AdtKind::Union => {
|
||||
DefIdForest::intersection(tcx, self.fields.iter().map(|f| {
|
||||
f.uninhabited_from(visited, tcx, substs, false)
|
||||
}))
|
||||
},
|
||||
AdtKind::Struct => {
|
||||
DefIdForest::union(tcx, self.fields.iter().map(|f| {
|
||||
f.uninhabited_from(visited, tcx, substs, false)
|
||||
}))
|
||||
},
|
||||
AdtKind::Enum => {
|
||||
DefIdForest::union(tcx, self.fields.iter().map(|f| {
|
||||
f.uninhabited_from(visited, tcx, substs, true)
|
||||
}))
|
||||
},
|
||||
}
|
||||
let is_enum = match adt_kind {
|
||||
// For now, `union`s are never considered uninhabited.
|
||||
// The precise semantics of inhabitedness with respect to unions is currently undecided.
|
||||
AdtKind::Union => return DefIdForest::empty(),
|
||||
AdtKind::Enum => true,
|
||||
AdtKind::Struct => false,
|
||||
};
|
||||
DefIdForest::union(tcx, self.fields.iter().map(|f| {
|
||||
f.uninhabited_from(visited, tcx, substs, is_enum)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,8 +187,8 @@ impl<'a, 'gcx, 'tcx> FieldDef {
|
||||
visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
substs: &'tcx Substs<'tcx>,
|
||||
is_enum: bool) -> DefIdForest
|
||||
{
|
||||
is_enum: bool,
|
||||
) -> DefIdForest {
|
||||
let mut data_uninhabitedness = move || {
|
||||
self.ty(tcx, substs).uninhabited_from(visited, tcx)
|
||||
};
|
||||
@ -253,14 +246,16 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
|
||||
let substs_set = visited.get_mut(&def.did).unwrap();
|
||||
substs_set.remove(substs);
|
||||
ret
|
||||
},
|
||||
}
|
||||
|
||||
Never => DefIdForest::full(tcx),
|
||||
|
||||
Tuple(ref tys) => {
|
||||
DefIdForest::union(tcx, tys.iter().map(|ty| {
|
||||
ty.uninhabited_from(visited, tcx)
|
||||
}))
|
||||
},
|
||||
}
|
||||
|
||||
Array(ty, len) => {
|
||||
match len.assert_usize(tcx) {
|
||||
// If the array is definitely non-empty, it's uninhabited if
|
||||
@ -269,9 +264,13 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
|
||||
_ => DefIdForest::empty()
|
||||
}
|
||||
}
|
||||
Ref(_, ty, _) => {
|
||||
ty.uninhabited_from(visited, tcx)
|
||||
}
|
||||
|
||||
// References to uninitialised memory is valid for any type, including
|
||||
// uninhabited types, in unsafe code, so we treat all references as
|
||||
// inhabited.
|
||||
// The precise semantics of inhabitedness with respect to references is currently
|
||||
// undecided.
|
||||
Ref(..) => DefIdForest::empty(),
|
||||
|
||||
_ => DefIdForest::empty(),
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ fn main() {
|
||||
let x: Result<u32, &!> = Ok(123);
|
||||
match x {
|
||||
Ok(y) => y,
|
||||
Err(_) => unimplemented!(),
|
||||
};
|
||||
|
||||
bar(&[]);
|
||||
|
32
src/test/ui/always-inhabited-union-ref.rs
Normal file
32
src/test/ui/always-inhabited-union-ref.rs
Normal file
@ -0,0 +1,32 @@
|
||||
// The precise semantics of inhabitedness with respect to unions and references is currently
|
||||
// undecided. This test file currently checks a conservative choice.
|
||||
|
||||
#![feature(exhaustive_patterns)]
|
||||
#![feature(never_type)]
|
||||
|
||||
#![allow(dead_code)]
|
||||
#![allow(unreachable_code)]
|
||||
|
||||
pub union Foo {
|
||||
foo: !,
|
||||
}
|
||||
|
||||
fn uninhab_ref() -> &'static ! {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn uninhab_union() -> Foo {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn match_on_uninhab() {
|
||||
match uninhab_ref() {
|
||||
//~^ ERROR non-exhaustive patterns: type `&'static !` is non-empty
|
||||
}
|
||||
|
||||
match uninhab_union() {
|
||||
//~^ ERROR non-exhaustive patterns: type `Foo` is non-empty
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
27
src/test/ui/always-inhabited-union-ref.stderr
Normal file
27
src/test/ui/always-inhabited-union-ref.stderr
Normal file
@ -0,0 +1,27 @@
|
||||
error[E0004]: non-exhaustive patterns: type `&'static !` is non-empty
|
||||
--> $DIR/always-inhabited-union-ref.rs:23:11
|
||||
|
|
||||
LL | match uninhab_ref() {
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
|
||||
--> $DIR/always-inhabited-union-ref.rs:23:11
|
||||
|
|
||||
LL | match uninhab_ref() {
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error[E0004]: non-exhaustive patterns: type `Foo` is non-empty
|
||||
--> $DIR/always-inhabited-union-ref.rs:27:11
|
||||
|
|
||||
LL | match uninhab_union() {
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
|
||||
--> $DIR/always-inhabited-union-ref.rs:27:11
|
||||
|
|
||||
LL | match uninhab_union() {
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0004`.
|
@ -1,26 +0,0 @@
|
||||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// error-pattern:reached recursion limit
|
||||
|
||||
#![feature(never_type)]
|
||||
#![feature(exhaustive_patterns)]
|
||||
|
||||
struct Foo<'a, T: 'a> {
|
||||
ph: std::marker::PhantomData<T>,
|
||||
foo: &'a Foo<'a, (T, T)>,
|
||||
}
|
||||
|
||||
fn wub(f: Foo<!>) {
|
||||
match f {}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
File diff suppressed because one or more lines are too long
@ -33,7 +33,10 @@ fn test_a() {
|
||||
|
||||
fn test_b() {
|
||||
let x: Option<Bar> = None;
|
||||
match x { None => () }
|
||||
match x {
|
||||
Some(_) => (),
|
||||
None => ()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() { }
|
||||
|
@ -8,14 +8,26 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// compile-fail
|
||||
|
||||
#![feature(never_type)]
|
||||
#![feature(exhaustive_patterns)]
|
||||
|
||||
#![allow(unreachable_code)]
|
||||
#![deny(unreachable_patterns)]
|
||||
|
||||
fn main() {
|
||||
let x: &[!] = &[];
|
||||
enum Void {}
|
||||
|
||||
for _ in x {}
|
||||
impl Iterator for Void {
|
||||
type Item = Void;
|
||||
|
||||
fn next(&mut self) -> Option<Void> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
for _ in unimplemented!() as Void {}
|
||||
//~^ ERROR unreachable pattern
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
error: unreachable pattern
|
||||
--> $DIR/unreachable-loop-patterns.rs:18:9
|
||||
--> $DIR/unreachable-loop-patterns.rs:30:9
|
||||
|
|
||||
LL | for _ in x {}
|
||||
LL | for _ in unimplemented!() as Void {}
|
||||
| ^
|
||||
|
|
||||
note: lint level defined here
|
||||
--> $DIR/unreachable-loop-patterns.rs:13:9
|
||||
--> $DIR/unreachable-loop-patterns.rs:17:9
|
||||
|
|
||||
LL | #![deny(unreachable_patterns)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
Loading…
x
Reference in New Issue
Block a user