Skip to content

Commit

Permalink
auto merge of #19899 : japaric/rust/unops-by-value, r=nikomatsakis
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
bors committed Dec 19, 2014
2 parents 840de07 + 9b5de39 commit 6bdce25
Show file tree
Hide file tree
Showing 10 changed files with 195 additions and 14 deletions.
100 changes: 100 additions & 0 deletions src/libcore/ops.rs
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
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
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
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
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
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
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
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
@@ -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
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

0 comments on commit 6bdce25

Please sign in to comment.