提交 6bdce25e 编写于 作者: B bors

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 
......@@ -542,12 +542,16 @@ fn rem(self, other: $t) -> $t {
/// -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 @@ fn neg(&self) -> $t { -*self }
)*)
}
// 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 @@ fn neg(&self) -> $t { -(*self as $t_signed) as $t }
}
}
/// 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 @@ fn neg(&self) -> $t { -(*self as $t_signed) as $t }
/// !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 @@ fn not(&self) -> $t { !*self }
)*)
}
/// 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 @@ pub fn walk_expr(&mut self, expr: &ast::Expr) {
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 @@ fn walk_overloaded_operator(&mut self,
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_user_unop<'a, 'tcx>(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_struct_fields_on_error(fcx: &FnCtxt,
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_struct_fields_on_error(fcx: &FnCtxt,
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 @@ fn sub(self, other: $BitFlags) -> $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 @@ 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),+,
......
......@@ -265,6 +265,8 @@ pub fn is_zero(&self) -> bool {
}
}
// NOTE(stage0): Remove impl after a snapshot
#[cfg(stage0)]
impl Neg<Duration> for Duration {
#[inline]
fn neg(&self) -> Duration {
......@@ -276,6 +278,18 @@ fn neg(&self) -> 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() ",
......
// 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 @@ fn sub(self, other: Point) -> 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 }
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册