diff --git a/src/libcollections/btree/map.rs b/src/libcollections/btree/map.rs index aedfbe546de65..8ca0d25bc0ad4 100644 --- a/src/libcollections/btree/map.rs +++ b/src/libcollections/btree/map.rs @@ -22,6 +22,8 @@ use core::fmt::Debug; use core::hash::{Hash, Hasher}; use core::iter::{Map, FromIterator}; use core::ops::Index; +#[cfg(not(stage0))] +use core::ops::{IndexAssign, IndexMut}; use core::{fmt, mem, usize}; use Bound::{self, Included, Excluded, Unbounded}; @@ -949,6 +951,28 @@ impl<'a, K: Ord, Q: ?Sized, V> Index<&'a Q> for BTreeMap } } +#[cfg(not(stage0))] +// TODO(japaric) update issue number +#[unstable(feature = "index_assign_trait", reason="recently added", issue="0")] +impl<'a, K: Ord, Q: ?Sized, V> IndexMut<&'a Q> for BTreeMap + where K: Borrow, Q: Ord +{ + #[inline] + fn index_mut(&mut self, key: &Q) -> &mut V { + self.get_mut(key).expect("no entry found for key") + } +} + +#[cfg(not(stage0))] +// TODO(japaric) update issue number +#[unstable(feature = "index_assign_trait", reason="recently added", issue="0")] +impl<'a, K: Ord, V> IndexAssign for BTreeMap { + #[inline] + fn index_assign(&mut self, key: K, value: V) { + self.insert(key, value); + } +} + /// Genericises over how to get the correct type of iterator from the correct type /// of Node ownership. trait Traverse { diff --git a/src/libcollections/lib.rs b/src/libcollections/lib.rs index fe06e3fb2093e..82b327d5c0444 100644 --- a/src/libcollections/lib.rs +++ b/src/libcollections/lib.rs @@ -59,6 +59,7 @@ #![feature(decode_utf16)] #![feature(utf8_error)] #![cfg_attr(test, feature(rand, test))] +#![cfg_attr(not(stage0), feature(index_assign_trait))] #![feature(no_std)] #![no_std] diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index 350ade22707ca..9f4450da440c1 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -1539,6 +1539,44 @@ pub trait IndexMut: Index { fn index_mut(&mut self, index: Idx) -> &mut Self::Output; } +/// The `IndexAssign` trait is used to specify the functionality of indexed assignment +/// operations like `arr[idx] = rhs`. +/// +/// # Examples +/// +/// A trivial implementation of `IndexAssign`. When `Foo[Bar] = Baz` happens, it ends up +/// calling `index_assign`, and therefore, `main` prints `Indexed assignment!`. +/// +/// ``` +/// #![feature(indexed_assignments)] +/// #![feature(index_assign_trait)] +/// +/// use std::ops::{IndexAssign}; +/// +/// #[derive(Copy, Clone)] +/// struct Foo; +/// struct Bar; +/// struct Baz; +/// +/// impl IndexAssign for Foo { +/// fn index_assign(&mut self, _index: Bar, _rhs: Baz) { +/// println!("Indexed assignment!"); +/// } +/// } +/// +/// fn main() { +/// Foo[Bar] = Baz; +/// } +/// ``` +#[cfg(not(stage0))] +#[lang = "index_assign"] +// TODO(japaric) update issue number +#[unstable(feature = "index_assign_trait", reason = "recently added", issue = "0")] +pub trait IndexAssign { + /// The method for the indexed assignment (`Foo[Bar] = Baz`) operation + fn index_assign(&mut self, index: Idx, rhs: Rhs); +} + /// An unbounded range. #[derive(Copy, Clone, PartialEq, Eq)] #[lang = "range_full"] diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 297084940486e..3e3a4ee7fc0b3 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -517,8 +517,16 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> { } hir::ExprAssign(ref lhs, ref rhs) => { - self.mutate_expr(expr, &**lhs, JustWrite); - self.consume_expr(&**rhs); + if let hir::ExprIndex(ref base, ref idx) = lhs.node { + if !self.walk_overloaded_operator(expr, base, vec![idx, rhs], + PassArgs::ByValue) { + self.mutate_expr(expr, &**lhs, JustWrite); + self.consume_expr(&**rhs); + } + } else { + self.mutate_expr(expr, &**lhs, JustWrite); + self.consume_expr(&**rhs); + } } hir::ExprCast(ref base, _) => { diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index b7572d43fbe25..27100089bf541 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -298,6 +298,7 @@ lets_do_this! { ShrAssignTraitLangItem, "shr_assign", shr_assign_trait; IndexTraitLangItem, "index", index_trait; IndexMutTraitLangItem, "index_mut", index_mut_trait; + IndexAssignTraitLangItem, "index_assign", index_assign_trait; RangeStructLangItem, "range", range_struct; RangeFromStructLangItem, "range_from", range_from_struct; RangeToStructLangItem, "range_to", range_to_struct; diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs index 124f50d660329..9462e355a061d 100644 --- a/src/librustc_trans/trans/callee.rs +++ b/src/librustc_trans/trans/callee.rs @@ -868,6 +868,8 @@ pub enum CallArgs<'a, 'tcx> { // arguments should be auto-referenced ArgOverloadedOp(Datum<'tcx, Expr>, Option<(Datum<'tcx, Expr>, ast::NodeId)>, bool), + ArgIndexedAssignment(Vec>), + // Supply value of arguments as a list of expressions that must be // translated, for overloaded call operators. ArgOverloadedCall(Vec<&'a hir::Expr>), @@ -1055,6 +1057,15 @@ pub fn trans_args<'a, 'blk, 'tcx>(cx: Block<'blk, 'tcx>, assert_eq!(arg_tys.len(), 1); } } + ArgIndexedAssignment(args) => { + assert!(!variadic); + assert_eq!(arg_tys.len(), 3); + + for (i, arg) in args.into_iter().enumerate() { + bcx = trans_arg_datum(bcx, arg_tys[i], arg, arg_cleanup_scope, DontAutorefArg, + llargs); + } + } ArgVals(vs) => { llargs.push_all(vs); } diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index b4252d4b8b0e8..fcd5f8ecda67b 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -999,6 +999,32 @@ fn trans_rvalue_stmt_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, hir::ExprLoop(ref body, _) => { controlflow::trans_loop(bcx, expr, &**body) } + // indexed assignments `a[b] = c` + hir::ExprAssign(ref lhs, ref rhs) if bcx.tcx().is_method_call(expr.id) => { + if let hir::ExprIndex(ref base, ref idx) = lhs.node { + let rhs = unpack_datum!(bcx, trans(bcx, &**rhs)); + + // NOTE `lhs` is not an lvalue in this case, but we still need to create a cleanup + // scope that covers `base` and `idx` + let cleanup_debug_loc = debuginfo::get_cleanup_debug_loc_for_ast_node(bcx.ccx(), + lhs.id, + lhs.span, + false); + + bcx.fcx.push_ast_cleanup_scope(cleanup_debug_loc); + + let base = unpack_datum!(bcx, trans(bcx, &**base)); + let idx = unpack_datum!(bcx, trans(bcx, &**idx)); + + bcx.fcx.pop_and_trans_ast_cleanup_scope(bcx, lhs.id); + + trans_indexed_assignment(bcx, expr, MethodCall::expr(expr.id), base, idx, rhs).bcx + } else { + bcx.tcx().sess.span_bug( + expr.span, + "the only overloadable assignments are indexed assignments"); + } + }, hir::ExprAssign(ref dst, ref src) => { let src_datum = unpack_datum!(bcx, trans(bcx, &**src)); let dst_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, &**dst, "assign")); @@ -2031,6 +2057,25 @@ fn trans_overloaded_op<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, dest) } +fn trans_indexed_assignment<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + expr: &hir::Expr, + method_call: MethodCall, + base: Datum<'tcx, Expr>, + idx: Datum<'tcx, Expr>, + rhs: Datum<'tcx, Expr>) + -> Result<'blk, 'tcx> { + callee::trans_call_inner(bcx, + expr.debug_loc(), + |bcx, arg_cleanup_scope| { + meth::trans_method_callee(bcx, + method_call, + None, + arg_cleanup_scope) + }, + callee::ArgIndexedAssignment(vec![base, idx, rhs]), + None) +} + fn trans_overloaded_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, expr: &hir::Expr, callee: &'a hir::Expr, @@ -2676,8 +2721,8 @@ fn expr_kind(tcx: &ty::ctxt, expr: &hir::Expr) -> ExprKind { // Overloaded operations are generally calls, and hence they are // generated via DPS, but there are a few exceptions: return match expr.node { - // `a += b` has a unit result. - hir::ExprAssignOp(..) => ExprKind::RvalueStmt, + // `a += b` and `a[b] = c` have a unit result. + hir::ExprAssignOp(..) | hir::ExprAssign(..) => ExprKind::RvalueStmt, // the deref method invoked for `*a` always yields an `&T` hir::ExprUnary(hir::UnDeref, _) => ExprKind::Lvalue, diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index abdcbf099a57a..64225b34e5ac8 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -574,7 +574,8 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { autoderefs, unsize, PreferMutLvalue, - index_expr_ty); + index_expr_ty, + false); if let Some((input_ty, return_ty)) = result { demand::suptype(self.fcx, index_expr.span, input_ty, index_expr_ty); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index fd03c11b4740e..a9d9780718537 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2247,7 +2247,8 @@ fn lookup_indexing<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, base_expr: &'tcx hir::Expr, base_ty: Ty<'tcx>, idx_ty: Ty<'tcx>, - lvalue_pref: LvaluePreference) + lvalue_pref: LvaluePreference, + is_assignment: bool) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> { // FIXME(#18741) -- this is almost but not quite the same as the @@ -2262,7 +2263,7 @@ fn lookup_indexing<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, lvalue_pref, |adj_ty, idx| { try_index_step(fcx, MethodCall::expr(expr.id), expr, base_expr, - adj_ty, idx, false, lvalue_pref, idx_ty) + adj_ty, idx, false, lvalue_pref, idx_ty, is_assignment) }); if final_mt.is_some() { @@ -2274,7 +2275,7 @@ fn lookup_indexing<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, if let ty::TyArray(element_ty, _) = ty.sty { let adjusted_ty = fcx.tcx().mk_slice(element_ty); try_index_step(fcx, MethodCall::expr(expr.id), expr, base_expr, - adjusted_ty, autoderefs, true, lvalue_pref, idx_ty) + adjusted_ty, autoderefs, true, lvalue_pref, idx_ty, is_assignment) } else { None } @@ -2292,21 +2293,45 @@ fn try_index_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, autoderefs: usize, unsize: bool, lvalue_pref: LvaluePreference, - index_ty: Ty<'tcx>) + index_ty: Ty<'tcx>, + is_assignment: bool) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> { let tcx = fcx.tcx(); debug!("try_index_step(expr={:?}, base_expr.id={:?}, adjusted_ty={:?}, \ - autoderefs={}, unsize={}, index_ty={:?})", + autoderefs={}, unsize={}, index_ty={:?}, is_assignment={})", expr, base_expr, adjusted_ty, autoderefs, unsize, - index_ty); + index_ty, + is_assignment); let input_ty = fcx.infcx().next_ty_var(); + // Try `IndexAssign` if this is an assignment operation + if is_assignment { + return tcx.lang_items.index_assign_trait().and_then(|trait_did| { + let rhs_ty = fcx.infcx().next_ty_var(); + + method::lookup_in_trait_adjusted(fcx, + expr.span, + Some(&*base_expr), + token::intern("index_assign"), + trait_did, + autoderefs, + unsize, + adjusted_ty, + Some(vec![input_ty, rhs_ty])) + .map(|method| { + fcx.inh.tables.borrow_mut().method_map.insert(method_call, method); + + (input_ty, rhs_ty) + }) + }) + } + // First, try built-in indexing. match (adjusted_ty.builtin_index(), &index_ty.sty) { (Some(ty), &ty::TyUint(ast::TyUs)) | (Some(ty), &ty::TyInfer(ty::IntVar(_))) => { @@ -3445,25 +3470,56 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, fcx.write_ty(id, fcx.infcx().next_diverging_ty_var()); } hir::ExprAssign(ref lhs, ref rhs) => { - check_expr_with_lvalue_pref(fcx, &**lhs, PreferMutLvalue); + if let hir::ExprIndex(ref base, ref idx) = lhs.node { + let lvalue_pref = PreferMutLvalue; + + check_expr_with_lvalue_pref(fcx, &**base, lvalue_pref); + check_expr(fcx, &**idx); + + let base_t = structurally_resolved_type(fcx, lhs.span, fcx.expr_ty(&**base)); + let idx_t = fcx.expr_ty(&**idx); + + lookup_indexing(fcx, expr, base, base_t, idx_t, lvalue_pref, true) + .map(|(index_ty, rhs_ty)| { + demand::eqtype(fcx, idx.span, index_ty, idx_t); + check_expr_coercable_to_type(fcx, &**rhs, rhs_ty); + fcx.write_nil(lhs.id); + fcx.write_nil(id); + + if !tcx.sess.features.borrow().indexed_assignments { + tcx.sess.span_err( + expr.span, + "overloaded indexed assignments are not stable"); + fileline_help!( + tcx.sess, + expr.span, + "add `#![feature(indexed_assignments)]` to the crate features to \ + enable"); + } + }) + } else { + None + }.unwrap_or_else(|| { + check_expr_with_lvalue_pref(fcx, &**lhs, PreferMutLvalue); - let tcx = fcx.tcx(); - if !tcx.expr_is_lval(&**lhs) { - span_err!(tcx.sess, expr.span, E0070, - "invalid left-hand side expression"); - } + let tcx = fcx.tcx(); + if !tcx.expr_is_lval(&**lhs) { + span_err!(tcx.sess, expr.span, E0070, + "invalid left-hand side expression"); + } - let lhs_ty = fcx.expr_ty(&**lhs); - check_expr_coercable_to_type(fcx, &**rhs, lhs_ty); - let rhs_ty = fcx.expr_ty(&**rhs); + let lhs_ty = fcx.expr_ty(&**lhs); + check_expr_coercable_to_type(fcx, &**rhs, lhs_ty); + let rhs_ty = fcx.expr_ty(&**rhs); - fcx.require_expr_have_sized_type(&**lhs, traits::AssignmentLhsSized); + fcx.require_expr_have_sized_type(&**lhs, traits::AssignmentLhsSized); - if lhs_ty.references_error() || rhs_ty.references_error() { - fcx.write_error(id); - } else { - fcx.write_nil(id); - } + if lhs_ty.references_error() || rhs_ty.references_error() { + fcx.write_error(id); + } else { + fcx.write_nil(id); + } + }) } hir::ExprIf(ref cond, ref then_blk, ref opt_else_expr) => { check_then_else(fcx, &**cond, &**then_blk, opt_else_expr.as_ref().map(|e| &**e), @@ -3666,7 +3722,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, fcx.write_ty(id, idx_t); } else { let base_t = structurally_resolved_type(fcx, expr.span, base_t); - match lookup_indexing(fcx, expr, base, base_t, idx_t, lvalue_pref) { + match lookup_indexing(fcx, expr, base, base_t, idx_t, lvalue_pref, false) { Some((index_ty, element_ty)) => { let idx_expr_ty = fcx.expr_ty(idx); demand::eqtype(fcx, expr.span, index_ty, idx_expr_ty); diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index b4a99dcb576ca..8321329ef7736 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -692,6 +692,23 @@ fn visit_expr(rcx: &mut Rcx, expr: &hir::Expr) { visit::walk_expr(rcx, expr); } + // indexed assignment `a[b! = c]` + hir::ExprAssign(ref lhs, ref rhs) if has_method_map => { + if let hir::ExprIndex(ref base, ref idx) = lhs.node { + constrain_call(rcx, expr, Some(&**base), [idx, rhs].iter().map(|&e| &**e), false); + + // NOTE we don't walk `lhs` here because for indexed assignments `a[b] is not an + // lvalue` + rcx.visit_expr(rhs); + rcx.visit_expr(base); + rcx.visit_expr(idx); + } else { + rcx.tcx().sess.span_bug( + expr.span, + "the only overloadable assignments are indexed assignments"); + } + } + hir::ExprAssignOp(_, ref lhs, ref rhs) => { if has_method_map { constrain_call(rcx, expr, Some(&**lhs), diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 4ad8fce8120aa..b194893257ac4 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -22,6 +22,8 @@ use iter::{self, Iterator, ExactSizeIterator, IntoIterator, FromIterator, Extend use marker::Sized; use mem::{self, replace}; use ops::{Deref, FnMut, FnOnce, Index}; +#[cfg(not(stage0))] +use ops::{IndexAssign, IndexMut}; use option::Option::{self, Some, None}; use rand::{self, Rng}; use result::Result; @@ -1255,6 +1257,33 @@ impl<'a, K, Q: ?Sized, V, S> Index<&'a Q> for HashMap } } +#[cfg(not(stage0))] +// TODO(japaric) update issue number +#[unstable(feature = "index_assign_trait", reason = "recently added", issue="0")] +impl<'a, K, Q: ?Sized, V, S> IndexMut<&'a Q> for HashMap + where K: Eq + Hash + Borrow, + Q: Eq + Hash, + S: HashState, +{ + #[inline] + fn index_mut(&mut self, index: &Q) -> &mut V { + self.get_mut(index).expect("no entry found for key") + } +} + +#[cfg(not(stage0))] +// TODO(japaric) update issue number +#[unstable(feature = "index_assign_trait", reason = "recently added", issue="0")] +impl IndexAssign for HashMap + where K: Eq + Hash, + S: HashState, +{ + #[inline] + fn index_assign(&mut self, key: K, value: V) { + self.insert(key, value); + } +} + /// HashMap iterator. #[stable(feature = "rust1", since = "1.0.0")] pub struct Iter<'a, K: 'a, V: 'a> { diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index c67a4182f54cb..a9772db493339 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -251,6 +251,7 @@ #![cfg_attr(test, feature(float_from_str_radix, range_inclusive, float_extras, hash_default))] #![cfg_attr(test, feature(test, rustc_private, float_consts))] #![cfg_attr(target_env = "msvc", feature(link_args))] +#![cfg_attr(not(stage0), feature(index_assign_trait))] // Don't link to std. We are std. #![no_std] diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index be95b58bf881e..c5b092a57773d 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -203,6 +203,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option, Status // allow `#[omit_gdb_pretty_printer_section]` ("omit_gdb_pretty_printer_section", "1.5.0", None, Active), + + // allow overloading indexed assignment operations like `a[b] = c` + ("indexed_assignments", "1.5.0", None, Active), ]; // (changing above list without updating src/doc/reference.md makes @cmr sad) @@ -472,6 +475,7 @@ pub struct Features { pub type_macros: bool, pub cfg_target_feature: bool, pub augmented_assignments: bool, + pub indexed_assignments: bool, } impl Features { @@ -501,6 +505,7 @@ impl Features { type_macros: false, cfg_target_feature: false, augmented_assignments: false, + indexed_assignments: false, } } } @@ -1070,6 +1075,7 @@ fn check_crate_inner(cm: &CodeMap, span_handler: &SpanHandler, type_macros: cx.has_feature("type_macros"), cfg_target_feature: cx.has_feature("cfg_target_feature"), augmented_assignments: cx.has_feature("augmented_assignments"), + indexed_assignments: cx.has_feature("indexed_assignments"), } } diff --git a/src/test/auxiliary/indexed_assignment.rs b/src/test/auxiliary/indexed_assignment.rs new file mode 100644 index 0000000000000..abc317e091dd4 --- /dev/null +++ b/src/test/auxiliary/indexed_assignment.rs @@ -0,0 +1,21 @@ +// 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(index_assign_trait)] + +use std::ops::IndexAssign; + +pub struct Array; + +impl IndexAssign<(), ()> for Array { + fn index_assign(&mut self, _: (), _: ()) { + unimplemented!() + } +} diff --git a/src/test/compile-fail/indexed-assignment-feature-gate-cross.rs b/src/test/compile-fail/indexed-assignment-feature-gate-cross.rs new file mode 100644 index 0000000000000..7c61755d5c725 --- /dev/null +++ b/src/test/compile-fail/indexed-assignment-feature-gate-cross.rs @@ -0,0 +1,21 @@ +// 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. + +// aux-build:indexed_assignment.rs + +extern crate indexed_assignment; + +use indexed_assignment::Array; + +fn main() { + let mut array = Array; + array[()] = (); + //~^ error: overloaded indexed assignments are not stable +} diff --git a/src/test/compile-fail/indexed-assignment-feature-gate.rs b/src/test/compile-fail/indexed-assignment-feature-gate.rs new file mode 100644 index 0000000000000..246ae4d7ba265 --- /dev/null +++ b/src/test/compile-fail/indexed-assignment-feature-gate.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. + +#![feature(index_assign_trait)] + +use std::ops::IndexAssign; + +struct Array; + +impl IndexAssign<(), ()> for Array { + fn index_assign(&mut self, _: (), _: ()) { + unimplemented!() + } +} + +fn main() { + let mut array = Array; + array[()] = (); + //~^ error: overloaded indexed assignments are not stable + //~| help: add `#![feature(indexed_assignments)]` to the crate features to enable +//error: overloaded indexed assignemnts are not stable +} diff --git a/src/test/compile-fail/indexed-assignment-trait.rs b/src/test/compile-fail/indexed-assignment-trait.rs new file mode 100644 index 0000000000000..454abf0edf506 --- /dev/null +++ b/src/test/compile-fail/indexed-assignment-trait.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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] + +use std::ops::IndexAssign; +//~^ error: use of unstable library feature 'index_assign_trait' + +struct Array; + +impl IndexAssign<(), ()> for Array { + //~^ error: use of unstable library feature 'index_assign_trait' + fn index_assign(&mut self, _: (), _: ()) { + //~^ error: use of unstable library feature 'index_assign_trait' + unimplemented!() + } +} + +fn main() {} diff --git a/src/test/compile-fail/indexed-assignment.rs b/src/test/compile-fail/indexed-assignment.rs new file mode 100644 index 0000000000000..9ae931687b784 --- /dev/null +++ b/src/test/compile-fail/indexed-assignment.rs @@ -0,0 +1,46 @@ +// 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. + +// Test the move/borrow semantics of indexed assignments + +#![feature(index_assign_trait)] +#![feature(indexed_assignments)] + +use std::ops::IndexAssign; + +struct Array; +struct Foo; + +impl IndexAssign for Array { + fn index_assign(&mut self, _: Foo, _: Foo) { + unimplemented!() + } +} + +fn lhs_not_mutable() { + let ref array = Array; + array[Foo] = Foo; + //~^ error: cannot borrow immutable borrowed content `*array` as mutable + + let array = Array; + array[Foo] = Foo; + //~^ error: cannot borrow immutable local variable `array` as mutable +} + +fn double_move() { + let foo = Foo; + let mut array = Array; + + array[ + foo //~ error: use of moved value: `foo` + ] = foo; //~ note: `foo` moved here +} + +fn main() {} diff --git a/src/test/run-pass/indexed-assignment-for-maps.rs b/src/test/run-pass/indexed-assignment-for-maps.rs new file mode 100644 index 0000000000000..5555be48a765d --- /dev/null +++ b/src/test/run-pass/indexed-assignment-for-maps.rs @@ -0,0 +1,42 @@ +// 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. + +// Test all the kinds of indexing on hash maps + +#![feature(indexed_assignments)] + +use std::collections::HashMap; + +fn main() { + let mut map: HashMap> = HashMap::new(); + + // insertion via IndexAssign + map[0] = vec![]; + + // get immutable reference via Index + assert_eq!(map[&0], []); + + // mutate element via IndexMut + map[&0].push(1); + + assert_eq!(map[&0], [1]); + + // get mutable reference via IndexMut + for x in &mut map[&0] { + *x = 0; + } + + assert_eq!(map[&0], [0]); + + // update element via IndexMut + *&mut map[&0] = vec![1, 2, 3]; + + assert_eq!(map[&0], [1, 2, 3]); +} diff --git a/src/test/run-pass/indexed-assignment-priority.rs b/src/test/run-pass/indexed-assignment-priority.rs new file mode 100644 index 0000000000000..bc8b5b47cf379 --- /dev/null +++ b/src/test/run-pass/indexed-assignment-priority.rs @@ -0,0 +1,64 @@ +// 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. + +// Test that if `A` implements both `IndexMut` and `IndexAssign`, then the +// expression `a[b] = c` will be evaluated using the `IndexAssign` trait. + +#![feature(index_assign_trait)] +#![feature(indexed_assignments)] + +use std::ops::{Index, IndexAssign, IndexMut}; + +struct Bar(()); + +impl Index<()> for Bar { + type Output = (); + + fn index(&self, _: ()) -> &() { + &self.0 + } +} + +impl IndexMut<()> for Bar { + fn index_mut(&mut self, _: ()) -> &mut () { + &mut self.0 + } +} + +struct Foo(()); + +impl Index<()> for Foo { + type Output = (); + + fn index(&self, _: ()) -> &() { + &self.0 + } +} + +impl IndexMut<()> for Foo { + fn index_mut(&mut self, _: ()) -> &mut () { + unreachable!(); + } +} + +impl IndexAssign<(), ()> for Foo { + fn index_assign(&mut self, _: (), _: ()) { + } +} + +fn main() { + // IndexMut + let mut bar = Bar(()); + bar[()] = (); + + // IndexAssign + let mut foo = Foo(()); + foo[()] = (); +} diff --git a/src/test/run-pass/indexed-assignment.rs b/src/test/run-pass/indexed-assignment.rs new file mode 100644 index 0000000000000..11642dd3676cd --- /dev/null +++ b/src/test/run-pass/indexed-assignment.rs @@ -0,0 +1,153 @@ +// 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(index_assign_trait)] +#![feature(indexed_assignments)] + +use std::collections::HashMap; +use std::mem; +use std::ops::{Deref, DerefMut, Index, IndexAssign, Range, RangeFull}; + +fn main() { + // test insertion via IndexAssign (no overloading) + let mut map = Map::new(); + + map[0] = 1; + assert_eq!(map[0], 1); + + map[1] = 2; + assert_eq!(map[1], 2); + + map[0] = 3; + assert_eq!(map[0], 3); + + // test overloading the index + let mut array = [0, 1, 2, 3]; + + { + let slice = Slice::new(&mut array); + slice[..] = 0; + } + + assert_eq!(array, [0, 0, 0, 0]); + + { + let slice = Slice::new(&mut array); + let rhs: &[_] = &[1, 2]; + slice[1..3] = rhs; + } + + assert_eq!(array, [0, 1, 2, 0]); + + // test overloading the RHS + { + let slice = Slice::new(&mut array); + slice[1..3] = 0; + } + + assert_eq!(array, [0, 0, 0, 0]); + + // test through deref + let mut v = Vector(vec![0, 0, 0, 0]); + + v[..] = 1; + + assert_eq!(v.0, [1, 1, 1, 1]); + + let rhs: &[_] = &[2, 3]; + v[1..3] = rhs; + + assert_eq!(v.0, [1, 2, 3, 1]); + + // test through proxy + Slice::new(&mut array)[..] = 1; + + assert_eq!(array, [1, 1, 1, 1]); +} + +struct Map(HashMap); + +impl Map { + fn new() -> Map { + Map(HashMap::new()) + } +} + +impl Index for Map { + type Output = i32; + + fn index(&self, idx: u32) -> &i32 { + &self.0[&idx] + } +} + +impl IndexAssign for Map { + fn index_assign(&mut self, idx: u32, rhs: i32) { + self.0.insert(idx, rhs); + } +} + +struct Vector(Vec); + +impl Deref for Vector { + type Target = Slice; + + fn deref(&self) -> &Slice { + unsafe { + mem::transmute(self.0.deref()) + } + } +} + +impl DerefMut for Vector { + fn deref_mut(&mut self) -> &mut Slice { + unsafe { + mem::transmute(self.0.deref_mut()) + } + } +} + +struct Slice([i32]); + +impl Slice { + fn new(xs: &mut [i32]) -> &mut Slice { + unsafe { + mem::transmute(xs) + } + } +} + +impl IndexAssign for Slice { + fn index_assign(&mut self, _: RangeFull, rhs: i32) { + for lhs in &mut self.0 { + *lhs = rhs.clone() + } + } +} + +impl<'a> IndexAssign, &'a [i32]> for Slice { + fn index_assign(&mut self, r: Range, rhs: &[i32]) { + let lhs = &mut self.0[r]; + + assert_eq!(lhs.len(), rhs.len()); + + for (lhs, rhs) in lhs.iter_mut().zip(rhs) { + *lhs = rhs.clone(); + } + } +} + +impl<'a> IndexAssign, i32> for Slice { + fn index_assign(&mut self, r: Range, rhs: i32) { + for lhs in &mut self.0[r] { + *lhs = rhs.clone(); + } + } +}