From 718ffcf33a0df421388a0235c1bec5cae83c0e7e Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 24 Sep 2023 14:27:15 +0000 Subject: [PATCH 1/3] Make inhabitedness a predicate. --- .../src/impl_wf_check/min_specialization.rs | 1 + .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 1 + compiler/rustc_infer/src/infer/mod.rs | 2 +- compiler/rustc_infer/src/traits/util.rs | 3 + compiler/rustc_lint/src/builtin.rs | 33 +-- compiler/rustc_lint/src/lints.rs | 7 +- compiler/rustc_lint/src/unused.rs | 4 +- compiler/rustc_middle/src/query/erase.rs | 1 - compiler/rustc_middle/src/query/keys.rs | 8 + compiler/rustc_middle/src/query/mod.rs | 17 +- compiler/rustc_middle/src/ty/flags.rs | 3 + compiler/rustc_middle/src/ty/inhabitedness.rs | 191 +++++++++++++++ .../ty/inhabitedness/inhabited_predicate.rs | 222 ------------------ .../rustc_middle/src/ty/inhabitedness/mod.rs | 208 ---------------- compiler/rustc_middle/src/ty/mod.rs | 8 +- compiler/rustc_middle/src/ty/print/pretty.rs | 7 + .../rustc_middle/src/ty/structural_impls.rs | 3 + .../rustc_mir_build/src/build/expr/into.rs | 7 +- .../src/build/matches/simplify.rs | 5 +- compiler/rustc_mir_build/src/errors.rs | 2 +- .../src/thir/pattern/check_match.rs | 6 +- .../src/thir/pattern/deconstruct_pat.rs | 24 +- .../src/thir/pattern/usefulness.rs | 2 +- compiler/rustc_passes/src/liveness.rs | 2 +- compiler/rustc_smir/src/rustc_smir/mod.rs | 1 + .../src/solve/eval_ctxt.rs | 3 + .../src/solve/fulfill.rs | 1 + .../rustc_trait_selection/src/solve/mod.rs | 97 ++++++++ .../src/traits/auto_trait.rs | 1 + .../src/traits/error_reporting/mod.rs | 4 + .../src/traits/fulfill.rs | 114 ++++++++- .../rustc_trait_selection/src/traits/mod.rs | 2 +- .../query/type_op/implied_outlives_bounds.rs | 1 + .../src/traits/select/mod.rs | 107 +++++++++ .../rustc_trait_selection/src/traits/wf.rs | 1 + .../src/normalize_erasing_regions.rs | 1 + compiler/rustc_ty_utils/src/common_traits.rs | 23 +- .../rustc_ty_utils/src/layout_sanity_check.rs | 5 +- compiler/rustc_ty_utils/src/lib.rs | 2 +- 39 files changed, 626 insertions(+), 504 deletions(-) create mode 100644 compiler/rustc_middle/src/ty/inhabitedness.rs delete mode 100644 compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs delete mode 100644 compiler/rustc_middle/src/ty/inhabitedness/mod.rs diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index 3760195a5e8ec..ecbd1887cacad 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -517,6 +517,7 @@ fn trait_predicate_kind<'tcx>( | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::Uninhabited(..) | ty::PredicateKind::Ambiguous => None, } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 415920221f553..aa66e7ea26de8 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -657,6 +657,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // code is looking for a self type of an unresolved // inference variable. | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::Uninhabited(..) | ty::PredicateKind::Ambiguous => None, }, diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index aeb3177af0275..85819135d4bcf 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1794,7 +1794,7 @@ impl<'tcx> TyOrConstInferVar<'tcx> { /// Tries to extract an inference variable from a type, returns `None` /// for types other than `ty::Infer(_)` (or `InferTy::Fresh*`). - fn maybe_from_ty(ty: Ty<'tcx>) -> Option { + pub fn maybe_from_ty(ty: Ty<'tcx>) -> Option { match *ty.kind() { ty::Infer(ty::TyVar(v)) => Some(TyOrConstInferVar::Ty(v)), ty::Infer(ty::IntVar(v)) => Some(TyOrConstInferVar::TyInt(v)), diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index 93dfbe63bcd1c..ce43c844699bf 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -380,6 +380,9 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> { ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) => { // Nothing to elaborate } + ty::PredicateKind::Uninhabited(..) => { + // Nothing to elaborate + } } } } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 536f78a73edb6..69698eb92f614 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -2511,18 +2511,11 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { // And now, enums. let span = cx.tcx.def_span(adt_def.did()); let mut potential_variants = adt_def.variants().iter().filter_map(|variant| { - let definitely_inhabited = match variant - .inhabited_predicate(cx.tcx, *adt_def) - .instantiate(cx.tcx, args) - .apply_any_module(cx.tcx, cx.param_env) - { + if variant.is_privately_uninhabited(cx.tcx, *adt_def, args, cx.param_env) { // Entirely skip uninhabited variants. - Some(false) => return None, - // Forward the others, but remember which ones are definitely inhabited. - Some(true) => true, - None => false, - }; - Some((variant, definitely_inhabited)) + return None; + } + Some(variant) }); let Some(first_variant) = potential_variants.next() else { return Some( @@ -2531,12 +2524,12 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { ); }; // So we have at least one potentially inhabited variant. Might we have two? - let Some(second_variant) = potential_variants.next() else { + let Some(_) = potential_variants.next() else { // There is only one potentially inhabited variant. So we can recursively check that variant! return variant_find_init_error( cx, ty, - &first_variant.0, + &first_variant, args, "field of the only potentially inhabited enum variant", init, @@ -2547,16 +2540,9 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { // then we have a tag and hence leaving this uninit is definitely disallowed. // (Leaving it zeroed could be okay, depending on which variant is encoded as zero tag.) if init == InitKind::Uninit { - let definitely_inhabited = (first_variant.1 as usize) - + (second_variant.1 as usize) - + potential_variants - .filter(|(_variant, definitely_inhabited)| *definitely_inhabited) - .count(); - if definitely_inhabited > 1 { - return Some(InitError::from( - "enums with multiple inhabited variants have to be initialized to a variant", - ).spanned(span)); - } + return Some(InitError::from( + "enums with multiple inhabited variants have to be initialized to a variant", + ).spanned(span)); } // We couldn't find anything wrong here. None @@ -2599,6 +2585,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { label: expr.span, sub, tcx: cx.tcx, + param_env: cx.param_env, }, ); } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index c091c260a470e..be22e7d762fcc 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -10,9 +10,7 @@ use rustc_errors::{ }; use rustc_hir::def_id::DefId; use rustc_macros::{LintDiagnostic, Subdiagnostic}; -use rustc_middle::ty::{ - inhabitedness::InhabitedPredicate, Clause, PolyExistentialTraitRef, Ty, TyCtxt, -}; +use rustc_middle::ty::{Clause, ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt}; use rustc_session::parse::ParseSess; use rustc_span::{edition::Edition, sym, symbol::Ident, Span, Symbol}; @@ -432,6 +430,7 @@ pub struct BuiltinUnpermittedTypeInit<'a> { pub label: Span, pub sub: BuiltinUnpermittedTypeInitSub, pub tcx: TyCtxt<'a>, + pub param_env: ParamEnv<'a>, } impl<'a> DecorateLint<'a, ()> for BuiltinUnpermittedTypeInit<'_> { @@ -441,7 +440,7 @@ impl<'a> DecorateLint<'a, ()> for BuiltinUnpermittedTypeInit<'_> { ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> { diag.set_arg("ty", self.ty); diag.span_label(self.label, fluent::lint_builtin_unpermitted_type_init_label); - if let InhabitedPredicate::True = self.ty.inhabited_predicate(self.tcx) { + if !self.ty.is_privately_uninhabited(self.tcx, self.param_env) { // Only suggest late `MaybeUninit::assume_init` initialization if the type is inhabited. diag.span_label( self.label, diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index d5beff4f101a8..bbc44e50401ce 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -269,10 +269,10 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { span: Span, ) -> Option { if ty.is_unit() - || !ty.is_inhabited_from( + || ty.is_uninhabited_from( cx.tcx, - cx.tcx.parent_module(expr.hir_id).to_def_id(), cx.param_env, + cx.tcx.parent_module(expr.hir_id).to_def_id(), ) { return Some(MustUsePath::Suppressed); diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 8ba3764bcc317..76b66d33c6c80 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -332,7 +332,6 @@ tcx_lifetime! { rustc_middle::ty::FnSig, rustc_middle::ty::GenericArg, rustc_middle::ty::GenericPredicates, - rustc_middle::ty::inhabitedness::InhabitedPredicate, rustc_middle::ty::Instance, rustc_middle::ty::InstanceDef, rustc_middle::ty::layout::FnAbiError, diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index b1f8379686274..442eac948d579 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -487,6 +487,14 @@ impl<'tcx, T: Key> Key for ty::ParamEnvAnd<'tcx, T> { } } +impl<'tcx> Key for ty::ParamEnvAnd<'tcx, (Ty<'tcx>, Option)> { + type CacheSelector = DefaultCacheSelector; + + fn default_span(&self, tcx: TyCtxt<'_>) -> Span { + self.value.0.default_span(tcx) + } +} + impl Key for Symbol { type CacheSelector = DefaultCacheSelector; diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 340c5a769dbc4..5ab337af2512f 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1362,6 +1362,14 @@ rustc_queries! { query has_significant_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` has a significant drop", env.value } } + /// Query backing `Ty::is_uninhabited*`. + query type_is_uninhabited_from_raw(key: ty::ParamEnvAnd<'tcx, (Ty<'tcx>, Option)>) -> bool { + desc { |tcx| + "computing whether `{}` is uninhabited{}", + key.value.0, + key.value.1.map_or(String::new(), |module| tcx.def_path_str(module)) + } + } /// Query backing `Ty::is_structural_eq_shallow`. /// @@ -1712,15 +1720,6 @@ rustc_queries! { feedable } - query inhabited_predicate_adt(key: DefId) -> ty::inhabitedness::InhabitedPredicate<'tcx> { - desc { "computing the uninhabited predicate of `{:?}`", key } - } - - /// Do not call this query directly: invoke `Ty::inhabited_predicate` instead. - query inhabited_predicate_type(key: Ty<'tcx>) -> ty::inhabitedness::InhabitedPredicate<'tcx> { - desc { "computing the uninhabited predicate of `{}`", key } - } - query dep_kind(_: CrateNum) -> CrateDepKind { eval_always desc { "fetching what a dependency looks like" } diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index 7ed31fbbfd885..1074561d371ef 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -294,6 +294,9 @@ impl FlagComputation { self.add_term(t1); self.add_term(t2); } + ty::PredicateKind::Uninhabited(ty, _) => { + self.add_ty(ty); + } } } diff --git a/compiler/rustc_middle/src/ty/inhabitedness.rs b/compiler/rustc_middle/src/ty/inhabitedness.rs new file mode 100644 index 0000000000000..eff7891e5d597 --- /dev/null +++ b/compiler/rustc_middle/src/ty/inhabitedness.rs @@ -0,0 +1,191 @@ +//! 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 +//! #![feature(never_type)] +//! mod a { +//! pub mod b { +//! pub struct SecretlyUninhabited { +//! _priv: !, +//! } +//! } +//! } +//! +//! mod c { +//! enum 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`. 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 +//! `Foo`. +//! +//! # Example +//! ```ignore(illustrative) +//! let foo_result: Result = ... ; +//! let Ok(t) = foo_result; +//! ``` +//! This code should only compile in modules where the uninhabitedness of `Foo` +//! is visible. + +use crate::ty::context::TyCtxt; +use crate::ty::{self, DefId, Ty, VariantDef, Visibility}; + +use rustc_type_ir::sty::TyKind::*; + +impl<'tcx> VariantDef { + fn is_uninhabited_module( + &self, + tcx: TyCtxt<'tcx>, + adt: ty::AdtDef<'_>, + args: ty::GenericArgsRef<'tcx>, + param_env: ty::ParamEnv<'tcx>, + module: Option, + ) -> bool { + debug_assert!(!adt.is_union()); + if self.is_field_list_non_exhaustive() && !self.def_id.is_local() { + // Non-exhaustive variants from other crates are always considered inhabited. + return false; + } + self.fields.iter().any(|field| { + let fty = tcx.type_of(field.did).instantiate(tcx, args); + if adt.is_struct() + && let Visibility::Restricted(from) = field.vis + && let Some(module) = module + && !tcx.is_descendant_of(module, from) + { + // The field may be uninhabited, this is not visible from `module`, so we return. + return false; + } + fty.is_uninhabited_module(tcx, param_env, module) + }) + } + + pub fn is_uninhabited_from( + &self, + tcx: TyCtxt<'tcx>, + adt: ty::AdtDef<'_>, + args: ty::GenericArgsRef<'tcx>, + param_env: ty::ParamEnv<'tcx>, + module: DefId, + ) -> bool { + self.is_uninhabited_module(tcx, adt, args, param_env, Some(module)) + } + + pub fn is_privately_uninhabited( + &self, + tcx: TyCtxt<'tcx>, + adt: ty::AdtDef<'_>, + args: ty::GenericArgsRef<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> bool { + self.is_uninhabited_module(tcx, adt, args, param_env, None) + } +} + +impl<'tcx> Ty<'tcx> { + pub fn is_trivially_uninhabited(self) -> Option { + match self.kind() { + // For now, unions are always considered inhabited + Adt(adt, _) if adt.is_union() => Some(false), + // Non-exhaustive ADTs from other crates are always considered inhabited + Adt(adt, _) if adt.is_variant_list_non_exhaustive() && !adt.did().is_local() => { + Some(false) + } + Never => Some(true), + Tuple(tys) if tys.is_empty() => Some(false), + // use a query for more complex cases + Param(_) | Alias(..) | Adt(..) | Array(..) | Tuple(_) => None, + // references and other types are inhabited + _ => Some(false), + } + } + + fn is_uninhabited_module( + self, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + module: Option, + ) -> bool { + if let Some(trivial) = self.is_trivially_uninhabited() { + trivial + } else { + tcx.type_is_uninhabited_from_raw(param_env.and((self, module))) + } + } + + /// 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 = ... ; + /// let Ok(t) = foo_result; + /// ``` + /// This code should only compile in modules where the uninhabitedness of Foo is + /// visible. + #[inline] + pub fn is_uninhabited_from( + self, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + module: DefId, + ) -> bool { + self.is_uninhabited_module(tcx, param_env, Some(module)) + } + + /// Returns true if the type is uninhabited without regard to visibility + #[inline] + pub fn is_privately_uninhabited( + self, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> bool { + self.is_uninhabited_module(tcx, param_env, None) + } +} diff --git a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs deleted file mode 100644 index f278cace99dc4..0000000000000 --- a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs +++ /dev/null @@ -1,222 +0,0 @@ -use crate::ty::context::TyCtxt; -use crate::ty::{self, DefId, 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::instantiate`]. - 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 { - 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( - self, - tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, - in_module: &impl Fn(DefId) -> Result, - ) -> Result { - match self { - Self::False => Ok(false), - Self::True => Ok(true), - Self::ConstIsZero(const_) => match const_.try_eval_target_usize(tcx, param_env) { - None | Some(0) => Ok(true), - Some(1..) => Ok(false), - }, - Self::NotInModule(id) => in_module(id).map(|in_mod| !in_mod), - // `t` may be a projection, for which `inhabited_predicate` returns a `GenericType`. As - // we have a param_env available, we can do better. - Self::GenericType(t) => { - let normalized_pred = tcx - .try_normalize_erasing_regions(param_env, t) - .map_or(self, |t| t.inhabited_predicate(tcx)); - match normalized_pred { - // We don't have more information than we started with, so consider inhabited. - Self::GenericType(_) => Ok(true), - pred => pred.apply_inner(tcx, param_env, in_module), - } - } - 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) -> 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) -> 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 { - 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 { - 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 instantiate(self, tcx: TyCtxt<'tcx>, args: ty::GenericArgsRef<'tcx>) -> Self { - self.instantiate_opt(tcx, args).unwrap_or(self) - } - - fn instantiate_opt(self, tcx: TyCtxt<'tcx>, args: ty::GenericArgsRef<'tcx>) -> Option { - match self { - Self::ConstIsZero(c) => { - let c = ty::EarlyBinder::bind(c).instantiate(tcx, args); - let pred = match c.try_to_target_usize(tcx) { - Some(0) => Self::True, - Some(1..) => Self::False, - None => Self::ConstIsZero(c), - }; - Some(pred) - } - Self::GenericType(t) => { - Some(ty::EarlyBinder::bind(t).instantiate(tcx, args).inhabited_predicate(tcx)) - } - Self::And(&[a, b]) => match a.instantiate_opt(tcx, args) { - None => b.instantiate_opt(tcx, args).map(|b| a.and(tcx, b)), - Some(InhabitedPredicate::False) => Some(InhabitedPredicate::False), - Some(a) => Some(a.and(tcx, b.instantiate_opt(tcx, args).unwrap_or(b))), - }, - Self::Or(&[a, b]) => match a.instantiate_opt(tcx, args) { - None => b.instantiate_opt(tcx, args).map(|b| a.or(tcx, b)), - Some(InhabitedPredicate::True) => Some(InhabitedPredicate::True), - Some(a) => Some(a.or(tcx, b.instantiate_opt(tcx, args).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(a: T, b: T, f: impl Fn(T) -> Result) -> Result { - 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(a: T, b: T, f: impl Fn(T) -> Result) -> Result { - 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), - } -} diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs deleted file mode 100644 index 4dac6891b3062..0000000000000 --- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs +++ /dev/null @@ -1,208 +0,0 @@ -//! 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 -//! #![feature(never_type)] -//! mod a { -//! pub mod b { -//! pub struct SecretlyUninhabited { -//! _priv: !, -//! } -//! } -//! } -//! -//! mod c { -//! enum 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`. 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 -//! `Foo`. -//! -//! # Example -//! ```ignore(illustrative) -//! let foo_result: Result = ... ; -//! let Ok(t) = foo_result; -//! ``` -//! This code should only compile in modules where the uninhabitedness of `Foo` -//! is visible. - -use crate::query::Providers; -use crate::ty::context::TyCtxt; -use crate::ty::{self, DefId, Ty, VariantDef, Visibility}; - -use rustc_type_ir::sty::TyKind::*; - -pub mod inhabited_predicate; - -pub use inhabited_predicate::InhabitedPredicate; - -pub(crate) fn provide(providers: &mut Providers) { - *providers = Providers { inhabited_predicate_adt, inhabited_predicate_type, ..*providers }; -} - -/// Returns an `InhabitedPredicate` that is generic over type parameters and -/// requires calling [`InhabitedPredicate::instantiate`] -fn inhabited_predicate_adt(tcx: TyCtxt<'_>, def_id: DefId) -> InhabitedPredicate<'_> { - if let Some(def_id) = def_id.as_local() { - if matches!(tcx.representability(def_id), ty::Representability::Infinite) { - return InhabitedPredicate::True; - } - } - let adt = tcx.adt_def(def_id); - InhabitedPredicate::any( - tcx, - adt.variants().iter().map(|variant| variant.inhabited_predicate(tcx, adt)), - ) -} - -impl<'tcx> VariantDef { - /// Calculates the forest of `DefId`s from which this variant is visibly uninhabited. - pub fn inhabited_predicate( - &self, - tcx: TyCtxt<'tcx>, - adt: ty::AdtDef<'_>, - ) -> InhabitedPredicate<'tcx> { - debug_assert!(!adt.is_union()); - if self.is_field_list_non_exhaustive() && !self.def_id.is_local() { - // Non-exhaustive variants from other crates are always considered inhabited. - return InhabitedPredicate::True; - } - InhabitedPredicate::all( - tcx, - self.fields.iter().map(|field| { - let pred = tcx.type_of(field.did).instantiate_identity().inhabited_predicate(tcx); - if adt.is_enum() { - return pred; - } - match field.vis { - Visibility::Public => pred, - Visibility::Restricted(from) => { - pred.or(tcx, InhabitedPredicate::NotInModule(from)) - } - } - }), - ) - } -} - -impl<'tcx> Ty<'tcx> { - pub fn inhabited_predicate(self, tcx: TyCtxt<'tcx>) -> InhabitedPredicate<'tcx> { - match self.kind() { - // For now, unions are always considered inhabited - Adt(adt, _) if adt.is_union() => InhabitedPredicate::True, - // Non-exhaustive ADTs from other crates are always considered inhabited - Adt(adt, _) if adt.is_variant_list_non_exhaustive() && !adt.did().is_local() => { - InhabitedPredicate::True - } - Never => InhabitedPredicate::False, - Param(_) | Alias(ty::Projection, _) => InhabitedPredicate::GenericType(self), - // FIXME(inherent_associated_types): Most likely we can just map to `GenericType` like above. - // However it's unclear if the args passed to `InhabitedPredicate::instantiate` are of the correct - // format, i.e. don't contain parent args. If you hit this case, please verify this beforehand. - Alias(ty::Inherent, _) => { - bug!("unimplemented: inhabitedness checking for inherent projections") - } - 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, - } - } - - /// 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 = ... ; - /// 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` -fn inhabited_predicate_type<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> InhabitedPredicate<'tcx> { - match *ty.kind() { - Adt(adt, args) => tcx.inhabited_predicate_adt(adt.did()).instantiate(tcx, args), - - Tuple(tys) => { - InhabitedPredicate::all(tcx, tys.iter().map(|ty| ty.inhabited_predicate(tcx))) - } - - // If we can evaluate the array length before having a `ParamEnv`, then - // we can simplify the predicate. This is an optimization. - Array(ty, len) => match len.try_to_target_usize(tcx) { - Some(0) => InhabitedPredicate::True, - Some(1..) => ty.inhabited_predicate(tcx), - None => ty.inhabited_predicate(tcx).or(tcx, InhabitedPredicate::ConstIsZero(len)), - }, - - _ => bug!("unexpected TyKind, use `Ty::inhabited_predicate`"), - } -} diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index aa1e7f216a069..babd28c876273 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -553,6 +553,7 @@ impl<'tcx> Predicate<'tcx> { | PredicateKind::Coerce(_) | PredicateKind::Clause(ClauseKind::ConstEvaluatable(_)) | PredicateKind::ConstEquate(_, _) + | PredicateKind::Uninhabited(..) | PredicateKind::Ambiguous => true, } } @@ -705,6 +706,9 @@ pub enum PredicateKind<'tcx> { /// /// Only used for new solver AliasRelate(Term<'tcx>, Term<'tcx>, AliasRelationDirection), + + /// The type is uninhabited at the given module, or anywhere if None. + Uninhabited(Ty<'tcx>, Option), } #[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] @@ -1363,6 +1367,7 @@ impl<'tcx> Predicate<'tcx> { | PredicateKind::Clause(ClauseKind::TypeOutlives(..)) | PredicateKind::Clause(ClauseKind::ConstEvaluatable(..)) | PredicateKind::ConstEquate(..) + | PredicateKind::Uninhabited(..) | PredicateKind::Ambiguous => None, } } @@ -1383,6 +1388,7 @@ impl<'tcx> Predicate<'tcx> { | PredicateKind::Clause(ClauseKind::TypeOutlives(..)) | PredicateKind::Clause(ClauseKind::ConstEvaluatable(..)) | PredicateKind::ConstEquate(..) + | PredicateKind::Uninhabited(..) | PredicateKind::Ambiguous => None, } } @@ -1403,6 +1409,7 @@ impl<'tcx> Predicate<'tcx> { | PredicateKind::ClosureKind(..) | PredicateKind::Clause(ClauseKind::ConstEvaluatable(..)) | PredicateKind::ConstEquate(..) + | PredicateKind::Uninhabited(..) | PredicateKind::Ambiguous => None, } } @@ -2669,7 +2676,6 @@ pub fn provide(providers: &mut Providers) { closure::provide(providers); context::provide(providers); erase_regions::provide(providers); - inhabitedness::provide(providers); util::provide(providers); print::provide(providers); super::util::bug::provide(providers); diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 2d7350387ca78..dc0a19d8b3cef 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2939,6 +2939,13 @@ define_print_and_forward_display! { } ty::PredicateKind::Ambiguous => p!("ambiguous"), ty::PredicateKind::AliasRelate(t1, t2, dir) => p!(print(t1), write(" {} ", dir), print(t2)), + ty::PredicateKind::Uninhabited(ty, None) => p!(print(ty), "is uninhabited"), + ty::PredicateKind::Uninhabited(ty, Some(module)) => p!( + print(ty), + "is visibly uninhabited in module `", + print_def_path(module, &[]), + "`" + ), } } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 2adbe9e030997..ea7865718a614 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -232,6 +232,9 @@ impl<'tcx> fmt::Debug for ty::PredicateKind<'tcx> { ty::PredicateKind::AliasRelate(t1, t2, dir) => { write!(f, "AliasRelate({t1:?}, {dir:?}, {t2:?})") } + ty::PredicateKind::Uninhabited(ty, module) => { + write!(f, "Uninhabited({ty:?}, {module:?})") + } } } } diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index a4de42d45c995..c84213deccf95 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -253,6 +253,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { debug!("expr_into_dest: fn_span={:?}", fn_span); + let expr_is_inhabited = + !expr.ty.is_uninhabited_from(this.tcx, this.param_env, this.parent_module); this.cfg.terminate( block, source_info, @@ -265,10 +267,7 @@ 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: expr - .ty - .is_inhabited_from(this.tcx, this.parent_module, this.param_env) - .then_some(success), + target: expr_is_inhabited.then_some(success), call_source: if from_hir_call { CallSource::Normal } else { diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs index 17ac1f4e0cea6..467160a5abf21 100644 --- a/compiler/rustc_mir_build/src/build/matches/simplify.rs +++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs @@ -263,10 +263,7 @@ 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) - .instantiate(self.tcx, args) - .apply_ignore_module(self.tcx, self.param_env) + && v.is_privately_uninhabited(self.tcx, adt_def, args, self.param_env) } }) && (adt_def.did().is_local() || !adt_def.is_variant_list_non_exhaustive()); diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index bee5ac550ddce..8cdbe72bf0e31 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -444,7 +444,7 @@ impl<'a> IntoDiagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> { } if let ty::Ref(_, sub_ty, _) = self.ty.kind() { - if !sub_ty.is_inhabited_from(self.cx.tcx, self.cx.module, self.cx.param_env) { + if sub_ty.is_uninhabited_from(self.cx.tcx, self.cx.param_env, self.cx.module) { diag.note(fluent::mir_build_reference_note); } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index d440ca319260c..47082a94e49a9 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -509,9 +509,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { && let Constructor::Variant(variant_index) = witness_1.ctor() { let variant = adt.variant(*variant_index); - let inhabited = variant.inhabited_predicate(cx.tcx, *adt).instantiate(cx.tcx, args); - assert!(inhabited.apply(cx.tcx, cx.param_env, cx.module)); - !inhabited.apply_ignore_module(cx.tcx, cx.param_env) + variant.is_privately_uninhabited(cx.tcx, *adt, args, cx.param_env) } else { false }; @@ -748,7 +746,7 @@ fn non_exhaustive_match<'p, 'tcx>( } if let ty::Ref(_, sub_ty, _) = scrut_ty.kind() { - if !sub_ty.is_inhabited_from(cx.tcx, cx.module, cx.param_env) { + if sub_ty.is_uninhabited_from(cx.tcx, cx.param_env, cx.module) { err.note("references are always considered inhabited"); } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index b79beb1c537ae..6d63d742baa0d 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -950,19 +950,17 @@ impl<'tcx> SplitWildcard<'tcx> { let is_secretly_empty = def.variants().is_empty() && !is_exhaustive_pat_feature && !pcx.is_top_level; - let mut ctors: SmallVec<[_; 1]> = - def.variants() - .iter_enumerated() - .filter(|(_, v)| { - // If `exhaustive_patterns` is enabled, we exclude variants known to be - // uninhabited. - !is_exhaustive_pat_feature - || v.inhabited_predicate(cx.tcx, *def) - .instantiate(cx.tcx, args) - .apply(cx.tcx, cx.param_env, cx.module) - }) - .map(|(idx, _)| Variant(idx)) - .collect(); + let mut ctors: SmallVec<[_; 1]> = def + .variants() + .iter_enumerated() + .filter(|(_, v)| { + // If `exhaustive_patterns` is enabled, we exclude variants known to be + // uninhabited. + !(is_exhaustive_pat_feature + && v.is_uninhabited_from(cx.tcx, *def, args, cx.param_env, cx.module)) + }) + .map(|(idx, _)| Variant(idx)) + .collect(); if is_secretly_empty || is_declared_nonexhaustive { ctors.push(NonExhaustive); diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 21031e8ba9d7e..79f78c784c246 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -341,7 +341,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 { - !ty.is_inhabited_from(self.tcx, self.module, self.param_env) + ty.is_uninhabited_from(self.tcx, self.param_env, self.module) } else { false } diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 20e996eaec4c9..63c8553c3c806 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -1287,7 +1287,7 @@ 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 ty.is_inhabited_from(self.ir.tcx, m, self.param_env) { + if !ty.is_uninhabited_from(self.ir.tcx, self.param_env, m) { return succ; } match self.ir.lnks[succ] { diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 5ff17613b4e56..e835c2106d959 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -1358,6 +1358,7 @@ impl<'tcx> Stable<'tcx> for ty::PredicateKind<'tcx> { PredicateKind::ConstEquate(a, b) => { stable_mir::ty::PredicateKind::ConstEquate(a.stable(tables), b.stable(tables)) } + PredicateKind::Uninhabited(..) => todo!(), PredicateKind::Ambiguous => stable_mir::ty::PredicateKind::Ambiguous, PredicateKind::AliasRelate(a, b, alias_relation_direction) => { stable_mir::ty::PredicateKind::AliasRelate( diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index 066129d8e4731..20db8fdbfd81b 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -495,6 +495,9 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { param_env, predicate: (lhs, rhs, direction), }), + ty::PredicateKind::Uninhabited(ty, module) => { + self.compute_uninhabited_goal(Goal { param_env, predicate: (ty, module) }) + } ty::PredicateKind::Ambiguous => { self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index f1d3091225c0f..de7333cef5939 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -136,6 +136,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { ty::PredicateKind::Clause(_) | ty::PredicateKind::ObjectSafe(_) | ty::PredicateKind::ClosureKind(_, _, _) + | ty::PredicateKind::Uninhabited(_, _) | ty::PredicateKind::Ambiguous => { FulfillmentErrorCode::CodeSelectionError( SelectionError::Unimplemented, diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 77a3b5e128457..e2cc8a6c6e0c6 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -221,6 +221,103 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { self.eq(goal.param_env, ct.ty(), ty)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } + + #[instrument(level = "debug", skip(self))] + fn compute_uninhabited_goal( + &mut self, + Goal { param_env, predicate: (ty, module) }: Goal<'tcx, (Ty<'tcx>, Option)>, + ) -> QueryResult<'tcx> { + let tcx = self.tcx(); + let Some(ty) = self.try_normalize_ty(param_env, ty)? else { + return self.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW); + }; + match ty.kind() { + ty::Never => self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes), + ty::Tuple(tys) => { + self.compute_uninhabited_goal_fields(param_env, module, tys.into_iter()) + } + ty::Adt(adt, args) => { + // For now, unions are always considered inhabited + if adt.is_union() { + return Err(NoSolution); + } + + // Non-exhaustive ADTs from other crates are always considered inhabited + if adt.is_variant_list_non_exhaustive() && !adt.did().is_local() { + return Err(NoSolution); + } + + // The ADT is uninhabited if all its variants are uninhabited. + for variant in adt.variants() { + if variant.is_field_list_non_exhaustive() && !variant.def_id.is_local() { + // Non-exhaustive variants from other crates are always considered inhabited. + return Err(NoSolution); + } + // This variant is uninhabited is any of its fields is uninhabited. + let field_tys = variant.fields.iter().filter_map(|field| { + let ty = tcx.type_of(field.did).instantiate(tcx, args); + match (field.vis, module) { + // The field is public or we do not care for visibility. + (ty::Visibility::Public, _) | (_, None) => Ok(ty), + (ty::Visibility::Restricted(from), Some(module)) => { + if tcx.is_descendant_of(module, from) { + Ok(ty) + } else { + // From `module`, this field may be privately uninhabited. + // Do not consider it as uninhabited. + Err(NoSolution) + } + } + } + .ok() + }); + self.compute_uninhabited_goal_fields(param_env, module, field_tys)?; + } + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + ty::Array(ty, len) => match len.try_eval_target_usize(tcx, param_env) { + None | Some(0) => Err(NoSolution), + Some(1..) => { + self.add_goal(Goal::new( + tcx, + param_env, + ty::PredicateKind::Uninhabited(*ty, module), + )); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + }, + ty::Alias(ty::Opaque, _) => Err(NoSolution), + // FIXME + ty::Alias(ty::Projection | ty::Inherent, _) => Err(NoSolution), + // use a query for more complex cases + // references and other types are inhabited + _ => Err(NoSolution), + } + } + + #[instrument(level = "debug", skip(self, components))] + fn compute_uninhabited_goal_fields( + &mut self, + param_env: ty::ParamEnv<'tcx>, + module: Option, + components: impl Iterator>, + ) -> QueryResult<'tcx> { + let responses: Vec<_> = components + .filter_map(|ty| { + self.probe_misc_candidate("uninhabitedness candidate") + .enter(|ecx| { + ecx.add_goal(Goal::new( + ecx.tcx(), + param_env, + ty::PredicateKind::Uninhabited(ty, module), + )); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + .ok() + }) + .collect(); + self.try_merge_responses(&responses).ok_or(NoSolution) + } } impl<'tcx> EvalCtxt<'_, 'tcx> { diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 8096d7969f397..d79b340ca5115 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -826,6 +826,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { | ty::PredicateKind::Subtype(..) // FIXME(generic_const_exprs): you can absolutely add this as a where clauses | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) + | ty::PredicateKind::Uninhabited(_, _) | ty::PredicateKind::Coerce(..) => {} ty::PredicateKind::Ambiguous => return false, }; diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 2a586f810d629..761ccff308672 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -1124,6 +1124,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); diag } + + ty::PredicateKind::Uninhabited(..) => span_bug!(span, + "Uninhabited predicate should never be the predicate cause of a SelectionError" + ), } } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index da357dac415fc..957a8a1fd62f6 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -6,11 +6,13 @@ use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::traits::ProjectionCacheKey; use rustc_infer::traits::{PolyTraitObligation, SelectionError, TraitEngine}; use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::GenericArgsRef; -use rustc_middle::ty::{self, Binder, Const, TypeVisitableExt}; +use rustc_middle::ty::{self, Binder, Const, Ty, TypeVisitableExt}; +use rustc_span::def_id::DefId; use std::marker::PhantomData; use super::const_evaluatable; @@ -354,6 +356,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { | ty::PredicateKind::Subtype(_) | ty::PredicateKind::Coerce(_) | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) + | ty::PredicateKind::Uninhabited(..) | ty::PredicateKind::ConstEquate(..) => { let pred = ty::Binder::dummy(infcx.instantiate_binder_with_placeholders(binder)); @@ -659,6 +662,16 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { )), } } + ty::PredicateKind::Uninhabited(ty, module) => { + match self.process_uninhabited_obligation(obligation, ty, module) { + Ok(Ok(nested)) => ProcessResult::Changed(mk_pending(nested)), + Ok(Err(stalled)) => { + pending_obligation.stalled_on.extend(stalled); + ProcessResult::Unchanged + } + Err(NoSolution) => ProcessResult::Error(CodeSelectionError(Unimplemented)), + } + } }, } } @@ -683,6 +696,105 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { + #[instrument(level = "debug", skip(self, obligation), ret)] + fn process_uninhabited_obligation( + &mut self, + obligation: &PredicateObligation<'tcx>, + ty: Ty<'tcx>, + module: Option, + ) -> Result>, Vec>>, NoSolution> + { + let infcx = self.selcx.infcx; + let tcx = infcx.tcx; + if let Some(ty) = TyOrConstInferVar::maybe_from_ty(ty) { + return Ok(Err(vec![ty])); + } + match ty.kind() { + ty::Never => Ok(Ok(vec![])), + ty::Tuple(tys) => { + self.process_uninhabited_obligation_fields(obligation, module, tys.into_iter()) + } + ty::Adt(adt, args) => { + // For now, unions are always considered inhabited + if adt.is_union() { + return Err(NoSolution); + } + + // Non-exhaustive ADTs from other crates are always considered inhabited + if adt.is_variant_list_non_exhaustive() && !adt.did().is_local() { + return Err(NoSolution); + } + + // The ADT is uninhabited if all its variants are uninhabited. + let mut new_obligations = vec![]; + for variant in adt.variants() { + if variant.is_field_list_non_exhaustive() && !variant.def_id.is_local() { + // Non-exhaustive variants from other crates are always considered inhabited. + return Err(NoSolution); + } + // This variant is uninhabited is any of its fields is uninhabited. + let field_tys = variant.fields.iter().filter_map(|field| { + let ty = tcx.type_of(field.did).instantiate(tcx, args); + match (field.vis, module) { + // The field is public or we do not care for visibility. + (ty::Visibility::Public, _) | (_, None) => Some(ty), + (ty::Visibility::Restricted(from), Some(module)) => { + if tcx.is_descendant_of(module, from) { + Some(ty) + } else { + // From `module`, this field may be privately uninhabited. + // Do not consider it as uninhabited. + None + } + } + } + }); + match self.process_uninhabited_obligation_fields(obligation, module, field_tys)? { + Ok(nested) => new_obligations.extend(nested), + Err(stalled) => return Ok(Err(stalled)), + } + } + Ok(Ok(new_obligations)) + } + ty::Array(ty, len) => match len.try_eval_target_usize(tcx, obligation.param_env) { + None | Some(0) => Err(NoSolution), + Some(1..) => { + Ok(Ok(vec![ + obligation.with(tcx, ty::PredicateKind::Uninhabited(*ty, module)), + ])) + } + }, + ty::Alias(ty::Opaque, _) | + // FIXME + ty::Alias(ty::Projection | ty::Inherent, _) | + // use a query for more complex cases + // references and other types are inhabited + _ => Err(NoSolution), + } + } + + #[instrument(level = "debug", skip(self, obligation, tys), ret)] + fn process_uninhabited_obligation_fields( + &mut self, + obligation: &PredicateObligation<'tcx>, + module: Option, + tys: impl Iterator>, + ) -> Result>, Vec>>, NoSolution> + { + let infcx = self.selcx.infcx; + let mut stalled_on = vec![]; + for ty in tys { + let response = + infcx.probe(|_| self.process_uninhabited_obligation(obligation, ty, module)); + match response { + Ok(Ok(nested)) => return Ok(Ok(nested)), + Ok(Err(stalled)) => stalled_on.extend(stalled), + Err(NoSolution) => {} + } + } + if stalled_on.is_empty() { Err(NoSolution) } else { Ok(Err(stalled_on)) } + } + #[instrument(level = "debug", skip(self, obligation, stalled_on))] fn process_trait_obligation( &mut self, diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 956f8e047d705..677cc415bddc4 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -141,7 +141,7 @@ pub fn type_known_to_meet_bound_modulo_regions<'tcx>( /// Ping me on zulip if you want to use this method and need help with finding /// an appropriate replacement. #[instrument(level = "debug", skip(infcx, param_env, pred), ret)] -fn pred_known_to_hold_modulo_regions<'tcx>( +pub fn pred_known_to_hold_modulo_regions<'tcx>( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, pred: impl ToPredicate<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index e415d70479e5d..1ada73035aa5f 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -136,6 +136,7 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous | ty::PredicateKind::AliasRelate(..) + | ty::PredicateKind::Uninhabited(..) => {} // We need to search through *all* WellFormed predicates diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index bc9ba85fc9f1b..9d40db9a60977 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1038,10 +1038,117 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Err(_) => Ok(EvaluatedToErr), } } + ty::PredicateKind::Uninhabited(ty, module) => { + self.evaluate_uninhabited_predicate(previous_stack, &obligation, ty, module) + } } }) } + #[instrument(level = "debug", skip(self, previous_stack, obligation), ret)] + fn evaluate_uninhabited_predicate( + &mut self, + previous_stack: TraitObligationStackList<'_, 'tcx>, + obligation: &PredicateObligation<'tcx>, + ty: Ty<'tcx>, + module: Option, + ) -> Result { + let infcx = self.infcx; + let tcx = infcx.tcx; + match ty.kind() { + ty::Never => Ok(EvaluatedToOk), + ty::Tuple(tys) => { + self.evaluate_uninhabited_predicate_fields(previous_stack, obligation, module, tys.into_iter()) + } + ty::Adt(adt, args) => { + // For now, unions are always considered inhabited + if adt.is_union() { + return Ok(EvaluatedToErr); + } + + // Non-exhaustive ADTs from other crates are always considered inhabited + if adt.is_variant_list_non_exhaustive() && !adt.did().is_local() { + return Ok(EvaluatedToErr); + } + + // The ADT is uninhabited if all its variants are uninhabited. + for variant in adt.variants() { + if variant.is_field_list_non_exhaustive() && !variant.def_id.is_local() { + // Non-exhaustive variants from other crates are always considered inhabited. + return Ok(EvaluatedToErr); + } + // This variant is uninhabited is any of its fields is uninhabited. + let field_tys = variant.fields.iter().filter_map(|field| { + let ty = tcx.type_of(field.did).instantiate(tcx, args); + match (field.vis, module) { + // The field is public or we do not care for visibility. + (ty::Visibility::Public, _) | (_, None) => Some(ty), + (ty::Visibility::Restricted(from), Some(module)) => { + if tcx.is_descendant_of(module, from) { + Some(ty) + } else { + // From `module`, this field may be privately uninhabited. + // Do not consider it as uninhabited. + None + } + } + } + }); + match self.evaluate_uninhabited_predicate_fields(previous_stack, obligation, module, field_tys)? { + EvaluatedToOk | EvaluatedToOkModuloRegions => {} + EvaluatedToOkModuloOpaqueTypes | EvaluatedToAmbig => return Ok(EvaluatedToAmbig), + EvaluatedToUnknown => return Ok(EvaluatedToUnknown), + EvaluatedToRecur => return Ok(EvaluatedToRecur), + EvaluatedToErr => return Ok(EvaluatedToErr), + } + } + Ok(EvaluatedToOk) + } + ty::Array(ty, len) => match len.try_eval_target_usize(tcx, obligation.param_env) { + None | Some(0) => Ok(EvaluatedToErr), + Some(1..) => { + self.evaluate_predicates_recursively(previous_stack, [ + obligation.with(tcx, ty::PredicateKind::Uninhabited(*ty, module)), + ]) + } + }, + ty::Alias(ty::Opaque, _) | + // FIXME + ty::Alias(ty::Projection | ty::Inherent, _) | + // use a query for more complex cases + // references and other types are inhabited + _ => Ok(EvaluatedToErr), + } + } + + #[instrument(level = "debug", skip(self, previous_stack, obligation, tys), ret)] + fn evaluate_uninhabited_predicate_fields( + &mut self, + previous_stack: TraitObligationStackList<'_, 'tcx>, + obligation: &PredicateObligation<'tcx>, + module: Option, + tys: impl Iterator>, + ) -> Result { + let infcx = self.infcx; + let tcx = infcx.tcx; + for ty in tys { + let response = infcx.probe(|_| { + self.evaluate_predicates_recursively( + previous_stack, + [obligation.with(tcx, ty::PredicateKind::Uninhabited(ty, module))], + ) + })?; + match response { + EvaluatedToOk | EvaluatedToOkModuloRegions => return Ok(EvaluatedToOk), + EvaluatedToOkModuloOpaqueTypes | EvaluatedToAmbig => return Ok(EvaluatedToAmbig), + EvaluatedToUnknown => return Ok(EvaluatedToUnknown), + EvaluatedToRecur => return Ok(EvaluatedToRecur), + EvaluatedToErr => {} + } + } + Ok(EvaluatedToErr) + } + #[instrument(skip(self, previous_stack), level = "debug", ret)] fn evaluate_trait_predicate_recursively<'o>( &mut self, diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index b04008d9ee561..b14c40edc78dc 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -184,6 +184,7 @@ pub fn predicate_obligations<'tcx>( | ty::PredicateKind::Coerce(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous + | ty::PredicateKind::Uninhabited(..) | ty::PredicateKind::AliasRelate(..) => { bug!("We should only wf check where clauses, unexpected predicate: {predicate:?}") } diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs index 2563e3ed1a34f..83f1ee46d414f 100644 --- a/compiler/rustc_traits/src/normalize_erasing_regions.rs +++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs @@ -69,6 +69,7 @@ fn not_outlives_predicate(p: ty::Predicate<'_>) -> bool { | ty::PredicateKind::Coerce(..) | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::Uninhabited(..) | ty::PredicateKind::Ambiguous => true, } } diff --git a/compiler/rustc_ty_utils/src/common_traits.rs b/compiler/rustc_ty_utils/src/common_traits.rs index 51b908881eb49..250ccab86677d 100644 --- a/compiler/rustc_ty_utils/src/common_traits.rs +++ b/compiler/rustc_ty_utils/src/common_traits.rs @@ -4,6 +4,7 @@ use rustc_hir::lang_items::LangItem; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::query::Providers; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::def_id::DefId; use rustc_trait_selection::traits; fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { @@ -33,6 +34,26 @@ fn is_item_raw<'tcx>( traits::type_known_to_meet_bound_modulo_regions(&infcx, param_env, ty, trait_def_id) } +fn type_is_uninhabited_from_raw<'tcx>( + tcx: TyCtxt<'tcx>, + query: ty::ParamEnvAnd<'tcx, (Ty<'tcx>, Option)>, +) -> bool { + let (param_env, (ty, module)) = query.into_parts(); + let infcx = tcx.infer_ctxt().build(); + traits::pred_known_to_hold_modulo_regions( + &infcx, + param_env, + ty::PredicateKind::Uninhabited(ty, module), + ) +} + pub(crate) fn provide(providers: &mut Providers) { - *providers = Providers { is_copy_raw, is_sized_raw, is_freeze_raw, is_unpin_raw, ..*providers }; + *providers = Providers { + is_copy_raw, + is_sized_raw, + is_freeze_raw, + is_unpin_raw, + type_is_uninhabited_from_raw, + ..*providers + }; } diff --git a/compiler/rustc_ty_utils/src/layout_sanity_check.rs b/compiler/rustc_ty_utils/src/layout_sanity_check.rs index 8633334381ada..4570cc270659e 100644 --- a/compiler/rustc_ty_utils/src/layout_sanity_check.rs +++ b/compiler/rustc_ty_utils/src/layout_sanity_check.rs @@ -13,7 +13,10 @@ pub(super) fn sanity_check_layout<'tcx>( ) { // Type-level uninhabitedness should always imply ABI uninhabitedness. if layout.ty.is_privately_uninhabited(cx.tcx, cx.param_env) { - assert!(layout.abi.is_uninhabited()); + assert!( + layout.abi.is_uninhabited(), + "{layout:?} has inhabited layout but solver says uninhabited." + ); } if layout.size.bytes() % layout.align.abi.bytes() != 0 { diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index 147b600f7ba52..cec3e4b881801 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -22,7 +22,7 @@ extern crate tracing; use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage}; use rustc_fluent_macro::fluent_messages; -use rustc_middle::query::Providers; +use rustc_middle::util::Providers; mod abi; mod assoc; From a8605ec2b3f30c58d3203ec030f5396b5c5ba16a Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 27 Sep 2023 20:44:15 +0000 Subject: [PATCH 2/3] Cache evaluations. --- compiler/rustc_middle/src/traits/select.rs | 2 +- .../src/traits/select/mod.rs | 43 +++++++++++++++++-- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 90bc5dd8f6962..d8dcbd63b1cb5 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -23,7 +23,7 @@ pub type SelectionCache<'tcx> = Cache< pub type EvaluationCache<'tcx> = Cache< // See above: this cache does not use `ParamEnvAnd` in its keys due to sometimes incorrectly // caching with the wrong `ParamEnv`. - (ty::ParamEnv<'tcx>, ty::PolyTraitPredicate<'tcx>), + (ty::ParamEnv<'tcx>, ty::Predicate<'tcx>), EvaluationResult, >; diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 9d40db9a60977..18a0fb7bced26 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1039,7 +1039,39 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } ty::PredicateKind::Uninhabited(ty, module) => { - self.evaluate_uninhabited_predicate(previous_stack, &obligation, ty, module) + let tcx = self.tcx(); + let dfn = previous_stack.cache.next_dfn(); + let predicate = obligation.predicate.fold_with(&mut self.freshener); + let param_env = obligation.param_env; + + if let Some(result) = self.check_evaluation_cache(param_env, predicate) { + debug!("CACHE HIT"); + return Ok(result); + } + + let (result, dep_node) = self.in_task(|this| { + this.evaluate_uninhabited_predicate( + previous_stack, + &obligation.with(tcx, predicate), + ty, + module, + ) + }); + let result = result?; + + if !result.must_apply_modulo_regions() { + previous_stack.cache.on_failure(dfn); + } + + let reached_depth = + previous_stack.head.map_or(0, |stack| stack.reached_depth.get()); + if reached_depth >= previous_stack.depth() { + debug!("CACHE MISS"); + self.insert_evaluation_cache(param_env, predicate, dep_node, result); + previous_stack.cache.on_completion(dfn); + } + + Ok(result) } } }) @@ -1174,7 +1206,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // If a trait predicate is in the (local or global) evaluation cache, // then we know it holds without cycles. - if let Some(result) = self.check_evaluation_cache(param_env, fresh_trait_pred) { + if let Some(result) = + self.check_evaluation_cache(param_env, fresh_trait_pred.to_predicate(self.tcx())) + { debug!("CACHE HIT"); return Ok(result); } @@ -1248,6 +1282,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let reached_depth = stack.reached_depth.get(); if reached_depth >= stack.depth { debug!("CACHE MISS"); + let fresh_trait_pred = fresh_trait_pred.to_predicate(self.tcx()); self.insert_evaluation_cache(param_env, fresh_trait_pred, dep_node, result); stack.cache().on_completion(stack.dfn); } else { @@ -1442,7 +1477,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn check_evaluation_cache( &self, param_env: ty::ParamEnv<'tcx>, - trait_pred: ty::PolyTraitPredicate<'tcx>, + trait_pred: ty::Predicate<'tcx>, ) -> Option { // Neither the global nor local cache is aware of intercrate // mode, so don't do any caching. In particular, we might @@ -1464,7 +1499,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn insert_evaluation_cache( &mut self, param_env: ty::ParamEnv<'tcx>, - trait_pred: ty::PolyTraitPredicate<'tcx>, + trait_pred: ty::Predicate<'tcx>, dep_node: DepNodeIndex, result: EvaluationResult, ) { From a91b48083904bff2faee426673854470e47c1d48 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Thu, 28 Sep 2023 20:47:41 +0000 Subject: [PATCH 3/3] Remove obsolete comment. --- compiler/rustc_middle/src/ty/inhabitedness.rs | 45 ------------------- 1 file changed, 45 deletions(-) diff --git a/compiler/rustc_middle/src/ty/inhabitedness.rs b/compiler/rustc_middle/src/ty/inhabitedness.rs index eff7891e5d597..f6b545f84e21f 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness.rs @@ -1,48 +1,3 @@ -//! 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 -//! #![feature(never_type)] -//! mod a { -//! pub mod b { -//! pub struct SecretlyUninhabited { -//! _priv: !, -//! } -//! } -//! } -//! -//! mod c { -//! enum 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`. 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 -//! `Foo`. -//! -//! # Example -//! ```ignore(illustrative) -//! let foo_result: Result = ... ; -//! let Ok(t) = foo_result; -//! ``` -//! This code should only compile in modules where the uninhabitedness of `Foo` -//! is visible. - use crate::ty::context::TyCtxt; use crate::ty::{self, DefId, Ty, VariantDef, Visibility};