don't create region vars in LUB coercions

instead, extract the target region out of the autoderef loop
This commit is contained in:
Niko Matsakis 2016-03-17 05:22:26 -04:00
parent 00c9420160
commit ad6ca084e7

View File

@ -71,7 +71,7 @@ use middle::ty::adjustment::{AdjustUnsafeFnPointer, AdjustMutToConstPointer};
use middle::ty::{self, LvaluePreference, TypeAndMut, Ty, TyCtxt};
use middle::ty::fold::TypeFoldable;
use middle::ty::error::TypeError;
use middle::ty::relate::{relate_substs, RelateResult, TypeRelation};
use middle::ty::relate::{relate_substs, Relate, RelateResult, TypeRelation};
use util::common::indent;
use std::cell::RefCell;
@ -112,8 +112,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
self.fcx.tcx()
}
/// Unify two types (using sub or lub) and produce a noop coercion.
fn unify_and_identity(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
let infcx = self.fcx.infcx();
infcx.commit_if_ok(|_| {
let trace = TypeTrace::types(self.origin, false, a, b);
@ -122,7 +121,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
} else {
infcx.sub(false, trace).relate(&a, &b)
}
}).and_then(|ty| self.identity(ty))
})
}
/// Unify two types (using sub or lub) and produce a noop coercion.
fn unify_and_identity(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
self.unify(&a, &b).and_then(|ty| self.identity(ty))
}
/// Synthesize an identity adjustment.
@ -167,7 +171,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
}
ty::TyRef(r_b, mt_b) => {
return self.coerce_borrowed_pointer(exprs, a, b, r_b, mt_b.mutbl);
return self.coerce_borrowed_pointer(exprs, a, b, r_b, mt_b);
}
_ => {}
@ -200,7 +204,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
a: Ty<'tcx>,
b: Ty<'tcx>,
r_b: &'tcx ty::Region,
mutbl_b: hir::Mutability)
mt_b: TypeAndMut<'tcx>)
-> CoerceResult<'tcx>
// FIXME(eddyb) use copyable iterators when that becomes ergonomic.
where E: Fn() -> I,
@ -214,69 +218,108 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
// to type check, we will construct the type that `&M*expr` would
// yield.
let (_r_a, _mutbl_a) = match a.sty {
let r_a = match a.sty {
ty::TyRef(r_a, mt_a) => {
try!(coerce_mutbls(mt_a.mutbl, mutbl_b));
(r_a, mt_a.mutbl)
try!(coerce_mutbls(mt_a.mutbl, mt_b.mutbl));
r_a
}
_ => return self.unify_and_identity(a, b)
};
let span = self.origin.span();
let coercion = Coercion(span);
let r_borrow = {
// If are coercing from `&'a T` to `&'b U`, then we want to
// reborrow the contents of `'a` for the lifetime `'b`
// (which ought to be a sublifetime of `'a`).
if !self.use_lub {
r_b
} else {
// With LUB, we need more flexibility.
let r_borrow = self.fcx.infcx().next_region_var(coercion);
self.tcx().mk_region(r_borrow)
}
};
let autoref = Some(AutoPtr(r_borrow, mutbl_b));
let lvalue_pref = LvaluePreference::from_mutbl(mutbl_b);
let lvalue_pref = LvaluePreference::from_mutbl(mt_b.mutbl);
let mut first_error = None;
let (_, autoderefs, success) = autoderef(self.fcx, span, a, exprs,
UnresolvedTypeAction::Ignore,
lvalue_pref,
|inner_ty, autoderef| {
|referent_ty, autoderef|
{
if autoderef == 0 {
// Don't let this pass, otherwise it would cause
// &T to autoref to &&T.
return None;
}
let ty = self.tcx().mk_ref(r_borrow,
TypeAndMut {ty: inner_ty, mutbl: mutbl_b});
match self.unify_and_identity(ty, b) {
// At this point, we have deref'd `a` to `referent_ty`. So
// imagine we are coercing from `&'a mut Vec<T>` to `&'b mut [T]`.
// In the autoderef loop for `&'a mut Vec<T>`, we would get
// three callbacks:
//
// - `&'a mut Vec<T>` -- 0 derefs, just ignore it
// - `Vec<T>` -- 1 deref
// - `[T]` -- 2 deref
//
// At each point after the first callback, we want to
// check to see whether this would match out target type
// (`&'b mut [T]`) if we autoref'd it. We can't just
// compare the referent types, though, because we still
// have to consider the mutability. E.g., in the case
// we've been considering, we have an `&mut` reference, so
// the `T` in `[T]` needs to be unified with equality.
//
// Therefore, we construct reference types reflecting what
// the types will be after we do the final auto-ref and
// compare those. Note that this means we use the target
// mutability [1], since it may be that we are coercing
// from `&mut T` to `&U`.
//
// One fine point concerns the region that we use [2]. We
// choose the region such that the region of the final
// type that results from `unify` will be the region we
// want for the autoref:
//
// - if in lub mode, that means we want to unify `&'a mut [T]`
// (from source) and `&'b mut [T]` (target).
// - if in sub mode, that means we want to use `'b` for
// both pointers. This is because sub mode (somewhat
// arbitrarily) returns the subtype region. In the case
// where we are coercing to a target type, we know we
// want to use that target type region (`'b`) because --
// for the program to type-check -- it must be the
// smaller of the two.
let r = if self.use_lub {r_a} else {r_b}; // [2] above
let derefd_ty_a = self.tcx().mk_ref(r, TypeAndMut {
ty: referent_ty,
mutbl: mt_b.mutbl // [1] above
});
match self.unify(derefd_ty_a, b) {
Ok(ty) => Some(ty),
Err(err) => {
if first_error.is_none() {
first_error = Some(err);
}
None
}
Ok((ty, _)) => Some(ty)
}
});
match success {
Some(ty) => {
// Extract type or return an error. We return the first error
// we got, which should be from relating the "base" type
// (e.g., in example above, the failure from relating `Vec<T>`
// to the target type), since that should be the least
// confusing.
let ty = match success {
Some(ty) => ty,
None => {
return Err(first_error.expect("coerce_borrowed_pointer had no error"));
}
};
// Now apply the autoref. We have to extract the region out of
// the final ref type we got.
let r_borrow = match ty.sty {
ty::TyRef(r, _) => r,
_ => self.tcx().sess.span_bug(span,
&format!("expected a ref type, got {:?}", ty))
};
let autoref = Some(AutoPtr(r_borrow, mt_b.mutbl));
Ok((ty, AdjustDerefRef(AutoDerefRef {
autoderefs: autoderefs,
autoref: autoref,
unsize: None
})))
}
None => {
// Return original error as if overloaded deref was never
// attempted, to avoid irrelevant/confusing error messages.
Err(first_error.expect("coerce_borrowed_pointer failed with no error?"))
}
}
}
// &[T; n] or &mut [T; n] -> &[T]