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 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 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 { + /// 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 for Sized? { /// The method for the unary `!` operator @@ -605,6 +663,8 @@ pub trait Not 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 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 { + /// 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, 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::.iter(), true); + None::.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 for Duration { #[inline] fn neg(&self) -> Duration { @@ -276,6 +278,18 @@ impl Neg for Duration { } } +#[cfg(not(stage0))] // NOTE(stage0): Remove cfg after a snapshot +impl Neg 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 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 or the MIT license +// , 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 + Clone>(x: T) { + !x; + + x.clone(); //~ ERROR: use of moved value +} + +fn move_borrowed>(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>(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 for Point { } impl ops::Neg for Point { - fn neg(&self) -> Point { + fn neg(self) -> Point { Point {x: -self.x, y: -self.y} } } impl ops::Not for Point { - fn not(&self) -> Point { + fn not(self) -> Point { Point {x: !self.x, y: !self.y } } }