diff --git a/src/librustc/middle/moves.rs b/src/librustc/middle/moves.rs index 0d9091e4e44..a9527831d3a 100644 --- a/src/librustc/middle/moves.rs +++ b/src/librustc/middle/moves.rs @@ -134,6 +134,7 @@ use middle::typeck::{method_map}; use util::ppaux; use util::ppaux::Repr; use util::common::indenter; +use util::ppaux::UserString; use std::at_vec; use std::hashmap::{HashSet, HashMap}; @@ -433,7 +434,21 @@ impl VisitContext { ty::type_moves_by_default(self.tcx, tf.mt.ty) }); + fn has_dtor(tcx: ty::ctxt, ty: ty::t) -> bool { + use middle::ty::{get,ty_struct,ty_enum}; + match get(ty).sty { + ty_struct(did, _) | ty_enum(did, _) => ty::has_dtor(tcx, did), + _ => false, + } + } + if consume_with { + if has_dtor(self.tcx, with_ty) { + self.tcx.sess.span_err(with_expr.span, + fmt!("cannot move out of type `%s`, \ + which defines the `Drop` trait", + with_ty.user_string(self.tcx))); + } self.consume_expr(*with_expr, visitor); } else { self.use_expr(*with_expr, Read, visitor); diff --git a/src/test/compile-fail/borrowck-struct-update-with-dtor.rs b/src/test/compile-fail/borrowck-struct-update-with-dtor.rs new file mode 100644 index 00000000000..5a0dae676f1 --- /dev/null +++ b/src/test/compile-fail/borrowck-struct-update-with-dtor.rs @@ -0,0 +1,29 @@ +// Copyright 2012-2013 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. + +// Issue 4691: Ensure that functional-struct-update can only copy, not +// move, when the struct implements Drop. + +use NC = std::util::NonCopyable; +struct S { a: int, nc: NC } +impl Drop for S { fn drop(&mut self) { } } + +struct T { a: int, mv: ~int } +impl Drop for T { fn drop(&mut self) { } } + +fn f(s0:S) { + let _s2 = S{a: 2, ..s0}; //~error: cannot move out of type `S`, which defines the `Drop` trait +} + +fn g(s0:T) { + let _s2 = T{a: 2, ..s0}; //~error: cannot move out of type `T`, which defines the `Drop` trait +} + +fn main() { } diff --git a/src/test/run-pass/struct-update-moves-and-copies.rs b/src/test/run-pass/struct-update-moves-and-copies.rs new file mode 100644 index 00000000000..a27703468ec --- /dev/null +++ b/src/test/run-pass/struct-update-moves-and-copies.rs @@ -0,0 +1,101 @@ +// Copyright 2012-2013 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. + +// Issue 4691: Ensure that functional-struct-updates operates +// correctly and moves rather than copy when appropriate. + +use NC = std::util::NonCopyable; + +struct ncint { nc: NC, v: int } +fn ncint(v: int) -> ncint { ncint { nc: NC, v: v } } + +struct NoFoo { copied: int, noncopy: ncint, } +impl NoFoo { + fn new(x:int,y:int) -> NoFoo { NoFoo { copied: x, noncopy: ncint(y) } } +} + +struct MoveFoo { copied: int, moved: ~int, } +impl MoveFoo { + fn new(x:int,y:int) -> MoveFoo { MoveFoo { copied: x, moved: ~y } } +} + +struct DropNoFoo { inner: NoFoo } +impl DropNoFoo { + fn new(x:int,y:int) -> DropNoFoo { DropNoFoo { inner: NoFoo::new(x,y) } } +} +impl Drop for DropNoFoo { fn drop(&mut self) { } } + +struct DropMoveFoo { inner: MoveFoo } +impl DropMoveFoo { + fn new(x:int,y:int) -> DropMoveFoo { DropMoveFoo { inner: MoveFoo::new(x,y) } } +} +impl Drop for DropMoveFoo { fn drop(&mut self) { } } + + +fn test0() { + // just copy implicitly copyable fields from `f`, no moves + // (and thus it is okay that these are Drop; compare against + // compile-fail test: borrowck-struct-update-with-dtor.rs). + + // Case 1: NonCopyable + let f = DropNoFoo::new(1, 2); + let b = DropNoFoo { inner: NoFoo { noncopy: ncint(3), ..f.inner }}; + let c = DropNoFoo { inner: NoFoo { noncopy: ncint(4), ..f.inner }}; + assert_eq!(f.inner.copied, 1); + assert_eq!(f.inner.noncopy.v, 2); + + assert_eq!(b.inner.copied, 1); + assert_eq!(b.inner.noncopy.v, 3); + + assert_eq!(c.inner.copied, 1); + assert_eq!(c.inner.noncopy.v, 4); + + // Case 2: Owned + let f = DropMoveFoo::new(5, 6); + let b = DropMoveFoo { inner: MoveFoo { moved: ~7, ..f.inner }}; + let c = DropMoveFoo { inner: MoveFoo { moved: ~8, ..f.inner }}; + assert_eq!(f.inner.copied, 5); + assert_eq!(*f.inner.moved, 6); + + assert_eq!(b.inner.copied, 1); + assert_eq!(*b.inner.moved, 7); + + assert_eq!(c.inner.copied, 1); + assert_eq!(*c.inner.moved, 8); +} + +fn test1() { + // copying move-by-default fields from `f`, so it moves: + let f = MoveFoo::new(11, 12); + + let b = MoveFoo {moved: ~13, ..f}; + let c = MoveFoo {copied: 14, ..f}; + assert_eq!(b.copied, 11); + assert_eq!(*b.moved, 13); + assert_eq!(c.copied, 14); + assert_eq!(*c.moved, 12); +} + +fn test2() { + // move non-copyable field + let f = NoFoo::new(21, 22); + let b = NoFoo {noncopy: ncint(23), ..f}; + let c = NoFoo {copied: 24, ..f}; + assert_eq!(b.copied, 21); + assert_eq!(b.noncopy.v, 23); + assert_eq!(c.copied, 24); + assert_eq!(c.noncopy.v, 22); +} + +fn main() { + test0(); + test1(); + test2(); +}