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 5f9a70d

Browse files
author
Jorge Aparicio
committedMar 11, 2015
add lang items, feature gate, tests; update typeck/trans
1 parent 3b6a27a commit 5f9a70d

File tree

12 files changed

+299
-28
lines changed

12 files changed

+299
-28
lines changed
 

‎src/librustc/middle/expr_use_visitor.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -584,10 +584,15 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
584584
}
585585

586586
ast::ExprAssignOp(_, ref lhs, ref rhs) => {
587-
// This will have to change if/when we support
588-
// overloaded operators for `+=` and so forth.
589587
self.mutate_expr(expr, &**lhs, WriteAndRead);
590-
self.consume_expr(&**rhs);
588+
589+
if self.typer.is_method_call(expr.id) {
590+
let r = ty::ReScope(region::CodeExtent::from_node_id(expr.id));
591+
self.borrow_expr(rhs, r, ty::ImmBorrow, OverloadedOperator);
592+
} else {
593+
// built-in assignment operations consume the RHS
594+
self.consume_expr(&**rhs);
595+
}
591596
}
592597

593598
ast::ExprRepeat(ref base, ref count) => {

‎src/librustc/middle/lang_items.rs

+11
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,17 @@ lets_do_this! {
265265
RangeToStructLangItem, "range_to", range_to_struct;
266266
RangeFullStructLangItem, "range_full", range_full_struct;
267267

268+
AddAssignTraitLangItem, "add_assign", add_assign_trait;
269+
BitAndAssignTraitLangItem, "bitand_assign", bitand_assign_trait;
270+
BitOrAssignTraitLangItem, "bitor_assign", bitor_assign_trait;
271+
BitXorAssignTraitLangItem, "bitxor_assign", bitxor_assign_trait;
272+
DivAssignTraitLangItem, "div_assign", div_assign_trait;
273+
MulAssignTraitLangItem, "mul_assign", mul_assign_trait;
274+
RemAssignTraitLangItem, "rem_assign", rem_assign_trait;
275+
ShlAssignTraitLangItem, "shl_assign", shl_assign_trait;
276+
ShrAssignTraitLangItem, "shr_assign", shr_assign_trait;
277+
SubAssignTraitLangItem, "sub_assign", sub_assign_trait;
278+
268279
UnsafeCellTypeLangItem, "unsafe_cell", unsafe_cell_type;
269280

270281
DerefTraitLangItem, "deref", deref_trait;

‎src/librustc_trans/trans/expr.rs

+12-3
Original file line numberDiff line numberDiff line change
@@ -1011,7 +1011,15 @@ fn trans_rvalue_stmt_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
10111011
}
10121012
}
10131013
ast::ExprAssignOp(op, ref dst, ref src) => {
1014-
trans_assign_op(bcx, expr, op, &**dst, &**src)
1014+
if ty::is_binopable(bcx.tcx(), node_id_type(bcx, dst.id), op) {
1015+
trans_assign_op(bcx, expr, op, &**dst, &**src)
1016+
} else {
1017+
let dst = unpack_datum!(bcx, trans(bcx, &**dst));
1018+
let src_datum = unpack_datum!(bcx, trans(bcx, &**src));
1019+
trans_overloaded_op(bcx, expr, MethodCall::expr(expr.id), dst,
1020+
vec![(src_datum, src.id)], None,
1021+
true).bcx
1022+
}
10151023
}
10161024
ast::ExprInlineAsm(ref a) => {
10171025
asm::trans_inline_asm(bcx, a)
@@ -1209,8 +1217,9 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
12091217
"expr_cast of non-trait");
12101218
}
12111219
}
1212-
ast::ExprAssignOp(op, ref dst, ref src) => {
1213-
trans_assign_op(bcx, expr, op, &**dst, &**src)
1220+
ast::ExprAssignOp(..) => {
1221+
bcx.tcx().sess.span_bug(expr.span,
1222+
"operator assignment (`+=`) should always be an rvalue_stmt");
12141223
}
12151224
_ => {
12161225
bcx.tcx().sess.span_bug(

‎src/librustc_typeck/check/mod.rs

+51-18
Original file line numberDiff line numberDiff line change
@@ -2988,19 +2988,20 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
29882988
let result_t = if is_binop_assignment == SimpleBinop {
29892989
check_user_binop(fcx, expr, lhs, lhs_t, op, rhs)
29902990
} else {
2991-
fcx.type_error_message(expr.span,
2992-
|actual| {
2993-
format!("binary assignment \
2994-
operation `{}=` \
2995-
cannot be applied to \
2996-
type `{}`",
2997-
ast_util::binop_to_string(op.node),
2998-
actual)
2999-
},
3000-
lhs_t,
3001-
None);
3002-
check_expr(fcx, &**rhs);
3003-
fcx.tcx().types.err
2991+
if fcx.tcx().sess.features.borrow().op_assign {
2992+
check_user_binop_assign(fcx, expr, lhs, lhs_t, op, rhs)
2993+
} else {
2994+
fcx.tcx().sess.span_err(
2995+
expr.span,
2996+
"overloaded augmented assignment is not stable",
2997+
);
2998+
fileline_help!(
2999+
fcx.tcx().sess,
3000+
expr.span,
3001+
"add `#![feature(op_assign)]` to the crate attributes to enable");
3002+
3003+
fcx.tcx().types.err
3004+
}
30043005
};
30053006

30063007
fcx.write_ty(expr.id, result_t);
@@ -3049,6 +3050,42 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
30493050
}, if ast_util::is_by_value_binop(op.node) { AutorefArgs::No } else { AutorefArgs::Yes })
30503051
}
30513052

3053+
fn check_user_binop_assign<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
3054+
ex: &'tcx ast::Expr,
3055+
lhs_expr: &'tcx ast::Expr,
3056+
lhs_resolved_t: Ty<'tcx>,
3057+
op: ast::BinOp,
3058+
rhs: &'tcx P<ast::Expr>) -> Ty<'tcx> {
3059+
let tcx = fcx.ccx.tcx;
3060+
let lang = &tcx.lang_items;
3061+
let (name, trait_did) = match op.node {
3062+
ast::BiAdd => ("add_assign", lang.add_assign_trait()),
3063+
ast::BiSub => ("sub_assign", lang.sub_assign_trait()),
3064+
ast::BiMul => ("mul_assign", lang.mul_assign_trait()),
3065+
ast::BiDiv => ("div_assign", lang.div_assign_trait()),
3066+
ast::BiRem => ("rem_assign", lang.rem_assign_trait()),
3067+
ast::BiBitXor => ("bitxor_assign", lang.bitxor_assign_trait()),
3068+
ast::BiBitAnd => ("bitand_assign", lang.bitand_assign_trait()),
3069+
ast::BiBitOr => ("bitor_assign", lang.bitor_assign_trait()),
3070+
ast::BiShl => ("shl_assign", lang.shl_assign_trait()),
3071+
ast::BiShr => ("shr_assign", lang.shr_assign_trait()),
3072+
ast::BiLt | ast::BiLe | ast::BiGe | ast::BiGt | ast::BiEq | ast::BiNe | ast::BiAnd |
3073+
ast::BiOr =>
3074+
{
3075+
check_expr(fcx, &**rhs);
3076+
return tcx.types.err;
3077+
}
3078+
};
3079+
lookup_op_method(fcx, ex, lhs_resolved_t, token::intern(name),
3080+
trait_did, lhs_expr, Some(rhs), || {
3081+
fcx.type_error_message(ex.span, |actual| {
3082+
format!("binary operation `{}=` cannot be applied to type `{}`",
3083+
ast_util::binop_to_string(op.node),
3084+
actual)
3085+
}, lhs_resolved_t, None)
3086+
}, AutorefArgs::Yes)
3087+
}
3088+
30523089
fn check_user_unop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
30533090
op_str: &str,
30543091
mname: &str,
@@ -3480,10 +3517,6 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
34803517
ast::ExprAssignOp(op, ref lhs, ref rhs) => {
34813518
check_binop(fcx, expr, op, &**lhs, rhs, BinopAssignment);
34823519

3483-
let lhs_t = fcx.expr_ty(&**lhs);
3484-
let result_t = fcx.expr_ty(expr);
3485-
demand::suptype(fcx, expr.span, result_t, lhs_t);
3486-
34873520
let tcx = fcx.tcx();
34883521
if !ty::expr_is_lval(tcx, &**lhs) {
34893522
span_err!(tcx.sess, lhs.span, E0067, "illegal left-hand side expression");
@@ -3494,7 +3527,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
34943527
// Overwrite result of check_binop...this preserves existing behavior
34953528
// but seems quite dubious with regard to user-defined methods
34963529
// and so forth. - Niko
3497-
if !ty::type_is_error(result_t) {
3530+
if !ty::type_is_error(fcx.expr_ty(expr)) {
34983531
fcx.write_nil(expr.id);
34993532
}
35003533
}

‎src/libsyntax/feature_gate.rs

+6
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[
152152
// below (it has to be checked before expansion possibly makes
153153
// macros disappear).
154154
("allow_internal_unstable", "1.0.0", Active),
155+
156+
// Augmented assignment `a += b`; RFC 953
157+
("op_assign", "1.0.0", Active),
155158
];
156159
// (changing above list without updating src/doc/reference.md makes @cmr sad)
157160

@@ -314,6 +317,7 @@ pub enum AttributeType {
314317

315318
/// A set of features to be used by later passes.
316319
pub struct Features {
320+
pub op_assign: bool,
317321
pub unboxed_closures: bool,
318322
pub rustc_diagnostic_macros: bool,
319323
pub visible_private_types: bool,
@@ -336,6 +340,7 @@ pub struct Features {
336340
impl Features {
337341
pub fn new() -> Features {
338342
Features {
343+
op_assign: false,
339344
unboxed_closures: false,
340345
rustc_diagnostic_macros: false,
341346
visible_private_types: false,
@@ -784,6 +789,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::C
784789
// to a single-pass (instead of N calls to `.has_feature`).
785790

786791
Features {
792+
op_assign: cx.has_feature("op_assign"),
787793
unboxed_closures: cx.has_feature("unboxed_closures"),
788794
rustc_diagnostic_macros: cx.has_feature("rustc_diagnostic_macros"),
789795
visible_private_types: cx.has_feature("visible_private_types"),

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
#![feature(op_assign)]
12+
1113
struct Foo;
1214

1315
fn main() {
1416
let mut a = Foo;
1517
let ref b = Foo;
16-
a += *b; //~ Error: binary assignment operation `+=` cannot be applied to type `Foo`
18+
a += *b; //~ Error: binary operation `+=` cannot be applied to type `Foo`
1719
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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 MyInt(i32);
14+
15+
impl AddAssign for MyInt {
16+
fn add_assign(&mut self, rhs: &MyInt) {
17+
self.0 += rhs.0
18+
}
19+
}
20+
21+
fn main() {
22+
let mut x = MyInt(1);
23+
x += MyInt(2);
24+
//~^ error: overloaded augmented assignment is not stable
25+
//~^^ help: add `#![feature(op_assign)]` to the crate attributes to enable
26+
x -= MyInt(3);
27+
//~^ error: overloaded augmented assignment is not stable
28+
//~^^ help: add `#![feature(op_assign)]` to the crate attributes to enable
29+
}

‎src/test/compile-fail/issue-10401.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
#![feature(op_assign)]
12+
1113
fn main() {
1214
let mut a = "a";
1315
a += { "b" };
14-
//~^ ERROR: binary assignment operation `+=` cannot be applied
16+
//~^ ERROR: binary operation `+=` cannot be applied to type `&str`
1517
}

‎src/test/compile-fail/issue-5239-1.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010

1111
// Regression test for issue #5239
1212

13+
#![feature(op_assign)]
14+
1315
fn main() {
1416
let x = |ref x: isize| -> isize { x += 1; };
15-
//~^ ERROR binary assignment operation `+=` cannot be applied to type `&isize`
17+
//~^ ERROR binary operation `+=` cannot be applied to type `&isize`
1618
}

‎src/test/compile-fail/issue-6738.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
#![feature(op_assign)]
12+
1113
struct Foo<T> {
1214
x: T,
1315
}
1416
impl<T> Foo<T> {
1517
fn add(&mut self, v: Foo<T>){
1618
self.x += v.x;
17-
//~^ ERROR: binary assignment operation `+=` cannot be applied
19+
//~^ ERROR: binary operation `+=` cannot be applied to type `T`
1820
}
1921
}
2022
fn main() {}
+33
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(op_assign)]
12+
13+
use std::ops::AddAssign;
14+
15+
struct Int(i32);
16+
17+
impl AddAssign for Int {
18+
fn add_assign(&mut self, rhs: &Int) {
19+
self.0 += rhs.0
20+
}
21+
}
22+
23+
fn main() {
24+
let mut x = Int(1);
25+
x //~ error: cannot borrow `x` as mutable because it is also borrowed as immutable
26+
+=
27+
x; //~ note: previous borrow of `x` occurs here
28+
29+
let y = Int(2);
30+
y //~ error: cannot borrow immutable local variable `y` as mutable
31+
+=
32+
Int(1);
33+
}

‎src/test/run-pass/op-assign.rs

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
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(op_assign)]
12+
13+
use std::ops::{
14+
AddAssign, BitAndAssign, BitOrAssign, BitXorAssign, DivAssign, MulAssign, RemAssign, ShlAssign,
15+
ShrAssign, SubAssign,
16+
};
17+
18+
#[derive(Debug, PartialEq)]
19+
struct Int(i32);
20+
21+
fn main() {
22+
let mut x = Int(1);
23+
24+
x += Int(2);
25+
assert_eq!(x, Int(0b11));
26+
27+
x &= Int(0b01);
28+
assert_eq!(x, Int(0b01));
29+
30+
x |= Int(0b10);
31+
assert_eq!(x, Int(0b11));
32+
33+
x ^= Int(0b01);
34+
assert_eq!(x, Int(0b10));
35+
36+
x /= Int(2);
37+
assert_eq!(x, Int(1));
38+
39+
x *= Int(3);
40+
assert_eq!(x, Int(3));
41+
42+
x %= Int(2);
43+
assert_eq!(x, Int(1));
44+
45+
// overloaded RHS
46+
x <<= 1u8;
47+
assert_eq!(x, Int(2));
48+
49+
x <<= 1u16;
50+
assert_eq!(x, Int(4));
51+
52+
x >>= 1u8;
53+
assert_eq!(x, Int(2));
54+
55+
x >>= 1u16;
56+
assert_eq!(x, Int(1));
57+
58+
x -= Int(1);
59+
assert_eq!(x, Int(0));
60+
61+
// indexing
62+
let mut v = vec![Int(1), Int(2)];
63+
v[0] += Int(2);
64+
assert_eq!(v[0], Int(3));
65+
}
66+
67+
impl AddAssign for Int {
68+
fn add_assign(&mut self, rhs: &Int) {
69+
self.0 += rhs.0;
70+
}
71+
}
72+
73+
impl BitAndAssign for Int {
74+
fn bitand_assign(&mut self, rhs: &Int) {
75+
self.0 &= rhs.0;
76+
}
77+
}
78+
79+
impl BitOrAssign for Int {
80+
fn bitor_assign(&mut self, rhs: &Int) {
81+
self.0 |= rhs.0;
82+
}
83+
}
84+
85+
impl BitXorAssign for Int {
86+
fn bitxor_assign(&mut self, rhs: &Int) {
87+
self.0 ^= rhs.0;
88+
}
89+
}
90+
91+
impl DivAssign for Int {
92+
fn div_assign(&mut self, rhs: &Int) {
93+
self.0 /= rhs.0;
94+
}
95+
}
96+
97+
impl MulAssign for Int {
98+
fn mul_assign(&mut self, rhs: &Int) {
99+
self.0 *= rhs.0;
100+
}
101+
}
102+
103+
impl RemAssign for Int {
104+
fn rem_assign(&mut self, rhs: &Int) {
105+
self.0 %= rhs.0;
106+
}
107+
}
108+
109+
impl ShlAssign<u8> for Int {
110+
fn shl_assign(&mut self, &rhs: &u8) {
111+
self.0 <<= rhs;
112+
}
113+
}
114+
115+
impl ShlAssign<u16> for Int {
116+
fn shl_assign(&mut self, &rhs: &u16) {
117+
self.0 <<= rhs;
118+
}
119+
}
120+
121+
impl ShrAssign<u8> for Int {
122+
fn shr_assign(&mut self, &rhs: &u8) {
123+
self.0 >>= rhs;
124+
}
125+
}
126+
127+
impl ShrAssign<u16> for Int {
128+
fn shr_assign(&mut self, &rhs: &u16) {
129+
self.0 >>= rhs;
130+
}
131+
}
132+
133+
impl SubAssign for Int {
134+
fn sub_assign(&mut self, rhs: &Int) {
135+
self.0 -= rhs.0;
136+
}
137+
}

0 commit comments

Comments
 (0)
Please sign in to comment.