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
This commit is contained in:
commit
6bdce25e15
@ -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 `&`.
|
||||
|
@ -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;
|
||||
},
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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),+,
|
||||
|
@ -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 {
|
||||
|
@ -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() ",
|
||||
|
37
src/test/compile-fail/unop-move-semantics.rs
Normal file
37
src/test/compile-fail/unop-move-semantics.rs
Normal file
@ -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() {}
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user