about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEduard Burtescu <edy.burt@gmail.com>2015-06-05 20:49:23 +0300
committerEduard Burtescu <edy.burt@gmail.com>2015-06-05 20:49:23 +0300
commit01dee1b77e3f594256fc380c348f7ba8fcff41db (patch)
treef6b8697355b415a94ad4f72209aa2dcb48cee697
parent19a39710c494d9300ffdc4e3f40dfda0ef62f7ba (diff)
downloadrust-01dee1b77e3f594256fc380c348f7ba8fcff41db.tar.gz
rust-01dee1b77e3f594256fc380c348f7ba8fcff41db.zip
Allow nested generics for the last field of structs in unsizing.
-rw-r--r--src/librustc/middle/traits/select.rs46
-rw-r--r--src/test/run-pass/dst-coerce-rc.rs5
2 files changed, 36 insertions, 15 deletions
diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs
index b568d40ba1c..4ea6f0cb26e 100644
--- a/src/librustc/middle/traits/select.rs
+++ b/src/librustc/middle/traits/select.rs
@@ -2523,41 +2523,57 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     ty::lookup_field_type_unsubstituted(tcx, def_id, f.id)
                 }).collect::<Vec<_>>();
 
-                // FIXME(#25351) The last field of the structure has to exist and be a
-                // type parameter (for now, to avoid tracking edge cases).
-                let i = if let Some(&ty::ty_param(p)) = fields.last().map(|ty| &ty.sty) {
-                    assert!(p.space == TypeSpace);
-                    p.idx as usize
+                // The last field of the structure has to exist and contain type parameters.
+                let field = if let Some(&field) = fields.last() {
+                    field
                 } else {
                     return Err(Unimplemented);
                 };
+                let mut ty_params = vec![];
+                ty::walk_ty(field, |ty| {
+                    if let ty::ty_param(p) = ty.sty {
+                        assert!(p.space == TypeSpace);
+                        let idx = p.idx as usize;
+                        if !ty_params.contains(&idx) {
+                            ty_params.push(idx);
+                        }
+                    }
+                });
+                if ty_params.is_empty() {
+                    return Err(Unimplemented);
+                }
 
-                // Replace the type parameter chosen for unsizing with
-                // ty_err and ensure it does not affect any other fields.
+                // Replace type parameters used in unsizing with
+                // ty_err and ensure they do not affect any other fields.
                 // This could be checked after type collection for any struct
                 // with a potentially unsized trailing field.
                 let mut new_substs = substs_a.clone();
-                new_substs.types.get_mut_slice(TypeSpace)[i] = tcx.types.err;
+                for &i in &ty_params {
+                    new_substs.types.get_mut_slice(TypeSpace)[i] = tcx.types.err;
+                }
                 for &ty in fields.init() {
                     if ty::type_is_error(ty.subst(tcx, &new_substs)) {
                         return Err(Unimplemented);
                     }
                 }
 
-                // Extract T and U from Struct<T> and Struct<U>.
-                let inner_source = *substs_a.types.get(TypeSpace, i);
-                let inner_target = *substs_b.types.get(TypeSpace, i);
+                // Extract Field<T> and Field<U> from Struct<T> and Struct<U>.
+                let inner_source = field.subst(tcx, substs_a);
+                let inner_target = field.subst(tcx, substs_b);
 
-                // Check that all the source structure with the unsized
-                // type parameter is a subtype of the target.
-                new_substs.types.get_mut_slice(TypeSpace)[i] = inner_target;
+                // Check that the source structure with the target's
+                // type parameters is a subtype of the target.
+                for &i in &ty_params {
+                    let param_b = *substs_b.types.get(TypeSpace, i);
+                    new_substs.types.get_mut_slice(TypeSpace)[i] = param_b;
+                }
                 let new_struct = ty::mk_struct(tcx, def_id, tcx.mk_substs(new_substs));
                 let origin = infer::Misc(obligation.cause.span);
                 if self.infcx.sub_types(false, origin, new_struct, target).is_err() {
                     return Err(Unimplemented);
                 }
 
-                // Construct the nested T: Unsize<U> predicate.
+                // Construct the nested Field<T>: Unsize<Field<U>> predicate.
                 nested.push(util::predicate_for_trait_def(tcx,
                     obligation.cause.clone(),
                     obligation.predicate.def_id(),
diff --git a/src/test/run-pass/dst-coerce-rc.rs b/src/test/run-pass/dst-coerce-rc.rs
index 32e7a6279c8..67dd4021cb5 100644
--- a/src/test/run-pass/dst-coerce-rc.rs
+++ b/src/test/run-pass/dst-coerce-rc.rs
@@ -12,6 +12,7 @@
 
 #![feature(core)]
 
+use std::cell::RefCell;
 use std::rc::Rc;
 
 trait Baz {
@@ -36,4 +37,8 @@ fn main() {
     assert_eq!(b.get(), 42);
 
     let _c = b.clone();
+
+    let a: Rc<RefCell<i32>> = Rc::new(RefCell::new(42));
+    let b: Rc<RefCell<Baz>> = a.clone();
+    assert_eq!(b.borrow().get(), 42);
 }