about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2015-09-19 21:19:29 +0000
committerbors <bors@rust-lang.org>2015-09-19 21:19:29 +0000
commit783c3fcc1ec19a804a63334d1945301fe89c52f6 (patch)
treed775ac4e2ee5b0a8d8c64786a6cef4d7b1407e32
parent837840c61fce44da78096110ff83c91099a83da7 (diff)
parentf5569ecd7682a22f9ae3293a89479c7b99b6d941 (diff)
downloadrust-783c3fcc1ec19a804a63334d1945301fe89c52f6.tar.gz
rust-783c3fcc1ec19a804a63334d1945301fe89c52f6.zip
Auto merge of #28345 - japaric:op-assign, r=nmatsakis
Implements overload-able augmented/compound assignments, like `a += b` via the `AddAssign` trait, as specified in RFC [953]

[953]: https://github.com/rust-lang/rfcs/blob/master/text/0953-op-assign.md

r? @nikomatsakis 
-rw-r--r--src/libcore/ops.rs528
-rw-r--r--src/librustc/middle/expr_use_visitor.rs13
-rw-r--r--src/librustc/middle/lang_items.rs10
-rw-r--r--src/librustc_trans/trans/expr.rs22
-rw-r--r--src/librustc_typeck/check/op.rs122
-rw-r--r--src/librustc_typeck/check/regionck.rs2
-rw-r--r--src/librustc_typeck/check/writeback.rs63
-rw-r--r--src/librustc_typeck/diagnostics.rs30
-rw-r--r--src/libsyntax/feature_gate.rs6
-rw-r--r--src/test/auxiliary/augmented_assignments.rs22
-rw-r--r--src/test/compile-fail/assignment-operator-unimplemented.rs2
-rw-r--r--src/test/compile-fail/augmented-assignments-feature-gate-cross.rs25
-rw-r--r--src/test/compile-fail/augmented-assignments-feature-gate.rs26
-rw-r--r--src/test/compile-fail/augmented-assignments-trait.rs24
-rw-r--r--src/test/compile-fail/augmented-assignments.rs33
-rw-r--r--src/test/run-pass/augmented-assignments.rs164
16 files changed, 1008 insertions, 84 deletions
diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs
index 5f0eb63edbc..350ade22707 100644
--- a/src/libcore/ops.rs
+++ b/src/libcore/ops.rs
@@ -927,6 +927,534 @@ macro_rules! shr_impl_all {
 
 shr_impl_all! { u8 u16 u32 u64 usize i8 i16 i32 i64 isize }
 
+/// The `AddAssign` trait is used to specify the functionality of `+=`.
+///
+/// # Examples
+///
+/// A trivial implementation of `AddAssign`. When `Foo += Foo` happens, it ends up
+/// calling `add_assign`, and therefore, `main` prints `Adding!`.
+///
+/// ```
+/// #![feature(augmented_assignments)]
+/// #![feature(op_assign_traits)]
+///
+/// use std::ops::AddAssign;
+///
+/// #[derive(Copy, Clone)]
+/// struct Foo;
+///
+/// impl AddAssign for Foo {
+///     fn add_assign(&mut self, _rhs: Foo) {
+///         println!("Adding!");
+///     }
+/// }
+///
+/// fn main() {
+///     let mut foo = Foo;
+///     foo += Foo;
+/// }
+/// ```
+#[cfg(not(stage0))]
+#[lang = "add_assign"]
+#[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+pub trait AddAssign<Rhs=Self> {
+    /// The method for the `+=` operator
+    fn add_assign(&mut self, Rhs);
+}
+
+#[cfg(not(stage0))]
+macro_rules! add_assign_impl {
+    ($($t:ty)+) => ($(
+        #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+        impl AddAssign for $t {
+            #[inline]
+            fn add_assign(&mut self, other: $t) { *self += other }
+        }
+    )+)
+}
+
+#[cfg(not(stage0))]
+add_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
+
+/// The `SubAssign` trait is used to specify the functionality of `-=`.
+///
+/// # Examples
+///
+/// A trivial implementation of `SubAssign`. When `Foo -= Foo` happens, it ends up
+/// calling `sub_assign`, and therefore, `main` prints `Subtracting!`.
+///
+/// ```
+/// #![feature(augmented_assignments)]
+/// #![feature(op_assign_traits)]
+///
+/// use std::ops::SubAssign;
+///
+/// #[derive(Copy, Clone)]
+/// struct Foo;
+///
+/// impl SubAssign for Foo {
+///     fn sub_assign(&mut self, _rhs: Foo) {
+///         println!("Subtracting!");
+///     }
+/// }
+///
+/// fn main() {
+///     let mut foo = Foo;
+///     foo -= Foo;
+/// }
+/// ```
+#[cfg(not(stage0))]
+#[lang = "sub_assign"]
+#[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+pub trait SubAssign<Rhs=Self> {
+    /// The method for the `-=` operator
+    fn sub_assign(&mut self, Rhs);
+}
+
+#[cfg(not(stage0))]
+macro_rules! sub_assign_impl {
+    ($($t:ty)+) => ($(
+        #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+        impl SubAssign for $t {
+            #[inline]
+            fn sub_assign(&mut self, other: $t) { *self -= other }
+        }
+    )+)
+}
+
+#[cfg(not(stage0))]
+sub_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
+
+/// The `MulAssign` trait is used to specify the functionality of `*=`.
+///
+/// # Examples
+///
+/// A trivial implementation of `MulAssign`. When `Foo *= Foo` happens, it ends up
+/// calling `mul_assign`, and therefore, `main` prints `Multiplying!`.
+///
+/// ```
+/// #![feature(augmented_assignments)]
+/// #![feature(op_assign_traits)]
+///
+/// use std::ops::MulAssign;
+///
+/// #[derive(Copy, Clone)]
+/// struct Foo;
+///
+/// impl MulAssign for Foo {
+///     fn mul_assign(&mut self, _rhs: Foo) {
+///         println!("Multiplying!");
+///     }
+/// }
+///
+/// fn main() {
+///     let mut foo = Foo;
+///     foo *= Foo;
+/// }
+/// ```
+#[cfg(not(stage0))]
+#[lang = "mul_assign"]
+#[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+pub trait MulAssign<Rhs=Self> {
+    /// The method for the `*=` operator
+    fn mul_assign(&mut self, Rhs);
+}
+
+#[cfg(not(stage0))]
+macro_rules! mul_assign_impl {
+    ($($t:ty)+) => ($(
+        #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+        impl MulAssign for $t {
+            #[inline]
+            fn mul_assign(&mut self, other: $t) { *self *= other }
+        }
+    )+)
+}
+
+#[cfg(not(stage0))]
+mul_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
+
+/// The `DivAssign` trait is used to specify the functionality of `/=`.
+///
+/// # Examples
+///
+/// A trivial implementation of `DivAssign`. When `Foo /= Foo` happens, it ends up
+/// calling `div_assign`, and therefore, `main` prints `Dividing!`.
+///
+/// ```
+/// #![feature(augmented_assignments)]
+/// #![feature(op_assign_traits)]
+///
+/// use std::ops::DivAssign;
+///
+/// #[derive(Copy, Clone)]
+/// struct Foo;
+///
+/// impl DivAssign for Foo {
+///     fn div_assign(&mut self, _rhs: Foo) {
+///         println!("Dividing!");
+///     }
+/// }
+///
+/// fn main() {
+///     let mut foo = Foo;
+///     foo /= Foo;
+/// }
+/// ```
+#[cfg(not(stage0))]
+#[lang = "div_assign"]
+#[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+pub trait DivAssign<Rhs=Self> {
+    /// The method for the `/=` operator
+    fn div_assign(&mut self, Rhs);
+}
+
+#[cfg(not(stage0))]
+macro_rules! div_assign_impl {
+    ($($t:ty)+) => ($(
+        #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+        impl DivAssign for $t {
+            #[inline]
+            fn div_assign(&mut self, other: $t) { *self /= other }
+        }
+    )+)
+}
+
+#[cfg(not(stage0))]
+div_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
+
+/// The `RemAssign` trait is used to specify the functionality of `%=`.
+///
+/// # Examples
+///
+/// A trivial implementation of `RemAssign`. When `Foo %= Foo` happens, it ends up
+/// calling `rem_assign`, and therefore, `main` prints `Remainder-ing!`.
+///
+/// ```
+/// #![feature(augmented_assignments)]
+/// #![feature(op_assign_traits)]
+///
+/// use std::ops::RemAssign;
+///
+/// #[derive(Copy, Clone)]
+/// struct Foo;
+///
+/// impl RemAssign for Foo {
+///     fn rem_assign(&mut self, _rhs: Foo) {
+///         println!("Remainder-ing!");
+///     }
+/// }
+///
+/// fn main() {
+///     let mut foo = Foo;
+///     foo %= Foo;
+/// }
+/// ```
+#[cfg(not(stage0))]
+#[lang = "rem_assign"]
+#[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+pub trait RemAssign<Rhs=Self> {
+    /// The method for the `%=` operator
+    fn rem_assign(&mut self, Rhs);
+}
+
+#[cfg(not(stage0))]
+macro_rules! rem_assign_impl {
+    ($($t:ty)+) => ($(
+        #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+        impl RemAssign for $t {
+            #[inline]
+            fn rem_assign(&mut self, other: $t) { *self %= other }
+        }
+    )+)
+}
+
+#[cfg(not(stage0))]
+rem_assign_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
+
+/// The `BitAndAssign` trait is used to specify the functionality of `&=`.
+///
+/// # Examples
+///
+/// A trivial implementation of `BitAndAssign`. When `Foo &= Foo` happens, it ends up
+/// calling `bitand_assign`, and therefore, `main` prints `Bitwise And-ing!`.
+///
+/// ```
+/// #![feature(augmented_assignments)]
+/// #![feature(op_assign_traits)]
+///
+/// use std::ops::BitAndAssign;
+///
+/// #[derive(Copy, Clone)]
+/// struct Foo;
+///
+/// impl BitAndAssign for Foo {
+///     fn bitand_assign(&mut self, _rhs: Foo) {
+///         println!("Bitwise And-ing!");
+///     }
+/// }
+///
+/// fn main() {
+///     let mut foo = Foo;
+///     foo &= Foo;
+/// }
+/// ```
+#[cfg(not(stage0))]
+#[lang = "bitand_assign"]
+#[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+pub trait BitAndAssign<Rhs=Self> {
+    /// The method for the `&` operator
+    fn bitand_assign(&mut self, Rhs);
+}
+
+#[cfg(not(stage0))]
+macro_rules! bitand_assign_impl {
+    ($($t:ty)+) => ($(
+        #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+        impl BitAndAssign for $t {
+            #[inline]
+            fn bitand_assign(&mut self, other: $t) { *self &= other }
+        }
+    )+)
+}
+
+#[cfg(not(stage0))]
+bitand_assign_impl! { bool usize u8 u16 u32 u64 isize i8 i16 i32 i64 }
+
+/// The `BitOrAssign` trait is used to specify the functionality of `|=`.
+///
+/// # Examples
+///
+/// A trivial implementation of `BitOrAssign`. When `Foo |= Foo` happens, it ends up
+/// calling `bitor_assign`, and therefore, `main` prints `Bitwise Or-ing!`.
+///
+/// ```
+/// #![feature(augmented_assignments)]
+/// #![feature(op_assign_traits)]
+///
+/// use std::ops::BitOrAssign;
+///
+/// #[derive(Copy, Clone)]
+/// struct Foo;
+///
+/// impl BitOrAssign for Foo {
+///     fn bitor_assign(&mut self, _rhs: Foo) {
+///         println!("Bitwise Or-ing!");
+///     }
+/// }
+///
+/// fn main() {
+///     let mut foo = Foo;
+///     foo |= Foo;
+/// }
+/// ```
+#[cfg(not(stage0))]
+#[lang = "bitor_assign"]
+#[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+pub trait BitOrAssign<Rhs=Self> {
+    /// The method for the `|=` operator
+    fn bitor_assign(&mut self, Rhs);
+}
+
+#[cfg(not(stage0))]
+macro_rules! bitor_assign_impl {
+    ($($t:ty)+) => ($(
+        #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+        impl BitOrAssign for $t {
+            #[inline]
+            fn bitor_assign(&mut self, other: $t) { *self |= other }
+        }
+    )+)
+}
+
+#[cfg(not(stage0))]
+bitor_assign_impl! { bool usize u8 u16 u32 u64 isize i8 i16 i32 i64 }
+
+/// The `BitXorAssign` trait is used to specify the functionality of `^=`.
+///
+/// # Examples
+///
+/// A trivial implementation of `BitXorAssign`. When `Foo ^= Foo` happens, it ends up
+/// calling `bitxor_assign`, and therefore, `main` prints `Bitwise Xor-ing!`.
+///
+/// ```
+/// #![feature(augmented_assignments)]
+/// #![feature(op_assign_traits)]
+///
+/// use std::ops::BitXorAssign;
+///
+/// #[derive(Copy, Clone)]
+/// struct Foo;
+///
+/// impl BitXorAssign for Foo {
+///     fn bitxor_assign(&mut self, _rhs: Foo) {
+///         println!("Bitwise Xor-ing!");
+///     }
+/// }
+///
+/// fn main() {
+///     let mut foo = Foo;
+///     foo ^= Foo;
+/// }
+/// ```
+#[cfg(not(stage0))]
+#[lang = "bitxor_assign"]
+#[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+pub trait BitXorAssign<Rhs=Self> {
+    /// The method for the `^=` operator
+    fn bitxor_assign(&mut self, Rhs);
+}
+
+#[cfg(not(stage0))]
+macro_rules! bitxor_assign_impl {
+    ($($t:ty)+) => ($(
+        #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+        impl BitXorAssign for $t {
+            #[inline]
+            fn bitxor_assign(&mut self, other: $t) { *self ^= other }
+        }
+    )+)
+}
+
+#[cfg(not(stage0))]
+bitxor_assign_impl! { bool usize u8 u16 u32 u64 isize i8 i16 i32 i64 }
+
+/// The `ShlAssign` trait is used to specify the functionality of `<<=`.
+///
+/// # Examples
+///
+/// A trivial implementation of `ShlAssign`. When `Foo <<= Foo` happens, it ends up
+/// calling `shl_assign`, and therefore, `main` prints `Shifting left!`.
+///
+/// ```
+/// #![feature(augmented_assignments)]
+/// #![feature(op_assign_traits)]
+///
+/// use std::ops::ShlAssign;
+///
+/// #[derive(Copy, Clone)]
+/// struct Foo;
+///
+/// impl ShlAssign<Foo> for Foo {
+///     fn shl_assign(&mut self, _rhs: Foo) {
+///         println!("Shifting left!");
+///     }
+/// }
+///
+/// fn main() {
+///     let mut foo = Foo;
+///     foo <<= Foo;
+/// }
+/// ```
+#[cfg(not(stage0))]
+#[lang = "shl_assign"]
+#[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+pub trait ShlAssign<Rhs> {
+    /// The method for the `<<=` operator
+    fn shl_assign(&mut self, Rhs);
+}
+
+#[cfg(not(stage0))]
+macro_rules! shl_assign_impl {
+    ($t:ty, $f:ty) => (
+        #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+        impl ShlAssign<$f> for $t {
+            #[inline]
+            fn shl_assign(&mut self, other: $f) {
+                *self <<= other
+            }
+        }
+    )
+}
+
+#[cfg(not(stage0))]
+macro_rules! shl_assign_impl_all {
+    ($($t:ty)*) => ($(
+        shl_assign_impl! { $t, u8 }
+        shl_assign_impl! { $t, u16 }
+        shl_assign_impl! { $t, u32 }
+        shl_assign_impl! { $t, u64 }
+        shl_assign_impl! { $t, usize }
+
+        shl_assign_impl! { $t, i8 }
+        shl_assign_impl! { $t, i16 }
+        shl_assign_impl! { $t, i32 }
+        shl_assign_impl! { $t, i64 }
+        shl_assign_impl! { $t, isize }
+    )*)
+}
+
+#[cfg(not(stage0))]
+shl_assign_impl_all! { u8 u16 u32 u64 usize i8 i16 i32 i64 isize }
+
+/// The `ShrAssign` trait is used to specify the functionality of `>>=`.
+///
+/// # Examples
+///
+/// A trivial implementation of `ShrAssign`. When `Foo >>= Foo` happens, it ends up
+/// calling `shr_assign`, and therefore, `main` prints `Shifting right!`.
+///
+/// ```
+/// #![feature(augmented_assignments)]
+/// #![feature(op_assign_traits)]
+///
+/// use std::ops::ShrAssign;
+///
+/// #[derive(Copy, Clone)]
+/// struct Foo;
+///
+/// impl ShrAssign<Foo> for Foo {
+///     fn shr_assign(&mut self, _rhs: Foo) {
+///         println!("Shifting right!");
+///     }
+/// }
+///
+/// fn main() {
+///     let mut foo = Foo;
+///     foo >>= Foo;
+/// }
+/// ```
+#[cfg(not(stage0))]
+#[lang = "shr_assign"]
+#[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+pub trait ShrAssign<Rhs=Self> {
+    /// The method for the `>>=` operator
+    fn shr_assign(&mut self, Rhs);
+}
+
+#[cfg(not(stage0))]
+macro_rules! shr_assign_impl {
+    ($t:ty, $f:ty) => (
+        #[unstable(feature = "op_assign_traits", reason = "recently added", issue = "28235")]
+        impl ShrAssign<$f> for $t {
+            #[inline]
+            fn shr_assign(&mut self, other: $f) {
+                *self >>= other
+            }
+        }
+    )
+}
+
+#[cfg(not(stage0))]
+macro_rules! shr_assign_impl_all {
+    ($($t:ty)*) => ($(
+        shr_assign_impl! { $t, u8 }
+        shr_assign_impl! { $t, u16 }
+        shr_assign_impl! { $t, u32 }
+        shr_assign_impl! { $t, u64 }
+        shr_assign_impl! { $t, usize }
+
+        shr_assign_impl! { $t, i8 }
+        shr_assign_impl! { $t, i16 }
+        shr_assign_impl! { $t, i32 }
+        shr_assign_impl! { $t, i64 }
+        shr_assign_impl! { $t, isize }
+    )*)
+}
+
+#[cfg(not(stage0))]
+shr_assign_impl_all! { u8 u16 u32 u64 usize i8 i16 i32 i64 isize }
+
 /// The `Index` trait is used to specify the functionality of indexing operations
 /// like `arr[idx]` when used in an immutable context.
 ///
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index c067c9b4d63..29708494048 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -525,11 +525,14 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
                 self.consume_expr(&**base);
             }
 
-            hir::ExprAssignOp(_, ref lhs, ref rhs) => {
-                // This will have to change if/when we support
-                // overloaded operators for `+=` and so forth.
-                self.mutate_expr(expr, &**lhs, WriteAndRead);
-                self.consume_expr(&**rhs);
+            hir::ExprAssignOp(op, ref lhs, ref rhs) => {
+                // NB All our assignment operations take the RHS by value
+                assert!(::rustc_front::util::is_by_value_binop(op.node));
+
+                if !self.walk_overloaded_operator(expr, lhs, vec![rhs], PassArgs::ByValue) {
+                    self.mutate_expr(expr, &**lhs, WriteAndRead);
+                    self.consume_expr(&**rhs);
+                }
             }
 
             hir::ExprRepeat(ref base, ref count) => {
diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs
index f0aa824c589..b7572d43fbe 100644
--- a/src/librustc/middle/lang_items.rs
+++ b/src/librustc/middle/lang_items.rs
@@ -286,6 +286,16 @@ lets_do_this! {
     BitOrTraitLangItem,              "bitor",                   bitor_trait;
     ShlTraitLangItem,                "shl",                     shl_trait;
     ShrTraitLangItem,                "shr",                     shr_trait;
+    AddAssignTraitLangItem,          "add_assign",              add_assign_trait;
+    SubAssignTraitLangItem,          "sub_assign",              sub_assign_trait;
+    MulAssignTraitLangItem,          "mul_assign",              mul_assign_trait;
+    DivAssignTraitLangItem,          "div_assign",              div_assign_trait;
+    RemAssignTraitLangItem,          "rem_assign",              rem_assign_trait;
+    BitXorAssignTraitLangItem,       "bitxor_assign",           bitxor_assign_trait;
+    BitAndAssignTraitLangItem,       "bitand_assign",           bitand_assign_trait;
+    BitOrAssignTraitLangItem,        "bitor_assign",            bitor_assign_trait;
+    ShlAssignTraitLangItem,          "shl_assign",              shl_assign_trait;
+    ShrAssignTraitLangItem,          "shr_assign",              shr_assign_trait;
     IndexTraitLangItem,              "index",                   index_trait;
     IndexMutTraitLangItem,           "index_mut",               index_mut_trait;
     RangeStructLangItem,             "range",                   range_struct;
diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs
index 370e5667c1f..b4252d4b8b0 100644
--- a/src/librustc_trans/trans/expr.rs
+++ b/src/librustc_trans/trans/expr.rs
@@ -1049,7 +1049,20 @@ fn trans_rvalue_stmt_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             }
         }
         hir::ExprAssignOp(op, ref dst, ref src) => {
-            trans_assign_op(bcx, expr, op, &**dst, &**src)
+            let has_method_map = bcx.tcx()
+                                    .tables
+                                    .borrow()
+                                    .method_map
+                                    .contains_key(&MethodCall::expr(expr.id));
+
+            if has_method_map {
+                let dst = unpack_datum!(bcx, trans(bcx, &**dst));
+                let src_datum = unpack_datum!(bcx, trans(bcx, &**src));
+                trans_overloaded_op(bcx, expr, MethodCall::expr(expr.id), dst,
+                                    Some((src_datum, src.id)), None, false).bcx
+            } else {
+                trans_assign_op(bcx, expr, op, &**dst, &**src)
+            }
         }
         hir::ExprInlineAsm(ref a) => {
             asm::trans_inline_asm(bcx, a)
@@ -1238,8 +1251,11 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             // Trait casts used to come this way, now they should be coercions.
             bcx.tcx().sess.span_bug(expr.span, "DPS expr_cast (residual trait cast?)")
         }
-        hir::ExprAssignOp(op, ref dst, ref src) => {
-            trans_assign_op(bcx, expr, op, &**dst, &**src)
+        hir::ExprAssignOp(op, _, _) => {
+            bcx.tcx().sess.span_bug(
+                expr.span,
+                &format!("augmented assignment `{}=` should always be a rvalue_stmt",
+                         rustc_front::util::binop_to_string(op.node)))
         }
         _ => {
             bcx.tcx().sess.span_bug(
diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs
index 07754e8506b..7be61327f81 100644
--- a/src/librustc_typeck/check/op.rs
+++ b/src/librustc_typeck/check/op.rs
@@ -17,10 +17,8 @@ use super::{
     demand,
     method,
     FnCtxt,
-    structurally_resolved_type,
 };
 use middle::def_id::DefId;
-use middle::traits;
 use middle::ty::{Ty, HasTypeFlags, PreferMutLvalue};
 use syntax::ast;
 use syntax::parse::token;
@@ -34,34 +32,24 @@ pub fn check_binop_assign<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
                                    lhs_expr: &'tcx hir::Expr,
                                    rhs_expr: &'tcx hir::Expr)
 {
-    let tcx = fcx.ccx.tcx;
-
     check_expr_with_lvalue_pref(fcx, lhs_expr, PreferMutLvalue);
-    check_expr(fcx, rhs_expr);
 
-    let lhs_ty = structurally_resolved_type(fcx, lhs_expr.span, fcx.expr_ty(lhs_expr));
-    let rhs_ty = structurally_resolved_type(fcx, rhs_expr.span, fcx.expr_ty(rhs_expr));
+    let lhs_ty = fcx.resolve_type_vars_if_possible(fcx.expr_ty(lhs_expr));
+    let (rhs_ty, return_ty) =
+        check_overloaded_binop(fcx, expr, lhs_expr, lhs_ty, rhs_expr, op, IsAssign::Yes);
+    let rhs_ty = fcx.resolve_type_vars_if_possible(rhs_ty);
 
-    if is_builtin_binop(lhs_ty, rhs_ty, op) {
+    if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
         enforce_builtin_binop_types(fcx, lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
         fcx.write_nil(expr.id);
     } else {
-        // error types are considered "builtin"
-        assert!(!lhs_ty.references_error() || !rhs_ty.references_error());
-        span_err!(tcx.sess, lhs_expr.span, E0368,
-                  "binary assignment operation `{}=` cannot be applied to types `{}` and `{}`",
-                  hir_util::binop_to_string(op.node),
-                  lhs_ty,
-                  rhs_ty);
-        fcx.write_error(expr.id);
+        fcx.write_ty(expr.id, return_ty);
     }
 
     let tcx = fcx.tcx();
     if !tcx.expr_is_lval(lhs_expr) {
         span_err!(tcx.sess, lhs_expr.span, E0067, "invalid left-hand side expression");
     }
-
-    fcx.require_expr_have_sized_type(lhs_expr, traits::AssignmentLhsSized);
 }
 
 /// Check a potentially overloaded binary operator.
@@ -95,7 +83,7 @@ pub fn check_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
             // overloaded. This is the way to be most flexible w/r/t
             // types that get inferred.
             let (rhs_ty, return_ty) =
-                check_overloaded_binop(fcx, expr, lhs_expr, lhs_ty, rhs_expr, op);
+                check_overloaded_binop(fcx, expr, lhs_expr, lhs_ty, rhs_expr, op, IsAssign::No);
 
             // Supply type inference hints if relevant. Probably these
             // hints should be enforced during select as part of the
@@ -167,14 +155,16 @@ fn check_overloaded_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                     lhs_expr: &'tcx hir::Expr,
                                     lhs_ty: Ty<'tcx>,
                                     rhs_expr: &'tcx hir::Expr,
-                                    op: hir::BinOp)
+                                    op: hir::BinOp,
+                                    is_assign: IsAssign)
                                     -> (Ty<'tcx>, Ty<'tcx>)
 {
-    debug!("check_overloaded_binop(expr.id={}, lhs_ty={:?})",
+    debug!("check_overloaded_binop(expr.id={}, lhs_ty={:?}, is_assign={:?})",
            expr.id,
-           lhs_ty);
+           lhs_ty,
+           is_assign);
 
-    let (name, trait_def_id) = name_and_trait_def_id(fcx, op);
+    let (name, trait_def_id) = name_and_trait_def_id(fcx, op, is_assign);
 
     // NB: As we have not yet type-checked the RHS, we don't have the
     // 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>,
         Err(()) => {
             // error types are considered "builtin"
             if !lhs_ty.references_error() {
-                span_err!(fcx.tcx().sess, lhs_expr.span, E0369,
-                          "binary operation `{}` cannot be applied to type `{}`",
-                          hir_util::binop_to_string(op.node),
-                          lhs_ty);
+                if let IsAssign::Yes = is_assign {
+                    span_err!(fcx.tcx().sess, lhs_expr.span, E0368,
+                              "binary assignment operation `{}=` cannot be applied to type `{}`",
+                              hir_util::binop_to_string(op.node),
+                              lhs_ty);
+                } else {
+                    span_err!(fcx.tcx().sess, lhs_expr.span, E0369,
+                              "binary operation `{}` cannot be applied to type `{}`",
+                              hir_util::binop_to_string(op.node),
+                              lhs_ty);
+                }
             }
             fcx.tcx().types.err
         }
@@ -231,27 +228,51 @@ pub fn check_user_unop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
     }
 }
 
-fn name_and_trait_def_id(fcx: &FnCtxt, op: hir::BinOp) -> (&'static str, Option<DefId>) {
+fn name_and_trait_def_id(fcx: &FnCtxt,
+                         op: hir::BinOp,
+                         is_assign: IsAssign)
+                         -> (&'static str, Option<DefId>) {
     let lang = &fcx.tcx().lang_items;
-    match op.node {
-        hir::BiAdd => ("add", lang.add_trait()),
-        hir::BiSub => ("sub", lang.sub_trait()),
-        hir::BiMul => ("mul", lang.mul_trait()),
-        hir::BiDiv => ("div", lang.div_trait()),
-        hir::BiRem => ("rem", lang.rem_trait()),
-        hir::BiBitXor => ("bitxor", lang.bitxor_trait()),
-        hir::BiBitAnd => ("bitand", lang.bitand_trait()),
-        hir::BiBitOr => ("bitor", lang.bitor_trait()),
-        hir::BiShl => ("shl", lang.shl_trait()),
-        hir::BiShr => ("shr", lang.shr_trait()),
-        hir::BiLt => ("lt", lang.ord_trait()),
-        hir::BiLe => ("le", lang.ord_trait()),
-        hir::BiGe => ("ge", lang.ord_trait()),
-        hir::BiGt => ("gt", lang.ord_trait()),
-        hir::BiEq => ("eq", lang.eq_trait()),
-        hir::BiNe => ("ne", lang.eq_trait()),
-        hir::BiAnd | hir::BiOr => {
-            fcx.tcx().sess.span_bug(op.span, "&& and || are not overloadable")
+
+    if let IsAssign::Yes = is_assign {
+        match op.node {
+            hir::BiAdd => ("add_assign", lang.add_assign_trait()),
+            hir::BiSub => ("sub_assign", lang.sub_assign_trait()),
+            hir::BiMul => ("mul_assign", lang.mul_assign_trait()),
+            hir::BiDiv => ("div_assign", lang.div_assign_trait()),
+            hir::BiRem => ("rem_assign", lang.rem_assign_trait()),
+            hir::BiBitXor => ("bitxor_assign", lang.bitxor_assign_trait()),
+            hir::BiBitAnd => ("bitand_assign", lang.bitand_assign_trait()),
+            hir::BiBitOr => ("bitor_assign", lang.bitor_assign_trait()),
+            hir::BiShl => ("shl_assign", lang.shl_assign_trait()),
+            hir::BiShr => ("shr_assign", lang.shr_assign_trait()),
+            hir::BiLt | hir::BiLe | hir::BiGe | hir::BiGt | hir::BiEq | hir::BiNe | hir::BiAnd |
+            hir::BiOr => {
+                fcx.tcx().sess.span_bug(op.span, &format!("impossible assignment operation: {}=",
+                                        hir_util::binop_to_string(op.node)))
+            }
+        }
+    } else {
+        match op.node {
+            hir::BiAdd => ("add", lang.add_trait()),
+            hir::BiSub => ("sub", lang.sub_trait()),
+            hir::BiMul => ("mul", lang.mul_trait()),
+            hir::BiDiv => ("div", lang.div_trait()),
+            hir::BiRem => ("rem", lang.rem_trait()),
+            hir::BiBitXor => ("bitxor", lang.bitxor_trait()),
+            hir::BiBitAnd => ("bitand", lang.bitand_trait()),
+            hir::BiBitOr => ("bitor", lang.bitor_trait()),
+            hir::BiShl => ("shl", lang.shl_trait()),
+            hir::BiShr => ("shr", lang.shr_trait()),
+            hir::BiLt => ("lt", lang.ord_trait()),
+            hir::BiLe => ("le", lang.ord_trait()),
+            hir::BiGe => ("ge", lang.ord_trait()),
+            hir::BiGt => ("gt", lang.ord_trait()),
+            hir::BiEq => ("eq", lang.eq_trait()),
+            hir::BiNe => ("ne", lang.eq_trait()),
+            hir::BiAnd | hir::BiOr => {
+                fcx.tcx().sess.span_bug(op.span, "&& and || are not overloadable")
+            }
         }
     }
 }
@@ -362,6 +383,13 @@ impl BinOpCategory {
     }
 }
 
+/// Whether the binary operation is an assignment (`a += b`), or not (`a + b`)
+#[derive(Clone, Copy, Debug)]
+enum IsAssign {
+    No,
+    Yes,
+}
+
 /// Returns true if this is a built-in arithmetic operation (e.g. u32
 /// + u32, i16x4 == i16x4) and false if these types would have to be
 /// overloaded to be legal. There are two reasons that we distinguish
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index 155caaa8cb0..b4a99dcb576 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -695,7 +695,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &hir::Expr) {
         hir::ExprAssignOp(_, ref lhs, ref rhs) => {
             if has_method_map {
                 constrain_call(rcx, expr, Some(&**lhs),
-                               Some(&**rhs).into_iter(), true);
+                               Some(&**rhs).into_iter(), false);
             }
 
             visit::walk_expr(rcx, expr);
diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs
index 6f8e064c9d8..22edd7e2c53 100644
--- a/src/librustc_typeck/check/writeback.rs
+++ b/src/librustc_typeck/check/writeback.rs
@@ -17,7 +17,7 @@ use astconv::AstConv;
 use check::FnCtxt;
 use middle::def_id::DefId;
 use middle::pat_util;
-use middle::ty::{self, Ty, MethodCall, MethodCallee};
+use middle::ty::{self, Ty, MethodCall, MethodCallee, HasTypeFlags};
 use middle::ty::adjustment;
 use middle::ty::fold::{TypeFolder,TypeFoldable};
 use middle::infer;
@@ -91,24 +91,53 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
     // we observe that something like `a+b` is (known to be)
     // operating on scalars, we clear the overload.
     fn fix_scalar_binary_expr(&mut self, e: &hir::Expr) {
-        if let hir::ExprBinary(ref op, ref lhs, ref rhs) = e.node {
-            let lhs_ty = self.fcx.node_ty(lhs.id);
-            let lhs_ty = self.fcx.infcx().resolve_type_vars_if_possible(&lhs_ty);
-
-            let rhs_ty = self.fcx.node_ty(rhs.id);
-            let rhs_ty = self.fcx.infcx().resolve_type_vars_if_possible(&rhs_ty);
-
-            if lhs_ty.is_scalar() && rhs_ty.is_scalar() {
-                self.fcx.inh.tables.borrow_mut().method_map.remove(&MethodCall::expr(e.id));
-
-                // weird but true: the by-ref binops put an
-                // adjustment on the lhs but not the rhs; the
-                // adjustment for rhs is kind of baked into the
-                // system.
-                if !hir_util::is_by_value_binop(op.node) {
-                    self.fcx.inh.tables.borrow_mut().adjustments.remove(&lhs.id);
+        match e.node {
+            hir::ExprBinary(ref op, ref lhs, ref rhs) |
+            hir::ExprAssignOp(ref op, ref lhs, ref rhs) => {
+                let lhs_ty = self.fcx.node_ty(lhs.id);
+                let lhs_ty = self.fcx.infcx().resolve_type_vars_if_possible(&lhs_ty);
+
+                let rhs_ty = self.fcx.node_ty(rhs.id);
+                let rhs_ty = self.fcx.infcx().resolve_type_vars_if_possible(&rhs_ty);
+
+                if lhs_ty.is_scalar() && rhs_ty.is_scalar() {
+                    self.fcx.inh.tables.borrow_mut().method_map.remove(&MethodCall::expr(e.id));
+
+                    // weird but true: the by-ref binops put an
+                    // adjustment on the lhs but not the rhs; the
+                    // adjustment for rhs is kind of baked into the
+                    // system.
+                    match e.node {
+                        hir::ExprBinary(..) => {
+                            if !hir_util::is_by_value_binop(op.node) {
+                                self.fcx.inh.tables.borrow_mut().adjustments.remove(&lhs.id);
+                            }
+                        },
+                        hir::ExprAssignOp(..) => {
+                            self.fcx.inh.tables.borrow_mut().adjustments.remove(&lhs.id);
+                        },
+                        _ => {},
+                    }
+                } else {
+                    let tcx = self.tcx();
+
+                    if let hir::ExprAssignOp(..) = e.node {
+                        if
+                            !tcx.sess.features.borrow().augmented_assignments &&
+                            !self.fcx.expr_ty(e).references_error()
+                        {
+                            tcx.sess.span_err(
+                                e.span,
+                                "overloaded augmented assignments are not stable");
+                            fileline_help!(
+                                tcx.sess, e.span,
+                                "add #![feature(augmented_assignments)] to the crate features \
+                                 to enable");
+                        }
+                    }
                 }
             }
+            _ => {},
         }
     }
 }
diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs
index 78eaedfbd1b..54724f61f86 100644
--- a/src/librustc_typeck/diagnostics.rs
+++ b/src/librustc_typeck/diagnostics.rs
@@ -2862,14 +2862,27 @@ impl <T: Foo> Drop for MyStructWrapper<T> {
 
 E0368: r##"
 This error indicates that a binary assignment operator like `+=` or `^=` was
-applied to the wrong types. For example:
+applied to a type that doesn't support it. For example:
 
 ```
-let mut x: u16 = 5;
-x ^= true; // error, `^=` cannot be applied to types `u16` and `bool`
-x += ();   // error, `+=` cannot be applied to types `u16` and `()`
+let mut x = 12f32; // error: binary operation `<<` cannot be applied to
+               //        type `f32`
+
+x <<= 2;
+```
+
+To fix this error, please check that this type implements this binary
+operation. Example:
+
+```
+let x = 12u32; // the `u32` type does implement the `ShlAssign` trait
+
+x <<= 2; // ok!
 ```
 
+It is also possible to overload most operators for your own type by
+implementing the `[OP]Assign` traits from `std::ops`.
+
 Another problem you might be facing is this: suppose you've overloaded the `+`
 operator for some type `Foo` by implementing the `std::ops::Add` trait for
 `Foo`, but you find that using `+=` does not work, as in this example:
@@ -2889,15 +2902,12 @@ impl Add for Foo {
 
 fn main() {
     let mut x: Foo = Foo(5);
-    x += Foo(7); // error, `+= cannot be applied to types `Foo` and `Foo`
+    x += Foo(7); // error, `+= cannot be applied to the type `Foo`
 }
 ```
 
-This is because the binary assignment operators currently do not work off of
-traits, so it is not possible to overload them. See [RFC 953] for a proposal
-to change this.
-
-[RFC 953]: https://github.com/rust-lang/rfcs/pull/953
+This is because `AddAssign` is not automatically implemented, so you need to
+manually implement it for your type.
 "##,
 
 E0369: r##"
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 989977a6917..1e9b244c57c 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -194,6 +194,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option<u32>, Status
 
     // allow empty structs/enum variants with braces
     ("braced_empty_structs", "1.5.0", None, Active),
+
+    // allow overloading augmented assignment operations like `a += b`
+    ("augmented_assignments", "1.5.0", None, Active),
 ];
 // (changing above list without updating src/doc/reference.md makes @cmr sad)
 
@@ -457,6 +460,7 @@ pub struct Features {
     pub default_type_parameter_fallback: bool,
     pub type_macros: bool,
     pub cfg_target_feature: bool,
+    pub augmented_assignments: bool,
 }
 
 impl Features {
@@ -485,6 +489,7 @@ impl Features {
             default_type_parameter_fallback: false,
             type_macros: false,
             cfg_target_feature: false,
+            augmented_assignments: false,
         }
     }
 }
@@ -1053,6 +1058,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
         default_type_parameter_fallback: cx.has_feature("default_type_parameter_fallback"),
         type_macros: cx.has_feature("type_macros"),
         cfg_target_feature: cx.has_feature("cfg_target_feature"),
+        augmented_assignments: cx.has_feature("augmented_assignments"),
     }
 }
 
diff --git a/src/test/auxiliary/augmented_assignments.rs b/src/test/auxiliary/augmented_assignments.rs
new file mode 100644
index 00000000000..9577e1ff0c7
--- /dev/null
+++ b/src/test/auxiliary/augmented_assignments.rs
@@ -0,0 +1,22 @@
+// Copyright 2015 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.
+
+#![feature(augmented_assignments)]
+#![feature(op_assign_traits)]
+
+use std::ops::AddAssign;
+
+pub struct Int(i32);
+
+impl AddAssign<i32> for Int {
+    fn add_assign(&mut self, _: i32) {
+        unimplemented!();
+    }
+}
diff --git a/src/test/compile-fail/assignment-operator-unimplemented.rs b/src/test/compile-fail/assignment-operator-unimplemented.rs
index fef27af5957..5b24c6bd79f 100644
--- a/src/test/compile-fail/assignment-operator-unimplemented.rs
+++ b/src/test/compile-fail/assignment-operator-unimplemented.rs
@@ -13,5 +13,5 @@ struct Foo;
 fn main() {
   let mut a = Foo;
   let ref b = Foo;
-  a += *b; //~ Error: binary assignment operation `+=` cannot be applied to types `Foo` and `Foo`
+  a += *b; //~ Error: binary assignment operation `+=` cannot be applied to type `Foo`
 }
diff --git a/src/test/compile-fail/augmented-assignments-feature-gate-cross.rs b/src/test/compile-fail/augmented-assignments-feature-gate-cross.rs
new file mode 100644
index 00000000000..0aca9a5c7ee
--- /dev/null
+++ b/src/test/compile-fail/augmented-assignments-feature-gate-cross.rs
@@ -0,0 +1,25 @@
+// Copyright 2015 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.
+
+// aux-build:augmented_assignments.rs
+
+// Test that the feature gate is needed when using augmented assignments that were overloaded in
+// another crate
+
+extern crate augmented_assignments;
+
+use augmented_assignments::Int;
+
+fn main() {
+    let mut x = Int(0);
+    x += 1;
+    //~^ error: overloaded augmented assignments are not stable
+    // | help: add #![feature(augmented_assignments)] to the crate features to enable
+}
diff --git a/src/test/compile-fail/augmented-assignments-feature-gate.rs b/src/test/compile-fail/augmented-assignments-feature-gate.rs
new file mode 100644
index 00000000000..66c3014c7a9
--- /dev/null
+++ b/src/test/compile-fail/augmented-assignments-feature-gate.rs
@@ -0,0 +1,26 @@
+// Copyright 2015 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.
+
+use std::ops::AddAssign;
+
+struct Int(i32);
+
+impl AddAssign<i32> for Int {
+    fn add_assign(&mut self, _: i32) {
+        unimplemented!()
+    }
+}
+
+fn main() {
+    let mut x = Int(0);
+    x += 1;
+    //~^ error: overloaded augmented assignments are not stable
+    // | help: add #![feature(augmented_assignments)] to the crate features to enable
+}
diff --git a/src/test/compile-fail/augmented-assignments-trait.rs b/src/test/compile-fail/augmented-assignments-trait.rs
new file mode 100644
index 00000000000..83e8d1f3b38
--- /dev/null
+++ b/src/test/compile-fail/augmented-assignments-trait.rs
@@ -0,0 +1,24 @@
+// Copyright 2015 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.
+
+use std::ops::AddAssign;
+//~^ error: use of unstable library feature 'op_assign_traits'
+
+struct Int(i32);
+
+impl AddAssign for Int {
+    //~^ error: use of unstable library feature 'op_assign_traits'
+    fn add_assign(&mut self, _: Int) {
+        //~^ error: use of unstable library feature 'op_assign_traits'
+        unimplemented!()
+    }
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/augmented-assignments.rs b/src/test/compile-fail/augmented-assignments.rs
new file mode 100644
index 00000000000..ee64171fd8c
--- /dev/null
+++ b/src/test/compile-fail/augmented-assignments.rs
@@ -0,0 +1,33 @@
+// Copyright 2015 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.
+
+#![feature(augmented_assignments)]
+
+use std::ops::AddAssign;
+
+struct Int(i32);
+
+impl AddAssign for Int {
+    fn add_assign(&mut self, _: Int) {
+        unimplemented!()
+    }
+}
+
+fn main() {
+    let mut x = Int(1);
+    x   //~ error: use of moved value: `x`
+    +=
+    x;  //~ note: `x` moved here because it has type `Int`, which is non-copyable
+
+    let y = Int(2);
+    y   //~ error: cannot borrow immutable local variable `y` as mutable
+    +=
+    Int(1);
+}
diff --git a/src/test/run-pass/augmented-assignments.rs b/src/test/run-pass/augmented-assignments.rs
new file mode 100644
index 00000000000..eb4c1dbb0b7
--- /dev/null
+++ b/src/test/run-pass/augmented-assignments.rs
@@ -0,0 +1,164 @@
+// Copyright 2015 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.
+
+#![feature(augmented_assignments)]
+#![feature(op_assign_traits)]
+
+use std::mem;
+use std::ops::{
+    AddAssign, BitAndAssign, BitOrAssign, BitXorAssign, DivAssign, Index, MulAssign, RemAssign,
+    ShlAssign, ShrAssign, SubAssign,
+};
+
+#[derive(Debug, PartialEq)]
+struct Int(i32);
+
+struct Slice([i32]);
+
+impl Slice {
+    fn new(slice: &mut [i32]) -> &mut Slice {
+        unsafe {
+            mem::transmute(slice)
+        }
+    }
+}
+
+fn main() {
+    let mut x = Int(1);
+
+    x += Int(2);
+    assert_eq!(x, Int(0b11));
+
+    x &= Int(0b01);
+    assert_eq!(x, Int(0b01));
+
+    x |= Int(0b10);
+    assert_eq!(x, Int(0b11));
+
+    x ^= Int(0b01);
+    assert_eq!(x, Int(0b10));
+
+    x /= Int(2);
+    assert_eq!(x, Int(1));
+
+    x *= Int(3);
+    assert_eq!(x, Int(3));
+
+    x %= Int(2);
+    assert_eq!(x, Int(1));
+
+    // overloaded RHS
+    x <<= 1u8;
+    assert_eq!(x, Int(2));
+
+    x <<= 1u16;
+    assert_eq!(x, Int(4));
+
+    x >>= 1u8;
+    assert_eq!(x, Int(2));
+
+    x >>= 1u16;
+    assert_eq!(x, Int(1));
+
+    x -= Int(1);
+    assert_eq!(x, Int(0));
+
+    // indexed LHS
+    let mut v = vec![Int(1), Int(2)];
+    v[0] += Int(2);
+    assert_eq!(v[0], Int(3));
+
+    // unsized RHS
+    let mut array = [0, 1, 2];
+    *Slice::new(&mut array) += 1;
+    assert_eq!(array[0], 1);
+    assert_eq!(array[1], 2);
+    assert_eq!(array[2], 3);
+}
+
+impl AddAssign for Int {
+    fn add_assign(&mut self, rhs: Int) {
+        self.0 += rhs.0;
+    }
+}
+
+impl BitAndAssign for Int {
+    fn bitand_assign(&mut self, rhs: Int) {
+        self.0 &= rhs.0;
+    }
+}
+
+impl BitOrAssign for Int {
+    fn bitor_assign(&mut self, rhs: Int) {
+        self.0 |= rhs.0;
+    }
+}
+
+impl BitXorAssign for Int {
+    fn bitxor_assign(&mut self, rhs: Int) {
+        self.0 ^= rhs.0;
+    }
+}
+
+impl DivAssign for Int {
+    fn div_assign(&mut self, rhs: Int) {
+        self.0 /= rhs.0;
+    }
+}
+
+impl MulAssign for Int {
+    fn mul_assign(&mut self, rhs: Int) {
+        self.0 *= rhs.0;
+    }
+}
+
+impl RemAssign for Int {
+    fn rem_assign(&mut self, rhs: Int) {
+        self.0 %= rhs.0;
+    }
+}
+
+impl ShlAssign<u8> for Int {
+    fn shl_assign(&mut self, rhs: u8) {
+        self.0 <<= rhs;
+    }
+}
+
+impl ShlAssign<u16> for Int {
+    fn shl_assign(&mut self, rhs: u16) {
+        self.0 <<= rhs;
+    }
+}
+
+impl ShrAssign<u8> for Int {
+    fn shr_assign(&mut self, rhs: u8) {
+        self.0 >>= rhs;
+    }
+}
+
+impl ShrAssign<u16> for Int {
+    fn shr_assign(&mut self, rhs: u16) {
+        self.0 >>= rhs;
+    }
+}
+
+impl SubAssign for Int {
+    fn sub_assign(&mut self, rhs: Int) {
+        self.0 -= rhs.0;
+    }
+}
+
+impl AddAssign<i32> for Slice {
+    fn add_assign(&mut self, rhs: i32) {
+        for lhs in &mut self.0 {
+            *lhs += rhs;
+        }
+    }
+}