Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 3ae3a5f

Browse files
author
Jorge Aparicio
committedSep 19, 2015
Overloaded augmented assignments
1 parent fb5de8c commit 3ae3a5f

File tree

15 files changed

+979
-85
lines changed

15 files changed

+979
-85
lines changed
 

‎src/libcore/ops.rs

Lines changed: 528 additions & 0 deletions
Large diffs are not rendered by default.

‎src/librustc/middle/expr_use_visitor.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -525,11 +525,17 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
525525
self.consume_expr(&**base);
526526
}
527527

528-
hir::ExprAssignOp(_, ref lhs, ref rhs) => {
529-
// This will have to change if/when we support
530-
// overloaded operators for `+=` and so forth.
531-
self.mutate_expr(expr, &**lhs, WriteAndRead);
532-
self.consume_expr(&**rhs);
528+
hir::ExprAssignOp(op, ref lhs, ref rhs) => {
529+
let pass_args = if ::rustc_front::util::is_by_value_binop(op.node) {
530+
PassArgs::ByValue
531+
} else {
532+
PassArgs::ByRef
533+
};
534+
535+
if !self.walk_overloaded_operator(expr, &**lhs, vec![&**rhs], pass_args) {
536+
self.mutate_expr(expr, &**lhs, WriteAndRead);
537+
self.consume_expr(&**rhs);
538+
}
533539
}
534540

535541
hir::ExprRepeat(ref base, ref count) => {

‎src/librustc/middle/lang_items.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,16 @@ lets_do_this! {
286286
BitOrTraitLangItem, "bitor", bitor_trait;
287287
ShlTraitLangItem, "shl", shl_trait;
288288
ShrTraitLangItem, "shr", shr_trait;
289+
AddAssignTraitLangItem, "add_assign", add_assign_trait;
290+
SubAssignTraitLangItem, "sub_assign", sub_assign_trait;
291+
MulAssignTraitLangItem, "mul_assign", mul_assign_trait;
292+
DivAssignTraitLangItem, "div_assign", div_assign_trait;
293+
RemAssignTraitLangItem, "rem_assign", rem_assign_trait;
294+
BitXorAssignTraitLangItem, "bitxor_assign", bitxor_assign_trait;
295+
BitAndAssignTraitLangItem, "bitand_assign", bitand_assign_trait;
296+
BitOrAssignTraitLangItem, "bitor_assign", bitor_assign_trait;
297+
ShlAssignTraitLangItem, "shl_assign", shl_assign_trait;
298+
ShrAssignTraitLangItem, "shr_assign", shr_assign_trait;
289299
IndexTraitLangItem, "index", index_trait;
290300
IndexMutTraitLangItem, "index_mut", index_mut_trait;
291301
RangeStructLangItem, "range", range_struct;

‎src/librustc_trans/trans/expr.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,7 +1018,20 @@ fn trans_rvalue_stmt_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
10181018
}
10191019
}
10201020
hir::ExprAssignOp(op, ref dst, ref src) => {
1021-
trans_assign_op(bcx, expr, op, &**dst, &**src)
1021+
let has_method_map = bcx.tcx()
1022+
.tables
1023+
.borrow()
1024+
.method_map
1025+
.contains_key(&MethodCall::expr(expr.id));
1026+
1027+
if has_method_map {
1028+
let dst = unpack_datum!(bcx, trans(bcx, &**dst));
1029+
let src_datum = unpack_datum!(bcx, trans(bcx, &**src));
1030+
trans_overloaded_op(bcx, expr, MethodCall::expr(expr.id), dst,
1031+
Some((src_datum, src.id)), None, false).bcx
1032+
} else {
1033+
trans_assign_op(bcx, expr, op, &**dst, &**src)
1034+
}
10221035
}
10231036
hir::ExprInlineAsm(ref a) => {
10241037
asm::trans_inline_asm(bcx, a)
@@ -1207,8 +1220,11 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
12071220
// Trait casts used to come this way, now they should be coercions.
12081221
bcx.tcx().sess.span_bug(expr.span, "DPS expr_cast (residual trait cast?)")
12091222
}
1210-
hir::ExprAssignOp(op, ref dst, ref src) => {
1211-
trans_assign_op(bcx, expr, op, &**dst, &**src)
1223+
hir::ExprAssignOp(op, _, _) => {
1224+
bcx.tcx().sess.span_bug(
1225+
expr.span,
1226+
&format!("augmented assignment `{}=` should always be a rvalue_stmt",
1227+
rustc_front::util::binop_to_string(op.node)))
12121228
}
12131229
_ => {
12141230
bcx.tcx().sess.span_bug(

‎src/librustc_typeck/check/op.rs

Lines changed: 68 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,8 @@ use super::{
1717
demand,
1818
method,
1919
FnCtxt,
20-
structurally_resolved_type,
2120
};
2221
use middle::def_id::DefId;
23-
use middle::traits;
2422
use middle::ty::{Ty, HasTypeFlags, PreferMutLvalue};
2523
use syntax::ast;
2624
use syntax::parse::token;
@@ -34,34 +32,24 @@ pub fn check_binop_assign<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
3432
lhs_expr: &'tcx hir::Expr,
3533
rhs_expr: &'tcx hir::Expr)
3634
{
37-
let tcx = fcx.ccx.tcx;
38-
3935
check_expr_with_lvalue_pref(fcx, lhs_expr, PreferMutLvalue);
40-
check_expr(fcx, rhs_expr);
4136

42-
let lhs_ty = structurally_resolved_type(fcx, lhs_expr.span, fcx.expr_ty(lhs_expr));
43-
let rhs_ty = structurally_resolved_type(fcx, rhs_expr.span, fcx.expr_ty(rhs_expr));
37+
let lhs_ty = fcx.resolve_type_vars_if_possible(fcx.expr_ty(lhs_expr));
38+
let (rhs_ty, return_ty) =
39+
check_overloaded_binop(fcx, expr, lhs_expr, lhs_ty, rhs_expr, op, true);
40+
let rhs_ty = fcx.resolve_type_vars_if_possible(rhs_ty);
4441

45-
if is_builtin_binop(lhs_ty, rhs_ty, op) {
42+
if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
4643
enforce_builtin_binop_types(fcx, lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
4744
fcx.write_nil(expr.id);
4845
} else {
49-
// error types are considered "builtin"
50-
assert!(!lhs_ty.references_error() || !rhs_ty.references_error());
51-
span_err!(tcx.sess, lhs_expr.span, E0368,
52-
"binary assignment operation `{}=` cannot be applied to types `{}` and `{}`",
53-
hir_util::binop_to_string(op.node),
54-
lhs_ty,
55-
rhs_ty);
56-
fcx.write_error(expr.id);
46+
fcx.write_ty(expr.id, return_ty);
5747
}
5848

5949
let tcx = fcx.tcx();
6050
if !tcx.expr_is_lval(lhs_expr) {
6151
span_err!(tcx.sess, lhs_expr.span, E0067, "invalid left-hand side expression");
6252
}
63-
64-
fcx.require_expr_have_sized_type(lhs_expr, traits::AssignmentLhsSized);
6553
}
6654

6755
/// Check a potentially overloaded binary operator.
@@ -95,7 +83,7 @@ pub fn check_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
9583
// overloaded. This is the way to be most flexible w/r/t
9684
// types that get inferred.
9785
let (rhs_ty, return_ty) =
98-
check_overloaded_binop(fcx, expr, lhs_expr, lhs_ty, rhs_expr, op);
86+
check_overloaded_binop(fcx, expr, lhs_expr, lhs_ty, rhs_expr, op, false);
9987

10088
// Supply type inference hints if relevant. Probably these
10189
// hints should be enforced during select as part of the
@@ -167,14 +155,16 @@ fn check_overloaded_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
167155
lhs_expr: &'tcx hir::Expr,
168156
lhs_ty: Ty<'tcx>,
169157
rhs_expr: &'tcx hir::Expr,
170-
op: hir::BinOp)
158+
op: hir::BinOp,
159+
assign: bool)
171160
-> (Ty<'tcx>, Ty<'tcx>)
172161
{
173-
debug!("check_overloaded_binop(expr.id={}, lhs_ty={:?})",
162+
debug!("check_overloaded_binop(expr.id={}, lhs_ty={:?}, assign={})",
174163
expr.id,
175-
lhs_ty);
164+
lhs_ty,
165+
assign);
176166

177-
let (name, trait_def_id) = name_and_trait_def_id(fcx, op);
167+
let (name, trait_def_id) = name_and_trait_def_id(fcx, op, assign);
178168

179169
// NB: As we have not yet type-checked the RHS, we don't have the
180170
// type at hand. Make a variable to represent it. The whole reason
@@ -191,10 +181,17 @@ fn check_overloaded_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
191181
Err(()) => {
192182
// error types are considered "builtin"
193183
if !lhs_ty.references_error() {
194-
span_err!(fcx.tcx().sess, lhs_expr.span, E0369,
195-
"binary operation `{}` cannot be applied to type `{}`",
196-
hir_util::binop_to_string(op.node),
197-
lhs_ty);
184+
if assign {
185+
span_err!(fcx.tcx().sess, lhs_expr.span, E0368,
186+
"binary assignment operation `{}=` cannot be applied to type `{}`",
187+
hir_util::binop_to_string(op.node),
188+
lhs_ty);
189+
} else {
190+
span_err!(fcx.tcx().sess, lhs_expr.span, E0369,
191+
"binary operation `{}` cannot be applied to type `{}`",
192+
hir_util::binop_to_string(op.node),
193+
lhs_ty);
194+
}
198195
}
199196
fcx.tcx().types.err
200197
}
@@ -231,27 +228,51 @@ pub fn check_user_unop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
231228
}
232229
}
233230

234-
fn name_and_trait_def_id(fcx: &FnCtxt, op: hir::BinOp) -> (&'static str, Option<DefId>) {
231+
fn name_and_trait_def_id(fcx: &FnCtxt,
232+
op: hir::BinOp,
233+
assign: bool)
234+
-> (&'static str, Option<DefId>) {
235235
let lang = &fcx.tcx().lang_items;
236-
match op.node {
237-
hir::BiAdd => ("add", lang.add_trait()),
238-
hir::BiSub => ("sub", lang.sub_trait()),
239-
hir::BiMul => ("mul", lang.mul_trait()),
240-
hir::BiDiv => ("div", lang.div_trait()),
241-
hir::BiRem => ("rem", lang.rem_trait()),
242-
hir::BiBitXor => ("bitxor", lang.bitxor_trait()),
243-
hir::BiBitAnd => ("bitand", lang.bitand_trait()),
244-
hir::BiBitOr => ("bitor", lang.bitor_trait()),
245-
hir::BiShl => ("shl", lang.shl_trait()),
246-
hir::BiShr => ("shr", lang.shr_trait()),
247-
hir::BiLt => ("lt", lang.ord_trait()),
248-
hir::BiLe => ("le", lang.ord_trait()),
249-
hir::BiGe => ("ge", lang.ord_trait()),
250-
hir::BiGt => ("gt", lang.ord_trait()),
251-
hir::BiEq => ("eq", lang.eq_trait()),
252-
hir::BiNe => ("ne", lang.eq_trait()),
253-
hir::BiAnd | hir::BiOr => {
254-
fcx.tcx().sess.span_bug(op.span, "&& and || are not overloadable")
236+
237+
if assign {
238+
match op.node {
239+
hir::BiAdd => ("add_assign", lang.add_assign_trait()),
240+
hir::BiSub => ("sub_assign", lang.sub_assign_trait()),
241+
hir::BiMul => ("mul_assign", lang.mul_assign_trait()),
242+
hir::BiDiv => ("div_assign", lang.div_assign_trait()),
243+
hir::BiRem => ("rem_assign", lang.rem_assign_trait()),
244+
hir::BiBitXor => ("bitxor_assign", lang.bitxor_assign_trait()),
245+
hir::BiBitAnd => ("bitand_assign", lang.bitand_assign_trait()),
246+
hir::BiBitOr => ("bitor_assign", lang.bitor_assign_trait()),
247+
hir::BiShl => ("shl_assign", lang.shl_assign_trait()),
248+
hir::BiShr => ("shr_assign", lang.shr_assign_trait()),
249+
hir::BiLt | hir::BiLe | hir::BiGe | hir::BiGt | hir::BiEq | hir::BiNe | hir::BiAnd |
250+
hir::BiOr => {
251+
fcx.tcx().sess.span_bug(op.span, &format!("impossible assignment operation: {}=",
252+
hir_util::binop_to_string(op.node)))
253+
}
254+
}
255+
} else {
256+
match op.node {
257+
hir::BiAdd => ("add", lang.add_trait()),
258+
hir::BiSub => ("sub", lang.sub_trait()),
259+
hir::BiMul => ("mul", lang.mul_trait()),
260+
hir::BiDiv => ("div", lang.div_trait()),
261+
hir::BiRem => ("rem", lang.rem_trait()),
262+
hir::BiBitXor => ("bitxor", lang.bitxor_trait()),
263+
hir::BiBitAnd => ("bitand", lang.bitand_trait()),
264+
hir::BiBitOr => ("bitor", lang.bitor_trait()),
265+
hir::BiShl => ("shl", lang.shl_trait()),
266+
hir::BiShr => ("shr", lang.shr_trait()),
267+
hir::BiLt => ("lt", lang.ord_trait()),
268+
hir::BiLe => ("le", lang.ord_trait()),
269+
hir::BiGe => ("ge", lang.ord_trait()),
270+
hir::BiGt => ("gt", lang.ord_trait()),
271+
hir::BiEq => ("eq", lang.eq_trait()),
272+
hir::BiNe => ("ne", lang.eq_trait()),
273+
hir::BiAnd | hir::BiOr => {
274+
fcx.tcx().sess.span_bug(op.span, "&& and || are not overloadable")
275+
}
255276
}
256277
}
257278
}

‎src/librustc_typeck/check/regionck.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -695,7 +695,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &hir::Expr) {
695695
hir::ExprAssignOp(_, ref lhs, ref rhs) => {
696696
if has_method_map {
697697
constrain_call(rcx, expr, Some(&**lhs),
698-
Some(&**rhs).into_iter(), true);
698+
Some(&**rhs).into_iter(), false);
699699
}
700700

701701
visit::walk_expr(rcx, expr);

‎src/librustc_typeck/check/writeback.rs

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use astconv::AstConv;
1717
use check::FnCtxt;
1818
use middle::def_id::DefId;
1919
use middle::pat_util;
20-
use middle::ty::{self, Ty, MethodCall, MethodCallee};
20+
use middle::ty::{self, Ty, MethodCall, MethodCallee, HasTypeFlags};
2121
use middle::ty::adjustment;
2222
use middle::ty::fold::{TypeFolder,TypeFoldable};
2323
use middle::infer;
@@ -91,24 +91,51 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
9191
// we observe that something like `a+b` is (known to be)
9292
// operating on scalars, we clear the overload.
9393
fn fix_scalar_binary_expr(&mut self, e: &hir::Expr) {
94-
if let hir::ExprBinary(ref op, ref lhs, ref rhs) = e.node {
95-
let lhs_ty = self.fcx.node_ty(lhs.id);
96-
let lhs_ty = self.fcx.infcx().resolve_type_vars_if_possible(&lhs_ty);
97-
98-
let rhs_ty = self.fcx.node_ty(rhs.id);
99-
let rhs_ty = self.fcx.infcx().resolve_type_vars_if_possible(&rhs_ty);
100-
101-
if lhs_ty.is_scalar() && rhs_ty.is_scalar() {
102-
self.fcx.inh.tables.borrow_mut().method_map.remove(&MethodCall::expr(e.id));
103-
104-
// weird but true: the by-ref binops put an
105-
// adjustment on the lhs but not the rhs; the
106-
// adjustment for rhs is kind of baked into the
107-
// system.
108-
if !hir_util::is_by_value_binop(op.node) {
109-
self.fcx.inh.tables.borrow_mut().adjustments.remove(&lhs.id);
94+
match e.node {
95+
hir::ExprBinary(ref op, ref lhs, ref rhs) |
96+
hir::ExprAssignOp(ref op, ref lhs, ref rhs) => {
97+
let lhs_ty = self.fcx.node_ty(lhs.id);
98+
let lhs_ty = self.fcx.infcx().resolve_type_vars_if_possible(&lhs_ty);
99+
100+
let rhs_ty = self.fcx.node_ty(rhs.id);
101+
let rhs_ty = self.fcx.infcx().resolve_type_vars_if_possible(&rhs_ty);
102+
103+
if lhs_ty.is_scalar() && rhs_ty.is_scalar() {
104+
self.fcx.inh.tables.borrow_mut().method_map.remove(&MethodCall::expr(e.id));
105+
106+
// weird but true: the by-ref binops put an
107+
// adjustment on the lhs but not the rhs; the
108+
// adjustment for rhs is kind of baked into the
109+
// system.
110+
match e.node {
111+
hir::ExprBinary(..) => {
112+
if !hir_util::is_by_value_binop(op.node) {
113+
self.fcx.inh.tables.borrow_mut().adjustments.remove(&lhs.id);
114+
}
115+
},
116+
hir::ExprAssignOp(..) => {
117+
self.fcx.inh.tables.borrow_mut().adjustments.remove(&lhs.id);
118+
},
119+
_ => {},
120+
}
121+
} else {
122+
let tcx = self.tcx();
123+
124+
if let hir::ExprAssignOp(..) = e.node {
125+
if !tcx.sess.features.borrow().augmented_assignments &&
126+
!self.fcx.expr_ty(e).references_error() {
127+
tcx.sess.span_err(
128+
e.span,
129+
"overloaded augmented assignments are not stable");
130+
fileline_help!(
131+
tcx.sess, e.span,
132+
"add #![feature(augmented_assignments)] to the crate features \
133+
to enable");
134+
}
135+
}
136+
}
110137
}
111-
}
138+
_ => {},
112139
}
113140
}
114141
}

‎src/librustc_typeck/diagnostics.rs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2862,14 +2862,27 @@ impl <T: Foo> Drop for MyStructWrapper<T> {
28622862

28632863
E0368: r##"
28642864
This error indicates that a binary assignment operator like `+=` or `^=` was
2865-
applied to the wrong types. For example:
2865+
applied to a type that doesn't support it. For example:
28662866
28672867
```
2868-
let mut x: u16 = 5;
2869-
x ^= true; // error, `^=` cannot be applied to types `u16` and `bool`
2870-
x += (); // error, `+=` cannot be applied to types `u16` and `()`
2868+
let mut x = 12f32; // error: binary operation `<<` cannot be applied to
2869+
// type `f32`
2870+
2871+
x <<= 2;
2872+
```
2873+
2874+
To fix this error, please check that this type implements this binary
2875+
operation. Example:
2876+
2877+
```
2878+
let x = 12u32; // the `u32` type does implement the `ShlAssign` trait
2879+
2880+
x <<= 2; // ok!
28712881
```
28722882
2883+
It is also possible to overload most operators for your own type by
2884+
implementing the `[OP]Assign` traits from `std::ops`.
2885+
28732886
Another problem you might be facing is this: suppose you've overloaded the `+`
28742887
operator for some type `Foo` by implementing the `std::ops::Add` trait for
28752888
`Foo`, but you find that using `+=` does not work, as in this example:
@@ -2889,15 +2902,12 @@ impl Add for Foo {
28892902
28902903
fn main() {
28912904
let mut x: Foo = Foo(5);
2892-
x += Foo(7); // error, `+= cannot be applied to types `Foo` and `Foo`
2905+
x += Foo(7); // error, `+= cannot be applied to the type `Foo`
28932906
}
28942907
```
28952908
2896-
This is because the binary assignment operators currently do not work off of
2897-
traits, so it is not possible to overload them. See [RFC 953] for a proposal
2898-
to change this.
2899-
2900-
[RFC 953]: https://github.com/rust-lang/rfcs/pull/953
2909+
This is because `AddAssign` is not automatically implemented, so you need to
2910+
manually implement it for your type.
29012911
"##,
29022912

29032913
E0369: r##"

‎src/libsyntax/feature_gate.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option<u32>, Status
194194

195195
// allow empty structs/enum variants with braces
196196
("braced_empty_structs", "1.5.0", None, Active),
197+
198+
// allow overloading augmented assignment operations like `a += b`
199+
("augmented_assignments", "1.5.0", None, Active),
197200
];
198201
// (changing above list without updating src/doc/reference.md makes @cmr sad)
199202

@@ -457,6 +460,7 @@ pub struct Features {
457460
pub default_type_parameter_fallback: bool,
458461
pub type_macros: bool,
459462
pub cfg_target_feature: bool,
463+
pub augmented_assignments: bool,
460464
}
461465

462466
impl Features {
@@ -485,6 +489,7 @@ impl Features {
485489
default_type_parameter_fallback: false,
486490
type_macros: false,
487491
cfg_target_feature: false,
492+
augmented_assignments: false,
488493
}
489494
}
490495
}
@@ -1053,6 +1058,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
10531058
default_type_parameter_fallback: cx.has_feature("default_type_parameter_fallback"),
10541059
type_macros: cx.has_feature("type_macros"),
10551060
cfg_target_feature: cx.has_feature("cfg_target_feature"),
1061+
augmented_assignments: cx.has_feature("augmented_assignments"),
10561062
}
10571063
}
10581064

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2015 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+
#![feature(augmented_assignments)]
12+
#![feature(op_assign_traits)]
13+
14+
use std::ops::AddAssign;
15+
16+
pub struct Int(i32);
17+
18+
impl AddAssign<i32> for Int {
19+
fn add_assign(&mut self, _: i32) {
20+
unimplemented!();
21+
}
22+
}

‎src/test/compile-fail/assignment-operator-unimplemented.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ struct Foo;
1313
fn main() {
1414
let mut a = Foo;
1515
let ref b = Foo;
16-
a += *b; //~ Error: binary assignment operation `+=` cannot be applied to types `Foo` and `Foo`
16+
a += *b; //~ Error: binary assignment operation `+=` cannot be applied to type `Foo`
1717
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2015 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+
// aux-build:augmented_assignments.rs
12+
13+
// Test that the feature gate is needed when using augmented assignments that were overloaded in
14+
// another crate
15+
16+
extern crate augmented_assignments;
17+
18+
use augmented_assignments::Int;
19+
20+
fn main() {
21+
let mut x = Int(0);
22+
x += 1;
23+
//~^ error: overloaded augmented assignments are not stable
24+
// | help: add #![feature(augmented_assignments)] to the crate features to enable
25+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2015 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+
use std::ops::AddAssign;
12+
13+
struct Int(i32);
14+
15+
impl AddAssign<i32> for Int {
16+
fn add_assign(&mut self, _: i32) {
17+
unimplemented!()
18+
}
19+
}
20+
21+
fn main() {
22+
let mut x = Int(0);
23+
x += 1;
24+
//~^ error: overloaded augmented assignments are not stable
25+
// | help: add #![feature(augmented_assignments)] to the crate features to enable
26+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2015 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+
#![feature(augmented_assignments)]
12+
13+
use std::ops::AddAssign;
14+
15+
struct Int(i32);
16+
17+
impl AddAssign for Int {
18+
fn add_assign(&mut self, _: Int) {
19+
unimplemented!()
20+
}
21+
}
22+
23+
fn main() {
24+
let mut x = Int(1);
25+
x //~ error: use of moved value: `x`
26+
+=
27+
x; //~ note: `x` moved here because it has type `Int`, which is non-copyable
28+
29+
let y = Int(2);
30+
y //~ error: cannot borrow immutable local variable `y` as mutable
31+
+=
32+
Int(1);
33+
}
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
// Copyright 2015 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+
#![feature(augmented_assignments)]
12+
#![feature(op_assign_traits)]
13+
14+
use std::mem;
15+
use std::ops::{
16+
AddAssign, BitAndAssign, BitOrAssign, BitXorAssign, DivAssign, Index, MulAssign, RemAssign,
17+
ShlAssign, ShrAssign, SubAssign,
18+
};
19+
20+
#[derive(Debug, PartialEq)]
21+
struct Int(i32);
22+
23+
struct Slice([i32]);
24+
25+
impl Slice {
26+
fn new(slice: &mut [i32]) -> &mut Slice {
27+
unsafe {
28+
mem::transmute(slice)
29+
}
30+
}
31+
}
32+
33+
fn main() {
34+
let mut x = Int(1);
35+
36+
x += Int(2);
37+
assert_eq!(x, Int(0b11));
38+
39+
x &= Int(0b01);
40+
assert_eq!(x, Int(0b01));
41+
42+
x |= Int(0b10);
43+
assert_eq!(x, Int(0b11));
44+
45+
x ^= Int(0b01);
46+
assert_eq!(x, Int(0b10));
47+
48+
x /= Int(2);
49+
assert_eq!(x, Int(1));
50+
51+
x *= Int(3);
52+
assert_eq!(x, Int(3));
53+
54+
x %= Int(2);
55+
assert_eq!(x, Int(1));
56+
57+
// overloaded RHS
58+
x <<= 1u8;
59+
assert_eq!(x, Int(2));
60+
61+
x <<= 1u16;
62+
assert_eq!(x, Int(4));
63+
64+
x >>= 1u8;
65+
assert_eq!(x, Int(2));
66+
67+
x >>= 1u16;
68+
assert_eq!(x, Int(1));
69+
70+
x -= Int(1);
71+
assert_eq!(x, Int(0));
72+
73+
// indexed LHS
74+
let mut v = vec![Int(1), Int(2)];
75+
v[0] += Int(2);
76+
assert_eq!(v[0], Int(3));
77+
78+
// unsized RHS
79+
let mut array = [0, 1, 2];
80+
*Slice::new(&mut array) += 1;
81+
assert_eq!(array[0], 1);
82+
assert_eq!(array[1], 2);
83+
assert_eq!(array[2], 3);
84+
}
85+
86+
impl AddAssign for Int {
87+
fn add_assign(&mut self, rhs: Int) {
88+
self.0 += rhs.0;
89+
}
90+
}
91+
92+
impl BitAndAssign for Int {
93+
fn bitand_assign(&mut self, rhs: Int) {
94+
self.0 &= rhs.0;
95+
}
96+
}
97+
98+
impl BitOrAssign for Int {
99+
fn bitor_assign(&mut self, rhs: Int) {
100+
self.0 |= rhs.0;
101+
}
102+
}
103+
104+
impl BitXorAssign for Int {
105+
fn bitxor_assign(&mut self, rhs: Int) {
106+
self.0 ^= rhs.0;
107+
}
108+
}
109+
110+
impl DivAssign for Int {
111+
fn div_assign(&mut self, rhs: Int) {
112+
self.0 /= rhs.0;
113+
}
114+
}
115+
116+
impl MulAssign for Int {
117+
fn mul_assign(&mut self, rhs: Int) {
118+
self.0 *= rhs.0;
119+
}
120+
}
121+
122+
impl RemAssign for Int {
123+
fn rem_assign(&mut self, rhs: Int) {
124+
self.0 %= rhs.0;
125+
}
126+
}
127+
128+
impl ShlAssign<u8> for Int {
129+
fn shl_assign(&mut self, rhs: u8) {
130+
self.0 <<= rhs;
131+
}
132+
}
133+
134+
impl ShlAssign<u16> for Int {
135+
fn shl_assign(&mut self, rhs: u16) {
136+
self.0 <<= rhs;
137+
}
138+
}
139+
140+
impl ShrAssign<u8> for Int {
141+
fn shr_assign(&mut self, rhs: u8) {
142+
self.0 >>= rhs;
143+
}
144+
}
145+
146+
impl ShrAssign<u16> for Int {
147+
fn shr_assign(&mut self, rhs: u16) {
148+
self.0 >>= rhs;
149+
}
150+
}
151+
152+
impl SubAssign for Int {
153+
fn sub_assign(&mut self, rhs: Int) {
154+
self.0 -= rhs.0;
155+
}
156+
}
157+
158+
impl AddAssign<i32> for Slice {
159+
fn add_assign(&mut self, rhs: i32) {
160+
for lhs in &mut self.0 {
161+
*lhs += rhs;
162+
}
163+
}
164+
}

0 commit comments

Comments
 (0)
Please sign in to comment.