Skip to content

Commit

Permalink
Implement "perfect forwarding" for HR impls (#19730).
Browse files Browse the repository at this point in the history
  • Loading branch information
nikomatsakis committed Dec 19, 2014
1 parent c2ca1a4 commit f45c0ef
Show file tree
Hide file tree
Showing 38 changed files with 759 additions and 416 deletions.
4 changes: 1 addition & 3 deletions src/librustc/lint/builtin.rs
Expand Up @@ -1784,9 +1784,7 @@ impl LintPass for Stability {
method_num: index,
..
}) => {
ty::trait_item(cx.tcx,
trait_ref.def_id(),
index).def_id()
ty::trait_item(cx.tcx, trait_ref.def_id, index).def_id()
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/metadata/csearch.rs
Expand Up @@ -262,7 +262,7 @@ pub fn get_field_type<'tcx>(tcx: &ty::ctxt<'tcx>, class_id: ast::DefId,
// if there is one.
pub fn get_impl_trait<'tcx>(tcx: &ty::ctxt<'tcx>,
def: ast::DefId)
-> Option<Rc<ty::PolyTraitRef<'tcx>>> {
-> Option<Rc<ty::TraitRef<'tcx>>> {
let cstore = &tcx.sess.cstore;
let cdata = cstore.get_crate_data(def.krate);
decoder::get_impl_trait(&*cdata, def.node, tcx)
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/metadata/decoder.rs
Expand Up @@ -425,11 +425,11 @@ pub fn get_repr_attrs(cdata: Cmd, id: ast::NodeId) -> Vec<attr::ReprAttr> {
pub fn get_impl_trait<'tcx>(cdata: Cmd,
id: ast::NodeId,
tcx: &ty::ctxt<'tcx>)
-> Option<Rc<ty::PolyTraitRef<'tcx>>>
-> Option<Rc<ty::TraitRef<'tcx>>>
{
let item_doc = lookup_item(id, cdata.data());
reader::maybe_get_doc(item_doc, tag_item_trait_ref).map(|tp| {
Rc::new(ty::Binder(doc_trait_ref(tp, tcx, cdata)))
Rc::new(doc_trait_ref(tp, tcx, cdata))
})
}

Expand Down
8 changes: 4 additions & 4 deletions src/librustc/middle/astencode.rs
Expand Up @@ -887,7 +887,7 @@ impl<'a, 'tcx> rbml_writer_helpers<'tcx> for Encoder<'a> {
this.emit_enum_variant("MethodTypeParam", 2, 1, |this| {
this.emit_struct("MethodParam", 2, |this| {
try!(this.emit_struct_field("trait_ref", 0, |this| {
Ok(this.emit_trait_ref(ecx, &p.trait_ref.0))
Ok(this.emit_trait_ref(ecx, &*p.trait_ref))
}));
try!(this.emit_struct_field("method_num", 0, |this| {
this.emit_uint(p.method_num)
Expand All @@ -901,7 +901,7 @@ impl<'a, 'tcx> rbml_writer_helpers<'tcx> for Encoder<'a> {
this.emit_enum_variant("MethodTraitObject", 3, 1, |this| {
this.emit_struct("MethodObject", 2, |this| {
try!(this.emit_struct_field("trait_ref", 0, |this| {
Ok(this.emit_trait_ref(ecx, &o.trait_ref.0))
Ok(this.emit_trait_ref(ecx, &*o.trait_ref))
}));
try!(this.emit_struct_field("object_trait_id", 0, |this| {
Ok(this.emit_def_id(o.object_trait_id))
Expand Down Expand Up @@ -1457,7 +1457,7 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> {
ty::MethodParam {
trait_ref: {
this.read_struct_field("trait_ref", 0, |this| {
Ok(this.read_poly_trait_ref(dcx))
Ok(this.read_trait_ref(dcx))
}).unwrap()
},
method_num: {
Expand All @@ -1475,7 +1475,7 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> {
ty::MethodObject {
trait_ref: {
this.read_struct_field("trait_ref", 0, |this| {
Ok(this.read_poly_trait_ref(dcx))
Ok(this.read_trait_ref(dcx))
}).unwrap()
},
object_trait_id: {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/dead.rs
Expand Up @@ -112,7 +112,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
..
}) => {
let trait_item = ty::trait_item(self.tcx,
trait_ref.def_id(),
trait_ref.def_id,
index);
match trait_item {
ty::MethodTraitItem(method) => {
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/middle/expr_use_visitor.rs
Expand Up @@ -265,7 +265,7 @@ impl OverloadedCallType {
}
Some(ref trait_ref) => (*trait_ref).clone(),
};
OverloadedCallType::from_trait_id(tcx, trait_ref.def_id())
OverloadedCallType::from_trait_id(tcx, trait_ref.def_id)
}

fn from_unboxed_closure(tcx: &ty::ctxt, closure_did: ast::DefId)
Expand All @@ -292,7 +292,7 @@ impl OverloadedCallType {
}
MethodTypeParam(MethodParam { ref trait_ref, .. }) |
MethodTraitObject(MethodObject { ref trait_ref, .. }) => {
OverloadedCallType::from_trait_id(tcx, trait_ref.def_id())
OverloadedCallType::from_trait_id(tcx, trait_ref.def_id)
}
}
}
Expand Down
13 changes: 12 additions & 1 deletion src/librustc/middle/infer/error_reporting.rs
Expand Up @@ -395,7 +395,8 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
fn values_str(&self, values: &ValuePairs<'tcx>) -> Option<String> {
match *values {
infer::Types(ref exp_found) => self.expected_found_str(exp_found),
infer::TraitRefs(ref exp_found) => self.expected_found_str(exp_found)
infer::TraitRefs(ref exp_found) => self.expected_found_str(exp_found),
infer::PolyTraitRefs(ref exp_found) => self.expected_found_str(exp_found)
}
}

Expand Down Expand Up @@ -1647,6 +1648,16 @@ impl<'tcx> Resolvable<'tcx> for Ty<'tcx> {
}
}

impl<'tcx> Resolvable<'tcx> for Rc<ty::TraitRef<'tcx>> {
fn resolve<'a>(&self, infcx: &InferCtxt<'a, 'tcx>)
-> Rc<ty::TraitRef<'tcx>> {
Rc::new(infcx.resolve_type_vars_if_possible(&**self))
}
fn contains_error(&self) -> bool {
ty::trait_ref_contains_error(&**self)
}
}

impl<'tcx> Resolvable<'tcx> for Rc<ty::PolyTraitRef<'tcx>> {
fn resolve<'a>(&self, infcx: &InferCtxt<'a, 'tcx>)
-> Rc<ty::PolyTraitRef<'tcx>> {
Expand Down
102 changes: 96 additions & 6 deletions src/librustc/middle/infer/higher_ranked/mod.rs
Expand Up @@ -219,7 +219,7 @@ impl<'tcx,C> HigherRankedRelations<'tcx> for C
self.infcx().resolve_type_vars_if_possible(&result0);
debug!("glb result0 = {}", result0.repr(self.tcx()));

// Generalize the regions appearing in fn_ty0 if possible
// Generalize the regions appearing in result0 if possible
let new_vars = self.infcx().region_vars_confined_to_snapshot(snapshot);
let span = self.trace().origin.span();
let result1 =
Expand Down Expand Up @@ -358,7 +358,7 @@ fn fold_regions_in<'tcx, T, F>(tcx: &ty::ctxt<'tcx>,
where T : Combineable<'tcx>,
F : FnMut(ty::Region, ty::DebruijnIndex) -> ty::Region,
{
unbound_value.fold_with(&mut ty_fold::RegionFolder::new(tcx, |region, current_depth| {
unbound_value.fold_with(&mut ty_fold::RegionFolder::new(tcx, &mut |region, current_depth| {
// we should only be encountering "escaping" late-bound regions here,
// because the ones at the current level should have been replaced
// with fresh variables
Expand Down Expand Up @@ -414,11 +414,11 @@ impl<'a,'tcx> InferCtxtExt<'tcx> for InferCtxt<'a,'tcx> {
*
* The reason is that when we walk through the subtyping
* algorith, we begin by replacing `'a` with a skolemized
* variable `'0`. We then have `fn(_#0t) <: fn(&'0 int)`. This
* can be made true by unifying `_#0t` with `&'0 int`. In the
* variable `'1`. We then have `fn(_#0t) <: fn(&'1 int)`. This
* can be made true by unifying `_#0t` with `&'1 int`. In the
* process, we create a fresh variable for the skolemized
* region, `'$0`, and hence we have that `_#0t == &'$0
* int`. However, because `'$0` was created during the sub
* region, `'$2`, and hence we have that `_#0t == &'$2
* int`. However, because `'$2` was created during the sub
* computation, if we're not careful we will erroneously
* assume it is one of the transient region variables
* representing a lub/glb internally. Not good.
Expand Down Expand Up @@ -522,3 +522,93 @@ pub fn leak_check<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
}
Ok(())
}

/// This code converts from skolemized regions back to late-bound
/// regions. It works by replacing each region in the taint set of a
/// skolemized region with a bound-region. The bound region will be bound
/// by the outer-most binder in `value`; the caller must ensure that there is
/// such a binder and it is the right place.
///
/// This routine is only intended to be used when the leak-check has
/// passed; currently, it's used in the trait matching code to create
/// a set of nested obligations frmo an impl that matches against
/// something higher-ranked. More details can be found in
/// `middle::traits::doc.rs`.
///
/// As a brief example, consider the obligation `for<'a> Fn(&'a int)
/// -> &'a int`, and the impl:
///
/// impl<A,R> Fn<A,R> for SomethingOrOther
/// where A : Clone
/// { ... }
///
/// Here we will have replaced `'a` with a skolemized region
/// `'0`. This means that our substitution will be `{A=>&'0
/// int, R=>&'0 int}`.
///
/// When we apply the substitution to the bounds, we will wind up with
/// `&'0 int : Clone` as a predicate. As a last step, we then go and
/// replace `'0` with a late-bound region `'a`. The depth is matched
/// to the depth of the predicate, in this case 1, so that the final
/// predicate is `for<'a> &'a int : Clone`.
pub fn plug_leaks<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>,
skol_map: SkolemizationMap,
snapshot: &CombinedSnapshot,
value: &T)
-> T
where T : TypeFoldable<'tcx> + Repr<'tcx>
{
debug_assert!(leak_check(infcx, &skol_map, snapshot).is_ok());

debug!("plug_leaks(skol_map={}, value={})",
skol_map.repr(infcx.tcx),
value.repr(infcx.tcx));

// Compute a mapping from the "taint set" of each skolemized
// region back to the `ty::BoundRegion` that it originally
// represented. Because `leak_check` passed, we know that that
// these taint sets are mutually disjoint.
let inv_skol_map: FnvHashMap<ty::Region, ty::BoundRegion> =
skol_map
.into_iter()
.flat_map(|(skol_br, skol)| {
infcx.tainted_regions(snapshot, skol)
.into_iter()
.map(move |tainted_region| (tainted_region, skol_br))
})
.collect();

debug!("plug_leaks: inv_skol_map={}",
inv_skol_map.repr(infcx.tcx));

// Remove any instantiated type variables from `value`; those can hide
// references to regions from the `fold_regions` code below.
let value = infcx.resolve_type_vars_if_possible(value);

// Map any skolemization byproducts back to a late-bound
// region. Put that late-bound region at whatever the outermost
// binder is that we encountered in `value`. The caller is
// responsible for ensuring that (a) `value` contains at least one
// binder and (b) that binder is the one we want to use.
let result = ty_fold::fold_regions(infcx.tcx, &value, |r, current_depth| {
match inv_skol_map.get(&r) {
None => r,
Some(br) => {
// It is the responsibility of the caller to ensure
// that each skolemized region appears within a
// binder. In practice, this routine is only used by
// trait checking, and all of the skolemized regions
// appear inside predicates, which always have
// binders, so this assert is satisfied.
assert!(current_depth > 1);

ty::ReLateBound(ty::DebruijnIndex::new(current_depth - 1), br.clone())
}
}
});

debug!("plug_leaks: result={}",
result.repr(infcx.tcx));

result
}
47 changes: 39 additions & 8 deletions src/librustc/middle/infer/mod.rs
Expand Up @@ -137,7 +137,8 @@ impl Copy for TypeOrigin {}
#[deriving(Clone, Show)]
pub enum ValuePairs<'tcx> {
Types(ty::expected_found<Ty<'tcx>>),
TraitRefs(ty::expected_found<Rc<ty::PolyTraitRef<'tcx>>>),
TraitRefs(ty::expected_found<Rc<ty::TraitRef<'tcx>>>),
PolyTraitRefs(ty::expected_found<Rc<ty::PolyTraitRef<'tcx>>>),
}

/// The trace designates the path through inference that we took to
Expand Down Expand Up @@ -349,7 +350,7 @@ pub fn can_mk_subty<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
b: Ty<'tcx>)
-> ures<'tcx> {
debug!("can_mk_subty({} <: {})", a.repr(cx.tcx), b.repr(cx.tcx));
cx.probe(|| {
cx.probe(|_| {
let trace = TypeTrace {
origin: Misc(codemap::DUMMY_SP),
values: Types(expected_found(true, a, b))
Expand All @@ -362,7 +363,7 @@ pub fn can_mk_eqty<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
a: Ty<'tcx>, b: Ty<'tcx>)
-> ures<'tcx> {
debug!("can_mk_subty({} <: {})", a.repr(cx.tcx), b.repr(cx.tcx));
cx.probe(|| {
cx.probe(|_| {
let trace = TypeTrace {
origin: Misc(codemap::DUMMY_SP),
values: Types(expected_found(true, a, b))
Expand Down Expand Up @@ -634,11 +635,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {

/// Execute `f` then unroll any bindings it creates
pub fn probe<R, F>(&self, f: F) -> R where
F: FnOnce() -> R,
F: FnOnce(&CombinedSnapshot) -> R,
{
debug!("probe()");
let snapshot = self.start_snapshot();
let r = f();
let r = f(&snapshot);
self.rollback_to(snapshot);
r
}
Expand Down Expand Up @@ -683,21 +684,39 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
})
}

pub fn sub_trait_refs(&self,
a_is_expected: bool,
origin: TypeOrigin,
a: Rc<ty::TraitRef<'tcx>>,
b: Rc<ty::TraitRef<'tcx>>)
-> ures<'tcx>
{
debug!("sub_trait_refs({} <: {})",
a.repr(self.tcx),
b.repr(self.tcx));
self.commit_if_ok(|| {
let trace = TypeTrace {
origin: origin,
values: TraitRefs(expected_found(a_is_expected, a.clone(), b.clone()))
};
self.sub(a_is_expected, trace).trait_refs(&*a, &*b).to_ures()
})
}

pub fn sub_poly_trait_refs(&self,
a_is_expected: bool,
origin: TypeOrigin,
a: Rc<ty::PolyTraitRef<'tcx>>,
b: Rc<ty::PolyTraitRef<'tcx>>)
-> ures<'tcx>
{
debug!("sub_trait_refs({} <: {})",
debug!("sub_poly_trait_refs({} <: {})",
a.repr(self.tcx),
b.repr(self.tcx));
self.commit_if_ok(|| {
let trace = TypeTrace {
origin: origin,
values: TraitRefs(expected_found(a_is_expected,
a.clone(), b.clone()))
values: PolyTraitRefs(expected_found(a_is_expected, a.clone(), b.clone()))
};
self.sub(a_is_expected, trace).binders(&*a, &*b).to_ures()
})
Expand Down Expand Up @@ -727,6 +746,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
}
}

pub fn plug_leaks<T>(&self,
skol_map: SkolemizationMap,
snapshot: &CombinedSnapshot,
value: &T)
-> T
where T : TypeFoldable<'tcx> + Repr<'tcx>
{
/*! See `higher_ranked::leak_check` */

higher_ranked::plug_leaks(self, skol_map, snapshot, value)
}

pub fn equality_predicate(&self,
span: Span,
predicate: &ty::PolyEquatePredicate<'tcx>)
Expand Down
10 changes: 5 additions & 5 deletions src/librustc/middle/privacy.rs
Expand Up @@ -257,8 +257,8 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EmbargoVisitor<'a, 'tcx> {
};
let tr = ty::impl_trait_ref(self.tcx, local_def(item.id));
let public_trait = tr.clone().map_or(false, |tr| {
!is_local(tr.def_id()) ||
self.exported_items.contains(&tr.def_id().node)
!is_local(tr.def_id) ||
self.exported_items.contains(&tr.def_id.node)
});

if public_ty || public_trait {
Expand Down Expand Up @@ -407,7 +407,7 @@ impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> {
match ty::impl_trait_ref(self.tcx, id) {
Some(t) => {
debug!("privacy - impl of trait {}", id);
self.def_privacy(t.def_id())
self.def_privacy(t.def_id)
}
None => {
debug!("privacy - found a method {}",
Expand All @@ -432,7 +432,7 @@ impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> {
match ty::impl_trait_ref(self.tcx, id) {
Some(t) => {
debug!("privacy - impl of trait {}", id);
self.def_privacy(t.def_id())
self.def_privacy(t.def_id)
}
None => {
debug!("privacy - found a typedef {}",
Expand Down Expand Up @@ -811,7 +811,7 @@ impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> {
// is whether the trait itself is accessible or not.
MethodTypeParam(MethodParam { ref trait_ref, .. }) |
MethodTraitObject(MethodObject { ref trait_ref, .. }) => {
self.report_error(self.ensure_public(span, trait_ref.def_id(),
self.report_error(self.ensure_public(span, trait_ref.def_id,
None, "source trait"));
}
}
Expand Down

0 comments on commit f45c0ef

Please sign in to comment.