Skip to content

Commit

Permalink
rustc: allow handling cycle errors gracefully in on-demand.
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyb committed Feb 25, 2017
1 parent ba11640 commit 9890e04
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 54 deletions.
6 changes: 6 additions & 0 deletions src/librustc/dep_graph/dep_tracking_map.rs
Expand Up @@ -11,6 +11,7 @@
use hir::def_id::DefId;
use rustc_data_structures::fx::FxHashMap;
use std::cell::RefCell;
use std::collections::hash_map::Entry;
use std::ops::Index;
use std::hash::Hash;
use std::marker::PhantomData;
Expand Down Expand Up @@ -67,6 +68,11 @@ impl<M: DepTrackingMapConfig> DepTrackingMap<M> {
assert!(old_value.is_none());
}

pub fn entry(&mut self, k: M::Key) -> Entry<M::Key, M::Value> {
self.write(&k);
self.map.entry(k)
}

pub fn contains_key(&self, k: &M::Key) -> bool {
self.read(k);
self.map.contains_key(k)
Expand Down
100 changes: 70 additions & 30 deletions src/librustc/ty/maps.rs
Expand Up @@ -13,10 +13,9 @@ use hir::def_id::{CrateNum, DefId};
use middle::const_val::ConstVal;
use mir;
use ty::{self, Ty, TyCtxt};
use util::common::MemoizationMap;

use rustc_data_structures::indexed_vec::IndexVec;
use std::cell::RefCell;
use std::cell::{RefCell, RefMut};
use std::rc::Rc;
use syntax_pos::{Span, DUMMY_SP};

Expand Down Expand Up @@ -66,8 +65,13 @@ impl<'tcx> Value<'tcx> for Ty<'tcx> {
}
}

pub struct CycleError<'a> {
span: Span,
cycle: RefMut<'a, [(Span, Query)]>
}

impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
fn report_cycle(self, span: Span, cycle: &[(Span, Query)]) {
pub fn report_cycle(self, CycleError { span, cycle }: CycleError) {
assert!(!cycle.is_empty());

let mut err = struct_span_err!(self.sess, span, E0391,
Expand All @@ -88,24 +92,27 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
err.emit();
}

fn cycle_check<F, R>(self, span: Span, query: Query, compute: F) -> R
fn cycle_check<F, R>(self, span: Span, query: Query, compute: F)
-> Result<R, CycleError<'a>>
where F: FnOnce() -> R
{
{
let mut stack = self.maps.query_stack.borrow_mut();
if let Some((i, _)) = stack.iter().enumerate().rev()
.find(|&(_, &(_, ref q))| *q == query) {
let cycle = &stack[i..];
self.report_cycle(span, cycle);
return R::from_cycle_error(self.global_tcx());
return Err(CycleError {
span: span,
cycle: RefMut::map(stack, |stack| &mut stack[i..])
});
}
stack.push((span, query));
}

let result = compute();

self.maps.query_stack.borrow_mut().pop();
result

Ok(result)
}
}

Expand Down Expand Up @@ -140,7 +147,7 @@ macro_rules! define_maps {
pub $name:ident: $node:ident($K:ty) -> $V:ty),*) => {
pub struct Maps<$tcx> {
providers: IndexVec<CrateNum, Providers<$tcx>>,
pub query_stack: RefCell<Vec<(Span, Query)>>,
query_stack: RefCell<Vec<(Span, Query)>>,
$($(#[$attr])* pub $name: RefCell<DepTrackingMap<queries::$name<$tcx>>>),*
}

Expand Down Expand Up @@ -182,7 +189,60 @@ macro_rules! define_maps {
$(impl<$tcx> DepTrackingMapConfig for queries::$name<$tcx> {
type Key = $K;
type Value = $V;
fn to_dep_node(key: &$K) -> DepNode<DefId> { DepNode::$node(*key) }

#[allow(unused)]
fn to_dep_node(key: &$K) -> DepNode<DefId> {
use dep_graph::DepNode::*;

$node(*key)
}
}
impl<'a, $tcx, 'lcx> queries::$name<$tcx> {
fn try_get_with<F, R>(tcx: TyCtxt<'a, $tcx, 'lcx>,
mut span: Span,
key: $K,
f: F)
-> Result<R, CycleError<'a>>
where F: FnOnce(&$V) -> R
{
if let Some(result) = tcx.maps.$name.borrow().get(&key) {
return Ok(f(result));
}

// FIXME(eddyb) Get more valid Span's on queries.
if span == DUMMY_SP {
span = key.default_span(tcx);
}

let _task = tcx.dep_graph.in_task(Self::to_dep_node(&key));

let result = tcx.cycle_check(span, Query::$name(key), || {
let provider = tcx.maps.providers[key.map_crate()].$name;
provider(tcx.global_tcx(), key)
})?;

Ok(f(&tcx.maps.$name.borrow_mut().entry(key).or_insert(result)))
}

pub fn try_get(tcx: TyCtxt<'a, $tcx, 'lcx>, span: Span, key: $K)
-> Result<$V, CycleError<'a>> {
Self::try_get_with(tcx, span, key, Clone::clone)
}

$(#[$attr])*
pub fn get(tcx: TyCtxt<'a, $tcx, 'lcx>, span: Span, key: $K) -> $V {
Self::try_get(tcx, span, key).unwrap_or_else(|e| {
tcx.report_cycle(e);
Value::from_cycle_error(tcx.global_tcx())
})
}

pub fn force(tcx: TyCtxt<'a, $tcx, 'lcx>, span: Span, key: $K) {
match Self::try_get_with(tcx, span, key, |_| ()) {
Ok(()) => {}
Err(e) => tcx.report_cycle(e)
}
}
})*

pub struct Providers<$tcx> {
Expand All @@ -203,26 +263,6 @@ macro_rules! define_maps {
Providers { $($name),* }
}
}

impl<'a, $tcx, 'lcx> Maps<$tcx> {
$($(#[$attr])*
pub fn $name(&self,
tcx: TyCtxt<'a, $tcx, 'lcx>,
mut span: Span,
key: $K) -> $V {
self.$name.memoize(key, || {
// FIXME(eddyb) Get more valid Span's on queries.
if span == DUMMY_SP {
span = key.default_span(tcx);
}

tcx.cycle_check(span, Query::$name(key), || {
let provider = self.providers[key.map_crate()].$name;
provider(tcx.global_tcx(), key)
})
})
})*
}
}
}

Expand Down
32 changes: 17 additions & 15 deletions src/librustc/ty/mod.rs
Expand Up @@ -75,6 +75,8 @@ pub use self::context::{Lift, TypeckTables};

pub use self::trait_def::{TraitDef, TraitFlags};

pub use self::maps::queries;

pub mod adjustment;
pub mod cast;
pub mod error;
Expand Down Expand Up @@ -1947,7 +1949,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
}

pub fn item_tables(self, def_id: DefId) -> &'gcx TypeckTables<'gcx> {
self.maps.typeck_tables(self, DUMMY_SP, def_id)
queries::typeck_tables::get(self, DUMMY_SP, def_id)
}

pub fn expr_span(self, id: NodeId) -> Span {
Expand Down Expand Up @@ -2055,12 +2057,12 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
}

pub fn custom_coerce_unsized_kind(self, did: DefId) -> adjustment::CustomCoerceUnsized {
self.maps.custom_coerce_unsized_kind(self, DUMMY_SP, did)
queries::custom_coerce_unsized_kind::get(self, DUMMY_SP, did)
}

pub fn associated_item(self, def_id: DefId) -> AssociatedItem {
if !def_id.is_local() {
return self.maps.associated_item(self, DUMMY_SP, def_id);
return queries::associated_item::get(self, DUMMY_SP, def_id);
}

self.maps.associated_item.memoize(def_id, || {
Expand Down Expand Up @@ -2165,7 +2167,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {

pub fn associated_item_def_ids(self, def_id: DefId) -> Rc<Vec<DefId>> {
if !def_id.is_local() {
return self.maps.associated_item_def_ids(self, DUMMY_SP, def_id);
return queries::associated_item_def_ids::get(self, DUMMY_SP, def_id);
}

self.maps.associated_item_def_ids.memoize(def_id, || {
Expand Down Expand Up @@ -2200,7 +2202,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
/// Returns the trait-ref corresponding to a given impl, or None if it is
/// an inherent impl.
pub fn impl_trait_ref(self, id: DefId) -> Option<TraitRef<'gcx>> {
self.maps.impl_trait_ref(self, DUMMY_SP, id)
queries::impl_trait_ref::get(self, DUMMY_SP, id)
}

// Returns `ty::VariantDef` if `def` refers to a struct,
Expand Down Expand Up @@ -2279,37 +2281,37 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
// If the given item is in an external crate, looks up its type and adds it to
// the type cache. Returns the type parameters and type.
pub fn item_type(self, did: DefId) -> Ty<'gcx> {
self.maps.ty(self, DUMMY_SP, did)
queries::ty::get(self, DUMMY_SP, did)
}

/// Given the did of a trait, returns its canonical trait ref.
pub fn lookup_trait_def(self, did: DefId) -> &'gcx TraitDef {
self.maps.trait_def(self, DUMMY_SP, did)
queries::trait_def::get(self, DUMMY_SP, did)
}

/// Given the did of an ADT, return a reference to its definition.
pub fn lookup_adt_def(self, did: DefId) -> &'gcx AdtDef {
self.maps.adt_def(self, DUMMY_SP, did)
queries::adt_def::get(self, DUMMY_SP, did)
}

/// Given the did of an item, returns its generics.
pub fn item_generics(self, did: DefId) -> &'gcx Generics {
self.maps.generics(self, DUMMY_SP, did)
queries::generics::get(self, DUMMY_SP, did)
}

/// Given the did of an item, returns its full set of predicates.
pub fn item_predicates(self, did: DefId) -> GenericPredicates<'gcx> {
self.maps.predicates(self, DUMMY_SP, did)
queries::predicates::get(self, DUMMY_SP, did)
}

/// Given the did of a trait, returns its superpredicates.
pub fn item_super_predicates(self, did: DefId) -> GenericPredicates<'gcx> {
self.maps.super_predicates(self, DUMMY_SP, did)
queries::super_predicates::get(self, DUMMY_SP, did)
}

/// Given the did of an item, returns its MIR, borrowed immutably.
pub fn item_mir(self, did: DefId) -> Ref<'gcx, Mir<'gcx>> {
self.maps.mir(self, DUMMY_SP, did).borrow()
queries::mir::get(self, DUMMY_SP, did).borrow()
}

/// If `type_needs_drop` returns true, then `ty` is definitely
Expand Down Expand Up @@ -2361,7 +2363,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
}

pub fn item_variances(self, item_id: DefId) -> Rc<Vec<ty::Variance>> {
self.maps.variances(self, DUMMY_SP, item_id)
queries::variances::get(self, DUMMY_SP, item_id)
}

pub fn trait_has_default_impl(self, trait_def_id: DefId) -> bool {
Expand Down Expand Up @@ -2436,11 +2438,11 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
}

pub fn closure_kind(self, def_id: DefId) -> ty::ClosureKind {
self.maps.closure_kind(self, DUMMY_SP, def_id)
queries::closure_kind::get(self, DUMMY_SP, def_id)
}

pub fn closure_type(self, def_id: DefId) -> ty::PolyFnSig<'tcx> {
self.maps.closure_type(self, DUMMY_SP, def_id)
queries::closure_type::get(self, DUMMY_SP, def_id)
}

/// Given the def_id of an impl, return the def_id of the trait it implements.
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_metadata/cstore_impl.rs
Expand Up @@ -47,7 +47,7 @@ macro_rules! provide {
(<$lt:tt> $tcx:ident, $def_id:ident, $cdata:ident $($name:ident => $compute:block)*) => {
pub fn provide<$lt>(providers: &mut Providers<$lt>) {
$(fn $name<'a, $lt:$lt>($tcx: TyCtxt<'a, $lt, $lt>, $def_id: DefId)
-> <ty::maps::queries::$name<$lt> as
-> <ty::queries::$name<$lt> as
DepTrackingMapConfig>::Value {
assert!(!$def_id.is_local());

Expand Down
2 changes: 1 addition & 1 deletion src/librustc_metadata/encoder.rs
Expand Up @@ -264,7 +264,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
discr: variant.discr,
evaluated_discr: match variant.discr {
ty::VariantDiscr::Explicit(def_id) => {
tcx.maps.monomorphic_const_eval(tcx, DUMMY_SP, def_id).ok()
ty::queries::monomorphic_const_eval::get(tcx, DUMMY_SP, def_id).ok()
}
ty::VariantDiscr::Relative(_) => None
},
Expand Down
9 changes: 5 additions & 4 deletions src/librustc_typeck/astconv.rs
Expand Up @@ -243,7 +243,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
let is_object = self_ty.map_or(false, |ty| ty.sty == TRAIT_OBJECT_DUMMY_SELF);
let default_needs_object_self = |p: &ty::TypeParameterDef| {
if is_object && p.has_default {
if tcx.maps.ty(tcx, span, p.def_id).has_self_ty() {
if ty::queries::ty::get(tcx, span, p.def_id).has_self_ty() {
// There is no suitable inference default for a type parameter
// that references self, in an object type.
return true;
Expand Down Expand Up @@ -310,7 +310,8 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
tcx.types.err
} else {
// This is a default type parameter.
tcx.maps.ty(tcx, span, def.def_id).subst_spanned(tcx, substs, Some(span))
ty::queries::ty::get(tcx, span, def.def_id)
.subst_spanned(tcx, substs, Some(span))
}
} else {
// We've already errored above about the mismatch.
Expand Down Expand Up @@ -599,7 +600,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
-> Ty<'tcx>
{
let substs = self.ast_path_substs_for_ty(span, did, item_segment);
self.tcx().maps.ty(self.tcx(), span, did).subst(self.tcx(), substs)
ty::queries::ty::get(self.tcx(), span, did).subst(self.tcx(), substs)
}

/// Transform a PolyTraitRef into a PolyExistentialTraitRef by
Expand Down Expand Up @@ -985,7 +986,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
assert_eq!(opt_self_ty, None);
tcx.prohibit_type_params(&path.segments);

let ty = tcx.maps.ty(tcx, span, def_id);
let ty = ty::queries::ty::get(tcx, span, def_id);
if let Some(free_substs) = self.get_free_substs() {
ty.subst(tcx, free_substs)
} else {
Expand Down
6 changes: 3 additions & 3 deletions src/librustc_typeck/collect.rs
Expand Up @@ -262,7 +262,7 @@ impl<'a, 'tcx> AstConv<'tcx, 'tcx> for ItemCtxt<'a, 'tcx> {
def_id: DefId)
-> ty::GenericPredicates<'tcx>
{
self.tcx.maps.type_param_predicates(self.tcx, span, (self.item_def_id, def_id))
ty::queries::type_param_predicates::get(self.tcx, span, (self.item_def_id, def_id))
}

fn get_free_substs(&self) -> Option<&Substs<'tcx>> {
Expand Down Expand Up @@ -532,7 +532,7 @@ fn convert_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, it: &hir::Item) {
hir::ItemTrait(..) => {
tcx.item_generics(def_id);
tcx.lookup_trait_def(def_id);
tcx.maps.super_predicates(tcx, it.span, def_id);
ty::queries::super_predicates::get(tcx, it.span, def_id);
tcx.item_predicates(def_id);
},
hir::ItemStruct(ref struct_def, _) |
Expand Down Expand Up @@ -836,7 +836,7 @@ fn super_predicates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
// Now require that immediate supertraits are converted,
// which will, in turn, reach indirect supertraits.
for bound in superbounds.iter().filter_map(|p| p.to_opt_poly_trait_ref()) {
tcx.maps.super_predicates(tcx, item.span, bound.def_id());
ty::queries::super_predicates::get(tcx, item.span, bound.def_id());
}

ty::GenericPredicates {
Expand Down
1 change: 1 addition & 0 deletions src/test/compile-fail/cycle-trait-default-type-trait.rs
Expand Up @@ -13,6 +13,7 @@

trait Foo<X = Box<Foo>> {
//~^ ERROR unsupported cyclic reference
//~| ERROR unsupported cyclic reference
}

fn main() { }

0 comments on commit 9890e04

Please sign in to comment.