diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 920154bc916..8ad11e19b34 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -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) } @@ -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()); + (&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, - 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; + // 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 - } + }) }) }