Skip to content

Commit

Permalink
Minor refactoring in coercion.rs
Browse files Browse the repository at this point in the history
  • Loading branch information
nrc committed Mar 24, 2015
1 parent 95602a7 commit 9374c21
Showing 1 changed file with 163 additions and 147 deletions.
310 changes: 163 additions & 147 deletions src/librustc_typeck/check/coercion.rs
Expand Up @@ -258,70 +258,64 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {

match (&a.sty, &b.sty) {
(&ty::ty_rptr(_, ty::mt{ty: t_a, mutbl: mutbl_a}), &ty::ty_rptr(_, mt_b)) => {
self.unpack_actual_value(t_a, |a| {
match self.unsize_ty(t_a, a, mt_b.ty) {
Some((ty, kind)) => {
if !can_coerce_mutbls(mutbl_a, mt_b.mutbl) {
return Err(ty::terr_mutability);
}

let coercion = Coercion(self.trace.clone());
let r_borrow = self.fcx.infcx().next_region_var(coercion);
let ty = ty::mk_rptr(self.tcx(),
self.tcx().mk_region(r_borrow),
ty::mt{ty: ty, mutbl: mt_b.mutbl});
try!(self.fcx.infcx().try(|_| self.subtype(ty, b)));
debug!("Success, coerced with AutoDerefRef(1, \
AutoPtr(AutoUnsize({:?})))", kind);
Ok(Some(AdjustDerefRef(AutoDerefRef {
autoderefs: 1,
autoref: Some(ty::AutoPtr(r_borrow, mt_b.mutbl,
Some(box AutoUnsize(kind))))
})))
match self.unsize_ty(t_a, mt_b.ty) {
Some((ty, kind)) => {
if !can_coerce_mutbls(mutbl_a, mt_b.mutbl) {
return Err(ty::terr_mutability);
}
_ => Err(ty::terr_mismatch)

let coercion = Coercion(self.trace.clone());
let r_borrow = self.fcx.infcx().next_region_var(coercion);
let ty = ty::mk_rptr(self.tcx(),
self.tcx().mk_region(r_borrow),
ty::mt{ty: ty, mutbl: mt_b.mutbl});
try!(self.fcx.infcx().try(|_| self.subtype(ty, b)));
debug!("Success, coerced with AutoDerefRef(1, \
AutoPtr(AutoUnsize({:?})))", kind);
Ok(Some(AdjustDerefRef(AutoDerefRef {
autoderefs: 1,
autoref: Some(ty::AutoPtr(r_borrow, mt_b.mutbl,
Some(box AutoUnsize(kind))))
})))
}
})
_ => Err(ty::terr_mismatch)
}
}
(&ty::ty_rptr(_, ty::mt{ty: t_a, mutbl: mutbl_a}), &ty::ty_ptr(mt_b)) => {
self.unpack_actual_value(t_a, |a| {
match self.unsize_ty(t_a, a, mt_b.ty) {
Some((ty, kind)) => {
if !can_coerce_mutbls(mutbl_a, mt_b.mutbl) {
return Err(ty::terr_mutability);
}

let ty = ty::mk_ptr(self.tcx(),
ty::mt{ty: ty, mutbl: mt_b.mutbl});
try!(self.fcx.infcx().try(|_| self.subtype(ty, b)));
debug!("Success, coerced with AutoDerefRef(1, \
AutoPtr(AutoUnsize({:?})))", kind);
Ok(Some(AdjustDerefRef(AutoDerefRef {
autoderefs: 1,
autoref: Some(ty::AutoUnsafe(mt_b.mutbl,
Some(box AutoUnsize(kind))))
})))
match self.unsize_ty(t_a, mt_b.ty) {
Some((ty, kind)) => {
if !can_coerce_mutbls(mutbl_a, mt_b.mutbl) {
return Err(ty::terr_mutability);
}
_ => Err(ty::terr_mismatch)

let ty = ty::mk_ptr(self.tcx(),
ty::mt{ty: ty, mutbl: mt_b.mutbl});
try!(self.fcx.infcx().try(|_| self.subtype(ty, b)));
debug!("Success, coerced with AutoDerefRef(1, \
AutoPtr(AutoUnsize({:?})))", kind);
Ok(Some(AdjustDerefRef(AutoDerefRef {
autoderefs: 1,
autoref: Some(ty::AutoUnsafe(mt_b.mutbl,
Some(box AutoUnsize(kind))))
})))
}
})
_ => Err(ty::terr_mismatch)
}
}
(&ty::ty_uniq(t_a), &ty::ty_uniq(t_b)) => {
self.unpack_actual_value(t_a, |a| {
match self.unsize_ty(t_a, a, t_b) {
Some((ty, kind)) => {
let ty = ty::mk_uniq(self.tcx(), ty);
try!(self.fcx.infcx().try(|_| self.subtype(ty, b)));
debug!("Success, coerced with AutoDerefRef(1, \
AutoUnsizeUniq({:?}))", kind);
Ok(Some(AdjustDerefRef(AutoDerefRef {
autoderefs: 1,
autoref: Some(ty::AutoUnsizeUniq(kind))
})))
}
_ => Err(ty::terr_mismatch)
match self.unsize_ty(t_a, t_b) {
Some((ty, kind)) => {
let ty = ty::mk_uniq(self.tcx(), ty);
try!(self.fcx.infcx().try(|_| self.subtype(ty, b)));
debug!("Success, coerced with AutoDerefRef(1, \
AutoUnsizeUniq({:?}))", kind);
Ok(Some(AdjustDerefRef(AutoDerefRef {
autoderefs: 1,
autoref: Some(ty::AutoUnsizeUniq(kind))
})))
}
})
_ => Err(ty::terr_mismatch)
}
}
_ => Err(ty::terr_mismatch)
}
Expand All @@ -332,112 +326,134 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
// E.g., `[T, ..n]` -> `([T], UnsizeLength(n))`
fn unsize_ty(&self,
ty_a: Ty<'tcx>,
a: Ty<'tcx>, // TODO unwrap ty_a here, not in the caller
ty_b: Ty<'tcx>)
-> Option<(Ty<'tcx>, ty::UnsizeKind<'tcx>)>
{
let tcx = self.tcx();

self.unpack_actual_value(ty_b, |b| {
debug!("unsize_ty(a={}, b={})", a.repr(self.tcx()), b.repr(self.tcx()));
match (&a.sty, &b.sty) {
(&ty::ty_vec(t_a, Some(len)), &ty::ty_vec(_, None)) => {
let ty = ty::mk_vec(tcx, t_a, None);
Some((ty, ty::UnsizeLength(len)))
}
(&ty::ty_trait(ref data_a), &ty::ty_trait(ref data_b)) => {
// Upcasts permit two things:
//
// 1. Dropping builtin bounds, e.g. `Foo+Send` to `Foo`
// 2. Tightening the region bound, e.g. `Foo+'a` to `Foo+'b` if `'a : 'b`
//
// Note that neither of these changes requires any
// change at runtime. Eventually this will be
// generalized.
//
// We always upcast when we can because of reason
// #2 (region bounds).
if data_a.bounds.builtin_bounds.is_superset(&data_b.bounds.builtin_bounds) {
// construct a type `a1` which is a version of
// `a` using the upcast bounds from `b`
let bounds_a1 = ty::ExistentialBounds {
// From type b
region_bound: data_b.bounds.region_bound,
builtin_bounds: data_b.bounds.builtin_bounds,

// From type a
projection_bounds: data_a.bounds.projection_bounds.clone(),
};
let ty_a1 = ty::mk_trait(tcx, data_a.principal.clone(), bounds_a1);

// relate `a1` to `b`
let result = self.fcx.infcx().try(|_| {
// it's ok to upcast from Foo+'a to Foo+'b so long as 'a : 'b
try!(self.outlives(data_a.bounds.region_bound,
data_b.bounds.region_bound));
self.subtype(ty_a1, ty_b)
});

// if that was successful, we have a coercion
match result {
Ok(_) => Some((ty_b, ty::UnsizeUpcast(ty_b))),
Err(_) => None,
}
} else {
None
self.unpack_actual_value(ty_a, |a| {
self.unpack_actual_value(ty_b, |b| {
debug!("unsize_ty(a={}, b={})", a.repr(self.tcx()), b.repr(self.tcx()));
match (&a.sty, &b.sty) {
(&ty::ty_vec(t_a, Some(len)), &ty::ty_vec(_, None)) => {
let ty = ty::mk_vec(tcx, t_a, None);
Some((ty, ty::UnsizeLength(len)))
}
}
(_, &ty::ty_trait(ref data)) => {
Some((ty_b, ty::UnsizeVtable(ty::TyTrait { principal: data.principal.clone(),
bounds: data.bounds.clone() },
ty_a)))
}
(&ty::ty_struct(did_a, substs_a), &ty::ty_struct(did_b, substs_b))
if did_a == did_b => {
debug!("unsizing a struct");
// Try unsizing each type param in turn to see if we end up with ty_b.
let ty_substs_a = substs_a.types.get_slice(subst::TypeSpace);
let ty_substs_b = substs_b.types.get_slice(subst::TypeSpace);
assert!(ty_substs_a.len() == ty_substs_b.len());

let mut result = None;
let tps = ty_substs_a.iter().zip(ty_substs_b.iter()).enumerate();
for (i, (tp_a, tp_b)) in tps {
if self.fcx.infcx().try(|_| self.subtype(*tp_a, *tp_b)).is_ok() {
continue;
(&ty::ty_trait(ref data_a), &ty::ty_trait(ref data_b)) => {
// Upcasts permit two things:
//
// 1. Dropping builtin bounds, e.g. `Foo+Send` to `Foo`
// 2. Tightening the region bound, e.g. `Foo+'a` to `Foo+'b` if `'a : 'b`
//
// Note that neither of these changes requires any
// change at runtime. Eventually this will be
// generalized.
//
// We always upcast when we can because of reason
// #2 (region bounds).
if data_a.bounds.builtin_bounds.is_superset(&data_b.bounds.builtin_bounds) {
// construct a type `a1` which is a version of
// `a` using the upcast bounds from `b`
let bounds_a1 = ty::ExistentialBounds {
// From type b
region_bound: data_b.bounds.region_bound,
builtin_bounds: data_b.bounds.builtin_bounds,

// From type a
projection_bounds: data_a.bounds.projection_bounds.clone(),
};
let ty_a1 = ty::mk_trait(tcx, data_a.principal.clone(), bounds_a1);

// relate `a1` to `b`
let result = self.fcx.infcx().try(|_| {
// it's ok to upcast from Foo+'a to Foo+'b so long as 'a : 'b
try!(self.outlives(data_a.bounds.region_bound,
data_b.bounds.region_bound));
self.subtype(ty_a1, ty_b)
});

// if that was successful, we have a coercion
match result {
Ok(_) => Some((ty_b, ty::UnsizeUpcast(ty_b))),
Err(_) => None,
}
} else {
None
}
match
self.unpack_actual_value(
*tp_a,
|tp| self.unsize_ty(*tp_a, tp, *tp_b))
}
(&ty::ty_trait(ref data_a), &ty::ty_trait(ref data_b)) => {
// For now, we only support upcasts from
// `Foo+Send` to `Foo` (really, any time there are
// fewer builtin bounds then before). These are
// convenient because they don't require any sort
// of change to the vtable at runtime.
if data_a.bounds.builtin_bounds != data_b.bounds.builtin_bounds &&
data_a.bounds.builtin_bounds.is_superset(&data_b.bounds.builtin_bounds)
{
Some((new_tp, k)) => {
// Check that the whole types match.
let mut new_substs = substs_a.clone();
new_substs.types.get_mut_slice(subst::TypeSpace)[i] = new_tp;
let ty = ty::mk_struct(tcx, did_a, tcx.mk_substs(new_substs));
if self.fcx.infcx().try(|_| self.subtype(ty, ty_b)).is_err() {
debug!("Unsized type parameter '{}', but still \
could not match types {} and {}",
ppaux::ty_to_string(tcx, *tp_a),
ppaux::ty_to_string(tcx, ty),
ppaux::ty_to_string(tcx, ty_b));
// We can only unsize a single type parameter, so
// if we unsize one and it doesn't give us the
// type we want, then we won't succeed later.
let bounds_a1 = ty::ExistentialBounds {
region_bound: data_a.bounds.region_bound,
builtin_bounds: data_b.bounds.builtin_bounds,
projection_bounds: data_a.bounds.projection_bounds.clone(),
};
let ty_a1 = ty::mk_trait(tcx, data_a.principal.clone(), bounds_a1);
match self.fcx.infcx().try(|_| self.subtype(ty_a1, ty_b)) {
Ok(_) => Some((ty_b, ty::UnsizeUpcast(ty_b))),
Err(_) => None,
}
} else {
None
}
}
(_, &ty::ty_trait(ref data)) => {
Some((ty_b, ty::UnsizeVtable(ty::TyTrait {
principal: data.principal.clone(),
bounds: data.bounds.clone()
},
ty_a)))
}
(&ty::ty_struct(did_a, substs_a), &ty::ty_struct(did_b, substs_b))
if did_a == did_b => {
debug!("unsizing a struct");
// Try unsizing each type param in turn to see if we end up with ty_b.
let ty_substs_a = substs_a.types.get_slice(subst::TypeSpace);
let ty_substs_b = substs_b.types.get_slice(subst::TypeSpace);
assert!(ty_substs_a.len() == ty_substs_b.len());

let mut result = None;
let tps = ty_substs_a.iter().zip(ty_substs_b.iter()).enumerate();
for (i, (tp_a, tp_b)) in tps {
if self.fcx.infcx().try(|_| self.subtype(*tp_a, *tp_b)).is_ok() {
continue;
}
match self.unsize_ty(*tp_a, *tp_b) {
Some((new_tp, k)) => {
// Check that the whole types match.
let mut new_substs = substs_a.clone();
new_substs.types.get_mut_slice(subst::TypeSpace)[i] = new_tp;
let ty = ty::mk_struct(tcx, did_a, tcx.mk_substs(new_substs));
if self.fcx.infcx().try(|_| self.subtype(ty, ty_b)).is_err() {
debug!("Unsized type parameter '{}', but still \
could not match types {} and {}",
ppaux::ty_to_string(tcx, *tp_a),
ppaux::ty_to_string(tcx, ty),
ppaux::ty_to_string(tcx, ty_b));
// We can only unsize a single type parameter, so
// if we unsize one and it doesn't give us the
// type we want, then we won't succeed later.
break;
}

result = Some((ty, ty::UnsizeStruct(box k, i)));
break;
}

result = Some((ty, ty::UnsizeStruct(box k, i)));
break;
None => {}
}
None => {}
}
result
}
result
_ => None
}
_ => None
}
})
})
}

Expand Down

0 comments on commit 9374c21

Please sign in to comment.