From c008919cc1304b3d6f884dee59dad4fe16407ab1 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 7 Mar 2015 21:55:30 -0500 Subject: [PATCH] add lang items, feature gate, tests; update typeck/trans --- src/librustc/middle/expr_use_visitor.rs | 11 +- src/librustc/middle/lang_items.rs | 11 ++ src/librustc_trans/trans/expr.rs | 15 +- src/librustc_typeck/check/mod.rs | 69 ++++++--- src/libsyntax/feature_gate.rs | 6 + .../assignment-operator-unimplemented.rs | 4 +- .../compile-fail/feature-gate-op-assign.rs | 29 ++++ src/test/compile-fail/issue-10401.rs | 4 +- src/test/compile-fail/issue-5239-1.rs | 4 +- src/test/compile-fail/issue-6738.rs | 4 +- src/test/compile-fail/op-assign-borrow.rs | 33 +++++ src/test/run-pass/op-assign.rs | 137 ++++++++++++++++++ 12 files changed, 299 insertions(+), 28 deletions(-) create mode 100644 src/test/compile-fail/feature-gate-op-assign.rs create mode 100644 src/test/compile-fail/op-assign-borrow.rs create mode 100644 src/test/run-pass/op-assign.rs diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 6d2392054f9d5..d4a3e6c561ea1 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -584,10 +584,15 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { } ast::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); + + if self.typer.is_method_call(expr.id) { + let r = ty::ReScope(region::CodeExtent::from_node_id(expr.id)); + self.borrow_expr(rhs, r, ty::ImmBorrow, OverloadedOperator); + } else { + // built-in assignment operations consume the RHS + self.consume_expr(&**rhs); + } } ast::ExprRepeat(ref base, ref count) => { diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 73d31a1f6201d..bd78e0b61e5fa 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -283,6 +283,17 @@ lets_do_this! { RangeToStructLangItem, "range_to", range_to_struct; RangeFullStructLangItem, "range_full", range_full_struct; + AddAssignTraitLangItem, "add_assign", add_assign_trait; + BitAndAssignTraitLangItem, "bitand_assign", bitand_assign_trait; + BitOrAssignTraitLangItem, "bitor_assign", bitor_assign_trait; + BitXorAssignTraitLangItem, "bitxor_assign", bitxor_assign_trait; + DivAssignTraitLangItem, "div_assign", div_assign_trait; + MulAssignTraitLangItem, "mul_assign", mul_assign_trait; + RemAssignTraitLangItem, "rem_assign", rem_assign_trait; + ShlAssignTraitLangItem, "shl_assign", shl_assign_trait; + ShrAssignTraitLangItem, "shr_assign", shr_assign_trait; + SubAssignTraitLangItem, "sub_assign", sub_assign_trait; + UnsafeCellTypeLangItem, "unsafe_cell", unsafe_cell_type; DerefTraitLangItem, "deref", deref_trait; diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index c316308c618bc..23212dcab0333 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -1041,7 +1041,15 @@ fn trans_rvalue_stmt_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, } } ast::ExprAssignOp(op, ref dst, ref src) => { - trans_assign_op(bcx, expr, op, &**dst, &**src) + if ty::is_binopable(bcx.tcx(), node_id_type(bcx, dst.id), op) { + trans_assign_op(bcx, expr, op, &**dst, &**src) + } else { + 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, + vec![(src_datum, src.id)], None, + true).bcx + } } ast::ExprInlineAsm(ref a) => { asm::trans_inline_asm(bcx, a) @@ -1239,8 +1247,9 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, "expr_cast of non-trait"); } } - ast::ExprAssignOp(op, ref dst, ref src) => { - trans_assign_op(bcx, expr, op, &**dst, &**src) + ast::ExprAssignOp(..) => { + bcx.tcx().sess.span_bug(expr.span, + "operator assignment (`+=`) should always be an rvalue_stmt"); } _ => { bcx.tcx().sess.span_bug( diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 45d4a1edc6b24..0e72f3086f50c 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2992,19 +2992,20 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, let result_t = if is_binop_assignment == SimpleBinop { check_user_binop(fcx, expr, lhs, lhs_t, op, rhs) } else { - fcx.type_error_message(expr.span, - |actual| { - format!("binary assignment \ - operation `{}=` \ - cannot be applied to \ - type `{}`", - ast_util::binop_to_string(op.node), - actual) - }, - lhs_t, - None); - check_expr(fcx, &**rhs); - fcx.tcx().types.err + if fcx.tcx().sess.features.borrow().op_assign { + check_user_binop_assign(fcx, expr, lhs, lhs_t, op, rhs) + } else { + fcx.tcx().sess.span_err( + expr.span, + "overloaded augmented assignment is not stable", + ); + fileline_help!( + fcx.tcx().sess, + expr.span, + "add `#![feature(op_assign)]` to the crate attributes to enable"); + + fcx.tcx().types.err + } }; fcx.write_ty(expr.id, result_t); @@ -3053,6 +3054,42 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, }, if ast_util::is_by_value_binop(op.node) { AutorefArgs::No } else { AutorefArgs::Yes }) } + fn check_user_binop_assign<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, + ex: &'tcx ast::Expr, + lhs_expr: &'tcx ast::Expr, + lhs_resolved_t: Ty<'tcx>, + op: ast::BinOp, + rhs: &'tcx P) -> Ty<'tcx> { + let tcx = fcx.ccx.tcx; + let lang = &tcx.lang_items; + let (name, trait_did) = match op.node { + ast::BiAdd => ("add_assign", lang.add_assign_trait()), + ast::BiSub => ("sub_assign", lang.sub_assign_trait()), + ast::BiMul => ("mul_assign", lang.mul_assign_trait()), + ast::BiDiv => ("div_assign", lang.div_assign_trait()), + ast::BiRem => ("rem_assign", lang.rem_assign_trait()), + ast::BiBitXor => ("bitxor_assign", lang.bitxor_assign_trait()), + ast::BiBitAnd => ("bitand_assign", lang.bitand_assign_trait()), + ast::BiBitOr => ("bitor_assign", lang.bitor_assign_trait()), + ast::BiShl => ("shl_assign", lang.shl_assign_trait()), + ast::BiShr => ("shr_assign", lang.shr_assign_trait()), + ast::BiLt | ast::BiLe | ast::BiGe | ast::BiGt | ast::BiEq | ast::BiNe | ast::BiAnd | + ast::BiOr => + { + check_expr(fcx, &**rhs); + return tcx.types.err; + } + }; + lookup_op_method(fcx, ex, lhs_resolved_t, token::intern(name), + trait_did, lhs_expr, Some(rhs), || { + fcx.type_error_message(ex.span, |actual| { + format!("binary operation `{}=` cannot be applied to type `{}`", + ast_util::binop_to_string(op.node), + actual) + }, lhs_resolved_t, None) + }, AutorefArgs::Yes) + } + fn check_user_unop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, op_str: &str, mname: &str, @@ -3484,10 +3521,6 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, ast::ExprAssignOp(op, ref lhs, ref rhs) => { check_binop(fcx, expr, op, &**lhs, rhs, BinopAssignment); - let lhs_t = fcx.expr_ty(&**lhs); - let result_t = fcx.expr_ty(expr); - demand::suptype(fcx, expr.span, result_t, lhs_t); - let tcx = fcx.tcx(); if !ty::expr_is_lval(tcx, &**lhs) { span_err!(tcx.sess, lhs.span, E0067, "illegal left-hand side expression"); @@ -3498,7 +3531,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, // Overwrite result of check_binop...this preserves existing behavior // but seems quite dubious with regard to user-defined methods // and so forth. - Niko - if !ty::type_is_error(result_t) { + if !ty::type_is_error(fcx.expr_ty(expr)) { fcx.write_nil(expr.id); } } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 0a9980c892527..8daa066102812 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -153,6 +153,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[ // below (it has to be checked before expansion possibly makes // macros disappear). ("allow_internal_unstable", "1.0.0", Active), + + // Augmented assignment `a += b`; RFC 953 + ("op_assign", "1.0.0", Active), ]; // (changing above list without updating src/doc/reference.md makes @cmr sad) @@ -317,6 +320,7 @@ pub enum AttributeType { /// A set of features to be used by later passes. pub struct Features { + pub op_assign: bool, pub unboxed_closures: bool, pub rustc_diagnostic_macros: bool, pub visible_private_types: bool, @@ -339,6 +343,7 @@ pub struct Features { impl Features { pub fn new() -> Features { Features { + op_assign: false, unboxed_closures: false, rustc_diagnostic_macros: false, visible_private_types: false, @@ -787,6 +792,7 @@ fn check_crate_inner(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::C // to a single-pass (instead of N calls to `.has_feature`). Features { + op_assign: cx.has_feature("op_assign"), unboxed_closures: cx.has_feature("unboxed_closures"), rustc_diagnostic_macros: cx.has_feature("rustc_diagnostic_macros"), visible_private_types: cx.has_feature("visible_private_types"), diff --git a/src/test/compile-fail/assignment-operator-unimplemented.rs b/src/test/compile-fail/assignment-operator-unimplemented.rs index 5b24c6bd79f96..433483e5459c3 100644 --- a/src/test/compile-fail/assignment-operator-unimplemented.rs +++ b/src/test/compile-fail/assignment-operator-unimplemented.rs @@ -8,10 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(op_assign)] + struct Foo; fn main() { let mut a = Foo; let ref b = Foo; - a += *b; //~ Error: binary assignment operation `+=` cannot be applied to type `Foo` + a += *b; //~ Error: binary operation `+=` cannot be applied to type `Foo` } diff --git a/src/test/compile-fail/feature-gate-op-assign.rs b/src/test/compile-fail/feature-gate-op-assign.rs new file mode 100644 index 0000000000000..f368198cae871 --- /dev/null +++ b/src/test/compile-fail/feature-gate-op-assign.rs @@ -0,0 +1,29 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::ops::AddAssign; + +struct MyInt(i32); + +impl AddAssign for MyInt { + fn add_assign(&mut self, rhs: &MyInt) { + self.0 += rhs.0 + } +} + +fn main() { + let mut x = MyInt(1); + x += MyInt(2); + //~^ error: overloaded augmented assignment is not stable + //~^^ help: add `#![feature(op_assign)]` to the crate attributes to enable + x -= MyInt(3); + //~^ error: overloaded augmented assignment is not stable + //~^^ help: add `#![feature(op_assign)]` to the crate attributes to enable +} diff --git a/src/test/compile-fail/issue-10401.rs b/src/test/compile-fail/issue-10401.rs index e36193aee25c0..6e109b75f9d46 100644 --- a/src/test/compile-fail/issue-10401.rs +++ b/src/test/compile-fail/issue-10401.rs @@ -8,8 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(op_assign)] + fn main() { let mut a = "a"; a += { "b" }; - //~^ ERROR: binary assignment operation `+=` cannot be applied + //~^ ERROR: binary operation `+=` cannot be applied to type `&str` } diff --git a/src/test/compile-fail/issue-5239-1.rs b/src/test/compile-fail/issue-5239-1.rs index 49a43ee37adca..208fab79e65fa 100644 --- a/src/test/compile-fail/issue-5239-1.rs +++ b/src/test/compile-fail/issue-5239-1.rs @@ -10,7 +10,9 @@ // Regression test for issue #5239 +#![feature(op_assign)] + fn main() { let x = |ref x: isize| -> isize { x += 1; }; - //~^ ERROR binary assignment operation `+=` cannot be applied to type `&isize` + //~^ ERROR binary operation `+=` cannot be applied to type `&isize` } diff --git a/src/test/compile-fail/issue-6738.rs b/src/test/compile-fail/issue-6738.rs index 447d0e061ee54..19a9f79d69d65 100644 --- a/src/test/compile-fail/issue-6738.rs +++ b/src/test/compile-fail/issue-6738.rs @@ -8,13 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(op_assign)] + struct Foo { x: T, } impl Foo { fn add(&mut self, v: Foo){ self.x += v.x; - //~^ ERROR: binary assignment operation `+=` cannot be applied + //~^ ERROR: binary operation `+=` cannot be applied to type `T` } } fn main() {} diff --git a/src/test/compile-fail/op-assign-borrow.rs b/src/test/compile-fail/op-assign-borrow.rs new file mode 100644 index 0000000000000..cc8091bd2304c --- /dev/null +++ b/src/test/compile-fail/op-assign-borrow.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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(op_assign)] + +use std::ops::AddAssign; + +struct Int(i32); + +impl AddAssign for Int { + fn add_assign(&mut self, rhs: &Int) { + self.0 += rhs.0 + } +} + +fn main() { + let mut x = Int(1); + x //~ error: cannot borrow `x` as mutable because it is also borrowed as immutable + += + x; //~ note: previous borrow of `x` occurs here + + let y = Int(2); + y //~ error: cannot borrow immutable local variable `y` as mutable + += + Int(1); +} diff --git a/src/test/run-pass/op-assign.rs b/src/test/run-pass/op-assign.rs new file mode 100644 index 0000000000000..855d530825158 --- /dev/null +++ b/src/test/run-pass/op-assign.rs @@ -0,0 +1,137 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(op_assign)] + +use std::ops::{ + AddAssign, BitAndAssign, BitOrAssign, BitXorAssign, DivAssign, MulAssign, RemAssign, ShlAssign, + ShrAssign, SubAssign, +}; + +#[derive(Debug, PartialEq)] +struct Int(i32); + +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)); + + // indexing + let mut v = vec![Int(1), Int(2)]; + v[0] += Int(2); + assert_eq!(v[0], Int(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 for Int { + fn shl_assign(&mut self, &rhs: &u8) { + self.0 <<= rhs; + } +} + +impl ShlAssign for Int { + fn shl_assign(&mut self, &rhs: &u16) { + self.0 <<= rhs; + } +} + +impl ShrAssign for Int { + fn shr_assign(&mut self, &rhs: &u8) { + self.0 >>= rhs; + } +} + +impl ShrAssign 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; + } +}