diff --git a/src/libcore/tuple.rs b/src/libcore/tuple.rs index 555843dba418e..47e9c7c903880 100644 --- a/src/libcore/tuple.rs +++ b/src/libcore/tuple.rs @@ -22,7 +22,6 @@ macro_rules! tuple_impls { )+) => { $( #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(stage0)] impl<$($T:Clone),+> Clone for ($($T,)+) { fn clone(&self) -> ($($T,)+) { ($(self.$idx.clone(),)+) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index c9e3c8cc3e0ca..a8df860d7084c 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -677,9 +677,8 @@ impl<'a, 'gcx, 'tcx> HashStable> for ty::In def_id.hash_stable(hcx, hasher); t.hash_stable(hcx, hasher); } - ty::InstanceDef::CloneShim(def_id, t) => { + ty::InstanceDef::CloneShim(def_id) => { def_id.hash_stable(hcx, hasher); - t.hash_stable(hcx, hasher); } } } diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 4e9398e505806..d46f8fbed6a92 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -1258,8 +1258,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { } else { if self.tcx().lang_items.clone_trait() == Some(def_id) { // Same builtin conditions as `Copy`, i.e. every type which has builtin support - // for `Copy` also has builtin support for `Clone`, + tuples and arrays of `Clone` - // types have builtin support for `Clone`. + // for `Copy` also has builtin support for `Clone`. let clone_conditions = self.copy_conditions(obligation); self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates)?; } @@ -1872,6 +1871,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { false }, + BuiltinCandidate { .. } => { + // Hack: we always prefer a builtin impl over another one. Should apply + // ONLY for `Clone` on tuples. + other.evaluation == EvaluatedToOk + } _ => false } } @@ -2157,7 +2161,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { fn confirm_candidate(&mut self, obligation: &TraitObligation<'tcx>, candidate: SelectionCandidate<'tcx>) - -> Result,SelectionError<'tcx>> + -> Result, SelectionError<'tcx>> { debug!("confirm_candidate({:?}, {:?})", obligation, @@ -2268,7 +2272,14 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { obligation, has_nested); let obligations = if has_nested { - let trait_def = obligation.predicate.def_id(); + let mut trait_def = obligation.predicate.def_id(); + + // Hack: for having a builtin `Clone` impl, the nested types need to + // implement `Copy`. + if Some(trait_def) == self.tcx().lang_items.clone_trait() { + trait_def = self.tcx().lang_items.copy_trait().unwrap(); + } + let conditions = match trait_def { _ if Some(trait_def) == self.tcx().lang_items.sized_trait() => { self.sized_conditions(obligation) @@ -2276,9 +2287,6 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { _ if Some(trait_def) == self.tcx().lang_items.copy_trait() => { self.copy_conditions(obligation) } - _ if Some(trait_def) == self.tcx().lang_items.clone_trait() => { - self.copy_conditions(obligation) - } _ => bug!("unexpected builtin trait {:?}", trait_def) }; let nested = match conditions { diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index 7d543f689c24d..730a0eb5ea7f1 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs @@ -38,8 +38,8 @@ pub enum InstanceDef<'tcx> { /// drop_in_place::; None for empty drop glue. DropGlue(DefId, Option>), - /// Builtin method implementation, e.g. `Clone::clone`. - CloneShim(DefId, Ty<'tcx>), + /// Builtin `::clone` implementation. + CloneShim(DefId), } impl<'tcx> InstanceDef<'tcx> { @@ -52,7 +52,7 @@ 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::CloneShim(def_id) => def_id } } @@ -81,15 +81,13 @@ impl<'tcx> fmt::Display for Instance<'tcx> { InstanceDef::FnPtrShim(_, ty) => { write!(f, " - shim({:?})", ty) } - InstanceDef::ClosureOnceShim { .. } => { + InstanceDef::ClosureOnceShim { .. } | + InstanceDef::CloneShim(_) => { write!(f, " - shim") } InstanceDef::DropGlue(_, ty) => { write!(f, " - shim({:?})", ty) } - InstanceDef::CloneShim(_, ty) => { - write!(f, " - shim({:?})", ty) - } } } } diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 1e0858d686451..aa16f60a47eeb 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -17,7 +17,6 @@ use rustc::mir::transform::MirSource; use rustc::ty::{self, Ty}; use rustc::ty::subst::{Kind, Subst, Substs}; use rustc::ty::maps::Providers; -use rustc_const_math::{ConstInt, ConstUsize}; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; @@ -99,10 +98,10 @@ fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, ty::InstanceDef::DropGlue(def_id, ty) => { build_drop_shim(tcx, def_id, ty) } - ty::InstanceDef::CloneShim(def_id, ty) => { + ty::InstanceDef::CloneShim(def_id) => { let name = tcx.item_name(def_id).as_str(); if name == "clone" { - build_clone_shim(tcx, def_id, ty) + build_clone_shim(tcx, def_id) } else if name == "clone_from" { debug!("make_shim({:?}: using default trait implementation", instance); return tcx.optimized_mir(def_id); @@ -281,45 +280,30 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> { /// Build a `Clone::clone` shim for `self_ty`. Here, `def_id` is `Clone::clone`. fn build_clone_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId, - self_ty: ty::Ty<'tcx>) + def_id: DefId) -> Mir<'tcx> { debug!("build_clone_shim(def_id={:?})", def_id); let mut builder = CloneShimBuilder::new(tcx, def_id); - let is_copy = !self_ty.moves_by_default(tcx, tcx.param_env(def_id), builder.span); - - match self_ty.sty { - _ if is_copy => builder.copy_shim(), - ty::TyArray(ty, len) => builder.array_shim(ty, len), - ty::TyTuple(tys, _) => builder.tuple_shim(tys), - _ => { - bug!("clone shim for `{:?}` which is not `Copy` and is not an aggregate", self_ty); - } - }; - + builder.copy_shim(); builder.into_mir() } -struct CloneShimBuilder<'a, 'tcx: 'a> { - tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId, +struct CloneShimBuilder<'tcx> { local_decls: IndexVec>, blocks: IndexVec>, span: Span, sig: ty::FnSig<'tcx>, } -impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { - fn new(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Self { +impl<'tcx> CloneShimBuilder<'tcx> { + fn new<'a>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Self { let sig = tcx.fn_sig(def_id); let sig = tcx.erase_late_bound_regions(&sig); let span = tcx.def_span(def_id); CloneShimBuilder { - tcx, - def_id, local_decls: local_decls_for_sig(&sig, span), blocks: IndexVec::new(), span, @@ -378,274 +362,6 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> { ); self.block(vec![ret_statement], TerminatorKind::Return, false); } - - fn make_lvalue(&mut self, mutability: Mutability, ty: ty::Ty<'tcx>) -> Lvalue<'tcx> { - let span = self.span; - Lvalue::Local( - self.local_decls.push(temp_decl(mutability, ty, span)) - ) - } - - fn make_clone_call( - &mut self, - ty: ty::Ty<'tcx>, - rcvr_field: Lvalue<'tcx>, - next: BasicBlock, - cleanup: BasicBlock - ) -> Lvalue<'tcx> { - let tcx = self.tcx; - - let substs = Substs::for_item( - tcx, - self.def_id, - |_, _| tcx.types.re_erased, - |_, _| ty - ); - - // `func == Clone::clone(&ty) -> ty` - let func = Operand::Constant(box Constant { - span: self.span, - ty: tcx.mk_fn_def(self.def_id, substs), - literal: Literal::Value { - value: ConstVal::Function(self.def_id, substs), - }, - }); - - let ref_loc = self.make_lvalue( - Mutability::Not, - tcx.mk_ref(tcx.types.re_erased, ty::TypeAndMut { - ty, - mutbl: hir::Mutability::MutImmutable, - }) - ); - - let loc = self.make_lvalue(Mutability::Not, ty); - - // `let ref_loc: &ty = &rcvr_field;` - let statement = self.make_statement( - StatementKind::Assign( - ref_loc.clone(), - Rvalue::Ref(tcx.types.re_erased, BorrowKind::Shared, rcvr_field) - ) - ); - - // `let loc = Clone::clone(ref_loc);` - self.block(vec![statement], TerminatorKind::Call { - func, - args: vec![Operand::Consume(ref_loc)], - destination: Some((loc.clone(), next)), - cleanup: Some(cleanup), - }, false); - - loc - } - - fn loop_header( - &mut self, - beg: Lvalue<'tcx>, - end: Lvalue<'tcx>, - loop_body: BasicBlock, - loop_end: BasicBlock, - is_cleanup: bool - ) { - let tcx = self.tcx; - - let cond = self.make_lvalue(Mutability::Mut, tcx.types.bool); - let compute_cond = self.make_statement( - StatementKind::Assign( - cond.clone(), - Rvalue::BinaryOp(BinOp::Ne, Operand::Consume(end), Operand::Consume(beg)) - ) - ); - - // `if end != beg { goto loop_body; } else { goto loop_end; }` - self.block( - vec![compute_cond], - TerminatorKind::if_(tcx, Operand::Consume(cond), loop_body, loop_end), - is_cleanup - ); - } - - fn make_usize(&self, value: usize) -> Box> { - let value = ConstUsize::new(value as u64, self.tcx.sess.target.uint_type).unwrap(); - box Constant { - span: self.span, - ty: self.tcx.types.usize, - literal: Literal::Value { - value: ConstVal::Integral(ConstInt::Usize(value)) - } - } - } - - fn array_shim(&mut self, ty: ty::Ty<'tcx>, len: usize) { - let tcx = self.tcx; - let rcvr = Lvalue::Local(Local::new(1+0)).deref(); - - let beg = self.make_lvalue(Mutability::Mut, tcx.types.usize); - let end = self.make_lvalue(Mutability::Not, tcx.types.usize); - let ret = self.make_lvalue(Mutability::Mut, tcx.mk_array(ty, len)); - - // BB #0 - // `let mut beg = 0;` - // `let end = len;` - // `goto #1;` - let inits = vec![ - self.make_statement( - StatementKind::Assign( - beg.clone(), - Rvalue::Use(Operand::Constant(self.make_usize(0))) - ) - ), - self.make_statement( - StatementKind::Assign( - end.clone(), - Rvalue::Use(Operand::Constant(self.make_usize(len))) - ) - ) - ]; - self.block(inits, TerminatorKind::Goto { target: BasicBlock::new(1) }, false); - - // BB #1: loop { - // BB #2; - // BB #3; - // } - // BB #4; - self.loop_header(beg.clone(), end, BasicBlock::new(2), BasicBlock::new(4), false); - - // BB #2 - // `let cloned = Clone::clone(rcvr[beg])`; - // Goto #3 if ok, #5 if unwinding happens. - let rcvr_field = rcvr.clone().index(Operand::Consume(beg.clone())); - let cloned = self.make_clone_call(ty, rcvr_field, BasicBlock::new(3), BasicBlock::new(5)); - - // BB #3 - // `ret[beg] = cloned;` - // `beg = beg + 1;` - // `goto #1`; - let ret_field = ret.clone().index(Operand::Consume(beg.clone())); - let statements = vec![ - self.make_statement( - StatementKind::Assign( - ret_field, - Rvalue::Use(Operand::Consume(cloned)) - ) - ), - self.make_statement( - StatementKind::Assign( - beg.clone(), - Rvalue::BinaryOp( - BinOp::Add, - Operand::Consume(beg.clone()), - Operand::Constant(self.make_usize(1)) - ) - ) - ) - ]; - self.block(statements, TerminatorKind::Goto { target: BasicBlock::new(1) }, false); - - // BB #4 - // `return ret;` - let ret_statement = self.make_statement( - StatementKind::Assign( - Lvalue::Local(RETURN_POINTER), - Rvalue::Use(Operand::Consume(ret.clone())), - ) - ); - self.block(vec![ret_statement], TerminatorKind::Return, false); - - // BB #5 (cleanup) - // `let end = beg;` - // `let mut beg = 0;` - // goto #6; - let end = beg; - let beg = self.make_lvalue(Mutability::Mut, tcx.types.usize); - let init = self.make_statement( - StatementKind::Assign( - beg.clone(), - Rvalue::Use(Operand::Constant(self.make_usize(0))) - ) - ); - self.block(vec![init], TerminatorKind::Goto { target: BasicBlock::new(6) }, true); - - // BB #6 (cleanup): loop { - // BB #7; - // BB #8; - // } - // BB #9; - self.loop_header(beg.clone(), end, BasicBlock::new(7), BasicBlock::new(9), true); - - // BB #7 (cleanup) - // `drop(ret[beg])`; - self.block(vec![], TerminatorKind::Drop { - location: ret.index(Operand::Consume(beg.clone())), - target: BasicBlock::new(8), - unwind: None, - }, true); - - // BB #8 (cleanup) - // `beg = beg + 1;` - // `goto #6;` - let statement = self.make_statement( - StatementKind::Assign( - beg.clone(), - Rvalue::BinaryOp( - BinOp::Add, - Operand::Consume(beg.clone()), - Operand::Constant(self.make_usize(1)) - ) - ) - ); - self.block(vec![statement], TerminatorKind::Goto { target: BasicBlock::new(6) }, true); - - // BB #9 (resume) - self.block(vec![], TerminatorKind::Resume, true); - } - - fn tuple_shim(&mut self, tys: &ty::Slice>) { - let rcvr = Lvalue::Local(Local::new(1+0)).deref(); - - let mut returns = Vec::new(); - for (i, ity) in tys.iter().enumerate() { - let rcvr_field = rcvr.clone().field(Field::new(i), *ity); - - // BB #(2i) - // `returns[i] = Clone::clone(&rcvr.i);` - // Goto #(2i + 2) if ok, #(2i + 1) if unwinding happens. - returns.push( - self.make_clone_call( - *ity, - rcvr_field, - BasicBlock::new(2 * i + 2), - BasicBlock::new(2 * i + 1), - ) - ); - - // BB #(2i + 1) (cleanup) - if i == 0 { - // Nothing to drop, just resume. - self.block(vec![], TerminatorKind::Resume, true); - } else { - // Drop previous field and goto previous cleanup block. - self.block(vec![], TerminatorKind::Drop { - location: returns[i - 1].clone(), - target: BasicBlock::new(2 * i - 1), - unwind: None, - }, true); - } - } - - // `return (returns[0], returns[1], ..., returns[tys.len() - 1]);` - let ret_statement = self.make_statement( - StatementKind::Assign( - Lvalue::Local(RETURN_POINTER), - Rvalue::Aggregate( - box AggregateKind::Tuple, - returns.into_iter().map(Operand::Consume).collect() - ) - ) - ); - self.block(vec![ret_statement], TerminatorKind::Return, false); - } } /// Build a "call" shim for `def_id`. The shim calls the diff --git a/src/librustc_trans/monomorphize.rs b/src/librustc_trans/monomorphize.rs index 4989ca8cc938c..ad16d280a73b7 100644 --- a/src/librustc_trans/monomorphize.rs +++ b/src/librustc_trans/monomorphize.rs @@ -151,7 +151,7 @@ fn resolve_associated_item<'a, 'tcx>( } traits::VtableBuiltin(..) if Some(trait_id) == tcx.lang_items.clone_trait() => { Instance { - def: ty::InstanceDef::CloneShim(def_id, trait_ref.self_ty()), + def: ty::InstanceDef::CloneShim(def_id), substs: rcvr_substs } } diff --git a/src/test/compile-fail/aggregate-clone.rs b/src/test/compile-fail/aggregate-clone.rs new file mode 100644 index 0000000000000..529a244b18cbb --- /dev/null +++ b/src/test/compile-fail/aggregate-clone.rs @@ -0,0 +1,20 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[derive(Clone)] +struct S; + +fn main() { + let a = (S, S, S, S, S, S, S, S, S, S, S, S, S).clone(); + //~^ ERROR no method named `clone` found for type + + let a = [S, S, S].clone(); + //~^ ERROR no method named `clone` found for type +} diff --git a/src/test/run-pass/builtin-clone-unwind.rs b/src/test/run-pass/builtin-clone-unwind.rs deleted file mode 100644 index 90a411352869c..0000000000000 --- a/src/test/run-pass/builtin-clone-unwind.rs +++ /dev/null @@ -1,65 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Test that builtin implementations of `Clone` cleanup everything -// in case of unwinding. - -use std::thread; -use std::rc::Rc; - -struct S(Rc<()>); - -impl Clone for S { - fn clone(&self) -> Self { - if Rc::strong_count(&self.0) == 7 { - panic!("oops"); - } - - S(self.0.clone()) - } -} - -fn main() { - let counter = Rc::new(()); - - // Unwinding with tuples... - let ccounter = counter.clone(); - let result = std::panic::catch_unwind(move || { - let _ = ( - S(ccounter.clone()), - S(ccounter.clone()), - S(ccounter.clone()), - S(ccounter) - ).clone(); - }); - - assert!(result.is_err()); - assert_eq!( - 1, - Rc::strong_count(&counter) - ); - - // ... and with arrays. - let ccounter = counter.clone(); - let child = std::panic::catch_unwind(move || { - let _ = [ - S(ccounter.clone()), - S(ccounter.clone()), - S(ccounter.clone()), - S(ccounter) - ].clone(); - }); - - assert!(result.is_err()); - assert_eq!( - 1, - Rc::strong_count(&counter) - ); -} diff --git a/src/test/run-pass/builtin-clone.rs b/src/test/run-pass/builtin-clone.rs index 95903610931b2..2d22b5bf0a751 100644 --- a/src/test/run-pass/builtin-clone.rs +++ b/src/test/run-pass/builtin-clone.rs @@ -9,8 +9,6 @@ // except according to those terms. // Test that `Clone` is correctly implemented for builtin types. -// Also test that cloning an array or a tuple is done right, i.e. -// each component is cloned. fn test_clone(arg: T) { let _ = arg.clone(); @@ -18,37 +16,11 @@ fn test_clone(arg: T) { fn foo() { } -#[derive(Debug, PartialEq, Eq)] -struct S(i32); - -impl Clone for S { - fn clone(&self) -> Self { - S(self.0 + 1) - } -} - fn main() { + test_clone((1, 1, 1)); + test_clone([1, 1, 1]); + test_clone(foo); test_clone([1; 56]); test_clone((1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)); - - let a = [S(0), S(1), S(2)]; - let b = [S(1), S(2), S(3)]; - assert_eq!(b, a.clone()); - - let a = ( - (S(1), S(0)), - ( - (S(0), S(0), S(1)), - S(0) - ) - ); - let b = ( - (S(2), S(1)), - ( - (S(1), S(1), S(2)), - S(1) - ) - ); - assert_eq!(b, a.clone()); }