Skip to content

Commit 0767d42

Browse files
author
Jorge Aparicio
committed
Overloaded indexed assignments
1 parent 547fd5c commit 0767d42

21 files changed

+667
-27
lines changed

src/libcollections/btree/map.rs

+24
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ use core::fmt::Debug;
2222
use core::hash::{Hash, Hasher};
2323
use core::iter::{Map, FromIterator};
2424
use core::ops::Index;
25+
#[cfg(not(stage0))]
26+
use core::ops::{IndexAssign, IndexMut};
2527
use core::{fmt, mem, usize};
2628
use Bound::{self, Included, Excluded, Unbounded};
2729

@@ -949,6 +951,28 @@ impl<'a, K: Ord, Q: ?Sized, V> Index<&'a Q> for BTreeMap<K, V>
949951
}
950952
}
951953

954+
#[cfg(not(stage0))]
955+
// TODO(japaric) update issue number
956+
#[unstable(feature = "index_assign_trait", reason="recently added", issue="0")]
957+
impl<'a, K: Ord, Q: ?Sized, V> IndexMut<&'a Q> for BTreeMap<K, V>
958+
where K: Borrow<Q>, Q: Ord
959+
{
960+
#[inline]
961+
fn index_mut(&mut self, key: &Q) -> &mut V {
962+
self.get_mut(key).expect("no entry found for key")
963+
}
964+
}
965+
966+
#[cfg(not(stage0))]
967+
// TODO(japaric) update issue number
968+
#[unstable(feature = "index_assign_trait", reason="recently added", issue="0")]
969+
impl<'a, K: Ord, V> IndexAssign<K, V> for BTreeMap<K, V> {
970+
#[inline]
971+
fn index_assign(&mut self, key: K, value: V) {
972+
self.insert(key, value);
973+
}
974+
}
975+
952976
/// Genericises over how to get the correct type of iterator from the correct type
953977
/// of Node ownership.
954978
trait Traverse<N> {

src/libcollections/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
#![feature(decode_utf16)]
6060
#![feature(utf8_error)]
6161
#![cfg_attr(test, feature(rand, test))]
62+
#![cfg_attr(not(stage0), feature(index_assign_trait))]
6263

6364
#![feature(no_std)]
6465
#![no_std]

src/libcore/ops.rs

+38
Original file line numberDiff line numberDiff line change
@@ -1539,6 +1539,44 @@ pub trait IndexMut<Idx: ?Sized>: Index<Idx> {
15391539
fn index_mut(&mut self, index: Idx) -> &mut Self::Output;
15401540
}
15411541

1542+
/// The `IndexAssign` trait is used to specify the functionality of indexed assignment
1543+
/// operations like `arr[idx] = rhs`.
1544+
///
1545+
/// # Examples
1546+
///
1547+
/// A trivial implementation of `IndexAssign`. When `Foo[Bar] = Baz` happens, it ends up
1548+
/// calling `index_assign`, and therefore, `main` prints `Indexed assignment!`.
1549+
///
1550+
/// ```
1551+
/// #![feature(indexed_assignments)]
1552+
/// #![feature(index_assign_trait)]
1553+
///
1554+
/// use std::ops::{IndexAssign};
1555+
///
1556+
/// #[derive(Copy, Clone)]
1557+
/// struct Foo;
1558+
/// struct Bar;
1559+
/// struct Baz;
1560+
///
1561+
/// impl IndexAssign<Bar, Baz> for Foo {
1562+
/// fn index_assign(&mut self, _index: Bar, _rhs: Baz) {
1563+
/// println!("Indexed assignment!");
1564+
/// }
1565+
/// }
1566+
///
1567+
/// fn main() {
1568+
/// Foo[Bar] = Baz;
1569+
/// }
1570+
/// ```
1571+
#[cfg(not(stage0))]
1572+
#[lang = "index_assign"]
1573+
// TODO(japaric) update issue number
1574+
#[unstable(feature = "index_assign_trait", reason = "recently added", issue = "0")]
1575+
pub trait IndexAssign<Idx, Rhs> {
1576+
/// The method for the indexed assignment (`Foo[Bar] = Baz`) operation
1577+
fn index_assign(&mut self, index: Idx, rhs: Rhs);
1578+
}
1579+
15421580
/// An unbounded range.
15431581
#[derive(Copy, Clone, PartialEq, Eq)]
15441582
#[lang = "range_full"]

src/librustc/middle/expr_use_visitor.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -517,8 +517,16 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
517517
}
518518

519519
hir::ExprAssign(ref lhs, ref rhs) => {
520-
self.mutate_expr(expr, &**lhs, JustWrite);
521-
self.consume_expr(&**rhs);
520+
if let hir::ExprIndex(ref base, ref idx) = lhs.node {
521+
if !self.walk_overloaded_operator(expr, base, vec![idx, rhs],
522+
PassArgs::ByValue) {
523+
self.mutate_expr(expr, &**lhs, JustWrite);
524+
self.consume_expr(&**rhs);
525+
}
526+
} else {
527+
self.mutate_expr(expr, &**lhs, JustWrite);
528+
self.consume_expr(&**rhs);
529+
}
522530
}
523531

524532
hir::ExprCast(ref base, _) => {

src/librustc/middle/lang_items.rs

+1
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ lets_do_this! {
298298
ShrAssignTraitLangItem, "shr_assign", shr_assign_trait;
299299
IndexTraitLangItem, "index", index_trait;
300300
IndexMutTraitLangItem, "index_mut", index_mut_trait;
301+
IndexAssignTraitLangItem, "index_assign", index_assign_trait;
301302
RangeStructLangItem, "range", range_struct;
302303
RangeFromStructLangItem, "range_from", range_from_struct;
303304
RangeToStructLangItem, "range_to", range_to_struct;

src/librustc_trans/trans/callee.rs

+11
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,8 @@ pub enum CallArgs<'a, 'tcx> {
868868
// arguments should be auto-referenced
869869
ArgOverloadedOp(Datum<'tcx, Expr>, Option<(Datum<'tcx, Expr>, ast::NodeId)>, bool),
870870

871+
ArgIndexedAssignment(Vec<Datum<'tcx, Expr>>),
872+
871873
// Supply value of arguments as a list of expressions that must be
872874
// translated, for overloaded call operators.
873875
ArgOverloadedCall(Vec<&'a hir::Expr>),
@@ -1055,6 +1057,15 @@ pub fn trans_args<'a, 'blk, 'tcx>(cx: Block<'blk, 'tcx>,
10551057
assert_eq!(arg_tys.len(), 1);
10561058
}
10571059
}
1060+
ArgIndexedAssignment(args) => {
1061+
assert!(!variadic);
1062+
assert_eq!(arg_tys.len(), 3);
1063+
1064+
for (i, arg) in args.into_iter().enumerate() {
1065+
bcx = trans_arg_datum(bcx, arg_tys[i], arg, arg_cleanup_scope, DontAutorefArg,
1066+
llargs);
1067+
}
1068+
}
10581069
ArgVals(vs) => {
10591070
llargs.push_all(vs);
10601071
}

src/librustc_trans/trans/expr.rs

+47-2
Original file line numberDiff line numberDiff line change
@@ -999,6 +999,32 @@ fn trans_rvalue_stmt_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
999999
hir::ExprLoop(ref body, _) => {
10001000
controlflow::trans_loop(bcx, expr, &**body)
10011001
}
1002+
// indexed assignments `a[b] = c`
1003+
hir::ExprAssign(ref lhs, ref rhs) if bcx.tcx().is_method_call(expr.id) => {
1004+
if let hir::ExprIndex(ref base, ref idx) = lhs.node {
1005+
let rhs = unpack_datum!(bcx, trans(bcx, &**rhs));
1006+
1007+
// NOTE `lhs` is not an lvalue in this case, but we still need to create a cleanup
1008+
// scope that covers `base` and `idx`
1009+
let cleanup_debug_loc = debuginfo::get_cleanup_debug_loc_for_ast_node(bcx.ccx(),
1010+
lhs.id,
1011+
lhs.span,
1012+
false);
1013+
1014+
bcx.fcx.push_ast_cleanup_scope(cleanup_debug_loc);
1015+
1016+
let base = unpack_datum!(bcx, trans(bcx, &**base));
1017+
let idx = unpack_datum!(bcx, trans(bcx, &**idx));
1018+
1019+
bcx.fcx.pop_and_trans_ast_cleanup_scope(bcx, lhs.id);
1020+
1021+
trans_indexed_assignment(bcx, expr, MethodCall::expr(expr.id), base, idx, rhs).bcx
1022+
} else {
1023+
bcx.tcx().sess.span_bug(
1024+
expr.span,
1025+
"the only overloadable assignments are indexed assignments");
1026+
}
1027+
},
10021028
hir::ExprAssign(ref dst, ref src) => {
10031029
let src_datum = unpack_datum!(bcx, trans(bcx, &**src));
10041030
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>,
20312057
dest)
20322058
}
20332059

2060+
fn trans_indexed_assignment<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
2061+
expr: &hir::Expr,
2062+
method_call: MethodCall,
2063+
base: Datum<'tcx, Expr>,
2064+
idx: Datum<'tcx, Expr>,
2065+
rhs: Datum<'tcx, Expr>)
2066+
-> Result<'blk, 'tcx> {
2067+
callee::trans_call_inner(bcx,
2068+
expr.debug_loc(),
2069+
|bcx, arg_cleanup_scope| {
2070+
meth::trans_method_callee(bcx,
2071+
method_call,
2072+
None,
2073+
arg_cleanup_scope)
2074+
},
2075+
callee::ArgIndexedAssignment(vec![base, idx, rhs]),
2076+
None)
2077+
}
2078+
20342079
fn trans_overloaded_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
20352080
expr: &hir::Expr,
20362081
callee: &'a hir::Expr,
@@ -2676,8 +2721,8 @@ fn expr_kind(tcx: &ty::ctxt, expr: &hir::Expr) -> ExprKind {
26762721
// Overloaded operations are generally calls, and hence they are
26772722
// generated via DPS, but there are a few exceptions:
26782723
return match expr.node {
2679-
// `a += b` has a unit result.
2680-
hir::ExprAssignOp(..) => ExprKind::RvalueStmt,
2724+
// `a += b` and `a[b] = c` have a unit result.
2725+
hir::ExprAssignOp(..) | hir::ExprAssign(..) => ExprKind::RvalueStmt,
26812726

26822727
// the deref method invoked for `*a` always yields an `&T`
26832728
hir::ExprUnary(hir::UnDeref, _) => ExprKind::Lvalue,

src/librustc_typeck/check/method/confirm.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,8 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
574574
autoderefs,
575575
unsize,
576576
PreferMutLvalue,
577-
index_expr_ty);
577+
index_expr_ty,
578+
false);
578579

579580
if let Some((input_ty, return_ty)) = result {
580581
demand::suptype(self.fcx, index_expr.span, input_ty, index_expr_ty);

src/librustc_typeck/check/mod.rs

+78-22
Original file line numberDiff line numberDiff line change
@@ -2247,7 +2247,8 @@ fn lookup_indexing<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
22472247
base_expr: &'tcx hir::Expr,
22482248
base_ty: Ty<'tcx>,
22492249
idx_ty: Ty<'tcx>,
2250-
lvalue_pref: LvaluePreference)
2250+
lvalue_pref: LvaluePreference,
2251+
is_assignment: bool)
22512252
-> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)>
22522253
{
22532254
// 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>,
22622263
lvalue_pref,
22632264
|adj_ty, idx| {
22642265
try_index_step(fcx, MethodCall::expr(expr.id), expr, base_expr,
2265-
adj_ty, idx, false, lvalue_pref, idx_ty)
2266+
adj_ty, idx, false, lvalue_pref, idx_ty, is_assignment)
22662267
});
22672268

22682269
if final_mt.is_some() {
@@ -2274,7 +2275,7 @@ fn lookup_indexing<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
22742275
if let ty::TyArray(element_ty, _) = ty.sty {
22752276
let adjusted_ty = fcx.tcx().mk_slice(element_ty);
22762277
try_index_step(fcx, MethodCall::expr(expr.id), expr, base_expr,
2277-
adjusted_ty, autoderefs, true, lvalue_pref, idx_ty)
2278+
adjusted_ty, autoderefs, true, lvalue_pref, idx_ty, is_assignment)
22782279
} else {
22792280
None
22802281
}
@@ -2292,21 +2293,45 @@ fn try_index_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
22922293
autoderefs: usize,
22932294
unsize: bool,
22942295
lvalue_pref: LvaluePreference,
2295-
index_ty: Ty<'tcx>)
2296+
index_ty: Ty<'tcx>,
2297+
is_assignment: bool)
22962298
-> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)>
22972299
{
22982300
let tcx = fcx.tcx();
22992301
debug!("try_index_step(expr={:?}, base_expr.id={:?}, adjusted_ty={:?}, \
2300-
autoderefs={}, unsize={}, index_ty={:?})",
2302+
autoderefs={}, unsize={}, index_ty={:?}, is_assignment={})",
23012303
expr,
23022304
base_expr,
23032305
adjusted_ty,
23042306
autoderefs,
23052307
unsize,
2306-
index_ty);
2308+
index_ty,
2309+
is_assignment);
23072310

23082311
let input_ty = fcx.infcx().next_ty_var();
23092312

2313+
// Try `IndexAssign` if this is an assignment operation
2314+
if is_assignment {
2315+
return tcx.lang_items.index_assign_trait().and_then(|trait_did| {
2316+
let rhs_ty = fcx.infcx().next_ty_var();
2317+
2318+
method::lookup_in_trait_adjusted(fcx,
2319+
expr.span,
2320+
Some(&*base_expr),
2321+
token::intern("index_assign"),
2322+
trait_did,
2323+
autoderefs,
2324+
unsize,
2325+
adjusted_ty,
2326+
Some(vec![input_ty, rhs_ty]))
2327+
.map(|method| {
2328+
fcx.inh.tables.borrow_mut().method_map.insert(method_call, method);
2329+
2330+
(input_ty, rhs_ty)
2331+
})
2332+
})
2333+
}
2334+
23102335
// First, try built-in indexing.
23112336
match (adjusted_ty.builtin_index(), &index_ty.sty) {
23122337
(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>,
34453470
fcx.write_ty(id, fcx.infcx().next_diverging_ty_var());
34463471
}
34473472
hir::ExprAssign(ref lhs, ref rhs) => {
3448-
check_expr_with_lvalue_pref(fcx, &**lhs, PreferMutLvalue);
3473+
if let hir::ExprIndex(ref base, ref idx) = lhs.node {
3474+
let lvalue_pref = PreferMutLvalue;
3475+
3476+
check_expr_with_lvalue_pref(fcx, &**base, lvalue_pref);
3477+
check_expr(fcx, &**idx);
3478+
3479+
let base_t = structurally_resolved_type(fcx, lhs.span, fcx.expr_ty(&**base));
3480+
let idx_t = fcx.expr_ty(&**idx);
3481+
3482+
lookup_indexing(fcx, expr, base, base_t, idx_t, lvalue_pref, true)
3483+
.map(|(index_ty, rhs_ty)| {
3484+
demand::eqtype(fcx, idx.span, index_ty, idx_t);
3485+
check_expr_coercable_to_type(fcx, &**rhs, rhs_ty);
3486+
fcx.write_nil(lhs.id);
3487+
fcx.write_nil(id);
3488+
3489+
if !tcx.sess.features.borrow().indexed_assignments {
3490+
tcx.sess.span_err(
3491+
expr.span,
3492+
"overloaded indexed assignments are not stable");
3493+
fileline_help!(
3494+
tcx.sess,
3495+
expr.span,
3496+
"add `#![feature(indexed_assignments)]` to the crate features to \
3497+
enable");
3498+
}
3499+
})
3500+
} else {
3501+
None
3502+
}.unwrap_or_else(|| {
3503+
check_expr_with_lvalue_pref(fcx, &**lhs, PreferMutLvalue);
34493504

3450-
let tcx = fcx.tcx();
3451-
if !tcx.expr_is_lval(&**lhs) {
3452-
span_err!(tcx.sess, expr.span, E0070,
3453-
"invalid left-hand side expression");
3454-
}
3505+
let tcx = fcx.tcx();
3506+
if !tcx.expr_is_lval(&**lhs) {
3507+
span_err!(tcx.sess, expr.span, E0070,
3508+
"invalid left-hand side expression");
3509+
}
34553510

3456-
let lhs_ty = fcx.expr_ty(&**lhs);
3457-
check_expr_coercable_to_type(fcx, &**rhs, lhs_ty);
3458-
let rhs_ty = fcx.expr_ty(&**rhs);
3511+
let lhs_ty = fcx.expr_ty(&**lhs);
3512+
check_expr_coercable_to_type(fcx, &**rhs, lhs_ty);
3513+
let rhs_ty = fcx.expr_ty(&**rhs);
34593514

3460-
fcx.require_expr_have_sized_type(&**lhs, traits::AssignmentLhsSized);
3515+
fcx.require_expr_have_sized_type(&**lhs, traits::AssignmentLhsSized);
34613516

3462-
if lhs_ty.references_error() || rhs_ty.references_error() {
3463-
fcx.write_error(id);
3464-
} else {
3465-
fcx.write_nil(id);
3466-
}
3517+
if lhs_ty.references_error() || rhs_ty.references_error() {
3518+
fcx.write_error(id);
3519+
} else {
3520+
fcx.write_nil(id);
3521+
}
3522+
})
34673523
}
34683524
hir::ExprIf(ref cond, ref then_blk, ref opt_else_expr) => {
34693525
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>,
36663722
fcx.write_ty(id, idx_t);
36673723
} else {
36683724
let base_t = structurally_resolved_type(fcx, expr.span, base_t);
3669-
match lookup_indexing(fcx, expr, base, base_t, idx_t, lvalue_pref) {
3725+
match lookup_indexing(fcx, expr, base, base_t, idx_t, lvalue_pref, false) {
36703726
Some((index_ty, element_ty)) => {
36713727
let idx_expr_ty = fcx.expr_ty(idx);
36723728
demand::eqtype(fcx, expr.span, index_ty, idx_expr_ty);

0 commit comments

Comments
 (0)