Skip to content

Make unary operators take their arguments by value #19899

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Dec 19, 2014
100 changes: 100 additions & 0 deletions src/libcore/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 }
Expand Down Expand Up @@ -598,13 +654,17 @@ 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
fn not(&self) -> Result;
}


// NOTE(stage0): Remove macro after a snapshot
#[cfg(stage0)]
macro_rules! not_impl {
($($t:ty)*) => ($(
impl Not<$t> for $t {
Expand All @@ -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 `&`.
Expand Down
14 changes: 11 additions & 3 deletions src/librustc/middle/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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;
},
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_trans/trans/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 5 additions & 4 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 => {
Expand All @@ -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);
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/librustc_typeck/check/regionck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
11 changes: 11 additions & 0 deletions src/libstd/bitflags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,13 +281,24 @@ 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]
fn not(&self) -> $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),+,
Expand Down
14 changes: 14 additions & 0 deletions src/libstd/time/duration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down
10 changes: 9 additions & 1 deletion src/libsyntax/ast_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand All @@ -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() ",
Expand Down
37 changes: 37 additions & 0 deletions src/test/compile-fail/unop-move-semantics.rs
Original file line number Diff line number Diff line change
@@ -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() {}
4 changes: 2 additions & 2 deletions src/test/run-pass/operator-overloading.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
}
}
Expand Down