Skip to content

Commit 6bdce25

Browse files
committed
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
2 parents 840de07 + 9b5de39 commit 6bdce25

File tree

10 files changed

+195
-14
lines changed

10 files changed

+195
-14
lines changed

src/libcore/ops.rs

+100
Original file line numberDiff line numberDiff line change
@@ -542,12 +542,16 @@ rem_float_impl! { f64, fmod }
542542
/// -Foo;
543543
/// }
544544
/// ```
545+
// NOTE(stage0): Remove trait after a snapshot
546+
#[cfg(stage0)]
545547
#[lang="neg"]
546548
pub trait Neg<Result> for Sized? {
547549
/// The method for the unary `-` operator
548550
fn neg(&self) -> Result;
549551
}
550552

553+
// NOTE(stage0): Remove macro after a snapshot
554+
#[cfg(stage0)]
551555
macro_rules! neg_impl {
552556
($($t:ty)*) => ($(
553557
impl Neg<$t> for $t {
@@ -557,6 +561,8 @@ macro_rules! neg_impl {
557561
)*)
558562
}
559563

564+
// NOTE(stage0): Remove macro after a snapshot
565+
#[cfg(stage0)]
560566
macro_rules! neg_uint_impl {
561567
($t:ty, $t_signed:ty) => {
562568
impl Neg<$t> for $t {
@@ -566,6 +572,56 @@ macro_rules! neg_uint_impl {
566572
}
567573
}
568574

575+
/// The `Neg` trait is used to specify the functionality of unary `-`.
576+
///
577+
/// # Example
578+
///
579+
/// A trivial implementation of `Neg`. When `-Foo` happens, it ends up calling
580+
/// `neg`, and therefore, `main` prints `Negating!`.
581+
///
582+
/// ```
583+
/// struct Foo;
584+
///
585+
/// impl Copy for Foo {}
586+
///
587+
/// impl Neg<Foo> for Foo {
588+
/// fn neg(self) -> Foo {
589+
/// println!("Negating!");
590+
/// self
591+
/// }
592+
/// }
593+
///
594+
/// fn main() {
595+
/// -Foo;
596+
/// }
597+
/// ```
598+
#[cfg(not(stage0))] // NOTE(stage0): Remove cfg after a snapshot
599+
#[lang="neg"]
600+
pub trait Neg<Result> {
601+
/// The method for the unary `-` operator
602+
fn neg(self) -> Result;
603+
}
604+
605+
#[cfg(not(stage0))] // NOTE(stage0): Remove cfg after a snapshot
606+
macro_rules! neg_impl {
607+
($($t:ty)*) => ($(
608+
impl Neg<$t> for $t {
609+
#[inline]
610+
fn neg(self) -> $t { -self }
611+
}
612+
)*)
613+
}
614+
615+
#[cfg(not(stage0))] // NOTE(stage0): Remove cfg after a snapshot
616+
macro_rules! neg_uint_impl {
617+
($t:ty, $t_signed:ty) => {
618+
impl Neg<$t> for $t {
619+
#[inline]
620+
fn neg(self) -> $t { -(self as $t_signed) as $t }
621+
}
622+
}
623+
}
624+
569625
neg_impl! { int i8 i16 i32 i64 f32 f64 }
570626

571627
neg_uint_impl! { uint, int }
@@ -598,13 +654,17 @@ neg_uint_impl! { u64, i64 }
598654
/// !Foo;
599655
/// }
600656
/// ```
657+
// NOTE(stage0): Remove macro after a snapshot
658+
#[cfg(stage0)]
601659
#[lang="not"]
602660
pub trait Not<Result> for Sized? {
603661
/// The method for the unary `!` operator
604662
fn not(&self) -> Result;
605663
}
606664

607665

666+
// NOTE(stage0): Remove macro after a snapshot
667+
#[cfg(stage0)]
608668
macro_rules! not_impl {
609669
($($t:ty)*) => ($(
610670
impl Not<$t> for $t {
@@ -614,6 +674,46 @@ macro_rules! not_impl {
614674
)*)
615675
}
616676

677+
/// The `Not` trait is used to specify the functionality of unary `!`.
678+
///
679+
/// # Example
680+
///
681+
/// A trivial implementation of `Not`. When `!Foo` happens, it ends up calling
682+
/// `not`, and therefore, `main` prints `Not-ing!`.
683+
///
684+
/// ```
685+
/// struct Foo;
686+
///
687+
/// impl Copy for Foo {}
688+
///
689+
/// impl Not<Foo> for Foo {
690+
/// fn not(self) -> Foo {
691+
/// println!("Not-ing!");
692+
/// self
693+
/// }
694+
/// }
695+
///
696+
/// fn main() {
697+
/// !Foo;
698+
/// }
699+
/// ```
700+
#[cfg(not(stage0))] // NOTE(stage0): Remove cfg after a snapshot
701+
#[lang="not"]
702+
pub trait Not<Result> {
703+
/// The method for the unary `!` operator
704+
fn not(self) -> Result;
705+
}
706+
707+
#[cfg(not(stage0))] // NOTE(stage0): Remove cfg after a snapshot
708+
macro_rules! not_impl {
709+
($($t:ty)*) => ($(
710+
impl Not<$t> for $t {
711+
#[inline]
712+
fn not(self) -> $t { !self }
713+
}
714+
)*)
715+
}
716+
617717
not_impl! { bool uint u8 u16 u32 u64 int i8 i16 i32 i64 }
618718

619719
/// The `BitAnd` trait is used to specify the functionality of `&`.

src/librustc/middle/expr_use_visitor.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -576,8 +576,14 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
576576
self.walk_block(&**blk);
577577
}
578578

579-
ast::ExprUnary(_, ref lhs) => {
580-
if !self.walk_overloaded_operator(expr, &**lhs, Vec::new(), PassArgs::ByRef) {
579+
ast::ExprUnary(op, ref lhs) => {
580+
let pass_args = if ast_util::is_by_value_unop(op) {
581+
PassArgs::ByValue
582+
} else {
583+
PassArgs::ByRef
584+
};
585+
586+
if !self.walk_overloaded_operator(expr, &**lhs, Vec::new(), pass_args) {
581587
self.consume_expr(&**lhs);
582588
}
583589
}
@@ -937,7 +943,9 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
937943
match pass_args {
938944
PassArgs::ByValue => {
939945
self.consume_expr(receiver);
940-
self.consume_expr(rhs[0]);
946+
for &arg in rhs.iter() {
947+
self.consume_expr(arg);
948+
}
941949

942950
return true;
943951
},

src/librustc_trans/trans/expr.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1101,11 +1101,11 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
11011101
vec![(rhs_datum, rhs.id)], Some(dest),
11021102
!ast_util::is_by_value_binop(op)).bcx
11031103
}
1104-
ast::ExprUnary(_, ref subexpr) => {
1104+
ast::ExprUnary(op, ref subexpr) => {
11051105
// if not overloaded, would be RvalueDatumExpr
11061106
let arg = unpack_datum!(bcx, trans(bcx, &**subexpr));
11071107
trans_overloaded_op(bcx, expr, MethodCall::expr(expr.id),
1108-
arg, Vec::new(), Some(dest), true).bcx
1108+
arg, Vec::new(), Some(dest), !ast_util::is_by_value_unop(op)).bcx
11091109
}
11101110
ast::ExprIndex(ref base, ref idx) => {
11111111
// if not overloaded, would be RvalueDatumExpr

src/librustc_typeck/check/mod.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -3356,14 +3356,15 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
33563356
trait_did: Option<ast::DefId>,
33573357
ex: &ast::Expr,
33583358
rhs_expr: &ast::Expr,
3359-
rhs_t: Ty<'tcx>) -> Ty<'tcx> {
3359+
rhs_t: Ty<'tcx>,
3360+
op: ast::UnOp) -> Ty<'tcx> {
33603361
lookup_op_method(fcx, ex, rhs_t, token::intern(mname),
33613362
trait_did, rhs_expr, None, || {
33623363
fcx.type_error_message(ex.span, |actual| {
33633364
format!("cannot apply unary operator `{}` to type `{}`",
33643365
op_str, actual)
33653366
}, rhs_t, None);
3366-
}, AutorefArgs::Yes)
3367+
}, if ast_util::is_by_value_unop(op) { AutorefArgs::No } else { AutorefArgs::Yes })
33673368
}
33683369

33693370
// Check field access expressions
@@ -3803,7 +3804,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
38033804
oprnd_t.sty == ty::ty_bool) {
38043805
oprnd_t = check_user_unop(fcx, "!", "not",
38053806
tcx.lang_items.not_trait(),
3806-
expr, &**oprnd, oprnd_t);
3807+
expr, &**oprnd, oprnd_t, unop);
38073808
}
38083809
}
38093810
ast::UnNeg => {
@@ -3813,7 +3814,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
38133814
ty::type_is_fp(oprnd_t)) {
38143815
oprnd_t = check_user_unop(fcx, "-", "neg",
38153816
tcx.lang_items.neg_trait(),
3816-
expr, &**oprnd, oprnd_t);
3817+
expr, &**oprnd, oprnd_t, unop);
38173818
}
38183819
}
38193820
}

src/librustc_typeck/check/regionck.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -682,10 +682,12 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) {
682682
visit::walk_expr(rcx, expr);
683683
}
684684

685-
ast::ExprUnary(_, ref lhs) if has_method_map => {
685+
ast::ExprUnary(op, ref lhs) if has_method_map => {
686+
let implicitly_ref_args = !ast_util::is_by_value_unop(op);
687+
686688
// As above.
687689
constrain_call(rcx, expr, Some(&**lhs),
688-
None::<ast::Expr>.iter(), true);
690+
None::<ast::Expr>.iter(), implicitly_ref_args);
689691

690692
visit::walk_expr(rcx, expr);
691693
}

src/libstd/bitflags.rs

+11
Original file line numberDiff line numberDiff line change
@@ -281,13 +281,24 @@ macro_rules! bitflags {
281281
}
282282
}
283283

284+
// NOTE(stage0): Remove impl after a snapshot
285+
#[cfg(stage0)]
284286
impl Not<$BitFlags> for $BitFlags {
285287
/// Returns the complement of this set of flags.
286288
#[inline]
287289
fn not(&self) -> $BitFlags {
288290
$BitFlags { bits: !self.bits } & $BitFlags::all()
289291
}
290292
}
293+
294+
#[cfg(not(stage0))] // NOTE(stage0): Remove cfg after a snapshot
295+
impl Not<$BitFlags> for $BitFlags {
296+
/// Returns the complement of this set of flags.
297+
#[inline]
298+
fn not(self) -> $BitFlags {
299+
$BitFlags { bits: !self.bits } & $BitFlags::all()
300+
}
301+
}
291302
};
292303
($(#[$attr:meta])* flags $BitFlags:ident: $T:ty {
293304
$($(#[$Flag_attr:meta])* const $Flag:ident = $value:expr),+,

src/libstd/time/duration.rs

+14
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,8 @@ impl Duration {
265265
}
266266
}
267267

268+
// NOTE(stage0): Remove impl after a snapshot
269+
#[cfg(stage0)]
268270
impl Neg<Duration> for Duration {
269271
#[inline]
270272
fn neg(&self) -> Duration {
@@ -276,6 +278,18 @@ impl Neg<Duration> for Duration {
276278
}
277279
}
278280

281+
#[cfg(not(stage0))] // NOTE(stage0): Remove cfg after a snapshot
282+
impl Neg<Duration> for Duration {
283+
#[inline]
284+
fn neg(self) -> Duration {
285+
if self.nanos == 0 {
286+
Duration { secs: -self.secs, nanos: 0 }
287+
} else {
288+
Duration { secs: -self.secs - 1, nanos: NANOS_PER_SEC - self.nanos }
289+
}
290+
}
291+
}
292+
279293
// NOTE(stage0): Remove impl after a snapshot
280294
#[cfg(stage0)]
281295
impl Add<Duration,Duration> for Duration {

src/libsyntax/ast_util.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ pub fn is_shift_binop(b: BinOp) -> bool {
8585
}
8686
}
8787

88-
/// Returns `true` is the binary operator takes its arguments by value
88+
/// Returns `true` if the binary operator takes its arguments by value
8989
pub fn is_by_value_binop(b: BinOp) -> bool {
9090
match b {
9191
BiAdd | BiSub | BiMul | BiDiv | BiRem | BiBitXor | BiBitAnd | BiBitOr | BiShl | BiShr => {
@@ -95,6 +95,14 @@ pub fn is_by_value_binop(b: BinOp) -> bool {
9595
}
9696
}
9797

98+
/// Returns `true` if the unary operator takes its argument by value
99+
pub fn is_by_value_unop(u: UnOp) -> bool {
100+
match u {
101+
UnNeg | UnNot => true,
102+
_ => false,
103+
}
104+
}
105+
98106
pub fn unop_to_string(op: UnOp) -> &'static str {
99107
match op {
100108
UnUniq => "box() ",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that move restrictions are enforced on overloaded unary operations
12+
13+
fn move_then_borrow<T: Not<T> + Clone>(x: T) {
14+
!x;
15+
16+
x.clone(); //~ ERROR: use of moved value
17+
}
18+
19+
fn move_borrowed<T: Not<T>>(x: T, mut y: T) {
20+
let m = &x;
21+
let n = &mut y;
22+
23+
!x; //~ ERROR: cannot move out of `x` because it is borrowed
24+
25+
!y; //~ ERROR: cannot move out of `y` because it is borrowed
26+
}
27+
28+
fn illegal_dereference<T: Not<T>>(mut x: T, y: T) {
29+
let m = &mut x;
30+
let n = &y;
31+
32+
!*m; //~ ERROR: cannot move out of dereference of `&mut`-pointer
33+
34+
!*n; //~ ERROR: cannot move out of dereference of `&`-pointer
35+
}
36+
37+
fn main() {}

src/test/run-pass/operator-overloading.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ impl ops::Sub<Point,Point> for Point {
3131
}
3232

3333
impl ops::Neg<Point> for Point {
34-
fn neg(&self) -> Point {
34+
fn neg(self) -> Point {
3535
Point {x: -self.x, y: -self.y}
3636
}
3737
}
3838

3939
impl ops::Not<Point> for Point {
40-
fn not(&self) -> Point {
40+
fn not(self) -> Point {
4141
Point {x: !self.x, y: !self.y }
4242
}
4343
}

0 commit comments

Comments
 (0)