Skip to content
Open
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
2 changes: 1 addition & 1 deletion compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2523,7 +2523,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
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)
.inhabited_predicate(cx.tcx)
.instantiate(cx.tcx, args)
.apply_any_module(cx.tcx, cx.typing_env())
{
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2170,7 +2170,7 @@ rustc_queries! {
feedable
}

query inhabited_predicate_adt(key: DefId) -> ty::inhabitedness::InhabitedPredicate<'tcx> {
query inhabited_predicate_for_def(key: DefId) -> ty::inhabitedness::InhabitedPredicate<'tcx> {
desc { "computing the uninhabited predicate of `{:?}`", key }
}

Expand Down
95 changes: 56 additions & 39 deletions compiler/rustc_middle/src/ty/inhabitedness/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,21 @@
use std::assert_matches;

use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::DefKind;
use rustc_span::def_id::LocalModId;
use rustc_type_ir::TyKind::*;
use tracing::instrument;

use crate::query::Providers;
use crate::ty::{self, DefId, Ty, TyCtxt, TypeVisitableExt, VariantDef, Visibility};
use crate::ty::{self, AdtDef, DefId, Ty, TyCtxt, TypeVisitableExt, VariantDef, Visibility};

pub mod inhabited_predicate;

pub use inhabited_predicate::InhabitedPredicate;

pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers {
inhabited_predicate_adt,
inhabited_predicate_for_def,
inhabited_predicate_type,
is_opsem_inhabited_raw,
..*providers
Expand All @@ -68,48 +69,64 @@ pub(crate) fn provide(providers: &mut 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() {
tcx.ensure_ok().check_representability(def_id);
fn inhabited_predicate_for_def(tcx: TyCtxt<'_>, def_id: DefId) -> InhabitedPredicate<'_> {
match tcx.def_kind(def_id) {
DefKind::Enum => {
if let Some(def_id) = def_id.as_local() {
tcx.ensure_ok().check_representability(def_id);
}
let adt = tcx.adt_def(def_id);
InhabitedPredicate::any(tcx, adt.variants().iter().map(|v| v.inhabited_predicate(tcx)))
}
DefKind::Struct => {
if let Some(def_id) = def_id.as_local() {
tcx.ensure_ok().check_representability(def_id);
}
let adt = tcx.adt_def(def_id);
variant_inhabited_predicate(tcx, adt, adt.non_enum_variant())
}
DefKind::Variant => {

@Nadrieril Nadrieril Jul 19, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels unexpected to me that a Variant would be an admissible input for a function called something_adt; this deserves an extra comment imo

View changes since the review

@Nadrieril Nadrieril Jul 19, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd prefer to rename the query to inhabited_predicate_for_def and have it return an EarlyBinder<InhabitedPredicate>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did the rename but still looking into EarlyBinder. Maybe that can be a separate PR?

@Nadrieril Nadrieril Jul 19, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just a wrapper that says "btw, the value you get from this query needs to be passed generic arguments", it's what we use for the output of most queries that get type-related information about a DefId, just as a reminder

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also happy to leave that, it's not crucial

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since both are potentially performance-affecting, let's move to TypeFolder later?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay how bout we merge this as is and I'll add EarlyBinder with or without TypeFolder separately.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes a lot of sense yes!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh it seems I can't just use EarlyBinder by itself because EarlyBinder::bind requires TypeFoldable.

Also I just found out that TypeFoldable requires TypeVisitable, so I don't think that's practical.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh! well, let's just leave things like this then

let adt = tcx.adt_def(tcx.parent(def_id));
let variant = adt.variant_with_id(def_id);
variant_inhabited_predicate(tcx, adt, variant)
}
def_kind => bug!("unexpected DefKind: {def_kind:?}"),
}

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());
InhabitedPredicate::all(
tcx,
self.fields.iter().map(|field| {
let pred = tcx
.type_of(field.did)
.instantiate_identity()
.skip_norm_wip()
.inhabited_predicate(tcx);
if adt.is_enum() {
return pred;
}
match field.vis {
Visibility::Public => pred,
Visibility::Restricted(from) => {
InhabitedPredicate::NotInModule(from).or(tcx, pred)
}
}
}),
)
impl VariantDef {
pub fn inhabited_predicate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> InhabitedPredicate<'tcx> {
if self.fields.is_empty() {
return InhabitedPredicate::True;
}
tcx.inhabited_predicate_for_def(self.def_id)
}
}

fn variant_inhabited_predicate<'tcx>(
tcx: TyCtxt<'tcx>,
adt: AdtDef<'tcx>,
variant: &VariantDef,
) -> InhabitedPredicate<'tcx> {
InhabitedPredicate::all(
tcx,
variant.fields.iter().map(|field| {
let pred = tcx
.type_of(field.did)
.instantiate_identity()
.skip_norm_wip()
.inhabited_predicate(tcx);
if adt.is_enum() {
return pred;
}
match field.vis {
Visibility::Public => pred,
Visibility::Restricted(from) => InhabitedPredicate::NotInModule(from).or(tcx, pred),
}
}),
)
}

impl<'tcx> Ty<'tcx> {
#[instrument(level = "debug", skip(tcx), ret)]
pub fn inhabited_predicate(self, tcx: TyCtxt<'tcx>) -> InhabitedPredicate<'tcx> {
Expand Down Expand Up @@ -231,7 +248,7 @@ impl<'tcx> Ty<'tcx> {
/// 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),
Adt(adt, args) => tcx.inhabited_predicate_for_def(adt.did()).instantiate(tcx, args),

Tuple(tys) => {
InhabitedPredicate::all(tcx, tys.iter().map(|ty| ty.inhabited_predicate(tcx)))
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 @@ -734,7 +734,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
{
let variant_inhabited = adt
.variant(*variant_index)
.inhabited_predicate(self.tcx, *adt)
.inhabited_predicate(self.tcx)
.instantiate(self.tcx, args);
variant_inhabited.apply(self.tcx, cx.typing_env, cx.module)
&& !variant_inhabited.apply_ignore_module(self.tcx, cx.typing_env)
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_pattern_analysis/src/rustc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
let variant_def_id = def.variant(idx).def_id;
// Visibly uninhabited variants.
let is_inhabited = v
.inhabited_predicate(cx.tcx, *def)
.inhabited_predicate(cx.tcx)
.instantiate(cx.tcx, args)
.apply_revealing_opaque(cx.tcx, cx.typing_env, cx.module, &|key| {
cx.reveal_opaque_key(key)
Expand Down
Loading