Overhaul coercion to use the lazy InferOk obligations passing.

This commit is contained in:
Eduard-Mihai Burtescu 2017-03-08 14:36:49 +02:00
parent 319890487a
commit 74bc7fda8c
2 changed files with 99 additions and 109 deletions

View file

@ -12,6 +12,7 @@ use astconv::AstConv;
use super::FnCtxt;
use rustc::infer::InferOk;
use rustc::traits;
use rustc::ty::{self, Ty, TraitRef};
use rustc::ty::{ToPredicate, TypeFoldable};
@ -149,6 +150,14 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
pub fn finalize<'b, I>(self, pref: LvaluePreference, exprs: I)
where I: IntoIterator<Item = &'b hir::Expr>
{
let fcx = self.fcx;
fcx.register_infer_ok_obligations(self.finalize_as_infer_ok(pref, exprs));
}
pub fn finalize_as_infer_ok<'b, I>(self, pref: LvaluePreference, exprs: I)
-> InferOk<'tcx, ()>
where I: IntoIterator<Item = &'b hir::Expr>
{
let methods: Vec<_> = self.steps
.iter()
@ -176,8 +185,9 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
}
}
for obligation in self.obligations {
self.fcx.register_predicate(obligation);
InferOk {
value: (),
obligations: self.obligations
}
}
}

View file

@ -64,7 +64,8 @@ use check::FnCtxt;
use rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::infer::{Coercion, InferOk, TypeTrace};
use rustc::infer::{Coercion, InferResult, InferOk, TypeTrace};
use rustc::infer::type_variable::TypeVariableOrigin;
use rustc::traits::{self, ObligationCause, ObligationCauseCode};
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow};
use rustc::ty::{self, LvaluePreference, TypeAndMut,
@ -75,9 +76,7 @@ use rustc::ty::relate::RelateResult;
use rustc::ty::subst::Subst;
use syntax::abi;
use syntax::feature_gate;
use util::common::indent;
use std::cell::RefCell;
use std::collections::VecDeque;
use std::ops::Deref;
@ -85,7 +84,6 @@ struct Coerce<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
cause: ObligationCause<'tcx>,
use_lub: bool,
unsizing_obligations: RefCell<Vec<traits::PredicateObligation<'tcx>>>,
}
impl<'a, 'gcx, 'tcx> Deref for Coerce<'a, 'gcx, 'tcx> {
@ -95,7 +93,7 @@ impl<'a, 'gcx, 'tcx> Deref for Coerce<'a, 'gcx, 'tcx> {
}
}
type CoerceResult<'tcx> = RelateResult<'tcx, (Ty<'tcx>, Adjust<'tcx>)>;
type CoerceResult<'tcx> = InferResult<'tcx, Adjustment<'tcx>>;
fn coerce_mutbls<'tcx>(from_mutbl: hir::Mutability,
to_mutbl: hir::Mutability)
@ -108,44 +106,53 @@ fn coerce_mutbls<'tcx>(from_mutbl: hir::Mutability,
}
}
fn identity<'tcx>() -> Adjust<'tcx> {
Adjust::DerefRef {
autoderefs: 0,
autoref: None,
unsize: false,
}
}
fn success<'tcx>(kind: Adjust<'tcx>,
target: Ty<'tcx>,
obligations: traits::PredicateObligations<'tcx>)
-> CoerceResult<'tcx> {
Ok(InferOk {
value: Adjustment {
kind,
target
},
obligations
})
}
impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
fn new(fcx: &'f FnCtxt<'f, 'gcx, 'tcx>, cause: ObligationCause<'tcx>) -> Self {
Coerce {
fcx: fcx,
cause: cause,
use_lub: false,
unsizing_obligations: RefCell::new(vec![]),
}
}
fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
self.commit_if_ok(|_| {
let trace = TypeTrace::types(&self.cause, false, a, b);
if self.use_lub {
self.lub(false, trace, &a, &b)
.map(|ok| self.register_infer_ok_obligations(ok))
} else {
self.sub(false, trace, &a, &b)
.map(|InferOk { value, obligations }| {
self.fcx.register_predicates(obligations);
value
})
}
})
}
/// 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.
fn identity(&self, ty: Ty<'tcx>) -> CoerceResult<'tcx> {
Ok((ty, Adjust::DerefRef {
autoderefs: 0,
autoref: None,
unsize: false,
}))
/// Unify two types (using sub or lub) and produce a specific coercion.
fn unify_and(&self, a: Ty<'tcx>, b: Ty<'tcx>, kind: Adjust<'tcx>)
-> CoerceResult<'tcx> {
self.unify(&a, &b).and_then(|InferOk { value: ty, obligations }| {
success(kind, ty, obligations)
})
}
fn coerce<'a, E, I>(&self, exprs: &E, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx>
@ -158,11 +165,11 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
// Just ignore error types.
if a.references_error() || b.references_error() {
return self.identity(b);
return success(identity(), b, vec![]);
}
if a.is_never() {
return Ok((b, Adjust::NeverToAny));
return success(Adjust::NeverToAny, b, vec![]);
}
// Consider coercing the subtype to a DST
@ -208,7 +215,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
}
_ => {
// Otherwise, just use unification rules.
self.unify_and_identity(a, b)
self.unify_and(a, b, identity())
}
}
}
@ -240,7 +247,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
coerce_mutbls(mt_a.mutbl, mt_b.mutbl)?;
(r_a, mt_a)
}
_ => return self.unify_and_identity(a, b),
_ => return self.unify_and(a, b, identity()),
};
let span = self.cause.span;
@ -248,7 +255,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
let mut first_error = None;
let mut r_borrow_var = None;
let mut autoderef = self.autoderef(span, a);
let mut success = None;
let mut found = None;
for (referent_ty, autoderefs) in autoderef.by_ref() {
if autoderefs == 0 {
@ -346,8 +353,8 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
mutbl: mt_b.mutbl, // [1] above
});
match self.unify(derefd_ty_a, b) {
Ok(ty) => {
success = Some((ty, autoderefs));
Ok(ok) => {
found = Some((ok, autoderefs));
break;
}
Err(err) => {
@ -363,7 +370,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
// (e.g., in example above, the failure from relating `Vec<T>`
// to the target type), since that should be the least
// confusing.
let (ty, autoderefs) = match success {
let (InferOk { value: ty, mut obligations }, autoderefs) = match found {
Some(d) => d,
None => {
let err = first_error.expect("coerce_borrowed_pointer had no error");
@ -372,12 +379,6 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
}
};
// This commits the obligations to the fulfillcx. After this succeeds,
// this snapshot can't be rolled back.
autoderef.finalize(LvaluePreference::from_mutbl(mt_b.mutbl), exprs());
// Now apply the autoref. We have to extract the region out of
// the final ref type we got.
if ty == a && mt_a.mutbl == hir::MutImmutable && autoderefs == 1 {
// As a special case, if we would produce `&'a *x`, that's
// a total no-op. We end up with the type `&'a T` just as
@ -391,8 +392,11 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
// `self.x`, but we auto-coerce it to `foo(&mut *self.x)`,
// which is a borrow.
assert_eq!(mt_b.mutbl, hir::MutImmutable); // can only coerce &T -> &U
return self.identity(ty);
return success(identity(), ty, obligations);
}
// 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_borrow, _) => r_borrow,
_ => span_bug!(span, "expected a ref type, got {:?}", ty),
@ -402,11 +406,15 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
ty,
autoderefs,
autoref);
Ok((ty, Adjust::DerefRef {
let pref = LvaluePreference::from_mutbl(mt_b.mutbl);
obligations.extend(autoderef.finalize_as_infer_ok(pref, exprs()).obligations);
success(Adjust::DerefRef {
autoderefs: autoderefs,
autoref: autoref,
unsize: false,
}))
}, ty, obligations)
}
@ -451,7 +459,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
// Use a FIFO queue for this custom fulfillment procedure.
let mut queue = VecDeque::new();
let mut leftover_predicates = vec![];
let mut obligations = vec![];
// Create an obligation for `Source: CoerceUnsized<Target>`.
let cause = ObligationCause::misc(self.cause.span, self.body_id);
@ -467,7 +475,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
let trait_ref = match obligation.predicate {
ty::Predicate::Trait(ref tr) if traits.contains(&tr.def_id()) => tr.clone(),
_ => {
leftover_predicates.push(obligation);
obligations.push(obligation);
continue;
}
};
@ -495,33 +503,30 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
}
}
*self.unsizing_obligations.borrow_mut() = leftover_predicates;
let adjustment = Adjust::DerefRef {
success(Adjust::DerefRef {
autoderefs: if reborrow.is_some() { 1 } else { 0 },
autoref: reborrow,
unsize: true,
};
debug!("Success, coerced with {:?}", adjustment);
Ok((target, adjustment))
}, target, obligations)
}
fn coerce_from_safe_fn(&self,
a: Ty<'tcx>,
fn_ty_a: ty::PolyFnSig<'tcx>,
b: Ty<'tcx>)
b: Ty<'tcx>,
to_unsafe: Adjust<'tcx>,
normal: Adjust<'tcx>)
-> CoerceResult<'tcx> {
if let ty::TyFnPtr(fn_ty_b) = b.sty {
match (fn_ty_a.unsafety(), fn_ty_b.unsafety()) {
(hir::Unsafety::Normal, hir::Unsafety::Unsafe) => {
let unsafe_a = self.tcx.safe_to_unsafe_fn_ty(fn_ty_a);
return self.unify_and_identity(unsafe_a, b)
.map(|(ty, _)| (ty, Adjust::UnsafeFnPointer));
return self.unify_and(unsafe_a, b, to_unsafe);
}
_ => {}
}
}
self.unify_and_identity(a, b)
self.unify_and(a, b, normal)
}
fn coerce_from_fn_pointer(&self,
@ -536,7 +541,8 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
let b = self.shallow_resolve(b);
debug!("coerce_from_fn_pointer(a={:?}, b={:?})", a, b);
self.coerce_from_safe_fn(a, fn_ty_a, b)
self.coerce_from_safe_fn(a, fn_ty_a, b,
Adjust::UnsafeFnPointer, identity())
}
fn coerce_from_fn_item(&self,
@ -554,10 +560,10 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
match b.sty {
ty::TyFnPtr(_) => {
let a_fn_pointer = self.tcx.mk_fn_ptr(fn_ty_a);
self.coerce_from_safe_fn(a_fn_pointer, fn_ty_a, b)
.map(|(ty, _)| (ty, Adjust::ReifyFnPointer))
self.coerce_from_safe_fn(a_fn_pointer, fn_ty_a, b,
Adjust::ReifyFnPointer, Adjust::ReifyFnPointer)
}
_ => self.unify_and_identity(a, b),
_ => self.unify_and(a, b, identity()),
}
}
@ -582,7 +588,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
self.cause.span,
feature_gate::GateIssue::Language,
feature_gate::CLOSURE_TO_FN_COERCION);
return self.unify_and_identity(a, b);
return self.unify_and(a, b, identity());
}
// We coerce the closure, which has fn type
// `extern "rust-call" fn((arg0,arg1,...)) -> _`
@ -607,10 +613,9 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
let pointer_ty = self.tcx.mk_fn_ptr(converted_sig);
debug!("coerce_closure_to_fn(a={:?}, b={:?}, pty={:?})",
a, b, pointer_ty);
self.unify_and_identity(pointer_ty, b)
.map(|(ty, _)| (ty, Adjust::ClosureFnPointer))
self.unify_and(pointer_ty, b, Adjust::ClosureFnPointer)
}
_ => self.unify_and_identity(a, b),
_ => self.unify_and(a, b, identity()),
}
}
@ -625,7 +630,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
ty::TyRef(_, mt) => (true, mt),
ty::TyRawPtr(mt) => (false, mt),
_ => {
return self.unify_and_identity(a, b);
return self.unify_and(a, b, identity());
}
};
@ -634,52 +639,24 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
mutbl: mutbl_b,
ty: mt_a.ty,
});
let (ty, noop) = self.unify_and_identity(a_unsafe, b)?;
coerce_mutbls(mt_a.mutbl, mutbl_b)?;
// Although references and unsafe ptrs have the same
// representation, we still register an Adjust::DerefRef so that
// regionck knows that the region for `a` must be valid here.
Ok((ty,
if is_ref {
Adjust::DerefRef {
autoderefs: 1,
autoref: Some(AutoBorrow::RawPtr(mutbl_b)),
unsize: false,
}
} else if mt_a.mutbl != mutbl_b {
Adjust::MutToConstPointer
} else {
noop
}))
self.unify_and(a_unsafe, b, if is_ref {
Adjust::DerefRef {
autoderefs: 1,
autoref: Some(AutoBorrow::RawPtr(mutbl_b)),
unsize: false,
}
} else if mt_a.mutbl != mutbl_b {
Adjust::MutToConstPointer
} else {
identity()
})
}
}
fn apply<'a, 'b, 'gcx, 'tcx, E, I>(coerce: &mut Coerce<'a, 'gcx, 'tcx>,
exprs: &E,
a: Ty<'tcx>,
b: Ty<'tcx>)
-> RelateResult<'tcx, Adjustment<'tcx>>
where E: Fn() -> I,
I: IntoIterator<Item = &'b hir::Expr>
{
let (ty, adjust) = indent(|| coerce.coerce(exprs, a, b))?;
let fcx = coerce.fcx;
if let Adjust::DerefRef { unsize: true, .. } = adjust {
let mut obligations = coerce.unsizing_obligations.borrow_mut();
for obligation in obligations.drain(..) {
fcx.register_predicate(obligation);
}
}
Ok(Adjustment {
kind: adjust,
target: ty
})
}
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
/// Attempt to coerce an expression to a type, and return the
/// adjusted type of the expression, if successful.
@ -694,9 +671,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);
let cause = self.cause(expr.span, ObligationCauseCode::ExprAssignable);
let mut coerce = Coerce::new(self, cause);
let coerce = Coerce::new(self, cause);
self.commit_if_ok(|_| {
let adjustment = apply(&mut coerce, &|| Some(expr), source, target)?;
let ok = coerce.coerce(&|| Some(expr), source, target)?;
let adjustment = self.register_infer_ok_obligations(ok);
if !adjustment.is_identity() {
debug!("Success, coerced with {:?}", adjustment);
match self.tables.borrow().adjustments.get(&expr.id) {
@ -773,9 +751,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// but only if the new expression has no coercion already applied to it.
let mut first_error = None;
if !self.tables.borrow().adjustments.contains_key(&new.id) {
let result = self.commit_if_ok(|_| apply(&mut coerce, &|| Some(new), new_ty, prev_ty));
let result = self.commit_if_ok(|_| coerce.coerce(&|| Some(new), new_ty, prev_ty));
match result {
Ok(adjustment) => {
Ok(ok) => {
let adjustment = self.register_infer_ok_obligations(ok);
if !adjustment.is_identity() {
self.write_adjustment(new.id, adjustment);
}
@ -816,7 +795,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
}
match self.commit_if_ok(|_| apply(&mut coerce, &exprs, prev_ty, new_ty)) {
match self.commit_if_ok(|_| coerce.coerce(&exprs, prev_ty, new_ty)) {
Err(_) => {
// Avoid giving strange errors on failed attempts.
if let Some(e) = first_error {
@ -828,7 +807,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
})
}
}
Ok(adjustment) => {
Ok(ok) => {
let adjustment = self.register_infer_ok_obligations(ok);
if !adjustment.is_identity() {
let mut tables = self.tables.borrow_mut();
for expr in exprs() {