about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-12-19 06:12:01 +0000
committerbors <bors@rust-lang.org>2014-12-19 06:12:01 +0000
commit6bdce25e155d846bb9252fa4a18baef7e74cf8bf (patch)
tree80099a51ee183950cfa5661299e8dfcdeafd20c0 /src
parent840de072085df360733c48396224e9966e2dc72c (diff)
parent9b5de39c25b9b19ffcff3d519821b72a31d39d6c (diff)
downloadrust-6bdce25e155d846bb9252fa4a18baef7e74cf8bf.tar.gz
rust-6bdce25e155d846bb9252fa4a18baef7e74cf8bf.zip
auto merge of #19899 : japaric/rust/unops-by-value, r=nikomatsakis
- The following operator traits now take their argument by value: `Neg`, `Not`. This breaks all existing implementations of these traits.

- The unary operation `OP a` now "desugars" to `OpTrait::op_method(a)` and consumes its argument.

[breaking-change]

---

r? @nikomatsakis This PR is very similar to the binops-by-value PR
cc @aturon 
Diffstat (limited to 'src')
-rw-r--r--src/libcore/ops.rs100
-rw-r--r--src/librustc/middle/expr_use_visitor.rs14
-rw-r--r--src/librustc_trans/trans/expr.rs4
-rw-r--r--src/librustc_typeck/check/mod.rs9
-rw-r--r--src/librustc_typeck/check/regionck.rs6
-rw-r--r--src/libstd/bitflags.rs11
-rw-r--r--src/libstd/time/duration.rs14
-rw-r--r--src/libsyntax/ast_util.rs10
-rw-r--r--src/test/compile-fail/unop-move-semantics.rs37
-rw-r--r--src/test/run-pass/operator-overloading.rs4
10 files changed, 195 insertions, 14 deletions
diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs
index bc29a2b4a58..0090da3cdad 100644
--- a/src/libcore/ops.rs
+++ b/src/libcore/ops.rs
@@ -542,12 +542,16 @@ rem_float_impl! { f64, fmod }
 ///     -Foo;
 /// }
 /// ```
+// NOTE(stage0): Remove trait after a snapshot
+#[cfg(stage0)]
 #[lang="neg"]
 pub trait Neg<Result> for Sized? {
     /// The method for the unary `-` operator
     fn neg(&self) -> Result;
 }
 
+// NOTE(stage0): Remove macro after a snapshot
+#[cfg(stage0)]
 macro_rules! neg_impl {
     ($($t:ty)*) => ($(
         impl Neg<$t> for $t {
@@ -557,6 +561,8 @@ macro_rules! neg_impl {
     )*)
 }
 
+// NOTE(stage0): Remove macro after a snapshot
+#[cfg(stage0)]
 macro_rules! neg_uint_impl {
     ($t:ty, $t_signed:ty) => {
         impl Neg<$t> for $t {
@@ -566,6 +572,56 @@ macro_rules! neg_uint_impl {
     }
 }
 
+/// The `Neg` trait is used to specify the functionality of unary `-`.
+///
+/// # Example
+///
+/// A trivial implementation of `Neg`. When `-Foo` happens, it ends up calling
+/// `neg`, and therefore, `main` prints `Negating!`.
+///
+/// ```
+/// struct Foo;
+///
+/// impl Copy for Foo {}
+///
+/// impl Neg<Foo> for Foo {
+///     fn neg(self) -> Foo {
+///         println!("Negating!");
+///         self
+///     }
+/// }
+///
+/// fn main() {
+///     -Foo;
+/// }
+/// ```
+#[cfg(not(stage0))]  // NOTE(stage0): Remove cfg after a snapshot
+#[lang="neg"]
+pub trait Neg<Result> {
+    /// The method for the unary `-` operator
+    fn neg(self) -> Result;
+}
+
+#[cfg(not(stage0))]  // NOTE(stage0): Remove cfg after a snapshot
+macro_rules! neg_impl {
+    ($($t:ty)*) => ($(
+        impl Neg<$t> for $t {
+            #[inline]
+            fn neg(self) -> $t { -self }
+        }
+    )*)
+}
+
+#[cfg(not(stage0))]  // NOTE(stage0): Remove cfg after a snapshot
+macro_rules! neg_uint_impl {
+    ($t:ty, $t_signed:ty) => {
+        impl Neg<$t> for $t {
+            #[inline]
+            fn neg(self) -> $t { -(self as $t_signed) as $t }
+        }
+    }
+}
+
 neg_impl! { int i8 i16 i32 i64 f32 f64 }
 
 neg_uint_impl! { uint, int }
@@ -598,6 +654,8 @@ neg_uint_impl! { u64, i64 }
 ///     !Foo;
 /// }
 /// ```
+// NOTE(stage0): Remove macro after a snapshot
+#[cfg(stage0)]
 #[lang="not"]
 pub trait Not<Result> for Sized? {
     /// The method for the unary `!` operator
@@ -605,6 +663,8 @@ pub trait Not<Result> for Sized? {
 }
 
 
+// NOTE(stage0): Remove macro after a snapshot
+#[cfg(stage0)]
 macro_rules! not_impl {
     ($($t:ty)*) => ($(
         impl Not<$t> for $t {
@@ -614,6 +674,46 @@ macro_rules! not_impl {
     )*)
 }
 
+/// The `Not` trait is used to specify the functionality of unary `!`.
+///
+/// # Example
+///
+/// A trivial implementation of `Not`. When `!Foo` happens, it ends up calling
+/// `not`, and therefore, `main` prints `Not-ing!`.
+///
+/// ```
+/// struct Foo;
+///
+/// impl Copy for Foo {}
+///
+/// impl Not<Foo> for Foo {
+///     fn not(self) -> Foo {
+///         println!("Not-ing!");
+///         self
+///     }
+/// }
+///
+/// fn main() {
+///     !Foo;
+/// }
+/// ```
+#[cfg(not(stage0))]  // NOTE(stage0): Remove cfg after a snapshot
+#[lang="not"]
+pub trait Not<Result> {
+    /// The method for the unary `!` operator
+    fn not(self) -> Result;
+}
+
+#[cfg(not(stage0))]  // NOTE(stage0): Remove cfg after a snapshot
+macro_rules! not_impl {
+    ($($t:ty)*) => ($(
+        impl Not<$t> for $t {
+            #[inline]
+            fn not(self) -> $t { !self }
+        }
+    )*)
+}
+
 not_impl! { bool uint u8 u16 u32 u64 int i8 i16 i32 i64 }
 
 /// The `BitAnd` trait is used to specify the functionality of `&`.
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index aacb994e5a4..901944cd016 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -576,8 +576,14 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
                 self.walk_block(&**blk);
             }
 
-            ast::ExprUnary(_, ref lhs) => {
-                if !self.walk_overloaded_operator(expr, &**lhs, Vec::new(), PassArgs::ByRef) {
+            ast::ExprUnary(op, ref lhs) => {
+                let pass_args = if ast_util::is_by_value_unop(op) {
+                    PassArgs::ByValue
+                } else {
+                    PassArgs::ByRef
+                };
+
+                if !self.walk_overloaded_operator(expr, &**lhs, Vec::new(), pass_args) {
                     self.consume_expr(&**lhs);
                 }
             }
@@ -937,7 +943,9 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
         match pass_args {
             PassArgs::ByValue => {
                 self.consume_expr(receiver);
-                self.consume_expr(rhs[0]);
+                for &arg in rhs.iter() {
+                    self.consume_expr(arg);
+                }
 
                 return true;
             },
diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs
index 690e7cf81f5..304142453a9 100644
--- a/src/librustc_trans/trans/expr.rs
+++ b/src/librustc_trans/trans/expr.rs
@@ -1101,11 +1101,11 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                 vec![(rhs_datum, rhs.id)], Some(dest),
                                 !ast_util::is_by_value_binop(op)).bcx
         }
-        ast::ExprUnary(_, ref subexpr) => {
+        ast::ExprUnary(op, ref subexpr) => {
             // if not overloaded, would be RvalueDatumExpr
             let arg = unpack_datum!(bcx, trans(bcx, &**subexpr));
             trans_overloaded_op(bcx, expr, MethodCall::expr(expr.id),
-                                arg, Vec::new(), Some(dest), true).bcx
+                                arg, Vec::new(), Some(dest), !ast_util::is_by_value_unop(op)).bcx
         }
         ast::ExprIndex(ref base, ref idx) => {
             // if not overloaded, would be RvalueDatumExpr
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index ad63806df66..def82ecd6c8 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -3356,14 +3356,15 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                                  trait_did: Option<ast::DefId>,
                                  ex: &ast::Expr,
                                  rhs_expr: &ast::Expr,
-                                 rhs_t: Ty<'tcx>) -> Ty<'tcx> {
+                                 rhs_t: Ty<'tcx>,
+                                 op: ast::UnOp) -> Ty<'tcx> {
        lookup_op_method(fcx, ex, rhs_t, token::intern(mname),
                         trait_did, rhs_expr, None, || {
             fcx.type_error_message(ex.span, |actual| {
                 format!("cannot apply unary operator `{}` to type `{}`",
                         op_str, actual)
             }, rhs_t, None);
-        }, AutorefArgs::Yes)
+        }, if ast_util::is_by_value_unop(op) { AutorefArgs::No } else { AutorefArgs::Yes })
     }
 
     // Check field access expressions
@@ -3803,7 +3804,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                          oprnd_t.sty == ty::ty_bool) {
                         oprnd_t = check_user_unop(fcx, "!", "not",
                                                   tcx.lang_items.not_trait(),
-                                                  expr, &**oprnd, oprnd_t);
+                                                  expr, &**oprnd, oprnd_t, unop);
                     }
                 }
                 ast::UnNeg => {
@@ -3813,7 +3814,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                          ty::type_is_fp(oprnd_t)) {
                         oprnd_t = check_user_unop(fcx, "-", "neg",
                                                   tcx.lang_items.neg_trait(),
-                                                  expr, &**oprnd, oprnd_t);
+                                                  expr, &**oprnd, oprnd_t, unop);
                     }
                 }
             }
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index 2ec7e2c3883..bfa3c384da7 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -682,10 +682,12 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) {
             visit::walk_expr(rcx, expr);
         }
 
-        ast::ExprUnary(_, ref lhs) if has_method_map => {
+        ast::ExprUnary(op, ref lhs) if has_method_map => {
+            let implicitly_ref_args = !ast_util::is_by_value_unop(op);
+
             // As above.
             constrain_call(rcx, expr, Some(&**lhs),
-                           None::<ast::Expr>.iter(), true);
+                           None::<ast::Expr>.iter(), implicitly_ref_args);
 
             visit::walk_expr(rcx, expr);
         }
diff --git a/src/libstd/bitflags.rs b/src/libstd/bitflags.rs
index 2be6f5057a1..f467b77dbf4 100644
--- a/src/libstd/bitflags.rs
+++ b/src/libstd/bitflags.rs
@@ -281,6 +281,8 @@ macro_rules! bitflags {
             }
         }
 
+        // NOTE(stage0): Remove impl after a snapshot
+        #[cfg(stage0)]
         impl Not<$BitFlags> for $BitFlags {
             /// Returns the complement of this set of flags.
             #[inline]
@@ -288,6 +290,15 @@ macro_rules! bitflags {
                 $BitFlags { bits: !self.bits } & $BitFlags::all()
             }
         }
+
+        #[cfg(not(stage0))]  // NOTE(stage0): Remove cfg after a snapshot
+        impl Not<$BitFlags> for $BitFlags {
+            /// Returns the complement of this set of flags.
+            #[inline]
+            fn not(self) -> $BitFlags {
+                $BitFlags { bits: !self.bits } & $BitFlags::all()
+            }
+        }
     };
     ($(#[$attr:meta])* flags $BitFlags:ident: $T:ty {
         $($(#[$Flag_attr:meta])* const $Flag:ident = $value:expr),+,
diff --git a/src/libstd/time/duration.rs b/src/libstd/time/duration.rs
index 8c4a5a6b8c7..85ed27853c4 100644
--- a/src/libstd/time/duration.rs
+++ b/src/libstd/time/duration.rs
@@ -265,6 +265,8 @@ impl Duration {
     }
 }
 
+// NOTE(stage0): Remove impl after a snapshot
+#[cfg(stage0)]
 impl Neg<Duration> for Duration {
     #[inline]
     fn neg(&self) -> Duration {
@@ -276,6 +278,18 @@ impl Neg<Duration> for Duration {
     }
 }
 
+#[cfg(not(stage0))]  // NOTE(stage0): Remove cfg after a snapshot
+impl Neg<Duration> for Duration {
+    #[inline]
+    fn neg(self) -> Duration {
+        if self.nanos == 0 {
+            Duration { secs: -self.secs, nanos: 0 }
+        } else {
+            Duration { secs: -self.secs - 1, nanos: NANOS_PER_SEC - self.nanos }
+        }
+    }
+}
+
 // NOTE(stage0): Remove impl after a snapshot
 #[cfg(stage0)]
 impl Add<Duration,Duration> for Duration {
diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs
index aaa172633be..5243f07f327 100644
--- a/src/libsyntax/ast_util.rs
+++ b/src/libsyntax/ast_util.rs
@@ -85,7 +85,7 @@ pub fn is_shift_binop(b: BinOp) -> bool {
     }
 }
 
-/// Returns `true` is the binary operator takes its arguments by value
+/// Returns `true` if the binary operator takes its arguments by value
 pub fn is_by_value_binop(b: BinOp) -> bool {
     match b {
         BiAdd | BiSub | BiMul | BiDiv | BiRem | BiBitXor | BiBitAnd | BiBitOr | BiShl | BiShr => {
@@ -95,6 +95,14 @@ pub fn is_by_value_binop(b: BinOp) -> bool {
     }
 }
 
+/// Returns `true` if the unary operator takes its argument by value
+pub fn is_by_value_unop(u: UnOp) -> bool {
+    match u {
+        UnNeg | UnNot => true,
+        _ => false,
+    }
+}
+
 pub fn unop_to_string(op: UnOp) -> &'static str {
     match op {
       UnUniq => "box() ",
diff --git a/src/test/compile-fail/unop-move-semantics.rs b/src/test/compile-fail/unop-move-semantics.rs
new file mode 100644
index 00000000000..ccdc7b833e7
--- /dev/null
+++ b/src/test/compile-fail/unop-move-semantics.rs
@@ -0,0 +1,37 @@
+// Copyright 2014 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that move restrictions are enforced on overloaded unary operations
+
+fn move_then_borrow<T: Not<T> + Clone>(x: T) {
+    !x;
+
+    x.clone();  //~ ERROR: use of moved value
+}
+
+fn move_borrowed<T: Not<T>>(x: T, mut y: T) {
+    let m = &x;
+    let n = &mut y;
+
+    !x;  //~ ERROR: cannot move out of `x` because it is borrowed
+
+    !y;  //~ ERROR: cannot move out of `y` because it is borrowed
+}
+
+fn illegal_dereference<T: Not<T>>(mut x: T, y: T) {
+    let m = &mut x;
+    let n = &y;
+
+    !*m;  //~ ERROR: cannot move out of dereference of `&mut`-pointer
+
+    !*n;  //~ ERROR: cannot move out of dereference of `&`-pointer
+}
+
+fn main() {}
diff --git a/src/test/run-pass/operator-overloading.rs b/src/test/run-pass/operator-overloading.rs
index 8d743ba42e8..1e646e9399c 100644
--- a/src/test/run-pass/operator-overloading.rs
+++ b/src/test/run-pass/operator-overloading.rs
@@ -31,13 +31,13 @@ impl ops::Sub<Point,Point> for Point {
 }
 
 impl ops::Neg<Point> for Point {
-    fn neg(&self) -> Point {
+    fn neg(self) -> Point {
         Point {x: -self.x, y: -self.y}
     }
 }
 
 impl ops::Not<Point> for Point {
-    fn not(&self) -> Point {
+    fn not(self) -> Point {
         Point {x: !self.x, y: !self.y }
     }
 }