Introduce InhabitedPredicate
This commit is contained in:
parent
0940040c04
commit
2928e9ef2c
8 changed files with 338 additions and 354 deletions
|
@ -2467,42 +2467,6 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines whether the given type is inhabited. `None` means that we don't know.
|
|
||||||
fn ty_inhabited<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<bool> {
|
|
||||||
use rustc_type_ir::sty::TyKind::*;
|
|
||||||
if !cx.tcx.type_uninhabited_from(cx.param_env.and(ty)).is_empty() {
|
|
||||||
// This is definitely uninhabited from some module.
|
|
||||||
return Some(false);
|
|
||||||
}
|
|
||||||
match ty.kind() {
|
|
||||||
Never => Some(false),
|
|
||||||
Int(_) | Uint(_) | Float(_) | Bool | Char | RawPtr(_) => Some(true),
|
|
||||||
// Fallback for more complicated types. (Note that `&!` might be considered
|
|
||||||
// uninhabited so references are "complicated", too.)
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Determines whether a product type formed from a list of types is inhabited.
|
|
||||||
fn tys_inhabited<'tcx>(
|
|
||||||
cx: &LateContext<'tcx>,
|
|
||||||
tys: impl Iterator<Item = Ty<'tcx>>,
|
|
||||||
) -> Option<bool> {
|
|
||||||
let mut definitely_inhabited = true; // with no fields, we are definitely inhabited.
|
|
||||||
for ty in tys {
|
|
||||||
match ty_inhabited(cx, ty) {
|
|
||||||
// If any type is uninhabited, the product is uninhabited.
|
|
||||||
Some(false) => return Some(false),
|
|
||||||
// Otherwise go searching for a `None`.
|
|
||||||
None => {
|
|
||||||
// We don't know.
|
|
||||||
definitely_inhabited = false;
|
|
||||||
}
|
|
||||||
Some(true) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if definitely_inhabited { Some(true) } else { None }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn variant_find_init_error<'tcx>(
|
fn variant_find_init_error<'tcx>(
|
||||||
cx: &LateContext<'tcx>,
|
cx: &LateContext<'tcx>,
|
||||||
variant: &VariantDef,
|
variant: &VariantDef,
|
||||||
|
@ -2599,11 +2563,11 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
|
||||||
// And now, enums.
|
// And now, enums.
|
||||||
let span = cx.tcx.def_span(adt_def.did());
|
let span = cx.tcx.def_span(adt_def.did());
|
||||||
let mut potential_variants = adt_def.variants().iter().filter_map(|variant| {
|
let mut potential_variants = adt_def.variants().iter().filter_map(|variant| {
|
||||||
let inhabited = tys_inhabited(
|
let definitely_inhabited = match variant
|
||||||
cx,
|
.inhabited_predicate(cx.tcx, *adt_def)
|
||||||
variant.fields.iter().map(|field| field.ty(cx.tcx, substs)),
|
.subst(cx.tcx, substs)
|
||||||
);
|
.apply_any_module(cx.tcx, cx.param_env)
|
||||||
let definitely_inhabited = match inhabited {
|
{
|
||||||
// Entirely skip uninhbaited variants.
|
// Entirely skip uninhbaited variants.
|
||||||
Some(false) => return None,
|
Some(false) => return None,
|
||||||
// Forward the others, but remember which ones are definitely inhabited.
|
// Forward the others, but remember which ones are definitely inhabited.
|
||||||
|
|
|
@ -1653,14 +1653,13 @@ rustc_queries! {
|
||||||
separate_provide_extern
|
separate_provide_extern
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the set of modules from which this type is visibly uninhabited.
|
query inhabited_predicate_adt(key: DefId) -> ty::inhabitedness::InhabitedPredicate<'tcx> {
|
||||||
/// To check whether a type is uninhabited at all (not just from a given module), you could
|
desc { "computing the uninhabited predicate of `{:?}`", key }
|
||||||
/// check whether the forest is empty.
|
}
|
||||||
query type_uninhabited_from(
|
|
||||||
key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>
|
/// Do not call this query directly: invoke `Ty::inhabited_predicate` instead.
|
||||||
) -> ty::inhabitedness::DefIdForest<'tcx> {
|
query inhabited_predicate_type(key: Ty<'tcx>) -> ty::inhabitedness::InhabitedPredicate<'tcx> {
|
||||||
desc { "computing the inhabitedness of `{}`", key.value }
|
desc { "computing the uninhabited predicate of `{}`", key }
|
||||||
remap_env_constness
|
|
||||||
}
|
}
|
||||||
|
|
||||||
query dep_kind(_: CrateNum) -> CrateDepKind {
|
query dep_kind(_: CrateNum) -> CrateDepKind {
|
||||||
|
|
|
@ -1,145 +0,0 @@
|
||||||
use crate::ty::context::TyCtxt;
|
|
||||||
use crate::ty::{DefId, DefIdTree};
|
|
||||||
use rustc_span::def_id::CRATE_DEF_ID;
|
|
||||||
use smallvec::SmallVec;
|
|
||||||
use std::mem;
|
|
||||||
|
|
||||||
use DefIdForest::*;
|
|
||||||
|
|
||||||
/// Represents a forest of `DefId`s closed under the ancestor relation. That is,
|
|
||||||
/// if a `DefId` representing a module is contained in the forest then all
|
|
||||||
/// `DefId`s defined in that module or submodules are also implicitly contained
|
|
||||||
/// in the forest.
|
|
||||||
///
|
|
||||||
/// This is used to represent a set of modules in which a type is visibly
|
|
||||||
/// uninhabited.
|
|
||||||
///
|
|
||||||
/// We store the minimal set of `DefId`s required to represent the whole set. If A and B are
|
|
||||||
/// `DefId`s in the `DefIdForest`, and A is a parent of B, then only A will be stored. When this is
|
|
||||||
/// used with `type_uninhabited_from`, there will very rarely be more than one `DefId` stored.
|
|
||||||
#[derive(Copy, Clone, HashStable, Debug)]
|
|
||||||
pub enum DefIdForest<'a> {
|
|
||||||
Empty,
|
|
||||||
Single(DefId),
|
|
||||||
/// This variant is very rare.
|
|
||||||
/// Invariant: >1 elements
|
|
||||||
Multiple(&'a [DefId]),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tests whether a slice of roots contains a given DefId.
|
|
||||||
#[inline]
|
|
||||||
fn slice_contains<'tcx>(tcx: TyCtxt<'tcx>, slice: &[DefId], id: DefId) -> bool {
|
|
||||||
slice.iter().any(|root_id| tcx.is_descendant_of(id, *root_id))
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'tcx> DefIdForest<'tcx> {
|
|
||||||
/// Creates an empty forest.
|
|
||||||
pub fn empty() -> DefIdForest<'tcx> {
|
|
||||||
DefIdForest::Empty
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a forest consisting of a single tree representing the entire
|
|
||||||
/// crate.
|
|
||||||
#[inline]
|
|
||||||
pub fn full() -> DefIdForest<'tcx> {
|
|
||||||
DefIdForest::from_id(CRATE_DEF_ID.to_def_id())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a forest containing a `DefId` and all its descendants.
|
|
||||||
pub fn from_id(id: DefId) -> DefIdForest<'tcx> {
|
|
||||||
DefIdForest::Single(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_slice(&self) -> &[DefId] {
|
|
||||||
match self {
|
|
||||||
Empty => &[],
|
|
||||||
Single(id) => std::slice::from_ref(id),
|
|
||||||
Multiple(root_ids) => root_ids,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only allocates in the rare `Multiple` case.
|
|
||||||
fn from_vec(tcx: TyCtxt<'tcx>, root_ids: SmallVec<[DefId; 1]>) -> DefIdForest<'tcx> {
|
|
||||||
match &root_ids[..] {
|
|
||||||
[] => Empty,
|
|
||||||
[id] => Single(*id),
|
|
||||||
_ => DefIdForest::Multiple(tcx.arena.alloc_from_iter(root_ids)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tests whether the forest is empty.
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
Empty => true,
|
|
||||||
Single(..) | Multiple(..) => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterate over the set of roots.
|
|
||||||
fn iter(&self) -> impl Iterator<Item = DefId> + '_ {
|
|
||||||
self.as_slice().iter().copied()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tests whether the forest contains a given DefId.
|
|
||||||
pub fn contains(&self, tcx: TyCtxt<'tcx>, id: DefId) -> bool {
|
|
||||||
slice_contains(tcx, self.as_slice(), id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calculate the intersection of a collection of forests.
|
|
||||||
pub fn intersection<I>(tcx: TyCtxt<'tcx>, iter: I) -> DefIdForest<'tcx>
|
|
||||||
where
|
|
||||||
I: IntoIterator<Item = DefIdForest<'tcx>>,
|
|
||||||
{
|
|
||||||
let mut iter = iter.into_iter();
|
|
||||||
let mut ret: SmallVec<[_; 1]> = if let Some(first) = iter.next() {
|
|
||||||
SmallVec::from_slice(first.as_slice())
|
|
||||||
} else {
|
|
||||||
return DefIdForest::full();
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut next_ret: SmallVec<[_; 1]> = SmallVec::new();
|
|
||||||
for next_forest in iter {
|
|
||||||
// No need to continue if the intersection is already empty.
|
|
||||||
if ret.is_empty() || next_forest.is_empty() {
|
|
||||||
return DefIdForest::empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
// We keep the elements in `ret` that are also in `next_forest`.
|
|
||||||
next_ret.extend(ret.iter().copied().filter(|&id| next_forest.contains(tcx, id)));
|
|
||||||
// We keep the elements in `next_forest` that are also in `ret`.
|
|
||||||
next_ret.extend(next_forest.iter().filter(|&id| slice_contains(tcx, &ret, id)));
|
|
||||||
|
|
||||||
mem::swap(&mut next_ret, &mut ret);
|
|
||||||
next_ret.clear();
|
|
||||||
}
|
|
||||||
DefIdForest::from_vec(tcx, ret)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calculate the union of a collection of forests.
|
|
||||||
pub fn union<I>(tcx: TyCtxt<'tcx>, iter: I) -> DefIdForest<'tcx>
|
|
||||||
where
|
|
||||||
I: IntoIterator<Item = DefIdForest<'tcx>>,
|
|
||||||
{
|
|
||||||
let mut ret: SmallVec<[_; 1]> = SmallVec::new();
|
|
||||||
let mut next_ret: SmallVec<[_; 1]> = SmallVec::new();
|
|
||||||
for next_forest in iter {
|
|
||||||
// Union with the empty set is a no-op.
|
|
||||||
if next_forest.is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We add everything in `ret` that is not in `next_forest`.
|
|
||||||
next_ret.extend(ret.iter().copied().filter(|&id| !next_forest.contains(tcx, id)));
|
|
||||||
// We add everything in `next_forest` that we haven't added yet.
|
|
||||||
for id in next_forest.iter() {
|
|
||||||
if !slice_contains(tcx, &next_ret, id) {
|
|
||||||
next_ret.push(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mem::swap(&mut next_ret, &mut ret);
|
|
||||||
next_ret.clear();
|
|
||||||
}
|
|
||||||
DefIdForest::from_vec(tcx, ret)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,204 @@
|
||||||
|
use crate::ty::context::TyCtxt;
|
||||||
|
use crate::ty::{self, DefId, DefIdTree, ParamEnv, Ty};
|
||||||
|
|
||||||
|
/// Represents whether some type is inhabited in a given context.
|
||||||
|
/// Examples of uninhabited types are `!`, `enum Void {}`, or a struct
|
||||||
|
/// containing either of those types.
|
||||||
|
/// A type's inhabitedness may depend on the `ParamEnv` as well as what types
|
||||||
|
/// are visible in the current module.
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, HashStable)]
|
||||||
|
pub enum InhabitedPredicate<'tcx> {
|
||||||
|
/// Inhabited
|
||||||
|
True,
|
||||||
|
/// Uninhabited
|
||||||
|
False,
|
||||||
|
/// Uninhabited when a const value is non-zero. This occurs when there is an
|
||||||
|
/// array of uninhabited items, but the array is inhabited if it is empty.
|
||||||
|
ConstIsZero(ty::Const<'tcx>),
|
||||||
|
/// Uninhabited if within a certain module. This occurs when an uninhabited
|
||||||
|
/// type has restricted visibility.
|
||||||
|
NotInModule(DefId),
|
||||||
|
/// Inhabited if some generic type is inhabited.
|
||||||
|
/// These are replaced by calling [`Self::subst`].
|
||||||
|
GenericType(Ty<'tcx>),
|
||||||
|
/// A AND B
|
||||||
|
And(&'tcx [InhabitedPredicate<'tcx>; 2]),
|
||||||
|
/// A OR B
|
||||||
|
Or(&'tcx [InhabitedPredicate<'tcx>; 2]),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> InhabitedPredicate<'tcx> {
|
||||||
|
/// Returns true if the corresponding type is inhabited in the given
|
||||||
|
/// `ParamEnv` and module
|
||||||
|
pub fn apply(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, module_def_id: DefId) -> bool {
|
||||||
|
let Ok(result) = self
|
||||||
|
.apply_inner::<!>(tcx, param_env, &|id| Ok(tcx.is_descendant_of(module_def_id, id)));
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same as `apply`, but returns `None` if self contains a module predicate
|
||||||
|
pub fn apply_any_module(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<bool> {
|
||||||
|
self.apply_inner(tcx, param_env, &|_| Err(())).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_inner<E>(
|
||||||
|
self,
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
param_env: ParamEnv<'tcx>,
|
||||||
|
in_module: &impl Fn(DefId) -> Result<bool, E>,
|
||||||
|
) -> Result<bool, E> {
|
||||||
|
match self {
|
||||||
|
Self::False => Ok(false),
|
||||||
|
Self::True => Ok(true),
|
||||||
|
Self::ConstIsZero(const_) => match const_.try_eval_usize(tcx, param_env) {
|
||||||
|
None | Some(0) => Ok(true),
|
||||||
|
Some(1..) => Ok(false),
|
||||||
|
},
|
||||||
|
Self::NotInModule(id) => in_module(id).map(|in_mod| !in_mod),
|
||||||
|
Self::GenericType(_) => Ok(true),
|
||||||
|
Self::And([a, b]) => try_and(a, b, |x| x.apply_inner(tcx, param_env, in_module)),
|
||||||
|
Self::Or([a, b]) => try_or(a, b, |x| x.apply_inner(tcx, param_env, in_module)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn and(self, tcx: TyCtxt<'tcx>, other: Self) -> Self {
|
||||||
|
self.reduce_and(tcx, other).unwrap_or_else(|| Self::And(tcx.arena.alloc([self, other])))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn or(self, tcx: TyCtxt<'tcx>, other: Self) -> Self {
|
||||||
|
self.reduce_or(tcx, other).unwrap_or_else(|| Self::Or(tcx.arena.alloc([self, other])))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all(tcx: TyCtxt<'tcx>, iter: impl IntoIterator<Item = Self>) -> Self {
|
||||||
|
let mut result = Self::True;
|
||||||
|
for pred in iter {
|
||||||
|
if matches!(pred, Self::False) {
|
||||||
|
return Self::False;
|
||||||
|
}
|
||||||
|
result = result.and(tcx, pred);
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn any(tcx: TyCtxt<'tcx>, iter: impl IntoIterator<Item = Self>) -> Self {
|
||||||
|
let mut result = Self::False;
|
||||||
|
for pred in iter {
|
||||||
|
if matches!(pred, Self::True) {
|
||||||
|
return Self::True;
|
||||||
|
}
|
||||||
|
result = result.or(tcx, pred);
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reduce_and(self, tcx: TyCtxt<'tcx>, other: Self) -> Option<Self> {
|
||||||
|
match (self, other) {
|
||||||
|
(Self::True, a) | (a, Self::True) => Some(a),
|
||||||
|
(Self::False, _) | (_, Self::False) => Some(Self::False),
|
||||||
|
(Self::ConstIsZero(a), Self::ConstIsZero(b)) if a == b => Some(Self::ConstIsZero(a)),
|
||||||
|
(Self::NotInModule(a), Self::NotInModule(b)) if a == b => Some(Self::NotInModule(a)),
|
||||||
|
(Self::NotInModule(a), Self::NotInModule(b)) if tcx.is_descendant_of(a, b) => {
|
||||||
|
Some(Self::NotInModule(b))
|
||||||
|
}
|
||||||
|
(Self::NotInModule(a), Self::NotInModule(b)) if tcx.is_descendant_of(b, a) => {
|
||||||
|
Some(Self::NotInModule(a))
|
||||||
|
}
|
||||||
|
(Self::GenericType(a), Self::GenericType(b)) if a == b => Some(Self::GenericType(a)),
|
||||||
|
(Self::And(&[a, b]), c) | (c, Self::And(&[a, b])) => {
|
||||||
|
if let Some(ac) = a.reduce_and(tcx, c) {
|
||||||
|
Some(ac.and(tcx, b))
|
||||||
|
} else if let Some(bc) = b.reduce_and(tcx, c) {
|
||||||
|
Some(Self::And(tcx.arena.alloc([a, bc])))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reduce_or(self, tcx: TyCtxt<'tcx>, other: Self) -> Option<Self> {
|
||||||
|
match (self, other) {
|
||||||
|
(Self::True, _) | (_, Self::True) => Some(Self::True),
|
||||||
|
(Self::False, a) | (a, Self::False) => Some(a),
|
||||||
|
(Self::ConstIsZero(a), Self::ConstIsZero(b)) if a == b => Some(Self::ConstIsZero(a)),
|
||||||
|
(Self::NotInModule(a), Self::NotInModule(b)) if a == b => Some(Self::NotInModule(a)),
|
||||||
|
(Self::NotInModule(a), Self::NotInModule(b)) if tcx.is_descendant_of(a, b) => {
|
||||||
|
Some(Self::NotInModule(a))
|
||||||
|
}
|
||||||
|
(Self::NotInModule(a), Self::NotInModule(b)) if tcx.is_descendant_of(b, a) => {
|
||||||
|
Some(Self::NotInModule(b))
|
||||||
|
}
|
||||||
|
(Self::GenericType(a), Self::GenericType(b)) if a == b => Some(Self::GenericType(a)),
|
||||||
|
(Self::Or(&[a, b]), c) | (c, Self::Or(&[a, b])) => {
|
||||||
|
if let Some(ac) = a.reduce_or(tcx, c) {
|
||||||
|
Some(ac.or(tcx, b))
|
||||||
|
} else if let Some(bc) = b.reduce_or(tcx, c) {
|
||||||
|
Some(Self::Or(tcx.arena.alloc([a, bc])))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replaces generic types with its corresponding predicate
|
||||||
|
pub fn subst(self, tcx: TyCtxt<'tcx>, substs: ty::SubstsRef<'tcx>) -> Self {
|
||||||
|
self.subst_opt(tcx, substs).unwrap_or(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn subst_opt(self, tcx: TyCtxt<'tcx>, substs: ty::SubstsRef<'tcx>) -> Option<Self> {
|
||||||
|
match self {
|
||||||
|
Self::ConstIsZero(c) => {
|
||||||
|
let c = ty::EarlyBinder(c).subst(tcx, substs);
|
||||||
|
let pred = match c.kind().try_to_machine_usize(tcx) {
|
||||||
|
Some(0) => Self::True,
|
||||||
|
Some(1..) => Self::False,
|
||||||
|
None => Self::ConstIsZero(c),
|
||||||
|
};
|
||||||
|
Some(pred)
|
||||||
|
}
|
||||||
|
Self::GenericType(t) => {
|
||||||
|
Some(ty::EarlyBinder(t).subst(tcx, substs).inhabited_predicate(tcx))
|
||||||
|
}
|
||||||
|
Self::And(&[a, b]) => match a.subst_opt(tcx, substs) {
|
||||||
|
None => b.subst_opt(tcx, substs).map(|b| a.and(tcx, b)),
|
||||||
|
Some(InhabitedPredicate::False) => Some(InhabitedPredicate::False),
|
||||||
|
Some(a) => Some(a.and(tcx, b.subst_opt(tcx, substs).unwrap_or(b))),
|
||||||
|
},
|
||||||
|
Self::Or(&[a, b]) => match a.subst_opt(tcx, substs) {
|
||||||
|
None => b.subst_opt(tcx, substs).map(|b| a.or(tcx, b)),
|
||||||
|
Some(InhabitedPredicate::True) => Some(InhabitedPredicate::True),
|
||||||
|
Some(a) => Some(a.or(tcx, b.subst_opt(tcx, substs).unwrap_or(b))),
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is basically like `f(a)? && f(b)?` but different in the case of
|
||||||
|
// `Ok(false) && Err(_) -> Ok(false)`
|
||||||
|
fn try_and<T, E>(a: T, b: T, f: impl Fn(T) -> Result<bool, E>) -> Result<bool, E> {
|
||||||
|
let a = f(a);
|
||||||
|
if matches!(a, Ok(false)) {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
match (a, f(b)) {
|
||||||
|
(_, Ok(false)) | (Ok(false), _) => Ok(false),
|
||||||
|
(Ok(true), Ok(true)) => Ok(true),
|
||||||
|
(Err(e), _) | (_, Err(e)) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_or<T, E>(a: T, b: T, f: impl Fn(T) -> Result<bool, E>) -> Result<bool, E> {
|
||||||
|
let a = f(a);
|
||||||
|
if matches!(a, Ok(true)) {
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
match (a, f(b)) {
|
||||||
|
(_, Ok(true)) | (Ok(true), _) => Ok(true),
|
||||||
|
(Ok(false), Ok(false)) => Ok(false),
|
||||||
|
(Err(e), _) | (_, Err(e)) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,57 +1,60 @@
|
||||||
pub use self::def_id_forest::DefIdForest;
|
//! This module contains logic for determining whether a type is inhabited or
|
||||||
|
//! uninhabited. The [`InhabitedPredicate`] type captures the minimum
|
||||||
|
//! information needed to determine whether a type is inhabited given a
|
||||||
|
//! `ParamEnv` and module ID.
|
||||||
|
//!
|
||||||
|
//! # Example
|
||||||
|
//! ```rust
|
||||||
|
//! enum Void {}
|
||||||
|
//! mod a {
|
||||||
|
//! pub mod b {
|
||||||
|
//! pub struct SecretlyUninhabited {
|
||||||
|
//! _priv: !,
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! mod c {
|
||||||
|
//! pub struct AlsoSecretlyUninhabited {
|
||||||
|
//! _priv: Void,
|
||||||
|
//! }
|
||||||
|
//! mod d {
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! struct Foo {
|
||||||
|
//! x: a::b::SecretlyUninhabited,
|
||||||
|
//! y: c::AlsoSecretlyUninhabited,
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//! In this code, the type `Foo` will only be visibly uninhabited inside the
|
||||||
|
//! modules `b`, `c` and `d`. Calling `uninhabited_predicate` on `Foo` will
|
||||||
|
//! return `NotInModule(b) AND NotInModule(c)`.
|
||||||
|
//!
|
||||||
|
//! We need this information for pattern-matching on `Foo` or types that contain
|
||||||
|
//! `Foo`.
|
||||||
|
//!
|
||||||
|
//! # Example
|
||||||
|
//! ```rust
|
||||||
|
//! let foo_result: Result<T, Foo> = ... ;
|
||||||
|
//! let Ok(t) = foo_result;
|
||||||
|
//! ```
|
||||||
|
//! This code should only compile in modules where the uninhabitedness of `Foo`
|
||||||
|
//! is visible.
|
||||||
|
|
||||||
use crate::ty;
|
|
||||||
use crate::ty::context::TyCtxt;
|
use crate::ty::context::TyCtxt;
|
||||||
use crate::ty::{AdtDef, FieldDef, Ty, VariantDef};
|
use crate::ty::{self, DefId, Ty, VariantDef, Visibility};
|
||||||
use crate::ty::{AdtKind, Visibility};
|
|
||||||
use crate::ty::{DefId, SubstsRef};
|
|
||||||
|
|
||||||
use rustc_type_ir::sty::TyKind::*;
|
use rustc_type_ir::sty::TyKind::*;
|
||||||
|
|
||||||
mod def_id_forest;
|
pub mod inhabited_predicate;
|
||||||
|
|
||||||
// The methods in this module calculate `DefIdForest`s of modules in which an
|
pub use inhabited_predicate::InhabitedPredicate;
|
||||||
// `AdtDef`/`VariantDef`/`FieldDef` is visibly uninhabited.
|
|
||||||
//
|
pub(crate) fn provide(providers: &mut ty::query::Providers) {
|
||||||
// # Example
|
*providers =
|
||||||
// ```rust
|
ty::query::Providers { inhabited_predicate_adt, inhabited_predicate_type, ..*providers };
|
||||||
// enum Void {}
|
}
|
||||||
// mod a {
|
|
||||||
// pub mod b {
|
|
||||||
// pub struct SecretlyUninhabited {
|
|
||||||
// _priv: !,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// mod c {
|
|
||||||
// pub struct AlsoSecretlyUninhabited {
|
|
||||||
// _priv: Void,
|
|
||||||
// }
|
|
||||||
// mod d {
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// struct Foo {
|
|
||||||
// x: a::b::SecretlyUninhabited,
|
|
||||||
// y: c::AlsoSecretlyUninhabited,
|
|
||||||
// }
|
|
||||||
// ```
|
|
||||||
// In this code, the type `Foo` will only be visibly uninhabited inside the
|
|
||||||
// modules `b`, `c` and `d`. Calling `uninhabited_from` on `Foo` or its `AdtDef` will
|
|
||||||
// return the forest of modules {`b`, `c`->`d`} (represented in a `DefIdForest` by the
|
|
||||||
// set {`b`, `c`}).
|
|
||||||
//
|
|
||||||
// We need this information for pattern-matching on `Foo` or types that contain
|
|
||||||
// `Foo`.
|
|
||||||
//
|
|
||||||
// # Example
|
|
||||||
// ```rust
|
|
||||||
// let foo_result: Result<T, Foo> = ... ;
|
|
||||||
// let Ok(t) = foo_result;
|
|
||||||
// ```
|
|
||||||
// This code should only compile in modules where the uninhabitedness of `Foo` is
|
|
||||||
// visible.
|
|
||||||
|
|
||||||
impl<'tcx> TyCtxt<'tcx> {
|
impl<'tcx> TyCtxt<'tcx> {
|
||||||
/// Checks whether a type is visibly uninhabited from a particular module.
|
/// Checks whether a type is visibly uninhabited from a particular module.
|
||||||
|
@ -100,131 +103,92 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
ty: Ty<'tcx>,
|
ty: Ty<'tcx>,
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// To check whether this type is uninhabited at all (not just from the
|
!ty.inhabited_predicate(self).apply(self, param_env, module)
|
||||||
// given node), you could check whether the forest is empty.
|
|
||||||
// ```
|
|
||||||
// forest.is_empty()
|
|
||||||
// ```
|
|
||||||
ty.uninhabited_from(self, param_env).contains(self, module)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> AdtDef<'tcx> {
|
/// Returns an `InhabitedPredicate` that is generic over type parameters and
|
||||||
/// Calculates the forest of `DefId`s from which this ADT is visibly uninhabited.
|
/// requires calling [`InhabitedPredicate::subst`]
|
||||||
fn uninhabited_from(
|
fn inhabited_predicate_adt(tcx: TyCtxt<'_>, def_id: DefId) -> InhabitedPredicate<'_> {
|
||||||
self,
|
if let Some(def_id) = def_id.as_local() {
|
||||||
tcx: TyCtxt<'tcx>,
|
if matches!(tcx.representability(def_id), ty::Representability::Infinite) {
|
||||||
substs: SubstsRef<'tcx>,
|
return InhabitedPredicate::True;
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
}
|
||||||
) -> DefIdForest<'tcx> {
|
}
|
||||||
// Non-exhaustive ADTs from other crates are always considered inhabited.
|
let adt = tcx.adt_def(def_id);
|
||||||
if self.is_variant_list_non_exhaustive() && !self.did().is_local() {
|
InhabitedPredicate::any(
|
||||||
DefIdForest::empty()
|
|
||||||
} else {
|
|
||||||
DefIdForest::intersection(
|
|
||||||
tcx,
|
tcx,
|
||||||
self.variants()
|
adt.variants().iter().map(|variant| variant.inhabited_predicate(tcx, adt)),
|
||||||
.iter()
|
|
||||||
.map(|v| v.uninhabited_from(tcx, substs, self.adt_kind(), param_env)),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'tcx> VariantDef {
|
impl<'tcx> VariantDef {
|
||||||
/// Calculates the forest of `DefId`s from which this variant is visibly uninhabited.
|
/// Calculates the forest of `DefId`s from which this variant is visibly uninhabited.
|
||||||
pub fn uninhabited_from(
|
pub fn inhabited_predicate(
|
||||||
&self,
|
&self,
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
substs: SubstsRef<'tcx>,
|
adt: ty::AdtDef<'_>,
|
||||||
adt_kind: AdtKind,
|
) -> InhabitedPredicate<'tcx> {
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
debug_assert!(!adt.is_union());
|
||||||
) -> DefIdForest<'tcx> {
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
// Non-exhaustive variants from other crates are always considered inhabited.
|
|
||||||
if self.is_field_list_non_exhaustive() && !self.def_id.is_local() {
|
if self.is_field_list_non_exhaustive() && !self.def_id.is_local() {
|
||||||
DefIdForest::empty()
|
// Non-exhaustive variants from other crates are always considered inhabited.
|
||||||
} else {
|
return InhabitedPredicate::True;
|
||||||
DefIdForest::union(
|
}
|
||||||
|
InhabitedPredicate::all(
|
||||||
tcx,
|
tcx,
|
||||||
self.fields.iter().map(|f| f.uninhabited_from(tcx, substs, is_enum, param_env)),
|
self.fields.iter().map(|field| {
|
||||||
)
|
let pred = tcx.type_of(field.did).inhabited_predicate(tcx);
|
||||||
|
if adt.is_enum() {
|
||||||
|
return pred;
|
||||||
}
|
}
|
||||||
}
|
match field.vis {
|
||||||
}
|
Visibility::Public => pred,
|
||||||
|
|
||||||
impl<'tcx> FieldDef {
|
|
||||||
/// Calculates the forest of `DefId`s from which this field is visibly uninhabited.
|
|
||||||
fn uninhabited_from(
|
|
||||||
&self,
|
|
||||||
tcx: TyCtxt<'tcx>,
|
|
||||||
substs: SubstsRef<'tcx>,
|
|
||||||
is_enum: bool,
|
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
|
||||||
) -> DefIdForest<'tcx> {
|
|
||||||
let data_uninhabitedness = move || self.ty(tcx, substs).uninhabited_from(tcx, param_env);
|
|
||||||
if is_enum {
|
|
||||||
data_uninhabitedness()
|
|
||||||
} else {
|
|
||||||
match self.vis {
|
|
||||||
Visibility::Restricted(from) => {
|
Visibility::Restricted(from) => {
|
||||||
let forest = DefIdForest::from_id(from);
|
pred.or(tcx, InhabitedPredicate::NotInModule(from))
|
||||||
let iter = Some(forest).into_iter().chain(Some(data_uninhabitedness()));
|
|
||||||
DefIdForest::intersection(tcx, iter)
|
|
||||||
}
|
|
||||||
Visibility::Public => data_uninhabitedness(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> Ty<'tcx> {
|
impl<'tcx> Ty<'tcx> {
|
||||||
/// Calculates the forest of `DefId`s from which this type is visibly uninhabited.
|
pub fn inhabited_predicate(self, tcx: TyCtxt<'tcx>) -> InhabitedPredicate<'tcx> {
|
||||||
fn uninhabited_from(
|
match self.kind() {
|
||||||
self,
|
// For now, union`s are always considered inhabited
|
||||||
tcx: TyCtxt<'tcx>,
|
Adt(adt, _) if adt.is_union() => InhabitedPredicate::True,
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
// Non-exhaustive ADTs from other crates are always considered inhabited
|
||||||
) -> DefIdForest<'tcx> {
|
Adt(adt, _) if adt.is_variant_list_non_exhaustive() && !adt.did().is_local() => {
|
||||||
tcx.type_uninhabited_from(param_env.and(self))
|
InhabitedPredicate::True
|
||||||
|
}
|
||||||
|
Never => InhabitedPredicate::False,
|
||||||
|
Param(_) | Projection(_) => InhabitedPredicate::GenericType(self),
|
||||||
|
Tuple(tys) if tys.is_empty() => InhabitedPredicate::True,
|
||||||
|
// use a query for more complex cases
|
||||||
|
Adt(..) | Array(..) | Tuple(_) => tcx.inhabited_predicate_type(self),
|
||||||
|
// references and other types are inhabited
|
||||||
|
_ => InhabitedPredicate::True,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query provider for `type_uninhabited_from`.
|
/// N.B. this query should only be called through `Ty::inhabited_predicate`
|
||||||
pub(crate) fn type_uninhabited_from<'tcx>(
|
fn inhabited_predicate_type<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> InhabitedPredicate<'tcx> {
|
||||||
tcx: TyCtxt<'tcx>,
|
|
||||||
key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
|
|
||||||
) -> DefIdForest<'tcx> {
|
|
||||||
let ty = key.value;
|
|
||||||
let param_env = key.param_env;
|
|
||||||
match *ty.kind() {
|
match *ty.kind() {
|
||||||
Adt(def, substs) => def.uninhabited_from(tcx, substs, param_env),
|
Adt(adt, substs) => tcx.inhabited_predicate_adt(adt.did()).subst(tcx, substs),
|
||||||
|
|
||||||
Never => DefIdForest::full(),
|
Tuple(tys) => {
|
||||||
|
InhabitedPredicate::all(tcx, tys.iter().map(|ty| ty.inhabited_predicate(tcx)))
|
||||||
Tuple(ref tys) => {
|
|
||||||
DefIdForest::union(tcx, tys.iter().map(|ty| ty.uninhabited_from(tcx, param_env)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Array(ty, len) => match len.try_eval_usize(tcx, param_env) {
|
// If we can evaluate the array length before having a `ParamEnv`, then
|
||||||
Some(0) | None => DefIdForest::empty(),
|
// we can simplify the predicate. This is an optimization.
|
||||||
// If the array is definitely non-empty, it's uninhabited if
|
Array(ty, len) => match len.kind().try_to_machine_usize(tcx) {
|
||||||
// the type of its elements is uninhabited.
|
Some(0) => InhabitedPredicate::True,
|
||||||
Some(1..) => ty.uninhabited_from(tcx, param_env),
|
Some(1..) => ty.inhabited_predicate(tcx),
|
||||||
|
None => ty.inhabited_predicate(tcx).or(tcx, InhabitedPredicate::ConstIsZero(len)),
|
||||||
},
|
},
|
||||||
|
|
||||||
// References to uninitialised memory are valid for any type, including
|
_ => bug!("unexpected TyKind, use `Ty::inhabited_predicate`"),
|
||||||
// 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(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2694,6 +2694,7 @@ pub fn provide(providers: &mut ty::query::Providers) {
|
||||||
closure::provide(providers);
|
closure::provide(providers);
|
||||||
context::provide(providers);
|
context::provide(providers);
|
||||||
erase_regions::provide(providers);
|
erase_regions::provide(providers);
|
||||||
|
inhabitedness::provide(providers);
|
||||||
util::provide(providers);
|
util::provide(providers);
|
||||||
print::provide(providers);
|
print::provide(providers);
|
||||||
super::util::bug::provide(providers);
|
super::util::bug::provide(providers);
|
||||||
|
@ -2701,7 +2702,6 @@ pub fn provide(providers: &mut ty::query::Providers) {
|
||||||
*providers = ty::query::Providers {
|
*providers = ty::query::Providers {
|
||||||
trait_impls_of: trait_def::trait_impls_of_provider,
|
trait_impls_of: trait_def::trait_impls_of_provider,
|
||||||
incoherent_impls: trait_def::incoherent_impls_provider,
|
incoherent_impls: trait_def::incoherent_impls_provider,
|
||||||
type_uninhabited_from: inhabitedness::type_uninhabited_from,
|
|
||||||
const_param_default: consts::const_param_default,
|
const_param_default: consts::const_param_default,
|
||||||
vtable_allocation: vtable::vtable_allocation_provider,
|
vtable_allocation: vtable::vtable_allocation_provider,
|
||||||
..*providers
|
..*providers
|
||||||
|
|
|
@ -264,14 +264,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
|
let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
|
||||||
i == variant_index || {
|
i == variant_index || {
|
||||||
self.tcx.features().exhaustive_patterns
|
self.tcx.features().exhaustive_patterns
|
||||||
&& !v
|
&& v.inhabited_predicate(self.tcx, adt_def)
|
||||||
.uninhabited_from(
|
.subst(self.tcx, substs)
|
||||||
self.tcx,
|
.apply_any_module(self.tcx, self.param_env)
|
||||||
substs,
|
!= Some(true)
|
||||||
adt_def.adt_kind(),
|
|
||||||
self.param_env,
|
|
||||||
)
|
|
||||||
.is_empty()
|
|
||||||
}
|
}
|
||||||
}) && (adt_def.did().is_local()
|
}) && (adt_def.did().is_local()
|
||||||
|| !adt_def.is_variant_list_non_exhaustive());
|
|| !adt_def.is_variant_list_non_exhaustive());
|
||||||
|
|
|
@ -988,10 +988,12 @@ impl<'tcx> SplitWildcard<'tcx> {
|
||||||
.filter(|(_, v)| {
|
.filter(|(_, v)| {
|
||||||
// If `exhaustive_patterns` is enabled, we exclude variants known to be
|
// If `exhaustive_patterns` is enabled, we exclude variants known to be
|
||||||
// uninhabited.
|
// uninhabited.
|
||||||
let is_uninhabited = is_exhaustive_pat_feature
|
!is_exhaustive_pat_feature
|
||||||
&& v.uninhabited_from(cx.tcx, substs, def.adt_kind(), cx.param_env)
|
|| v.inhabited_predicate(cx.tcx, *def).subst(cx.tcx, substs).apply(
|
||||||
.contains(cx.tcx, cx.module);
|
cx.tcx,
|
||||||
!is_uninhabited
|
cx.param_env,
|
||||||
|
cx.module,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.map(|(idx, _)| Variant(idx))
|
.map(|(idx, _)| Variant(idx))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
Loading…
Add table
Reference in a new issue