diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index 4ed25646d436d..5f2fc0064b342 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -2150,7 +2150,27 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                 }
             }
 
-            ty::TyAdt(..) | ty::TyProjection(..) | ty::TyParam(..) | ty::TyAnon(..) => {
+            ty::TyAdt(adt, substs) => {
+                let attrs = self.tcx().get_attrs(adt.did);
+                if adt.is_enum() && attrs.iter().any(|a| a.check_name("rustc_nocopy_clone_marker")) {
+                    let trait_id = obligation.predicate.def_id();
+                    if Some(trait_id) == self.tcx().lang_items().clone_trait() {
+                        // for Clone
+                        // this doesn't work for recursive types (FIXME(Manishearth))
+                        // let mut iter = substs.types()
+                        //     .chain(adt.all_fields().map(|f| f.ty(self.tcx(), substs)));
+                        let mut iter = substs.types();
+                        Where(ty::Binder(iter.collect()))
+                    } else {
+                        None
+                    }
+                } else {
+                    // Fallback to whatever user-defined impls exist in this case.
+                    None                
+                }
+            }
+
+            ty::TyProjection(..) | ty::TyParam(..) | ty::TyAnon(..) => {
                 // Fallback to whatever user-defined impls exist in this case.
                 None
             }
diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs
index 63bf52a9bdf78..f208a6f286b60 100644
--- a/src/librustc/ty/instance.rs
+++ b/src/librustc/ty/instance.rs
@@ -69,6 +69,15 @@ impl<'tcx> InstanceDef<'tcx> {
         }
     }
 
+    #[inline]
+    pub fn shim_def_id(&self) -> Option<DefId> {
+        if let InstanceDef::CloneShim(_, ty) = *self {
+            ty.ty_to_def_id()
+        } else {
+            None
+        }
+    }
+
     #[inline]
     pub fn attrs<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> ty::Attributes<'tcx> {
         tcx.get_attrs(self.def_id())
diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs
index 42ffcc194ca8c..68c2ef29dc472 100644
--- a/src/librustc_mir/shim.rs
+++ b/src/librustc_mir/shim.rs
@@ -301,7 +301,7 @@ fn build_clone_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     let src = Place::Local(Local::new(1+0)).deref();
 
     match self_ty.sty {
-        _ if is_copy => builder.copy_shim(),
+        _ if is_copy => { builder.copy_shim(dest, src); }
         ty::TyArray(ty, len) => {
             let len = len.val.to_const_int().unwrap().to_u64().unwrap();
             builder.array_shim(dest, src, ty, len)
@@ -313,6 +313,7 @@ fn build_clone_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             )
         }
         ty::TyTuple(tys, _) => builder.tuple_like_shim(dest, src, tys.iter().cloned()),
+        ty::TyAdt(adt, substs) => builder.enum_shim(adt, substs, dest, src),
         _ => {
             bug!("clone shim for `{:?}` which is not `Copy` and is not an aggregate", self_ty)
         }
@@ -340,7 +341,13 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> {
         let substs = tcx.mk_substs_trait(self_ty, &[]);
         let sig = tcx.fn_sig(def_id).subst(tcx, substs);
         let sig = tcx.erase_late_bound_regions(&sig);
-        let span = tcx.def_span(def_id);
+        let span_def_id = if let Some(did) = self_ty.ty_to_def_id() {
+            did
+        } else {
+            def_id
+        };
+
+        let span = tcx.def_span(span_def_id);
 
         CloneShimBuilder {
             tcx,
@@ -401,15 +408,15 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> {
         }
     }
 
-    fn copy_shim(&mut self) {
-        let rcvr = Place::Local(Local::new(1+0)).deref();
+    fn copy_shim(&mut self, dest: Place<'tcx>, src: Place<'tcx>) -> BasicBlock {
+        let rcvr = src;
         let ret_statement = self.make_statement(
             StatementKind::Assign(
-                Place::Local(RETURN_PLACE),
+                dest,
                 Rvalue::Use(Operand::Copy(rcvr))
             )
         );
-        self.block(vec![ret_statement], TerminatorKind::Return, false);
+        self.block(vec![ret_statement], TerminatorKind::Return, false)
     }
 
     fn make_place(&mut self, mutability: Mutability, ty: Ty<'tcx>) -> Place<'tcx> {
@@ -671,6 +678,97 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> {
 
         self.block(vec![], TerminatorKind::Return, false);
     }
+
+    fn enum_shim(&mut self, adt: &'tcx ty::AdtDef, substs: &'tcx Substs<'tcx>,
+                 dest: Place<'tcx>, src: Place<'tcx>) {
+        use rustc::ty::util::IntTypeExt;
+        if !adt.is_enum() {
+            bug!("We only make Clone shims for enum ADTs");
+        }
+
+        let param_env = self.tcx.param_env(adt.did);
+        let all_copy = adt.all_fields().all(|field| {
+            !field.ty(self.tcx, substs)
+                  .moves_by_default(self.tcx, param_env, self.span)
+        });
+
+        if all_copy {
+            self.copy_shim(dest, src);
+            return;
+        }
+
+        // should be u128 maybe?
+        let discr_ty = adt.repr.discr_type().to_ty(self.tcx);
+        let discr = self.make_place(Mutability::Not, discr_ty);
+
+        let assign_discr = self.make_statement(
+            StatementKind::Assign(
+                discr.clone(),
+                Rvalue::Discriminant(src.clone())
+            )
+        );
+        // insert dummy first block
+        let entry_block = self.block(vec![], TerminatorKind::Abort, false);
+        let switch = self.make_enum_match(adt, substs, discr, dest, src, param_env);
+
+        let source_info = self.source_info();
+        self.blocks[entry_block].statements = vec![assign_discr];
+        self.blocks[entry_block].terminator = Some(Terminator { source_info, kind: switch });
+    }
+
+    fn make_enum_match(&mut self, adt: &'tcx ty::AdtDef,
+                       substs: &'tcx Substs<'tcx>,
+                       discr: Place<'tcx>,
+                       dest: Place<'tcx>,
+                       receiver: Place<'tcx>,
+                       param_env: ty::ParamEnv<'tcx>) -> TerminatorKind<'tcx> {
+
+        let mut values = vec![];
+        let mut targets = vec![];
+        for (idx, variant) in adt.variants.iter().enumerate() {
+
+            // in case a variant is Copy, don't generate the manual Clone
+            // match arm, instead let the copy_shim default deal with it
+            let all_copy = variant.fields.iter().all(|field| {
+                !field.ty(self.tcx, substs)
+                      .moves_by_default(self.tcx, param_env, self.span)
+            });
+
+            if all_copy {
+                continue;
+            }
+
+            values.push(adt.discriminant_for_variant(self.tcx, idx));
+
+            let src_variant = receiver.clone().downcast(adt, idx);
+            let dest_variant = dest.clone().downcast(adt, idx);
+
+
+            // the next to next block created will be the one
+            // from tuple_like_shim and will handle the cloning
+            let clone_block = self.block_index_offset(1);
+            let set_discr = self.make_statement(StatementKind::SetDiscriminant {
+                place: dest.clone(),
+                variant_index: idx
+            });
+
+            targets.push(self.block(vec![set_discr], TerminatorKind::Goto {
+                target: clone_block
+            }, false));
+
+            let tcx = self.tcx;
+            let iter = variant.fields.iter().map(|field| field.ty(tcx, substs));
+            self.tuple_like_shim(dest_variant, src_variant, iter);
+        }
+        // In the default arm, fall back to a copy
+        targets.push(self.copy_shim(dest, receiver));
+        TerminatorKind::SwitchInt {
+            discr: Operand::Move(discr),
+            switch_ty: self.tcx.types.usize,
+            values: From::from(values),
+            targets,
+        }
+    }
 }
 
 /// Build a "call" shim for `def_id`. The shim calls the
diff --git a/src/librustc_trans_utils/symbol_names.rs b/src/librustc_trans_utils/symbol_names.rs
index fb299bf7eea0c..dd3dc5abf5b5d 100644
--- a/src/librustc_trans_utils/symbol_names.rs
+++ b/src/librustc_trans_utils/symbol_names.rs
@@ -226,8 +226,8 @@ fn get_symbol_hash<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     hasher.finish()
 }
 
-fn def_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
-                             -> ty::SymbolName
+// The boolean is whether this is a clone shim
+fn def_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> ty::SymbolName
 {
     let mut buffer = SymbolPathBuffer::new();
     item_path::with_forced_absolute_paths(|| {
@@ -329,7 +329,19 @@ fn compute_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance
 
     let hash = get_symbol_hash(tcx, def_id, instance, instance_ty, substs);
 
-    SymbolPathBuffer::from_interned(tcx.def_symbol_name(def_id)).finish(hash)
+    let shim_id = instance.def.shim_def_id();
+
+    let lookup_def_id = if let Some(shim_id) = shim_id {
+        shim_id
+    } else {
+        def_id
+    };
+
+    let mut buf = SymbolPathBuffer::from_interned(tcx.def_symbol_name(lookup_def_id));
+    if shim_id.is_some() {
+        buf.push("{{clone-shim}}");
+    }
+    buf.finish(hash)
 }
 
 // Follow C++ namespace-mangling style, see
diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs
index c7fa0331c1bd5..ab5dff07d47f0 100644
--- a/src/libsyntax/ext/derive.rs
+++ b/src/libsyntax/ext/derive.rs
@@ -77,6 +77,9 @@ pub fn add_derived_markers<T>(cx: &mut ExtCtxt, span: Span, traits: &[ast::Path]
         if names.contains(&Symbol::intern("Copy")) {
             let meta = cx.meta_word(span, Symbol::intern("rustc_copy_clone_marker"));
             attrs.push(cx.attribute(span, meta));
+        } else if names.contains(&Symbol::intern("Clone")) {
+            let meta = cx.meta_word(span, Symbol::intern("rustc_nocopy_clone_marker"));
+            attrs.push(cx.attribute(span, meta));
         }
         attrs
     })
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 3e858c3b923a1..b31d079d031e1 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -869,7 +869,10 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
                                                    "rustc_attrs",
                                                    "internal implementation detail",
                                                    cfg_fn!(rustc_attrs))),
-
+    ("rustc_nocopy_clone_marker", Whitelisted, Gated(Stability::Unstable,
+                                                   "rustc_attrs",
+                                                   "internal implementation detail",
+                                                   cfg_fn!(rustc_attrs))),
     // FIXME: #14408 whitelist docs since rustdoc looks at them
     ("doc", Whitelisted, Ungated),
 
diff --git a/src/libsyntax_ext/deriving/clone.rs b/src/libsyntax_ext/deriving/clone.rs
index f23d22b0c365f..445b345e38041 100644
--- a/src/libsyntax_ext/deriving/clone.rs
+++ b/src/libsyntax_ext/deriving/clone.rs
@@ -55,6 +55,16 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt,
                         substructure = combine_substructure(Box::new(|c, s, sub| {
                             cs_clone_shallow("Clone", c, s, sub, false)
                         }));
+                    } else if attr::contains_name(&annitem.attrs, "rustc_nocopy_clone_marker") {
+                        if let ItemKind::Enum(..) = annitem.node {
+                            // Do nothing, this will be handled in a shim
+                            return
+                        }
+                        bounds = vec![];
+                        is_shallow = false;
+                        substructure = combine_substructure(Box::new(|c, s, sub| {
+                            cs_clone("Clone", c, s, sub)
+                        }));
                     } else {
                         bounds = vec![];
                         is_shallow = false;