diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs
index 107779ec3fa15..cb7f1221c7d58 100644
--- a/src/librustc/ich/impls_ty.rs
+++ b/src/librustc/ich/impls_ty.rs
@@ -758,7 +758,14 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for ty::InstanceDef<'gcx> {
                 def_id.hash_stable(hcx, hasher);
                 t.hash_stable(hcx, hasher);
             }
-            ty::InstanceDef::CloneShim(def_id, t) => {
+            ty::InstanceDef::CloneCopyShim(def_id) => {
+                def_id.hash_stable(hcx, hasher);
+            }
+            ty::InstanceDef::CloneNominalShim { clone, ty } => {
+                clone.hash_stable(hcx, hasher);
+                ty.hash_stable(hcx, hasher);
+            }
+            ty::InstanceDef::CloneStructuralShim(def_id, t) => {
                 def_id.hash_stable(hcx, hasher);
                 t.hash_stable(hcx, hasher);
             }
diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs
index 63bf52a9bdf78..6480deb35cb84 100644
--- a/src/librustc/ty/instance.rs
+++ b/src/librustc/ty/instance.rs
@@ -13,6 +13,7 @@ use ty::{self, Ty, TypeFoldable, Substs, TyCtxt};
 use ty::subst::Kind;
 use traits;
 use syntax::abi::Abi;
+use syntax::codemap::DUMMY_SP;
 use util::ppaux;
 
 use std::fmt;
@@ -39,10 +40,25 @@ pub enum InstanceDef<'tcx> {
     ClosureOnceShim { call_once: DefId },
 
     /// drop_in_place::<T>; None for empty drop glue.
+    ///
+    /// The DefId is the DefId of drop_in_place
     DropGlue(DefId, Option<Ty<'tcx>>),
 
-    ///`<T as Clone>::clone` shim.
-    CloneShim(DefId, Ty<'tcx>),
+    ///`<T as Clone>::clone` shim for Copy types
+    ///
+    /// The DefId is the DefId of the Clone::clone function
+    CloneCopyShim(DefId),
+    ///`<T as Clone>::clone` shim for arrays and tuples
+    ///
+    /// The DefId is the DefId of the Clone::clone function
+    CloneStructuralShim(DefId, Ty<'tcx>),
+    ///`<T as Clone>::clone` shim for closures
+    CloneNominalShim {
+        /// The DefId of the Clone::clone trait method def
+        clone: DefId,
+        /// The DefId of the self type
+        ty: DefId
+    },
 }
 
 impl<'a, 'tcx> Instance<'tcx> {
@@ -65,7 +81,9 @@ impl<'tcx> InstanceDef<'tcx> {
             InstanceDef::Intrinsic(def_id, ) |
             InstanceDef::ClosureOnceShim { call_once: def_id } |
             InstanceDef::DropGlue(def_id, _) |
-            InstanceDef::CloneShim(def_id, _) => def_id
+            InstanceDef::CloneCopyShim(def_id) |
+            InstanceDef::CloneStructuralShim(def_id, _) |
+            InstanceDef::CloneNominalShim{ clone: def_id, ..} => def_id
         }
     }
 
@@ -131,7 +149,11 @@ impl<'tcx> fmt::Display for Instance<'tcx> {
             InstanceDef::DropGlue(_, ty) => {
                 write!(f, " - shim({:?})", ty)
             }
-            InstanceDef::CloneShim(_, ty) => {
+            InstanceDef::CloneCopyShim(def) |
+            InstanceDef::CloneNominalShim { ty: def, ..} => {
+                write!(f, " - shim({:?})", def)
+            }
+            InstanceDef::CloneStructuralShim(_, ty) => {
                 write!(f, " - shim({:?})", ty)
             }
         }
@@ -289,9 +311,31 @@ fn resolve_associated_item<'a, 'tcx>(
         }
         traits::VtableBuiltin(..) => {
             if let Some(_) = tcx.lang_items().clone_trait() {
+                let mut substs = rcvr_substs;
+                let name = tcx.item_name(def_id);
+                let def = if name == "clone" {
+                    let self_ty = trait_ref.self_ty();
+                    match self_ty.sty {
+                        _ if !self_ty.moves_by_default(tcx, param_env, DUMMY_SP) => {
+                            ty::InstanceDef::CloneCopyShim(def_id)
+                        }
+                        ty::TyArray(..) => ty::InstanceDef::CloneStructuralShim(def_id, self_ty),
+                        ty::TyTuple(..) => ty::InstanceDef::CloneStructuralShim(def_id, self_ty),
+                        ty::TyClosure(ty_did, closure_substs) => {
+                            substs = closure_substs.substs;
+                            ty::InstanceDef::CloneNominalShim {
+                                clone: def_id,
+                                ty: ty_did
+                            }
+                        }
+                        _ => unreachable!("Type {:?} does not have clone shims", self_ty)
+                    }
+                } else {
+                    ty::InstanceDef::Item(def_id)
+                };
                 Some(Instance {
-                    def: ty::InstanceDef::CloneShim(def_id, trait_ref.self_ty()),
-                    substs: rcvr_substs
+                    def,
+                    substs
                 })
             } else {
                 None
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index f52f2ea0f9fc8..e29fa4a4d8e90 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -2351,7 +2351,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
             ty::InstanceDef::Virtual(..) |
             ty::InstanceDef::ClosureOnceShim { .. } |
             ty::InstanceDef::DropGlue(..) |
-            ty::InstanceDef::CloneShim(..) => {
+            ty::InstanceDef::CloneCopyShim(..) |
+            ty::InstanceDef::CloneStructuralShim(..) |
+            ty::InstanceDef::CloneNominalShim { .. } => {
                 self.mir_shims(instance)
             }
         }
diff --git a/src/librustc_mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs
index c8a0dbdd90308..932e4c5e3a9e3 100644
--- a/src/librustc_mir/interpret/terminator/mod.rs
+++ b/src/librustc_mir/interpret/terminator/mod.rs
@@ -295,7 +295,9 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
             }
             ty::InstanceDef::FnPtrShim(..) |
             ty::InstanceDef::DropGlue(..) |
-            ty::InstanceDef::CloneShim(..) |
+            ty::InstanceDef::CloneCopyShim(..) |
+            ty::InstanceDef::CloneStructuralShim(..) |
+            ty::InstanceDef::CloneNominalShim { .. } |
             ty::InstanceDef::Item(_) => {
                 // Push the stack frame, and potentially be entirely done if the call got hooked
                 if M::eval_fn_call(self, instance, destination, args, span, sig)? {
diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs
index f16187797d4e5..37c407c14265d 100644
--- a/src/librustc_mir/monomorphize/collector.rs
+++ b/src/librustc_mir/monomorphize/collector.rs
@@ -711,7 +711,9 @@ fn visit_instance_use<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         ty::InstanceDef::ClosureOnceShim { .. } |
         ty::InstanceDef::Item(..) |
         ty::InstanceDef::FnPtrShim(..) |
-        ty::InstanceDef::CloneShim(..) => {
+        ty::InstanceDef::CloneCopyShim(..) |
+        ty::InstanceDef::CloneStructuralShim(..) |
+        ty::InstanceDef::CloneNominalShim { .. } => {
             output.push(create_fn_mono_item(instance));
         }
     }
@@ -729,7 +731,9 @@ fn should_monomorphize_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance:
         ty::InstanceDef::FnPtrShim(..) |
         ty::InstanceDef::DropGlue(..) |
         ty::InstanceDef::Intrinsic(_) |
-        ty::InstanceDef::CloneShim(..) => return true
+        ty::InstanceDef::CloneCopyShim(..) |
+        ty::InstanceDef::CloneStructuralShim(..) |
+        ty::InstanceDef::CloneNominalShim { .. } => return true
     };
     match tcx.hir.get_if_local(def_id) {
         Some(hir_map::NodeForeignItem(..)) => {
diff --git a/src/librustc_mir/monomorphize/partitioning.rs b/src/librustc_mir/monomorphize/partitioning.rs
index e9471cdb4f949..a53325172901d 100644
--- a/src/librustc_mir/monomorphize/partitioning.rs
+++ b/src/librustc_mir/monomorphize/partitioning.rs
@@ -175,7 +175,9 @@ pub trait CodegenUnitExt<'tcx> {
                         InstanceDef::Virtual(..) |
                         InstanceDef::ClosureOnceShim { .. } |
                         InstanceDef::DropGlue(..) |
-                        InstanceDef::CloneShim(..) => {
+                        InstanceDef::CloneCopyShim(..) |
+                        InstanceDef::CloneStructuralShim(..) |
+                        InstanceDef::CloneNominalShim { .. } => {
                             None
                         }
                     }
@@ -376,7 +378,9 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                             InstanceDef::Intrinsic(..) |
                             InstanceDef::ClosureOnceShim { .. } |
                             InstanceDef::DropGlue(..) |
-                            InstanceDef::CloneShim(..) => {
+                            InstanceDef::CloneCopyShim(..) |
+                            InstanceDef::CloneStructuralShim(..) |
+                            InstanceDef::CloneNominalShim { .. } => {
                                 Visibility::Hidden
                             }
                         };
@@ -619,7 +623,9 @@ fn characteristic_def_id_of_trans_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                 ty::InstanceDef::Intrinsic(..) |
                 ty::InstanceDef::DropGlue(..) |
                 ty::InstanceDef::Virtual(..) |
-                ty::InstanceDef::CloneShim(..) => return None
+                ty::InstanceDef::CloneCopyShim(..) |
+                ty::InstanceDef::CloneStructuralShim(..) |
+                ty::InstanceDef::CloneNominalShim { .. } => return None
             };
 
             // If this is a method, we want to put it into the same module as
diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs
index 42ffcc194ca8c..e77d9479f6809 100644
--- a/src/librustc_mir/shim.rs
+++ b/src/librustc_mir/shim.rs
@@ -99,16 +99,19 @@ fn make_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         ty::InstanceDef::DropGlue(def_id, ty) => {
             build_drop_shim(tcx, def_id, ty)
         }
-        ty::InstanceDef::CloneShim(def_id, ty) => {
-            let name = tcx.item_name(def_id);
-            if name == "clone" {
-                build_clone_shim(tcx, def_id, ty)
-            } else if name == "clone_from" {
-                debug!("make_shim({:?}: using default trait implementation", instance);
-                return tcx.optimized_mir(def_id);
-            } else {
-                bug!("builtin clone shim {:?} not supported", instance)
-            }
+        ty::InstanceDef::CloneCopyShim(def_id) => {
+            let substs = Substs::identity_for_item(tcx, def_id);
+            let self_ty = substs.type_at(0);
+            let mut builder = CloneShimBuilder::new(tcx, def_id, self_ty);
+            builder.copy_shim();
+            builder.into_mir()
+        }
+        ty::InstanceDef::CloneNominalShim { clone, ty } => {
+            let ty = tcx.type_of(ty);
+            build_clone_shim(tcx, clone, ty)
+        }
+        ty::InstanceDef::CloneStructuralShim(def_id, ty) => {
+            build_clone_shim(tcx, def_id, ty)
         }
         ty::InstanceDef::Intrinsic(_) => {
             bug!("creating shims from intrinsics ({:?}) is unsupported", instance)
@@ -295,13 +298,11 @@ fn build_clone_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     debug!("build_clone_shim(def_id={:?})", def_id);
 
     let mut builder = CloneShimBuilder::new(tcx, def_id, self_ty);
-    let is_copy = !self_ty.moves_by_default(tcx, tcx.param_env(def_id), builder.span);
 
     let dest = Place::Local(RETURN_PLACE);
     let src = Place::Local(Local::new(1+0)).deref();
 
     match self_ty.sty {
-        _ if is_copy => builder.copy_shim(),
         ty::TyArray(ty, len) => {
             let len = len.val.to_const_int().unwrap().to_u64().unwrap();
             builder.array_shim(dest, src, ty, len)