about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorNick Cameron <ncameron@mozilla.com>2015-03-23 15:26:51 +1300
committerNick Cameron <ncameron@mozilla.com>2015-03-25 10:06:13 +1300
commit9374c216f6040189301aeb043d317c12ffb17a0b (patch)
tree443d83ee955ff7a9ebf840c59a9306eeaf5ab900 /src
parent95602a759d9190cad92279aa5929d30166f2255c (diff)
Minor refactoring in coercion.rs
Diffstat (limited to 'src')
-rw-r--r--src/librustc_typeck/check/coercion.rs310
1 files changed, 163 insertions, 147 deletions
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());
-
-                    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
-            }
+            })
         })
     }