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

Factor out conservative_is_privately_uninhabited #103454

Merged
merged 3 commits into from
Nov 21, 2022
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
5 changes: 1 addition & 4 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1564,10 +1564,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
}
None => {
if !self
.tcx()
.conservative_is_privately_uninhabited(self.param_env.and(sig.output()))
{
if !sig.output().is_privately_uninhabited(self.tcx(), self.param_env) {
span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
let ty = self.tcx.erase_regions(ty);
let m = self.tcx.parent_module(expr.hir_id).to_def_id();
let param_env = self.tcx.param_env(m.expect_local());
if self.tcx.is_ty_uninhabited_from(m, ty, param_env) {
if !ty.is_inhabited_from(self.tcx, m, param_env) {
// This function will not return. We model this fact as an infinite loop.
self.drop_ranges.add_control_edge(self.expr_index + 1, self.expr_index + 1);
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_hir_typeck/src/generator_interior/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,10 +542,10 @@ fn check_must_not_suspend_ty<'tcx>(
data: SuspendCheckData<'_, 'tcx>,
) -> bool {
if ty.is_unit()
// FIXME: should this check `is_ty_uninhabited_from`. This query is not available in this stage
// FIXME: should this check `Ty::is_inhabited_from`. This query is not available in this stage
// of typeck (before ReVar and RePlaceholder are removed), but may remove noise, like in
// `must_use`
// || fcx.tcx.is_ty_uninhabited_from(fcx.tcx.parent_module(hir_id).to_def_id(), ty, fcx.param_env)
// || !ty.is_inhabited_from(fcx.tcx, fcx.tcx.parent_module(hir_id).to_def_id(), fcx.param_env)
{
return false;
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_lint/src/unused.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
plural_len: usize,
) -> bool {
if ty.is_unit()
|| cx.tcx.is_ty_uninhabited_from(
|| !ty.is_inhabited_from(
cx.tcx,
cx.tcx.parent_module(expr.hir_id).to_def_id(),
ty,
cx.param_env,
)
{
Expand Down
11 changes: 0 additions & 11 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2078,17 +2078,6 @@ rustc_queries! {
desc { "normalizing opaque types in `{:?}`", key }
}

/// Checks whether a type is definitely uninhabited. This is
/// conservative: for some types that are uninhabited we return `false`,
/// but we only return `true` for types that are definitely uninhabited.
/// `ty.conservative_is_privately_uninhabited` implies that any value of type `ty`
/// will be `Abi::Uninhabited`. (Note that uninhabited types may have nonzero
/// size, to account for partial initialisation. See #49298 for details.)
query conservative_is_privately_uninhabited(key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
desc { "conservatively checking if `{}` is privately uninhabited", key.value }
remap_env_constness
}

query limits(key: ()) -> Limits {
desc { "looking up limits" }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ impl<'tcx> InhabitedPredicate<'tcx> {
self.apply_inner(tcx, param_env, &|_| Err(())).ok()
}

/// Same as `apply`, but `NotInModule(_)` predicates yield `false`. That is,
/// privately uninhabited types are considered always uninhabited.
pub fn apply_ignore_module(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> bool {
let Ok(result) = self.apply_inner::<!>(tcx, param_env, &|_| Ok(true));
result
}

fn apply_inner<E>(
self,
tcx: TyCtxt<'tcx>,
Expand Down
111 changes: 59 additions & 52 deletions compiler/rustc_middle/src/ty/inhabitedness/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
//! }
//! ```
//! In this code, the type `Foo` will only be visibly uninhabited inside the
//! modules `b`, `c` and `d`. Calling `uninhabited_predicate` on `Foo` will
//! modules `b`, `c` and `d`. Calling `inhabited_predicate` on `Foo` will
//! return `NotInModule(b) AND NotInModule(c)`.
//!
//! We need this information for pattern-matching on `Foo` or types that contain
Expand Down Expand Up @@ -57,57 +57,6 @@ pub(crate) fn provide(providers: &mut ty::query::Providers) {
ty::query::Providers { inhabited_predicate_adt, inhabited_predicate_type, ..*providers };
}

impl<'tcx> TyCtxt<'tcx> {
/// Checks whether a type is visibly uninhabited from a particular module.
///
/// # Example
/// ```
/// #![feature(never_type)]
/// # fn main() {}
/// enum Void {}
/// mod a {
/// pub mod b {
/// pub struct SecretlyUninhabited {
/// _priv: !,
/// }
/// }
/// }
///
/// mod c {
/// use super::Void;
/// 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. This effects pattern-matching on `Foo` or types that
/// contain `Foo`.
///
/// # Example
/// ```ignore (illustrative)
/// 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.
pub fn is_ty_uninhabited_from(
self,
module: DefId,
ty: Ty<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> bool {
!ty.inhabited_predicate(self).apply(self, param_env, module)
}
}

/// Returns an `InhabitedPredicate` that is generic over type parameters and
/// requires calling [`InhabitedPredicate::subst`]
fn inhabited_predicate_adt(tcx: TyCtxt<'_>, def_id: DefId) -> InhabitedPredicate<'_> {
Expand Down Expand Up @@ -171,6 +120,64 @@ impl<'tcx> Ty<'tcx> {
_ => InhabitedPredicate::True,
}
}

/// Checks whether a type is visibly uninhabited from a particular module.
///
/// # Example
/// ```
/// #![feature(never_type)]
/// # fn main() {}
/// enum Void {}
/// mod a {
/// pub mod b {
/// pub struct SecretlyUninhabited {
/// _priv: !,
/// }
/// }
/// }
///
/// mod c {
/// use super::Void;
/// 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. This effects pattern-matching on `Foo` or types that
/// contain `Foo`.
///
/// # Example
/// ```ignore (illustrative)
/// 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.
pub fn is_inhabited_from(
self,
tcx: TyCtxt<'tcx>,
module: DefId,
param_env: ty::ParamEnv<'tcx>,
) -> bool {
self.inhabited_predicate(tcx).apply(tcx, param_env, module)
}

/// Returns true if the type is uninhabited without regard to visibility
pub fn is_privately_uninhabited(
self,
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> bool {
!self.inhabited_predicate(tcx).apply_ignore_module(tcx, param_env)
}
}

/// N.B. this query should only be called through `Ty::inhabited_predicate`
Expand Down
13 changes: 4 additions & 9 deletions compiler/rustc_mir_build/src/build/expr/into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,15 +271,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// MIR checks and ultimately whether code is accepted or not. We can only
// omit the return edge if a return type is visibly uninhabited to a module
// that makes the call.
target: if this.tcx.is_ty_uninhabited_from(
this.parent_module,
expr.ty,
this.param_env,
) {
None
} else {
Some(success)
},
target: expr
.ty
.is_inhabited_from(this.tcx, this.parent_module, this.param_env)
.then_some(success),
from_hir_call,
fn_span,
},
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_mir_build/src/build/matches/simplify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,10 +264,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
i == variant_index || {
self.tcx.features().exhaustive_patterns
&& v.inhabited_predicate(self.tcx, adt_def)
&& !v
.inhabited_predicate(self.tcx, adt_def)
.subst(self.tcx, substs)
.apply_any_module(self.tcx, self.param_env)
!= Some(true)
.apply_ignore_module(self.tcx, self.param_env)
}
}) && (adt_def.did().is_local()
|| !adt_def.is_variant_list_non_exhaustive());
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,7 @@ fn non_exhaustive_match<'p, 'tcx>(
}
}
if let ty::Ref(_, sub_ty, _) = scrut_ty.kind() {
if cx.tcx.is_ty_uninhabited_from(cx.module, *sub_ty, cx.param_env) {
if !sub_ty.is_inhabited_from(cx.tcx, cx.module, cx.param_env) {
err.note("references are always considered inhabited");
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ pub(crate) struct MatchCheckCtxt<'p, 'tcx> {
impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
pub(super) fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
if self.tcx.features().exhaustive_patterns {
self.tcx.is_ty_uninhabited_from(self.module, ty, self.param_env)
!ty.is_inhabited_from(self.tcx, self.module, self.param_env)
} else {
false
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_transform/src/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1015,7 +1015,7 @@ fn insert_panic_block<'tcx>(

fn can_return<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
// Returning from a function with an uninhabited return type is undefined behavior.
if tcx.conservative_is_privately_uninhabited(param_env.and(body.return_ty())) {
if body.return_ty().is_privately_uninhabited(tcx, param_env) {
return false;
}

Expand Down
25 changes: 12 additions & 13 deletions compiler/rustc_passes/src/liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1284,20 +1284,19 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
fn check_is_ty_uninhabited(&mut self, expr: &Expr<'_>, succ: LiveNode) -> LiveNode {
let ty = self.typeck_results.expr_ty(expr);
let m = self.ir.tcx.parent_module(expr.hir_id).to_def_id();
if self.ir.tcx.is_ty_uninhabited_from(m, ty, self.param_env) {
match self.ir.lnks[succ] {
LiveNodeKind::ExprNode(succ_span, succ_id) => {
self.warn_about_unreachable(expr.span, ty, succ_span, succ_id, "expression");
}
LiveNodeKind::VarDefNode(succ_span, succ_id) => {
self.warn_about_unreachable(expr.span, ty, succ_span, succ_id, "definition");
}
_ => {}
};
self.exit_ln
} else {
succ
if ty.is_inhabited_from(self.ir.tcx, m, self.param_env) {
return succ;
}
match self.ir.lnks[succ] {
LiveNodeKind::ExprNode(succ_span, succ_id) => {
self.warn_about_unreachable(expr.span, ty, succ_span, succ_id, "expression");
}
LiveNodeKind::VarDefNode(succ_span, succ_id) => {
self.warn_about_unreachable(expr.span, ty, succ_span, succ_id, "definition");
}
_ => {}
};
self.exit_ln
}

fn warn_about_unreachable(
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_ty_utils/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,8 +442,7 @@ fn layout_of_uncached<'tcx>(
let element = cx.layout_of(element)?;
let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow(ty))?;

let abi = if count != 0 && tcx.conservative_is_privately_uninhabited(param_env.and(ty))
{
let abi = if count != 0 && ty.is_privately_uninhabited(tcx, param_env) {
Abi::Uninhabited
} else {
Abi::Aggregate { sized: true }
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ty_utils/src/layout_sanity_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub(super) fn sanity_check_layout<'tcx>(
layout: &TyAndLayout<'tcx>,
) {
// Type-level uninhabitedness should always imply ABI uninhabitedness.
if cx.tcx.conservative_is_privately_uninhabited(cx.param_env.and(layout.ty)) {
if layout.ty.is_privately_uninhabited(cx.tcx, cx.param_env) {
assert!(layout.abi.is_uninhabited());
}

Expand Down
57 changes: 0 additions & 57 deletions compiler/rustc_ty_utils/src/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,62 +416,6 @@ fn asyncness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::IsAsync {
node.fn_sig().map_or(hir::IsAsync::NotAsync, |sig| sig.header.asyncness)
}

/// Don't call this directly: use ``tcx.conservative_is_privately_uninhabited`` instead.
pub fn conservative_is_privately_uninhabited_raw<'tcx>(
tcx: TyCtxt<'tcx>,
param_env_and: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
) -> bool {
let (param_env, ty) = param_env_and.into_parts();
match ty.kind() {
ty::Never => {
debug!("ty::Never =>");
true
}
ty::Adt(def, _) if def.is_union() => {
debug!("ty::Adt(def, _) if def.is_union() =>");
// For now, `union`s are never considered uninhabited.
false
}
ty::Adt(def, substs) => {
debug!("ty::Adt(def, _) if def.is_not_union() =>");
// Any ADT is uninhabited if either:
// (a) It has no variants (i.e. an empty `enum`);
// (b) Each of its variants (a single one in the case of a `struct`) has at least
// one uninhabited field.
def.variants().iter().all(|var| {
var.fields.iter().any(|field| {
let ty = tcx.bound_type_of(field.did).subst(tcx, substs);
tcx.conservative_is_privately_uninhabited(param_env.and(ty))
})
})
}
ty::Tuple(fields) => {
debug!("ty::Tuple(..) =>");
fields.iter().any(|ty| tcx.conservative_is_privately_uninhabited(param_env.and(ty)))
}
ty::Array(ty, len) => {
debug!("ty::Array(ty, len) =>");
match len.try_eval_usize(tcx, param_env) {
Some(0) | None => false,
// If the array is definitely non-empty, it's uninhabited if
// the type of its elements is uninhabited.
Some(1..) => tcx.conservative_is_privately_uninhabited(param_env.and(*ty)),
}
}
ty::Ref(..) => {
debug!("ty::Ref(..) =>");
// References to uninitialised memory is valid for any type, including
// uninhabited types, in unsafe code, so we treat all references as
// inhabited.
false
}
_ => {
debug!("_ =>");
false
}
}
}

pub fn provide(providers: &mut ty::query::Providers) {
*providers = ty::query::Providers {
asyncness,
Expand All @@ -481,7 +425,6 @@ pub fn provide(providers: &mut ty::query::Providers) {
instance_def_size_estimate,
issue33140_self_ty,
impl_defaultness,
conservative_is_privately_uninhabited: conservative_is_privately_uninhabited_raw,
..*providers
};
}
Loading