Turn type inhabitedness into a query

This commit is contained in:
Nadrieril 2020-12-03 00:25:04 +00:00
parent b82f149d08
commit 8598c9f6e5
4 changed files with 64 additions and 31 deletions

View file

@ -1308,6 +1308,15 @@ rustc_queries! {
eval_always
desc { |tcx| "computing visibility of `{}`", tcx.def_path_str(def_id) }
}
/// Computes the set of modules from which this type is visibly uninhabited.
/// To check whether a type is uninhabited at all (not just from a given module), you could
/// check whether the forest is empty.
query type_uninhabited_from(
key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>
) -> Arc<ty::inhabitedness::DefIdForest> {
desc { "computing the inhabitedness of `{:?}`", key }
}
}
Other {

View file

@ -11,7 +11,7 @@ use std::mem;
///
/// This is used to represent a set of modules in which a type is visibly
/// uninhabited.
#[derive(Clone)]
#[derive(Clone, HashStable)]
pub struct DefIdForest {
/// The minimal set of `DefId`s required to represent the whole set.
/// If A and B are DefIds in the `DefIdForest`, and A is a descendant
@ -72,6 +72,9 @@ impl<'tcx> DefIdForest {
break;
}
// `next_ret` and `old_ret` are empty here.
// We keep the elements in `ret` that are also in `next_forest`. Those that aren't are
// put back in `ret` via `old_ret`.
for id in ret.root_ids.drain(..) {
if next_forest.contains(tcx, id) {
next_ret.push(id);
@ -81,7 +84,13 @@ impl<'tcx> DefIdForest {
}
ret.root_ids.extend(old_ret.drain(..));
// We keep the elements in `next_forest` that are also in `ret`.
// You'd think this is not needed because `next_ret` already contains `ret \inter
// next_forest`. But those aren't just sets of things. If `ret = [a]`, `next_forest =
// [b]` and `b` is a submodule of `a`, then `b` belongs in the intersection but we
// didn't catch it in the loop above.
next_ret.extend(next_forest.root_ids.into_iter().filter(|&id| ret.contains(tcx, id)));
// `next_ret` now contains the intersection of the original `ret` and `next_forest`.
mem::swap(&mut next_ret, &mut ret.root_ids);
next_ret.drain(..);

View file

@ -6,7 +6,8 @@ use crate::ty::TyKind::*;
use crate::ty::{AdtDef, FieldDef, Ty, TyS, VariantDef};
use crate::ty::{AdtKind, Visibility};
use crate::ty::{DefId, SubstsRef};
use rustc_data_structures::stack::ensure_sufficient_stack;
use std::sync::Arc;
mod def_id_forest;
@ -187,34 +188,47 @@ impl<'tcx> FieldDef {
impl<'tcx> TyS<'tcx> {
/// Calculates the forest of `DefId`s from which this type is visibly uninhabited.
fn uninhabited_from(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> DefIdForest {
match *self.kind() {
Adt(def, substs) => {
ensure_sufficient_stack(|| def.uninhabited_from(tcx, substs, param_env))
}
Never => DefIdForest::full(tcx),
Tuple(ref tys) => DefIdForest::union(
tcx,
tys.iter().map(|ty| ty.expect_ty().uninhabited_from(tcx, param_env)),
),
Array(ty, len) => match len.try_eval_usize(tcx, param_env) {
Some(0) | None => DefIdForest::empty(),
// If the array is definitely non-empty, it's uninhabited if
// the type of its elements is uninhabited.
Some(1..) => ty.uninhabited_from(tcx, param_env),
},
// References to uninitialised memory are 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(),
}
fn uninhabited_from(
&'tcx self,
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> DefIdForest {
tcx.type_uninhabited_from(param_env.and(self)).as_ref().clone()
}
}
// Query provider for `type_uninhabited_from`.
pub(crate) fn type_uninhabited_from<'tcx>(
tcx: TyCtxt<'tcx>,
key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
) -> Arc<DefIdForest> {
let ty = key.value;
let param_env = key.param_env;
let forest = match *ty.kind() {
Adt(def, substs) => def.uninhabited_from(tcx, substs, param_env),
Never => DefIdForest::full(tcx),
Tuple(ref tys) => DefIdForest::union(
tcx,
tys.iter().map(|ty| ty.expect_ty().uninhabited_from(tcx, param_env)),
),
Array(ty, len) => match len.try_eval_usize(tcx, param_env) {
Some(0) | None => DefIdForest::empty(),
// If the array is definitely non-empty, it's uninhabited if
// the type of its elements is uninhabited.
Some(1..) => ty.uninhabited_from(tcx, param_env),
},
// References to uninitialised memory are 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(),
};
Arc::new(forest)
}

View file

@ -3146,6 +3146,7 @@ pub fn provide(providers: &mut ty::query::Providers) {
*providers = ty::query::Providers {
trait_impls_of: trait_def::trait_impls_of_provider,
all_local_trait_impls: trait_def::all_local_trait_impls,
type_uninhabited_from: inhabitedness::type_uninhabited_from,
..*providers
};
}