Skip to content

Commit

Permalink
restructure the public inhabitedness APIs and remove the cache
Browse files Browse the repository at this point in the history
The cache was broken anyhow and this computation doesn't look that
expensive. These public accessors could potentially become queries,
but we'd have to add some more complex logic around lift. I'd prefer
to have some test cases to profile with before doing that.

Fixes #44402.
  • Loading branch information
nikomatsakis committed Oct 16, 2017
1 parent a5673de commit 5c26509
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 41 deletions.
4 changes: 0 additions & 4 deletions src/librustc/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ use ty::RegionKind;
use ty::{TyVar, TyVid, IntVar, IntVid, FloatVar, FloatVid};
use ty::TypeVariants::*;
use ty::layout::{Layout, TargetDataLayout};
use ty::inhabitedness::DefIdForest;
use ty::maps;
use ty::steal::Steal;
use ty::BindingMode;
Expand Down Expand Up @@ -896,8 +895,6 @@ pub struct GlobalCtxt<'tcx> {
// FIXME dep tracking -- should be harmless enough
pub normalized_cache: RefCell<FxHashMap<Ty<'tcx>, Ty<'tcx>>>,

pub inhabitedness_cache: RefCell<FxHashMap<Ty<'tcx>, DefIdForest>>,

/// Caches the results of trait selection. This cache is used
/// for things that do not have to do with the parameters in scope.
pub selection_cache: traits::SelectionCache<'tcx>,
Expand Down Expand Up @@ -1179,7 +1176,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
mir_passes,
rcache: RefCell::new(FxHashMap()),
normalized_cache: RefCell::new(FxHashMap()),
inhabitedness_cache: RefCell::new(FxHashMap()),
selection_cache: traits::SelectionCache::new(),
evaluation_cache: traits::EvaluationCache::new(),
rvalue_promotable_to_static: RefCell::new(NodeMap()),
Expand Down
51 changes: 18 additions & 33 deletions src/librustc/ty/inhabitedness/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,16 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
/// 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>) -> bool {
let forest = ty.uninhabited_from(&mut FxHashMap(), self);

// To check whether this type is uninhabited at all (not just from the
// given node) you could check whether the forest is empty.
// ```
// forest.is_empty()
// ```
forest.contains(self, module)
self.ty_inhabitedness_forest(ty).contains(self, module)
}

fn ty_inhabitedness_forest(self, ty: Ty<'tcx>) -> DefIdForest {
ty.uninhabited_from(&mut FxHashMap(), self)
}

pub fn is_enum_variant_uninhabited_from(self,
Expand All @@ -116,17 +118,25 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
substs: &'tcx Substs<'tcx>)
-> bool
{
let adt_kind = AdtKind::Enum;
variant.uninhabited_from(&mut FxHashMap(), self, substs, adt_kind).contains(self, module)
self.variant_inhabitedness_forest(variant, substs).contains(self, module)
}

pub fn is_variant_uninhabited_from_all_modules(self,
variant: &'tcx VariantDef,
substs: &'tcx Substs<'tcx>,
adt_kind: AdtKind)
substs: &'tcx Substs<'tcx>)
-> bool
{
!variant.uninhabited_from(&mut FxHashMap(), self, substs, adt_kind).is_empty()
!self.variant_inhabitedness_forest(variant, substs).is_empty()
}

fn variant_inhabitedness_forest(self, variant: &'tcx VariantDef, substs: &'tcx Substs<'tcx>)
-> DefIdForest {
// Determine the ADT kind:
let adt_def_id = self.adt_def_id_of_variant(variant);
let adt_kind = self.adt_def(adt_def_id).adt_kind();

// Compute inhabitedness forest:
variant.uninhabited_from(&mut FxHashMap(), self, substs, adt_kind)
}
}

Expand Down Expand Up @@ -210,31 +220,6 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
&self,
visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest
{
match tcx.lift_to_global(&self) {
Some(global_ty) => {
{
let cache = tcx.inhabitedness_cache.borrow();
if let Some(forest) = cache.get(&global_ty) {
return forest.clone();
}
}
let forest = global_ty.uninhabited_from_inner(visited, tcx);
let mut cache = tcx.inhabitedness_cache.borrow_mut();
cache.insert(global_ty, forest.clone());
forest
},
None => {
let forest = self.uninhabited_from_inner(visited, tcx);
forest
},
}
}

fn uninhabited_from_inner(
&self,
visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest
{
match self.sty {
TyAdt(def, substs) => {
Expand Down
15 changes: 15 additions & 0 deletions src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub use self::fold::TypeFoldable;
use hir::{map as hir_map, FreevarMap, TraitMap};
use hir::def::{Def, CtorKind, ExportMap};
use hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE};
use hir::map::DefPathData;
use ich::StableHashingContext;
use middle::const_val::ConstVal;
use middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem, FnOnceTraitLangItem};
Expand Down Expand Up @@ -2232,6 +2233,20 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
}
}

/// Given a `VariantDef`, returns the def-id of the `AdtDef` of which it is a part.
pub fn adt_def_id_of_variant(self, variant_def: &'tcx VariantDef) -> DefId {
let def_key = self.def_key(variant_def.did);
match def_key.disambiguated_data.data {
// for enum variants and tuple structs, the def-id of the ADT itself
// is the *parent* of the variant
DefPathData::EnumVariant(..) | DefPathData::StructCtor =>
DefId { krate: variant_def.did.krate, index: def_key.parent.unwrap() },

// otherwise, for structs and unions, they share a def-id
_ => variant_def.did,
}
}

pub fn item_name(self, id: DefId) -> InternedString {
if let Some(id) = self.hir.as_local_node_id(id) {
self.hir.name(id).as_str()
Expand Down
5 changes: 1 addition & 4 deletions src/librustc_mir/build/matches/simplify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
if self.hir.tcx().sess.features.borrow().never_type {
let irrefutable = adt_def.variants.iter().enumerate().all(|(i, v)| {
i == variant_index || {
let adt_kind = adt_def.adt_kind();
self.hir.tcx().is_variant_uninhabited_from_all_modules(v,
substs,
adt_kind)
self.hir.tcx().is_variant_uninhabited_from_all_modules(v, substs)
}
});
if irrefutable {
Expand Down
36 changes: 36 additions & 0 deletions src/test/run-pass/issue-44402.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(never_type)]

// Regression test for inhabitedness check. The old
// cache used to cause us to incorrectly decide
// that `test_b` was invalid.

struct Foo {
field1: !,
field2: Option<&'static Bar>,
}

struct Bar {
field1: &'static Foo
}

fn test_a() {
let x: Option<Foo> = None;
match x { None => () }
}

fn test_b() {
let x: Option<Bar> = None;
match x { None => () }
}

fn main() { }

0 comments on commit 5c26509

Please sign in to comment.