Skip to content

Commit

Permalink
add lang items, feature gate, tests; update typeck/trans
Browse files Browse the repository at this point in the history
  • Loading branch information
Jorge Aparicio committed Mar 21, 2015
1 parent 8251771 commit c008919
Show file tree
Hide file tree
Showing 12 changed files with 299 additions and 28 deletions.
11 changes: 8 additions & 3 deletions src/librustc/middle/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
11 changes: 11 additions & 0 deletions src/librustc/middle/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
15 changes: 12 additions & 3 deletions src/librustc_trans/trans/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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(
Expand Down
69 changes: 51 additions & 18 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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<ast::Expr>) -> 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,
Expand Down Expand Up @@ -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");
Expand All @@ -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);
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -787,6 +792,7 @@ fn check_crate_inner<F>(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"),
Expand Down
4 changes: 3 additions & 1 deletion src/test/compile-fail/assignment-operator-unimplemented.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`
}
29 changes: 29 additions & 0 deletions src/test/compile-fail/feature-gate-op-assign.rs
Original file line number Diff line number Diff line change
@@ -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 <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 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
}
4 changes: 3 additions & 1 deletion src/test/compile-fail/issue-10401.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`
}
4 changes: 3 additions & 1 deletion src/test/compile-fail/issue-5239-1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`
}
4 changes: 3 additions & 1 deletion src/test/compile-fail/issue-6738.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(op_assign)]

struct Foo<T> {
x: T,
}
impl<T> Foo<T> {
fn add(&mut self, v: Foo<T>){
self.x += v.x;
//~^ ERROR: binary assignment operation `+=` cannot be applied
//~^ ERROR: binary operation `+=` cannot be applied to type `T`
}
}
fn main() {}
33 changes: 33 additions & 0 deletions src/test/compile-fail/op-assign-borrow.rs
Original file line number Diff line number Diff line change
@@ -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(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);
}
Loading

0 comments on commit c008919

Please sign in to comment.