diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index d2a42650e3df9..f3c3c808446f0 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -277,67 +277,151 @@ fn build_clone_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, self_ty: ty::Ty<'tcx>) -> Mir<'tcx> { - let sig = tcx.fn_sig(def_id); - let sig = tcx.erase_late_bound_regions(&sig); - let span = tcx.def_span(def_id); - debug!("build_clone_shim(def_id={:?})", def_id); - let mut local_decls = local_decls_for_sig(&sig, span); - let source_info = SourceInfo { span, scope: ARGUMENT_VISIBILITY_SCOPE }; + let mut builder = CloneShimBuilder::new(tcx, def_id); + let is_copy = !self_ty.moves_by_default(tcx, tcx.param_env(def_id), builder.span); - let rcvr = Lvalue::Local(Local::new(1+0)).deref(); + 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); + } + }; - let mut blocks = IndexVec::new(); - let block = |blocks: &mut IndexVec<_, _>, statements, kind, is_cleanup| { - blocks.push(BasicBlockData { + builder.into_mir() +} + +struct CloneShimBuilder<'a, 'tcx: 'a> { + tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, + 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 { + 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, + sig, + } + } + + fn into_mir(self) -> Mir<'tcx> { + Mir::new( + self.blocks, + IndexVec::from_elem_n( + VisibilityScopeData { span: self.span, parent_scope: None }, 1 + ), + IndexVec::new(), + self.sig.output(), + self.local_decls, + self.sig.inputs().len(), + vec![], + self.span + ) + } + + fn source_info(&self) -> SourceInfo { + SourceInfo { span: self.span, scope: ARGUMENT_VISIBILITY_SCOPE } + } + + fn block( + &mut self, + statements: Vec>, + kind: TerminatorKind<'tcx>, + is_cleanup: bool + ) -> BasicBlock { + let source_info = self.source_info(); + self.blocks.push(BasicBlockData { statements, terminator: Some(Terminator { source_info, kind }), is_cleanup, }) - }; + } - let make_lvalue = |mutability, ty, local_decls: &mut IndexVec<_, _>| { + fn make_statement(&self, kind: StatementKind<'tcx>) -> Statement<'tcx> { + Statement { + source_info: self.source_info(), + kind, + } + } + + fn copy_shim(&mut self) { + let rcvr = Lvalue::Local(Local::new(1+0)).deref(); + let ret_statement = self.make_statement( + StatementKind::Assign( + Lvalue::Local(RETURN_POINTER), + Rvalue::Use(Operand::Consume(rcvr)) + ) + ); + 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( - local_decls.push(temp_decl(mutability, ty, span)) + 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 + ); - let call_clone = |ty, rcvr_field, next, cleanup, - blocks: &mut _, local_decls: &mut IndexVec<_, _>| - { // `func == Clone::clone(&ty) -> ty` - let substs = Substs::for_item(tcx, def_id, |_, _| tcx.types.re_erased, |_, _| ty); let func = Operand::Constant(box Constant { - span: span, - ty: tcx.mk_fn_def(def_id, substs), + span: self.span, + ty: tcx.mk_fn_def(self.def_id, substs), literal: Literal::Value { - value: ConstVal::Function(def_id, substs), + value: ConstVal::Function(self.def_id, substs), }, }); - let ref_loc = make_lvalue( + let ref_loc = self.make_lvalue( Mutability::Not, tcx.mk_ref(tcx.types.re_erased, ty::TypeAndMut { ty, mutbl: hir::Mutability::MutImmutable, - }), - local_decls + }) ); - let loc = make_lvalue(Mutability::Not, ty, local_decls); + let loc = self.make_lvalue(Mutability::Not, ty); // `let ref_loc: &ty = &rcvr_field;` - let statement = Statement { - source_info, - kind: StatementKind::Assign( + 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);` - block(blocks, vec![statement], TerminatorKind::Call { + self.block(vec![statement], TerminatorKind::Call { func, args: vec![Operand::Consume(ref_loc)], destination: Some((loc.clone(), next)), @@ -345,280 +429,214 @@ fn build_clone_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, }, false); loc - }; - - let is_copy = !self_ty.moves_by_default(tcx, tcx.param_env(def_id), span); - match self_ty.sty { - _ if is_copy => { - // `return *self;` - let ret_statement = Statement { - source_info, - kind: StatementKind::Assign( - Lvalue::Local(RETURN_POINTER), - Rvalue::Use(Operand::Consume(rcvr)) - ) - }; - block(&mut blocks, vec![ret_statement], TerminatorKind::Return, false); - } - ty::TyArray(ty, len) => { - let make_loop = |beg, end, loop_body, loop_end, - blocks: &mut _, local_decls: &mut _, is_cleanup| - { - let cond = make_lvalue(Mutability::Mut, tcx.types.bool, local_decls); - let compute_cond = Statement { - source_info, - kind: StatementKind::Assign( - cond.clone(), - Rvalue::BinaryOp(BinOp::Ne, Operand::Consume(end), Operand::Consume(beg)) - ) - }; - - // `if end != beg { goto loop_body; } else { goto loop_end; }` - block( - blocks, - vec![compute_cond], - TerminatorKind::if_(tcx, Operand::Consume(cond), loop_body, loop_end), - is_cleanup - ); - }; - - let make_usize = |value| { - let value = ConstUsize::new(value as u64, tcx.sess.target.uint_type).unwrap(); - box Constant { - span, - ty: tcx.types.usize, - literal: Literal::Value { - value: ConstVal::Integral(ConstInt::Usize(value)) - } - } - }; + } - let beg = make_lvalue(Mutability::Mut, tcx.types.usize, &mut local_decls); - let end = make_lvalue(Mutability::Not, tcx.types.usize, &mut local_decls); - let ret = make_lvalue(Mutability::Mut, tcx.mk_array(ty, len), &mut local_decls); - - // BB #0 - // `let mut beg = 0;` - // `let end = len;` - // `goto #1;` - let inits = vec![ - Statement { - source_info, - kind: StatementKind::Assign( - beg.clone(), - Rvalue::Use(Operand::Constant(make_usize(0))) - ) - }, - Statement { - source_info, - kind: StatementKind::Assign( - end.clone(), - Rvalue::Use(Operand::Constant(make_usize(len))) - ) - } - ]; - block(&mut blocks, inits, TerminatorKind::Goto { target: BasicBlock::new(1) }, false); - - // BB #1: loop { - // BB #2; - // BB #3; - // } - // BB #4; - make_loop( - beg.clone(), - end, - BasicBlock::new(2), - BasicBlock::new(4), - &mut blocks, - &mut local_decls, - false - ); + 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)) + ) + ); - // 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 = call_clone( - ty, - rcvr_field, - BasicBlock::new(3), - BasicBlock::new(5), - &mut blocks, - &mut local_decls - ); + // `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 + ); + } - // BB #3 - // `ret[beg] = cloned;` - // `beg = beg + 1;` - // `goto #1`; - let ret_field = ret.clone().index(Operand::Consume(beg.clone())); - let statements = vec![ - Statement { - source_info, - kind: StatementKind::Assign( - ret_field, - Rvalue::Use(Operand::Consume(cloned)) - ) - }, - Statement { - source_info, - kind: StatementKind::Assign( - beg.clone(), - Rvalue::BinaryOp( - BinOp::Add, - Operand::Consume(beg.clone()), - Operand::Constant(make_usize(1)) - ) - ) - } - ]; - block( - &mut blocks, - statements, - TerminatorKind::Goto { target: BasicBlock::new(1) }, - false - ); + 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)) + } + } + } - // BB #4 - // `return ret;` - let ret_statement = Statement { - source_info: source_info, - kind: StatementKind::Assign( - Lvalue::Local(RETURN_POINTER), - Rvalue::Use(Operand::Consume(ret.clone())), - ) - }; - block(&mut blocks, vec![ret_statement], TerminatorKind::Return, false); - - // BB #5 (cleanup) - // `let end = beg;` - // `let mut beg = 0;` - // goto #6; - let end = beg; - let beg = make_lvalue(Mutability::Mut, tcx.types.usize, &mut local_decls); - let init = Statement { - source_info, - kind: StatementKind::Assign( + 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(make_usize(0))) + Rvalue::Use(Operand::Constant(self.make_usize(0))) ) - }; - block( - &mut blocks, - vec![init], - TerminatorKind::Goto { target: BasicBlock::new(6) }, - true - ); - - // BB #6 (cleanup): loop { - // BB #7; - // BB #8; - // } - // BB #9; - make_loop( - beg.clone(), - end, - BasicBlock::new(7), - BasicBlock::new(9), - &mut blocks, - &mut local_decls, - true - ); - - // BB #7 (cleanup) - // `drop(ret[beg])`; - block(&mut blocks, 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 = Statement { - source_info, - kind: StatementKind::Assign( + ), + 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(make_usize(1)) + Operand::Constant(self.make_usize(1)) ) ) - }; - block( - &mut blocks, - vec![statement], - TerminatorKind::Goto { target: BasicBlock::new(6) }, - true - ); + ) + ]; + self.block(statements, TerminatorKind::Goto { target: BasicBlock::new(1) }, false); - // BB #9 (resume) - block(&mut blocks, vec![], TerminatorKind::Resume, true); - } - ty::TyTuple(tys, _) => { - 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(call_clone( + // 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), - &mut blocks, - &mut local_decls - )); - - // BB #(2i + 1) (cleanup) - if i == 0 { - // Nothing to drop, just resume. - block(&mut blocks, vec![], TerminatorKind::Resume, true); - } else { - // Drop previous field and goto previous cleanup block. - block(&mut blocks, 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 = Statement { - source_info, - kind: StatementKind::Assign( - Lvalue::Local(RETURN_POINTER), - Rvalue::Aggregate( - box AggregateKind::Tuple, - returns.into_iter().map(Operand::Consume).collect() - ) ) - }; - block(&mut blocks, vec![ret_statement], TerminatorKind::Return, false); - } - _ => { - bug!("clone shim for `{:?}` which is not `Copy` and is not an aggregate", self_ty); + ); + + // 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); + } } - }; - let mir = Mir::new( - blocks, - IndexVec::from_elem_n( - VisibilityScopeData { span: span, parent_scope: None }, 1 - ), - IndexVec::new(), - sig.output(), - local_decls, - sig.inputs().len(), - vec![], - span - ); - mir + // `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/test/run-pass/builtin-clone-unwind.rs b/src/test/run-pass/builtin-clone-unwind.rs index 2829f79812c59..90a411352869c 100644 --- a/src/test/run-pass/builtin-clone-unwind.rs +++ b/src/test/run-pass/builtin-clone-unwind.rs @@ -12,13 +12,13 @@ // in case of unwinding. use std::thread; -use std::sync::Arc; +use std::rc::Rc; -struct S(Arc<()>); +struct S(Rc<()>); impl Clone for S { fn clone(&self) -> Self { - if Arc::strong_count(&self.0) == 7 { + if Rc::strong_count(&self.0) == 7 { panic!("oops"); } @@ -27,11 +27,11 @@ impl Clone for S { } fn main() { - let counter = Arc::new(()); + let counter = Rc::new(()); // Unwinding with tuples... let ccounter = counter.clone(); - let child = thread::spawn(move || { + let result = std::panic::catch_unwind(move || { let _ = ( S(ccounter.clone()), S(ccounter.clone()), @@ -40,15 +40,15 @@ fn main() { ).clone(); }); - assert!(child.join().is_err()); + assert!(result.is_err()); assert_eq!( 1, - Arc::strong_count(&counter) + Rc::strong_count(&counter) ); // ... and with arrays. let ccounter = counter.clone(); - let child = thread::spawn(move || { + let child = std::panic::catch_unwind(move || { let _ = [ S(ccounter.clone()), S(ccounter.clone()), @@ -57,9 +57,9 @@ fn main() { ].clone(); }); - assert!(child.join().is_err()); + assert!(result.is_err()); assert_eq!( 1, - Arc::strong_count(&counter) + Rc::strong_count(&counter) ); }