Skip to content

Commit

Permalink
rustc_typeck: rework coherence to be almost completely on-demand.
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyb committed Feb 25, 2017
1 parent 9890e04 commit c832e6f
Show file tree
Hide file tree
Showing 53 changed files with 824 additions and 670 deletions.
2 changes: 2 additions & 0 deletions src/librustc/dep_graph/dep_node.rs
Expand Up @@ -70,6 +70,7 @@ pub enum DepNode<D: Clone + Debug> {
Resolve,
EntryPoint,
CheckEntryFn,
CoherenceCheckTrait(D),
CoherenceCheckImpl(D),
CoherenceOverlapCheck(D),
CoherenceOverlapCheckSpecial(D),
Expand Down Expand Up @@ -241,6 +242,7 @@ impl<D: Clone + Debug> DepNode<D> {
MetaData(ref d) => op(d).map(MetaData),
CollectItem(ref d) => op(d).map(CollectItem),
CollectItemSig(ref d) => op(d).map(CollectItemSig),
CoherenceCheckTrait(ref d) => op(d).map(CoherenceCheckTrait),
CoherenceCheckImpl(ref d) => op(d).map(CoherenceCheckImpl),
CoherenceOverlapCheck(ref d) => op(d).map(CoherenceOverlapCheck),
CoherenceOverlapCheckSpecial(ref d) => op(d).map(CoherenceOverlapCheckSpecial),
Expand Down
22 changes: 21 additions & 1 deletion src/librustc/hir/lowering.rs
Expand Up @@ -80,6 +80,9 @@ pub struct LoweringContext<'a> {
impl_items: BTreeMap<hir::ImplItemId, hir::ImplItem>,
bodies: FxHashMap<hir::BodyId, hir::Body>,

trait_impls: BTreeMap<DefId, Vec<NodeId>>,
trait_default_impl: BTreeMap<DefId, NodeId>,

loop_scopes: Vec<NodeId>,
is_in_loop_condition: bool,

Expand Down Expand Up @@ -116,6 +119,8 @@ pub fn lower_crate(sess: &Session,
trait_items: BTreeMap::new(),
impl_items: BTreeMap::new(),
bodies: FxHashMap(),
trait_impls: BTreeMap::new(),
trait_default_impl: BTreeMap::new(),
loop_scopes: Vec::new(),
is_in_loop_condition: false,
type_def_lifetime_params: DefIdMap(),
Expand Down Expand Up @@ -201,6 +206,8 @@ impl<'a> LoweringContext<'a> {
trait_items: self.trait_items,
impl_items: self.impl_items,
bodies: self.bodies,
trait_impls: self.trait_impls,
trait_default_impl: self.trait_default_impl,
}
}

Expand Down Expand Up @@ -1089,14 +1096,27 @@ impl<'a> LoweringContext<'a> {
hir::ItemUnion(vdata, self.lower_generics(generics))
}
ItemKind::DefaultImpl(unsafety, ref trait_ref) => {
let trait_ref = self.lower_trait_ref(trait_ref);

if let Def::Trait(def_id) = trait_ref.path.def {
self.trait_default_impl.insert(def_id, id);
}

hir::ItemDefaultImpl(self.lower_unsafety(unsafety),
self.lower_trait_ref(trait_ref))
trait_ref)
}
ItemKind::Impl(unsafety, polarity, ref generics, ref ifce, ref ty, ref impl_items) => {
let new_impl_items = impl_items.iter()
.map(|item| self.lower_impl_item_ref(item))
.collect();
let ifce = ifce.as_ref().map(|trait_ref| self.lower_trait_ref(trait_ref));

if let Some(ref trait_ref) = ifce {
if let Def::Trait(def_id) = trait_ref.path.def {
self.trait_impls.entry(def_id).or_insert(vec![]).push(id);
}
}

hir::ItemImpl(self.lower_unsafety(unsafety),
self.lower_impl_polarity(polarity),
self.lower_generics(generics),
Expand Down
20 changes: 20 additions & 0 deletions src/librustc/hir/map/mod.rs
Expand Up @@ -461,6 +461,26 @@ impl<'hir> Map<'hir> {
}
}

pub fn trait_impls(&self, trait_did: DefId) -> &'hir [NodeId] {
self.dep_graph.read(DepNode::TraitImpls(trait_did));

// NB: intentionally bypass `self.forest.krate()` so that we
// do not trigger a read of the whole krate here
self.forest.krate.trait_impls.get(&trait_did).map_or(&[], |xs| &xs[..])
}

pub fn trait_default_impl(&self, trait_did: DefId) -> Option<NodeId> {
self.dep_graph.read(DepNode::TraitImpls(trait_did));

// NB: intentionally bypass `self.forest.krate()` so that we
// do not trigger a read of the whole krate here
self.forest.krate.trait_default_impl.get(&trait_did).cloned()
}

pub fn trait_is_auto(&self, trait_did: DefId) -> bool {
self.trait_default_impl(trait_did).is_some()
}

/// Get the attributes on the krate. This is preferable to
/// invoking `krate.attrs` because it registers a tighter
/// dep-graph access.
Expand Down
3 changes: 3 additions & 0 deletions src/librustc/hir/mod.rs
Expand Up @@ -410,6 +410,9 @@ pub struct Crate {
pub trait_items: BTreeMap<TraitItemId, TraitItem>,
pub impl_items: BTreeMap<ImplItemId, ImplItem>,
pub bodies: FxHashMap<BodyId, Body>,

pub trait_impls: BTreeMap<DefId, Vec<NodeId>>,
pub trait_default_impl: BTreeMap<DefId, NodeId>,
}

impl Crate {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/infer/mod.rs
Expand Up @@ -505,7 +505,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'gcx> {
evaluation_cache: traits::EvaluationCache::new(),
projection_cache: RefCell::new(traits::ProjectionCache::new()),
reported_trait_errors: RefCell::new(FxHashSet()),
projection_mode: Reveal::NotSpecializable,
projection_mode: Reveal::UserFacing,
tainted_by_errors_flag: Cell::new(false),
err_count_on_creation: self.sess.err_count(),
obligations_in_snapshot: Cell::new(false),
Expand Down
2 changes: 0 additions & 2 deletions src/librustc/middle/cstore.rs
Expand Up @@ -191,7 +191,6 @@ pub trait CrateStore {

// flags
fn is_const_fn(&self, did: DefId) -> bool;
fn is_defaulted_trait(&self, did: DefId) -> bool;
fn is_default_impl(&self, impl_did: DefId) -> bool;
fn is_foreign_item(&self, did: DefId) -> bool;
fn is_dllimport_foreign_item(&self, def: DefId) -> bool;
Expand Down Expand Up @@ -327,7 +326,6 @@ impl CrateStore for DummyCrateStore {

// flags
fn is_const_fn(&self, did: DefId) -> bool { bug!("is_const_fn") }
fn is_defaulted_trait(&self, did: DefId) -> bool { bug!("is_defaulted_trait") }
fn is_default_impl(&self, impl_did: DefId) -> bool { bug!("is_default_impl") }
fn is_foreign_item(&self, did: DefId) -> bool { bug!("is_foreign_item") }
fn is_dllimport_foreign_item(&self, id: DefId) -> bool { false }
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/traits/mod.rs
Expand Up @@ -473,7 +473,7 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,

let elaborated_env = unnormalized_env.with_caller_bounds(predicates);

tcx.infer_ctxt(elaborated_env, Reveal::NotSpecializable).enter(|infcx| {
tcx.infer_ctxt(elaborated_env, Reveal::UserFacing).enter(|infcx| {
let predicates = match fully_normalize(&infcx, cause,
&infcx.parameter_environment.caller_bounds) {
Ok(predicates) => predicates,
Expand Down
37 changes: 4 additions & 33 deletions src/librustc/traits/project.rs
Expand Up @@ -38,36 +38,6 @@ use util::common::FN_OUTPUT_NAME;
/// more or less conservative.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Reveal {
/// FIXME (#32205)
/// At coherence-checking time, we're still constructing the
/// specialization graph, and thus we only project
/// non-`default` associated types that are defined directly in
/// the applicable impl. (This behavior should be improved over
/// time, to allow for successful projections modulo cycles
/// between different impls).
///
/// Here's an example that will fail due to the restriction:
///
/// ```
/// trait Assoc {
/// type Output;
/// }
///
/// impl<T> Assoc for T {
/// type Output = bool;
/// }
///
/// impl Assoc for u8 {} // <- inherits the non-default type from above
///
/// trait Foo {}
/// impl Foo for u32 {}
/// impl Foo for <u8 as Assoc>::Output {} // <- this projection will fail
/// ```
///
/// The projection would succeed if `Output` had been defined
/// directly in the impl for `u8`.
ExactMatch,

/// At type-checking time, we refuse to project any associated
/// type that is marked `default`. Non-`default` ("final") types
/// are always projected. This is necessary in general for
Expand All @@ -90,7 +60,7 @@ pub enum Reveal {
/// fn main() {
/// let <() as Assoc>::Output = true;
/// }
NotSpecializable,
UserFacing,

/// At trans time, all monomorphic projections will succeed.
/// Also, `impl Trait` is normalized to the concrete type,
Expand Down Expand Up @@ -1347,8 +1317,9 @@ fn assoc_ty_def<'cx, 'gcx, 'tcx>(
-> Option<specialization_graph::NodeItem<ty::AssociatedItem>>
{
let trait_def_id = selcx.tcx().impl_trait_ref(impl_def_id).unwrap().def_id;
let trait_def = selcx.tcx().lookup_trait_def(trait_def_id);

if selcx.projection_mode() == Reveal::ExactMatch {
if !trait_def.is_complete(selcx.tcx()) {
let impl_node = specialization_graph::Node::Impl(impl_def_id);
for item in impl_node.items(selcx.tcx()) {
if item.kind == ty::AssociatedKind::Type && item.name == assoc_ty_name {
Expand All @@ -1360,7 +1331,7 @@ fn assoc_ty_def<'cx, 'gcx, 'tcx>(
}
None
} else {
selcx.tcx().lookup_trait_def(trait_def_id)
trait_def
.ancestors(impl_def_id)
.defs(selcx.tcx(), assoc_ty_name, ty::AssociatedKind::Type)
.next()
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/traits/specialize/mod.rs
Expand Up @@ -189,7 +189,7 @@ pub fn specializes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
.subst(tcx, &penv.free_substs);

// Create a infcx, taking the predicates of impl1 as assumptions:
let result = tcx.infer_ctxt(penv, Reveal::ExactMatch).enter(|infcx| {
let result = tcx.infer_ctxt(penv, Reveal::UserFacing).enter(|infcx| {
// Normalize the trait reference. The WF rules ought to ensure
// that this always succeeds.
let impl1_trait_ref =
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/traits/specialize/specialization_graph.rs
Expand Up @@ -108,7 +108,7 @@ impl<'a, 'gcx, 'tcx> Children {
let possible_sibling = *slot;

let tcx = tcx.global_tcx();
let (le, ge) = tcx.infer_ctxt((), Reveal::ExactMatch).enter(|infcx| {
let (le, ge) = tcx.infer_ctxt((), Reveal::UserFacing).enter(|infcx| {
let overlap = traits::overlapping_impls(&infcx,
possible_sibling,
impl_def_id);
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/ty/contents.rs
Expand Up @@ -219,7 +219,7 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
res = res - TC::OwnsDtor;
}

if def.has_dtor() {
if def.has_dtor(tcx) {
res = res | TC::OwnsDtor;
}

Expand Down
49 changes: 49 additions & 0 deletions src/librustc/ty/maps.rs
Expand Up @@ -24,6 +24,15 @@ trait Key {
fn default_span(&self, tcx: TyCtxt) -> Span;
}

impl Key for CrateNum {
fn map_crate(&self) -> CrateNum {
*self
}
fn default_span(&self, _: TyCtxt) -> Span {
DUMMY_SP
}
}

impl Key for DefId {
fn map_crate(&self) -> CrateNum {
self.krate
Expand All @@ -42,6 +51,15 @@ impl Key for (DefId, DefId) {
}
}

impl Key for (CrateNum, DefId) {
fn map_crate(&self) -> CrateNum {
self.0
}
fn default_span(&self, tcx: TyCtxt) -> Span {
self.1.default_span(tcx)
}
}

trait Value<'tcx>: Sized {
fn from_cycle_error<'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Self;
}
Expand Down Expand Up @@ -141,6 +159,19 @@ impl<'tcx> QueryDescription for queries::type_param_predicates<'tcx> {
}
}

impl<'tcx> QueryDescription for queries::coherent_trait<'tcx> {
fn describe(tcx: TyCtxt, (_, def_id): (CrateNum, DefId)) -> String {
format!("coherence checking all impls of trait `{}`",
tcx.item_path_str(def_id))
}
}

impl<'tcx> QueryDescription for queries::coherent_inherent_impls<'tcx> {
fn describe(_: TyCtxt, _: CrateNum) -> String {
format!("coherence checking all inherent impls")
}
}

macro_rules! define_maps {
(<$tcx:tt>
$($(#[$attr:meta])*
Expand Down Expand Up @@ -238,6 +269,12 @@ macro_rules! define_maps {
}

pub fn force(tcx: TyCtxt<'a, $tcx, 'lcx>, span: Span, key: $K) {
// FIXME(eddyb) Move away from using `DepTrackingMap`
// so we don't have to explicitly ignore a false edge:
// we can't observe a value dependency, only side-effects,
// through `force`, and once everything has been updated,
// perhaps only diagnostics, if those, will remain.
let _ignore = tcx.dep_graph.in_ignore();
match Self::try_get_with(tcx, span, key, |_| ()) {
Ok(()) => {}
Err(e) => tcx.report_cycle(e)
Expand Down Expand Up @@ -338,7 +375,19 @@ define_maps! { <'tcx>

pub typeck_tables: TypeckTables(DefId) -> &'tcx ty::TypeckTables<'tcx>,

pub coherent_trait: coherent_trait_dep_node((CrateNum, DefId)) -> (),

pub coherent_inherent_impls: coherent_inherent_impls_dep_node(CrateNum) -> (),

/// Results of evaluating monomorphic constants embedded in
/// other items, such as enum variant explicit discriminants.
pub monomorphic_const_eval: MonomorphicConstEval(DefId) -> Result<ConstVal, ()>
}

fn coherent_trait_dep_node((_, def_id): (CrateNum, DefId)) -> DepNode<DefId> {
DepNode::CoherenceCheckTrait(def_id)
}

fn coherent_inherent_impls_dep_node(_: CrateNum) -> DepNode<DefId> {
DepNode::Coherence
}

0 comments on commit c832e6f

Please sign in to comment.