about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-06-29 15:04:31 +0000
committerbors <bors@rust-lang.org>2017-06-29 15:04:31 +0000
commit686ec28ceaedb2a26423d48b90da210d491915cc (patch)
treec6010b7391aba410170fad64d310b1dc3eee2f13
parentd0e0f53376766308850d3f79ec6ed0b12dc10e49 (diff)
parent94862c601be15a8edd93539244177f5a9145374d (diff)
downloadrust-686ec28ceaedb2a26423d48b90da210d491915cc.tar.gz
rust-686ec28ceaedb2a26423d48b90da210d491915cc.zip
Auto merge of #42527 - qnighy:unsized-tuple-coercions, r=arielb1
Unsized tuple coercions

Part of #18469. Fixes #32702.

#37685 and #34451 might also be related.

This PR does the following:

- Introduce explicit `Sized` constraints on tuple initializers, similar to that of record-struct initializers. Not much relevant to the main contribution but I noticed this when making tests for unsized tuple coercions.
- Implement `(.., T): Unsize<(.., U)>` where `T: Unsize<U>`.
- Assume `(.., T)` is MaybeUnsizedUnivariant.
- Modify `src/librustc/ty/util.rs` and `src/librustc_trans/glue.rs` so that tuples and structs are uniformly traversed when translating.
-rw-r--r--src/doc/unstable-book/src/language-features/unsized-tuple-coercion.md27
-rw-r--r--src/librustc/traits/error_reporting.rs5
-rw-r--r--src/librustc/traits/mod.rs2
-rw-r--r--src/librustc/traits/select.rs40
-rw-r--r--src/librustc/traits/structural_impls.rs3
-rw-r--r--src/librustc/ty/layout.rs10
-rw-r--r--src/librustc/ty/util.rs29
-rw-r--r--src/librustc_trans/glue.rs12
-rw-r--r--src/librustc_typeck/check/coercion.rs21
-rw-r--r--src/librustc_typeck/check/mod.rs1
-rw-r--r--src/libsyntax/feature_gate.rs6
-rw-r--r--src/test/compile-fail/dst-bad-assign-3.rs50
-rw-r--r--src/test/compile-fail/dst-bad-coerce1.rs14
-rw-r--r--src/test/compile-fail/dst-bad-coerce2.rs10
-rw-r--r--src/test/compile-fail/dst-bad-coerce3.rs12
-rw-r--r--src/test/compile-fail/dst-bad-coerce4.rs10
-rw-r--r--src/test/compile-fail/dst-bad-deep-2.rs23
-rw-r--r--src/test/compile-fail/feature-gate-unsized_tuple_coercion.rs14
-rw-r--r--src/test/compile-fail/unsized3.rs1
-rw-r--r--src/test/run-pass-valgrind/dst-dtor-3.rs34
-rw-r--r--src/test/run-pass-valgrind/dst-dtor-4.rs31
-rw-r--r--src/test/run-pass/dst-irrefutable-bind.rs12
-rw-r--r--src/test/run-pass/dst-raw.rs34
-rw-r--r--src/test/run-pass/dst-trait-tuple.rs111
-rw-r--r--src/test/run-pass/dst-tuple-sole.rs85
-rw-r--r--src/test/run-pass/dst-tuple.rs129
26 files changed, 707 insertions, 19 deletions
diff --git a/src/doc/unstable-book/src/language-features/unsized-tuple-coercion.md b/src/doc/unstable-book/src/language-features/unsized-tuple-coercion.md
new file mode 100644
index 00000000000..200a9c19462
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/unsized-tuple-coercion.md
@@ -0,0 +1,27 @@
+# `unsized_tuple_coercion`
+
+The tracking issue for this feature is: [#42877]
+
+[#42877]: https://github.com/rust-lang/rust/issues/42877
+
+------------------------
+
+This is a part of [RFC0401]. According to the RFC, there should be an implementation like this:
+
+```rust
+impl<..., T, U: ?Sized> Unsized<(..., U)> for (..., T) where T: Unsized<U> {}
+```
+
+This implementation is currently gated behind `#[feature(unsized_tuple_coercion)]` to avoid insta-stability. Therefore you can use it like this:
+
+```rust
+#![feature(unsized_tuple_coercion)]
+
+fn main() {
+    let x : ([i32; 3], [i32; 3]) = ([1, 2, 3], [4, 5, 6]);
+    let y : &([i32; 3], [i32]) = &x;
+    assert_eq!(y.1[0], 4);
+}
+```
+
+[RFC0401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index e2e3d520d47..0bf0e21baaf 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -1060,7 +1060,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                 err.note("slice and array elements must have `Sized` type");
             }
             ObligationCauseCode::TupleElem => {
-                err.note("tuple elements must have `Sized` type");
+                err.note("only the last element of a tuple may have a dynamically sized type");
             }
             ObligationCauseCode::ProjectionWf(data) => {
                 err.note(&format!("required so that the projection `{}` is well-formed",
@@ -1097,6 +1097,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             ObligationCauseCode::AssignmentLhsSized => {
                 err.note("the left-hand-side of an assignment must have a statically known size");
             }
+            ObligationCauseCode::TupleInitializerSized => {
+                err.note("tuples must have a statically known size to be initialized");
+            }
             ObligationCauseCode::StructInitializerSized => {
                 err.note("structs must have a statically known size to be initialized");
             }
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index c128438aea0..16c41c816b4 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -121,6 +121,8 @@ pub enum ObligationCauseCode<'tcx> {
     // Various cases where expressions must be sized/copy/etc:
     /// L = X implies that L is Sized
     AssignmentLhsSized,
+    /// (x1, .., xn) must be Sized
+    TupleInitializerSized,
     /// S { ... } must be Sized
     StructInitializerSized,
     /// Type of each variable must be Sized
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index a66b6b86354..856fea7c2c4 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -1651,6 +1651,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 def_id_a == def_id_b
             }
 
+            // (.., T) -> (.., U).
+            (&ty::TyTuple(tys_a, _), &ty::TyTuple(tys_b, _)) => {
+                tys_a.len() == tys_b.len()
+            }
+
             _ => false
         };
 
@@ -2591,8 +2596,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 let inner_source = field.subst(tcx, substs_a);
                 let inner_target = field.subst(tcx, substs_b);
 
-                // Check that the source structure with the target's
-                // type parameters is a subtype of the target.
+                // Check that the source struct with the target's
+                // unsized parameters is equal to the target.
                 let params = substs_a.iter().enumerate().map(|(i, &k)| {
                     if ty_params.contains(i) {
                         Kind::from(substs_b.type_at(i))
@@ -2617,6 +2622,37 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                     &[inner_target]));
             }
 
+            // (.., T) -> (.., U).
+            (&ty::TyTuple(tys_a, _), &ty::TyTuple(tys_b, _)) => {
+                assert_eq!(tys_a.len(), tys_b.len());
+
+                // The last field of the tuple has to exist.
+                let (a_last, a_mid) = if let Some(x) = tys_a.split_last() {
+                    x
+                } else {
+                    return Err(Unimplemented);
+                };
+                let b_last = tys_b.last().unwrap();
+
+                // Check that the source tuple with the target's
+                // last element is equal to the target.
+                let new_tuple = tcx.mk_tup(a_mid.iter().chain(Some(b_last)), false);
+                let InferOk { obligations, .. } =
+                    self.infcx.at(&obligation.cause, obligation.param_env)
+                              .eq(target, new_tuple)
+                              .map_err(|_| Unimplemented)?;
+                self.inferred_obligations.extend(obligations);
+
+                // Construct the nested T: Unsize<U> predicate.
+                nested.push(tcx.predicate_for_trait_def(
+                    obligation.param_env,
+                    obligation.cause.clone(),
+                    obligation.predicate.def_id(),
+                    obligation.recursion_depth + 1,
+                    a_last,
+                    &[b_last]));
+            }
+
             _ => bug!()
         };
 
diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs
index 0d6df78c2ac..c4479e69032 100644
--- a/src/librustc/traits/structural_impls.rs
+++ b/src/librustc/traits/structural_impls.rs
@@ -189,6 +189,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
                 tcx.lift(&ty).map(super::ObjectCastObligation)
             }
             super::AssignmentLhsSized => Some(super::AssignmentLhsSized),
+            super::TupleInitializerSized => Some(super::TupleInitializerSized),
             super::StructInitializerSized => Some(super::StructInitializerSized),
             super::VariableType(id) => Some(super::VariableType(id)),
             super::ReturnType(id) => Some(super::ReturnType(id)),
@@ -476,6 +477,7 @@ impl<'tcx> TypeFoldable<'tcx> for traits::ObligationCauseCode<'tcx> {
             super::TupleElem |
             super::ItemObligation(_) |
             super::AssignmentLhsSized |
+            super::TupleInitializerSized |
             super::StructInitializerSized |
             super::VariableType(_) |
             super::ReturnType(_) |
@@ -523,6 +525,7 @@ impl<'tcx> TypeFoldable<'tcx> for traits::ObligationCauseCode<'tcx> {
             super::TupleElem |
             super::ItemObligation(_) |
             super::AssignmentLhsSized |
+            super::TupleInitializerSized |
             super::StructInitializerSized |
             super::VariableType(_) |
             super::ReturnType(_) |
diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs
index 12af56d5c3d..e1aa89078a3 100644
--- a/src/librustc/ty/layout.rs
+++ b/src/librustc/ty/layout.rs
@@ -1220,12 +1220,16 @@ impl<'a, 'tcx> Layout {
             }
 
             ty::TyTuple(tys, _) => {
-                // FIXME(camlorn): if we ever allow unsized tuples, this needs to be checked.
-                // See the univariant case below to learn how.
+                let kind = if tys.len() == 0 {
+                    StructKind::AlwaysSizedUnivariant
+                } else {
+                    StructKind::MaybeUnsizedUnivariant
+                };
+
                 let st = Struct::new(dl,
                     &tys.iter().map(|ty| ty.layout(tcx, param_env))
                       .collect::<Result<Vec<_>, _>>()?,
-                    &ReprOptions::default(), StructKind::AlwaysSizedUnivariant, ty)?;
+                    &ReprOptions::default(), kind, ty)?;
                 Univariant { variant: st, non_zero: false }
             }
 
diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs
index 98ef7918fef..df4bbad3859 100644
--- a/src/librustc/ty/util.rs
+++ b/src/librustc/ty/util.rs
@@ -317,15 +317,26 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
                                  target: Ty<'tcx>)
                                  -> (Ty<'tcx>, Ty<'tcx>) {
         let (mut a, mut b) = (source, target);
-        while let (&TyAdt(a_def, a_substs), &TyAdt(b_def, b_substs)) = (&a.sty, &b.sty) {
-            if a_def != b_def || !a_def.is_struct() {
-                break;
-            }
-            match a_def.struct_variant().fields.last() {
-                Some(f) => {
-                    a = f.ty(self, a_substs);
-                    b = f.ty(self, b_substs);
-                }
+        loop {
+            match (&a.sty, &b.sty) {
+                (&TyAdt(a_def, a_substs), &TyAdt(b_def, b_substs))
+                        if a_def == b_def && a_def.is_struct() => {
+                    if let Some(f) = a_def.struct_variant().fields.last() {
+                        a = f.ty(self, a_substs);
+                        b = f.ty(self, b_substs);
+                    } else {
+                        break;
+                    }
+                },
+                (&TyTuple(a_tys, _), &TyTuple(b_tys, _))
+                        if a_tys.len() == b_tys.len() => {
+                    if let Some(a_last) = a_tys.last() {
+                        a = a_last;
+                        b = b_tys.last().unwrap();
+                    } else {
+                        break;
+                    }
+                },
                 _ => break,
             }
         }
diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs
index 367f0398fa8..c2f44c089a2 100644
--- a/src/librustc_trans/glue.rs
+++ b/src/librustc_trans/glue.rs
@@ -76,7 +76,7 @@ pub fn size_and_align_of_dst<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, inf
     }
     assert!(!info.is_null());
     match t.sty {
-        ty::TyAdt(def, substs) => {
+        ty::TyAdt(..) | ty::TyTuple(..) => {
             let ccx = bcx.ccx;
             // First get the size of all statically known fields.
             // Don't use size_of because it also rounds up to alignment, which we
@@ -101,8 +101,14 @@ pub fn size_and_align_of_dst<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, inf
 
             // Recurse to get the size of the dynamically sized field (must be
             // the last field).
-            let last_field = def.struct_variant().fields.last().unwrap();
-            let field_ty = monomorphize::field_ty(bcx.tcx(), substs, last_field);
+            let field_ty = match t.sty {
+                ty::TyAdt(def, substs) => {
+                    let last_field = def.struct_variant().fields.last().unwrap();
+                    monomorphize::field_ty(bcx.tcx(), substs, last_field)
+                },
+                ty::TyTuple(tys, _) => tys.last().unwrap(),
+                _ => unreachable!(),
+            };
             let (unsized_size, unsized_align) = size_and_align_of_dst(bcx, field_ty, info);
 
             // FIXME (#26403, #27023): We should be adding padding
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index 81aa59e956a..968e893b9a0 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -76,6 +76,7 @@ use rustc::ty::relate::RelateResult;
 use rustc::ty::subst::Subst;
 use errors::DiagnosticBuilder;
 use syntax::abi;
+use syntax::feature_gate;
 use syntax::ptr::P;
 use syntax_pos;
 
@@ -520,6 +521,8 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
                                                          coerce_source,
                                                          &[coerce_target]));
 
+        let mut has_unsized_tuple_coercion = false;
+
         // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid
         // emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where
         // inference might unify those two inner type variables later.
@@ -527,7 +530,15 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
         while let Some(obligation) = queue.pop_front() {
             debug!("coerce_unsized resolve step: {:?}", obligation);
             let trait_ref = match obligation.predicate {
-                ty::Predicate::Trait(ref tr) if traits.contains(&tr.def_id()) => tr.clone(),
+                ty::Predicate::Trait(ref tr) if traits.contains(&tr.def_id()) => {
+                    if unsize_did == tr.def_id() {
+                        if let ty::TyTuple(..) = tr.0.input_types().nth(1).unwrap().sty {
+                            debug!("coerce_unsized: found unsized tuple coercion");
+                            has_unsized_tuple_coercion = true;
+                        }
+                    }
+                    tr.clone()
+                }
                 _ => {
                     coercion.obligations.push(obligation);
                     continue;
@@ -557,6 +568,14 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
             }
         }
 
+        if has_unsized_tuple_coercion && !self.tcx.sess.features.borrow().unsized_tuple_coercion {
+            feature_gate::emit_feature_err(&self.tcx.sess.parse_sess,
+                                           "unsized_tuple_coercion",
+                                           self.cause.span,
+                                           feature_gate::GateIssue::Language,
+                                           feature_gate::EXPLAIN_UNSIZED_TUPLE_COERCION);
+        }
+
         Ok(coercion)
     }
 
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index a8d0be2bd04..967df57e153 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -3854,6 +3854,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             if tuple.references_error() {
                 tcx.types.err
             } else {
+                self.require_type_is_sized(tuple, expr.span, traits::TupleInitializerSized);
                 tuple
             }
           }
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 74bf19b841e..df8ee189d21 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -357,6 +357,9 @@ declare_features! (
 
     // Allows a test to fail without failing the whole suite
     (active, allow_fail, "1.19.0", Some(42219)),
+
+    // Allows unsized tuple coercion.
+    (active, unsized_tuple_coercion, "1.20.0", Some(42877)),
 );
 
 declare_features! (
@@ -1041,6 +1044,9 @@ pub const EXPLAIN_VIS_MATCHER: &'static str =
 pub const EXPLAIN_PLACEMENT_IN: &'static str =
     "placement-in expression syntax is experimental and subject to change.";
 
+pub const EXPLAIN_UNSIZED_TUPLE_COERCION: &'static str =
+    "Unsized tuple coercion is not stable enough for use and is subject to change";
+
 struct PostExpansionVisitor<'a> {
     context: &'a Context<'a>,
 }
diff --git a/src/test/compile-fail/dst-bad-assign-3.rs b/src/test/compile-fail/dst-bad-assign-3.rs
new file mode 100644
index 00000000000..1c3bad5ba56
--- /dev/null
+++ b/src/test/compile-fail/dst-bad-assign-3.rs
@@ -0,0 +1,50 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Forbid assignment into a dynamically sized type.
+
+#![feature(unsized_tuple_coercion)]
+
+type Fat<T: ?Sized> = (isize, &'static str, T);
+//~^ WARNING trait bounds are not (yet) enforced
+
+#[derive(PartialEq,Eq)]
+struct Bar;
+
+#[derive(PartialEq,Eq)]
+struct Bar1 {
+    f: isize
+}
+
+trait ToBar {
+    fn to_bar(&self) -> Bar;
+    fn to_val(&self) -> isize;
+}
+
+impl ToBar for Bar1 {
+    fn to_bar(&self) -> Bar {
+        Bar
+    }
+    fn to_val(&self) -> isize {
+        self.f
+    }
+}
+
+pub fn main() {
+    // Assignment.
+    let f5: &mut Fat<ToBar> = &mut (5, "some str", Bar1 {f :42});
+    let z: Box<ToBar> = Box::new(Bar1 {f: 36});
+    f5.2 = Bar1 {f: 36};
+    //~^ ERROR mismatched types
+    //~| expected type `ToBar`
+    //~| found type `Bar1`
+    //~| expected trait ToBar, found struct `Bar1`
+    //~| ERROR `ToBar: std::marker::Sized` is not satisfied
+}
diff --git a/src/test/compile-fail/dst-bad-coerce1.rs b/src/test/compile-fail/dst-bad-coerce1.rs
index 9a3ea54a3a4..b0de84a5300 100644
--- a/src/test/compile-fail/dst-bad-coerce1.rs
+++ b/src/test/compile-fail/dst-bad-coerce1.rs
@@ -10,6 +10,8 @@
 
 // Attempt to change the type as well as unsizing.
 
+#![feature(unsized_tuple_coercion)]
+
 struct Fat<T: ?Sized> {
     ptr: T
 }
@@ -29,4 +31,16 @@ pub fn main() {
     let f2: &Fat<Foo> = &f1;
     let f3: &Fat<Bar> = f2;
     //~^ ERROR `Foo: Bar` is not satisfied
+
+    // Tuple with a vec of isize.
+    let f1 = ([1, 2, 3],);
+    let f2: &([isize; 3],) = &f1;
+    let f3: &([usize],) = f2;
+    //~^ ERROR mismatched types
+
+    // Tuple with a trait.
+    let f1 = (Foo,);
+    let f2: &(Foo,) = &f1;
+    let f3: &(Bar,) = f2;
+    //~^ ERROR `Foo: Bar` is not satisfied
 }
diff --git a/src/test/compile-fail/dst-bad-coerce2.rs b/src/test/compile-fail/dst-bad-coerce2.rs
index 160197368d6..9e92f649b2d 100644
--- a/src/test/compile-fail/dst-bad-coerce2.rs
+++ b/src/test/compile-fail/dst-bad-coerce2.rs
@@ -28,4 +28,14 @@ pub fn main() {
     let f1 = Fat { ptr: Foo };
     let f2: &Fat<Foo> = &f1;
     let f3: &mut Fat<Bar> = f2; //~ ERROR mismatched types
+
+    // Tuple with a vec of ints.
+    let f1 = ([1, 2, 3],);
+    let f2: &([isize; 3],) = &f1;
+    let f3: &mut ([isize],) = f2; //~ ERROR mismatched types
+
+    // Tuple with a trait.
+    let f1 = (Foo,);
+    let f2: &(Foo,) = &f1;
+    let f3: &mut (Bar,) = f2; //~ ERROR mismatched types
 }
diff --git a/src/test/compile-fail/dst-bad-coerce3.rs b/src/test/compile-fail/dst-bad-coerce3.rs
index 7bad3bd69d3..35a147c15bb 100644
--- a/src/test/compile-fail/dst-bad-coerce3.rs
+++ b/src/test/compile-fail/dst-bad-coerce3.rs
@@ -10,6 +10,8 @@
 
 // Attempt to extend the lifetime as well as unsizing.
 
+#![feature(unsized_tuple_coercion)]
+
 struct Fat<T: ?Sized> {
     ptr: T
 }
@@ -28,6 +30,16 @@ fn baz<'a>() {
     let f1 = Fat { ptr: Foo };
     let f2: &Fat<Foo> = &f1; //~ ERROR `f1` does not live long enough
     let f3: &'a Fat<Bar> = f2;
+
+    // Tuple with a vec of ints.
+    let f1 = ([1, 2, 3],);
+    let f2: &([isize; 3],) = &f1; //~ ERROR `f1` does not live long enough
+    let f3: &'a ([isize],) = f2;
+
+    // Tuple with a trait.
+    let f1 = (Foo,);
+    let f2: &(Foo,) = &f1; //~ ERROR `f1` does not live long enough
+    let f3: &'a (Bar,) = f2;
 }
 
 pub fn main() {
diff --git a/src/test/compile-fail/dst-bad-coerce4.rs b/src/test/compile-fail/dst-bad-coerce4.rs
index 9d4d56cf791..874b7588ff9 100644
--- a/src/test/compile-fail/dst-bad-coerce4.rs
+++ b/src/test/compile-fail/dst-bad-coerce4.rs
@@ -10,6 +10,8 @@
 
 // Attempt to coerce from unsized to sized.
 
+#![feature(unsized_tuple_coercion)]
+
 struct Fat<T: ?Sized> {
     ptr: T
 }
@@ -22,4 +24,12 @@ pub fn main() {
     //~| expected type `&Fat<[isize; 3]>`
     //~| found type `&Fat<[isize]>`
     //~| expected array of 3 elements, found slice
+
+    // Tuple with a vec of isizes.
+    let f1: &([isize],) = &([1, 2, 3],);
+    let f2: &([isize; 3],) = f1;
+    //~^ ERROR mismatched types
+    //~| expected type `&([isize; 3],)`
+    //~| found type `&([isize],)`
+    //~| expected array of 3 elements, found slice
 }
diff --git a/src/test/compile-fail/dst-bad-deep-2.rs b/src/test/compile-fail/dst-bad-deep-2.rs
new file mode 100644
index 00000000000..0c812b1d815
--- /dev/null
+++ b/src/test/compile-fail/dst-bad-deep-2.rs
@@ -0,0 +1,23 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Try to initialise a DST struct where the lost information is deeply nested.
+// This is an error because it requires an unsized rvalue. This is a problem
+// because it would require stack allocation of an unsized temporary (*g in the
+// test).
+
+#![feature(unsized_tuple_coercion)]
+
+pub fn main() {
+    let f: ([isize; 3],) = ([5, 6, 7],);
+    let g: &([isize],) = &f;
+    let h: &(([isize],),) = &(*g,);
+    //~^ ERROR `[isize]: std::marker::Sized` is not satisfied
+}
diff --git a/src/test/compile-fail/feature-gate-unsized_tuple_coercion.rs b/src/test/compile-fail/feature-gate-unsized_tuple_coercion.rs
new file mode 100644
index 00000000000..4ddde011263
--- /dev/null
+++ b/src/test/compile-fail/feature-gate-unsized_tuple_coercion.rs
@@ -0,0 +1,14 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    let _ : &(Send,) = &((),);
+    //~^ ERROR Unsized tuple coercion is not stable enough
+}
diff --git a/src/test/compile-fail/unsized3.rs b/src/test/compile-fail/unsized3.rs
index 9b6ccf22c8d..e96e0ea3aec 100644
--- a/src/test/compile-fail/unsized3.rs
+++ b/src/test/compile-fail/unsized3.rs
@@ -54,6 +54,7 @@ fn f9<X: ?Sized>(x1: Box<S<X>>) {
 fn f10<X: ?Sized>(x1: Box<S<X>>) {
     f5(&(32, *x1));
     //~^ ERROR `X: std::marker::Sized` is not satisfied
+    //~| ERROR `X: std::marker::Sized` is not satisfied
 }
 
 pub fn main() {
diff --git a/src/test/run-pass-valgrind/dst-dtor-3.rs b/src/test/run-pass-valgrind/dst-dtor-3.rs
new file mode 100644
index 00000000000..1ae66a28a84
--- /dev/null
+++ b/src/test/run-pass-valgrind/dst-dtor-3.rs
@@ -0,0 +1,34 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// no-prefer-dynamic
+
+#![feature(unsized_tuple_coercion)]
+
+static mut DROP_RAN: bool = false;
+
+struct Foo;
+impl Drop for Foo {
+    fn drop(&mut self) {
+        unsafe { DROP_RAN = true; }
+    }
+}
+
+trait Trait { fn dummy(&self) { } }
+impl Trait for Foo {}
+
+pub fn main() {
+    {
+        let _x: Box<(i32, Trait)> = Box::<(i32, Foo)>::new((42, Foo));
+    }
+    unsafe {
+        assert!(DROP_RAN);
+    }
+}
diff --git a/src/test/run-pass-valgrind/dst-dtor-4.rs b/src/test/run-pass-valgrind/dst-dtor-4.rs
new file mode 100644
index 00000000000..e416f25bc03
--- /dev/null
+++ b/src/test/run-pass-valgrind/dst-dtor-4.rs
@@ -0,0 +1,31 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// no-prefer-dynamic
+
+#![feature(unsized_tuple_coercion)]
+
+static mut DROP_RAN: isize = 0;
+
+struct Foo;
+impl Drop for Foo {
+    fn drop(&mut self) {
+        unsafe { DROP_RAN += 1; }
+    }
+}
+
+pub fn main() {
+    {
+        let _x: Box<(i32, [Foo])> = Box::<(i32, [Foo; 3])>::new((42, [Foo, Foo, Foo]));
+    }
+    unsafe {
+        assert_eq!(DROP_RAN, 3);
+    }
+}
diff --git a/src/test/run-pass/dst-irrefutable-bind.rs b/src/test/run-pass/dst-irrefutable-bind.rs
index 9f8067f372a..b1d6c732e7f 100644
--- a/src/test/run-pass/dst-irrefutable-bind.rs
+++ b/src/test/run-pass/dst-irrefutable-bind.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(unsized_tuple_coercion)]
+
 struct Test<T: ?Sized>(T);
 
 fn main() {
@@ -21,4 +23,14 @@ fn main() {
     let slice = &[1,2,3];
     let x = Test(&slice);
     let Test(&_slice) = x;
+
+
+    let x = (10, [1,2,3]);
+    let x : &(i32, [i32]) = &x;
+
+    let & ref _y = x;
+
+    let slice = &[1,2,3];
+    let x = (10, &slice);
+    let (_, &_slice) = x;
 }
diff --git a/src/test/run-pass/dst-raw.rs b/src/test/run-pass/dst-raw.rs
index 3a74626b029..9ebfbee8a33 100644
--- a/src/test/run-pass/dst-raw.rs
+++ b/src/test/run-pass/dst-raw.rs
@@ -11,6 +11,8 @@
 // Test DST raw pointers
 
 
+#![feature(unsized_tuple_coercion)]
+
 trait Trait {
     fn foo(&self) -> isize;
 }
@@ -45,6 +47,14 @@ pub fn main() {
     };
     assert_eq!(r, 42);
 
+    // raw DST tuple
+    let p = (A { f: 42 },);
+    let o: *const (Trait,) = &p;
+    let r = unsafe {
+        (&*o).0.foo()
+    };
+    assert_eq!(r, 42);
+
     // raw slice
     let a: *const [_] = &[1, 2, 3];
     unsafe {
@@ -72,6 +82,15 @@ pub fn main() {
         assert_eq!(len, 3);
     }
 
+    // raw DST tuple with slice
+    let c: *const ([_],) = &([1, 2, 3],);
+    unsafe {
+        let b = (&*c).0[0];
+        assert_eq!(b, 1);
+        let len = (&*c).0.len();
+        assert_eq!(len, 3);
+    }
+
     // all of the above with *mut
     let mut x = A { f: 42 };
     let z: *mut Trait = &mut x;
@@ -87,6 +106,13 @@ pub fn main() {
     };
     assert_eq!(r, 42);
 
+    let mut p = (A { f: 42 },);
+    let o: *mut (Trait,) = &mut p;
+    let r = unsafe {
+        (&*o).0.foo()
+    };
+    assert_eq!(r, 42);
+
     let a: *mut [_] = &mut [1, 2, 3];
     unsafe {
         let b = (*a)[2];
@@ -110,4 +136,12 @@ pub fn main() {
         let len = (&*c).f.len();
         assert_eq!(len, 3);
     }
+
+    let c: *mut ([_],) = &mut ([1, 2, 3],);
+    unsafe {
+        let b = (&*c).0[0];
+        assert_eq!(b, 1);
+        let len = (&*c).0.len();
+        assert_eq!(len, 3);
+    }
 }
diff --git a/src/test/run-pass/dst-trait-tuple.rs b/src/test/run-pass/dst-trait-tuple.rs
new file mode 100644
index 00000000000..9803e26f5f8
--- /dev/null
+++ b/src/test/run-pass/dst-trait-tuple.rs
@@ -0,0 +1,111 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+
+#![allow(unused_features)]
+#![feature(box_syntax)]
+#![feature(unsized_tuple_coercion)]
+
+type Fat<T: ?Sized> = (isize, &'static str, T);
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+struct Bar;
+
+#[derive(Copy, Clone, PartialEq, Eq)]
+struct Bar1 {
+    f: isize
+}
+
+trait ToBar {
+    fn to_bar(&self) -> Bar;
+    fn to_val(&self) -> isize;
+}
+
+impl ToBar for Bar {
+    fn to_bar(&self) -> Bar {
+        *self
+    }
+    fn to_val(&self) -> isize {
+        0
+    }
+}
+impl ToBar for Bar1 {
+    fn to_bar(&self) -> Bar {
+        Bar
+    }
+    fn to_val(&self) -> isize {
+        self.f
+    }
+}
+
+// x is a fat pointer
+fn foo(x: &Fat<ToBar>) {
+    assert_eq!(x.0, 5);
+    assert_eq!(x.1, "some str");
+    assert_eq!(x.2.to_bar(), Bar);
+    assert_eq!(x.2.to_val(), 42);
+
+    let y = &x.2;
+    assert_eq!(y.to_bar(), Bar);
+    assert_eq!(y.to_val(), 42);
+}
+
+fn bar(x: &ToBar) {
+    assert_eq!(x.to_bar(), Bar);
+    assert_eq!(x.to_val(), 42);
+}
+
+fn baz(x: &Fat<Fat<ToBar>>) {
+    assert_eq!(x.0, 5);
+    assert_eq!(x.1, "some str");
+    assert_eq!((x.2).0, 8);
+    assert_eq!((x.2).1, "deep str");
+    assert_eq!((x.2).2.to_bar(), Bar);
+    assert_eq!((x.2).2.to_val(), 42);
+
+    let y = &(x.2).2;
+    assert_eq!(y.to_bar(), Bar);
+    assert_eq!(y.to_val(), 42);
+
+}
+
+pub fn main() {
+    let f1 = (5, "some str", Bar1 {f :42});
+    foo(&f1);
+    let f2 = &f1;
+    foo(f2);
+    let f3: &Fat<ToBar> = f2;
+    foo(f3);
+    let f4: &Fat<ToBar> = &f1;
+    foo(f4);
+    let f5: &Fat<ToBar> = &(5, "some str", Bar1 {f :42});
+    foo(f5);
+
+    // Zero size object.
+    let f6: &Fat<ToBar> = &(5, "some str", Bar);
+    assert_eq!(f6.2.to_bar(), Bar);
+
+    // &*
+    //
+    let f7: Box<ToBar> = Box::new(Bar1 {f :42});
+    bar(&*f7);
+
+    // Deep nesting
+    let f1 = (5, "some str", (8, "deep str", Bar1 {f :42}));
+    baz(&f1);
+    let f2 = &f1;
+    baz(f2);
+    let f3: &Fat<Fat<ToBar>> = f2;
+    baz(f3);
+    let f4: &Fat<Fat<ToBar>> = &f1;
+    baz(f4);
+    let f5: &Fat<Fat<ToBar>> = &(5, "some str", (8, "deep str", Bar1 {f :42}));
+    baz(f5);
+}
diff --git a/src/test/run-pass/dst-tuple-sole.rs b/src/test/run-pass/dst-tuple-sole.rs
new file mode 100644
index 00000000000..a788e25218e
--- /dev/null
+++ b/src/test/run-pass/dst-tuple-sole.rs
@@ -0,0 +1,85 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// As dst-tuple.rs, but the unsized field is the only field in the tuple.
+
+
+#![feature(unsized_tuple_coercion)]
+
+type Fat<T: ?Sized> = (T,);
+
+// x is a fat pointer
+fn foo(x: &Fat<[isize]>) {
+    let y = &x.0;
+    assert_eq!(x.0.len(), 3);
+    assert_eq!(y[0], 1);
+    assert_eq!(x.0[1], 2);
+}
+
+fn foo2<T:ToBar>(x: &Fat<[T]>) {
+    let y = &x.0;
+    let bar = Bar;
+    assert_eq!(x.0.len(), 3);
+    assert_eq!(y[0].to_bar(), bar);
+    assert_eq!(x.0[1].to_bar(), bar);
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+struct Bar;
+
+trait ToBar {
+    fn to_bar(&self) -> Bar;
+}
+
+impl ToBar for Bar {
+    fn to_bar(&self) -> Bar {
+        *self
+    }
+}
+
+pub fn main() {
+    // With a vec of ints.
+    let f1 = ([1, 2, 3],);
+    foo(&f1);
+    let f2 = &f1;
+    foo(f2);
+    let f3: &Fat<[isize]> = f2;
+    foo(f3);
+    let f4: &Fat<[isize]> = &f1;
+    foo(f4);
+    let f5: &Fat<[isize]> = &([1, 2, 3],);
+    foo(f5);
+
+    // With a vec of Bars.
+    let bar = Bar;
+    let f1 = ([bar, bar, bar],);
+    foo2(&f1);
+    let f2 = &f1;
+    foo2(f2);
+    let f3: &Fat<[Bar]> = f2;
+    foo2(f3);
+    let f4: &Fat<[Bar]> = &f1;
+    foo2(f4);
+    let f5: &Fat<[Bar]> = &([bar, bar, bar],);
+    foo2(f5);
+
+    // Assignment.
+    let f5: &mut Fat<[isize]> = &mut ([1, 2, 3],);
+    f5.0[1] = 34;
+    assert_eq!(f5.0[0], 1);
+    assert_eq!(f5.0[1], 34);
+    assert_eq!(f5.0[2], 3);
+
+    // Zero size vec.
+    let f5: &Fat<[isize]> = &([],);
+    assert!(f5.0.is_empty());
+    let f5: &Fat<[Bar]> = &([],);
+    assert!(f5.0.is_empty());
+}
diff --git a/src/test/run-pass/dst-tuple.rs b/src/test/run-pass/dst-tuple.rs
new file mode 100644
index 00000000000..2f5b28495b8
--- /dev/null
+++ b/src/test/run-pass/dst-tuple.rs
@@ -0,0 +1,129 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+
+#![allow(unknown_features)]
+#![feature(box_syntax)]
+#![feature(unsized_tuple_coercion)]
+
+type Fat<T: ?Sized> = (isize, &'static str, T);
+
+// x is a fat pointer
+fn foo(x: &Fat<[isize]>) {
+    let y = &x.2;
+    assert_eq!(x.2.len(), 3);
+    assert_eq!(y[0], 1);
+    assert_eq!(x.2[1], 2);
+    assert_eq!(x.0, 5);
+    assert_eq!(x.1, "some str");
+}
+
+fn foo2<T:ToBar>(x: &Fat<[T]>) {
+    let y = &x.2;
+    let bar = Bar;
+    assert_eq!(x.2.len(), 3);
+    assert_eq!(y[0].to_bar(), bar);
+    assert_eq!(x.2[1].to_bar(), bar);
+    assert_eq!(x.0, 5);
+    assert_eq!(x.1, "some str");
+}
+
+fn foo3(x: &Fat<Fat<[isize]>>) {
+    let y = &(x.2).2;
+    assert_eq!(x.0, 5);
+    assert_eq!(x.1, "some str");
+    assert_eq!((x.2).0, 8);
+    assert_eq!((x.2).1, "deep str");
+    assert_eq!((x.2).2.len(), 3);
+    assert_eq!(y[0], 1);
+    assert_eq!((x.2).2[1], 2);
+}
+
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+struct Bar;
+
+trait ToBar {
+    fn to_bar(&self) -> Bar;
+}
+
+impl ToBar for Bar {
+    fn to_bar(&self) -> Bar {
+        *self
+    }
+}
+
+pub fn main() {
+    // With a vec of ints.
+    let f1 = (5, "some str", [1, 2, 3]);
+    foo(&f1);
+    let f2 = &f1;
+    foo(f2);
+    let f3: &Fat<[isize]> = f2;
+    foo(f3);
+    let f4: &Fat<[isize]> = &f1;
+    foo(f4);
+    let f5: &Fat<[isize]> = &(5, "some str", [1, 2, 3]);
+    foo(f5);
+
+    // With a vec of Bars.
+    let bar = Bar;
+    let f1 = (5, "some str", [bar, bar, bar]);
+    foo2(&f1);
+    let f2 = &f1;
+    foo2(f2);
+    let f3: &Fat<[Bar]> = f2;
+    foo2(f3);
+    let f4: &Fat<[Bar]> = &f1;
+    foo2(f4);
+    let f5: &Fat<[Bar]> = &(5, "some str", [bar, bar, bar]);
+    foo2(f5);
+
+    // Assignment.
+    let f5: &mut Fat<[isize]> = &mut (5, "some str", [1, 2, 3]);
+    f5.2[1] = 34;
+    assert_eq!(f5.2[0], 1);
+    assert_eq!(f5.2[1], 34);
+    assert_eq!(f5.2[2], 3);
+
+    // Zero size vec.
+    let f5: &Fat<[isize]> = &(5, "some str", []);
+    assert!(f5.2.is_empty());
+    let f5: &Fat<[Bar]> = &(5, "some str", []);
+    assert!(f5.2.is_empty());
+
+    // Deeply nested.
+    let f1 = (5, "some str", (8, "deep str", [1, 2, 3]));
+    foo3(&f1);
+    let f2 = &f1;
+    foo3(f2);
+    let f3: &Fat<Fat<[isize]>> = f2;
+    foo3(f3);
+    let f4: &Fat<Fat<[isize]>> = &f1;
+    foo3(f4);
+    let f5: &Fat<Fat<[isize]>> = &(5, "some str", (8, "deep str", [1, 2, 3]));
+    foo3(f5);
+
+    // Box.
+    let f1 = Box::new([1, 2, 3]);
+    assert_eq!((*f1)[1], 2);
+    let f2: Box<[isize]> = f1;
+    assert_eq!((*f2)[1], 2);
+
+    // Nested Box.
+    let f1 : Box<Fat<[isize; 3]>> = box (5, "some str", [1, 2, 3]);
+    foo(&*f1);
+    let f2 : Box<Fat<[isize]>> = f1;
+    foo(&*f2);
+
+    let f3 : Box<Fat<[isize]>> =
+        Box::<Fat<[_; 3]>>::new((5, "some str", [1, 2, 3]));
+    foo(&*f3);
+}