From 7efaf3fa343776d547d87984ab47bc26bc1f59a0 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 15 Dec 2014 15:49:41 -0500 Subject: [PATCH 01/11] syntax/ast_util: add `is_by_value_unop()` --- src/libsyntax/ast_util.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index aaa172633be21..5243f07f32749 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() ", From c5b6d9412d3745b2737041034076918d7cd95ac0 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 15 Dec 2014 16:11:34 -0500 Subject: [PATCH 02/11] Tell typeck that user unops are by value --- src/librustc_typeck/check/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index ad63806df6696..5a08880e2691e 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3363,7 +3363,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, format!("cannot apply unary operator `{}` to type `{}`", op_str, actual) }, rhs_t, None); - }, AutorefArgs::Yes) + }, AutorefArgs::No) } // Check field access expressions From 9a962a7bdc9a794fbaa1e96ae951c22dd31eac86 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 15 Dec 2014 16:14:25 -0500 Subject: [PATCH 03/11] Tell trans that user unops are by value --- src/librustc_trans/trans/expr.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index 690e7cf81f5f5..304142453a9c8 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 From b98c3bd4d2e03f801435165fe8fa75cbf59c0582 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 15 Dec 2014 16:16:48 -0500 Subject: [PATCH 04/11] Tell expr_use_visitor that user unops are by value --- src/librustc/middle/expr_use_visitor.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index aacb994e5a49b..901944cd0168e 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; }, From 5caebb23cd3b2c4c8519cc94759a7bc0a377f959 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 15 Dec 2014 16:18:17 -0500 Subject: [PATCH 05/11] Tell regionck that user unops are by value --- src/librustc_typeck/check/regionck.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 2ec7e2c38836d..bfa3c384da7b2 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); } From 5359879fb6d51a97c2b830f41b41d75e0c393e5f Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 15 Dec 2014 16:24:24 -0500 Subject: [PATCH 06/11] libcore: convert unop traits to by value --- src/libcore/ops.rs | 100 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index bc29a2b4a58cd..0090da3cdad6e 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 `&`. From 340f042e03f6c50f229a8e21a93af22485d725f2 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 15 Dec 2014 17:06:10 -0500 Subject: [PATCH 07/11] libstd: convert `BitFlags` unops to by value --- src/libstd/bitflags.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/libstd/bitflags.rs b/src/libstd/bitflags.rs index 2be6f5057a178..f467b77dbf4cf 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),+, From 5f347d77084d873d213ca1aa29ccfd8fdd27e28a Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 15 Dec 2014 17:06:34 -0500 Subject: [PATCH 08/11] libstd: convert `Duration` unops to by value --- src/libstd/time/duration.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/libstd/time/duration.rs b/src/libstd/time/duration.rs index 8c4a5a6b8c7fd..85ed27853c454 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 { From 5d49999ad36e7f6716d0537b40224d829bfd2c60 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 15 Dec 2014 18:24:07 -0500 Subject: [PATCH 09/11] Fix run pass tests --- src/test/run-pass/operator-overloading.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/run-pass/operator-overloading.rs b/src/test/run-pass/operator-overloading.rs index 8d743ba42e890..1e646e9399c56 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 } } } From d15d1529e0b54d22a3597bcc817b6e1be5e3e0e7 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 15 Dec 2014 18:24:33 -0500 Subject: [PATCH 10/11] Test unop move semantics --- src/test/compile-fail/unop-move-semantics.rs | 37 ++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/test/compile-fail/unop-move-semantics.rs 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 0000000000000..ccdc7b833e714 --- /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() {} From 9b5de39c25b9b19ffcff3d519821b72a31d39d6c Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 16 Dec 2014 19:40:57 -0500 Subject: [PATCH 11/11] Address Niko's comments --- src/librustc_typeck/check/mod.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 5a08880e2691e..def82ecd6c85b 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::No) + }, 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); } } }