diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 9015868b499..bf438b44989 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -1058,6 +1058,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_lhs_assignable(lhs, "E0070", span, |err| { let rhs_ty = self.check_expr(&rhs); + // FIXME: This could be done any time lhs_ty is DerefMut into something that + // is compatible with rhs_ty, and not _just_ `&mut` if let ty::Ref(_, lhs_inner_ty, hir::Mutability::Mut) = lhs_ty.kind() { if self.can_coerce(rhs_ty, *lhs_inner_ty) { err.span_suggestion_verbose( diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs index 5066e21dc8d..d250f38fffa 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -41,7 +41,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return_ty }; - self.check_lhs_assignable(lhs, "E0067", op.span, |_| {}); + self.check_lhs_assignable(lhs, "E0067", op.span, |err| { + if let Ref(_, rty, hir::Mutability::Mut) = lhs_ty.kind() { + if self + .lookup_op_method(*rty, Some(rhs_ty), Some(rhs), Op::Binary(op, IsAssign::Yes)) + .is_ok() + { + // Suppress this error, since we already emitted + // a deref suggestion in check_overloaded_binop + err.delay_as_bug(); + } + } + }); ty } @@ -404,8 +415,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (err, missing_trait, use_output) } }; - if let Ref(_, rty, _) = lhs_ty.kind() { - if self.infcx.type_is_copy_modulo_regions(self.param_env, *rty, lhs_expr.span) + if let Ref(_, rty, mutability) = lhs_ty.kind() { + let is_copy = + self.infcx.type_is_copy_modulo_regions(self.param_env, *rty, lhs_expr.span); + // We should suggest `a + b` => `*a + b` if `a` is copy, and suggest + // `a += b` => `*a += b` if a is a mut ref. + // FIXME: This could be done any time lhs_ty is DerefMut into something that + // is compatible with rhs_ty, and not _just_ `&mut` (for IsAssign::Yes). + if ((is_assign == IsAssign::No && is_copy) + || (is_assign == IsAssign::Yes && *mutability == hir::Mutability::Mut)) && self .lookup_op_method( *rty, diff --git a/src/test/ui/issues/issue-5239-1.stderr b/src/test/ui/issues/issue-5239-1.stderr index b743f0df4b1..f53ddb95416 100644 --- a/src/test/ui/issues/issue-5239-1.stderr +++ b/src/test/ui/issues/issue-5239-1.stderr @@ -5,11 +5,6 @@ LL | let x = |ref x: isize| { x += 1; }; | -^^^^^ | | | cannot use `+=` on type `&isize` - | -help: `+=` can be used on `isize`, you can dereference `x` - | -LL | let x = |ref x: isize| { *x += 1; }; - | + error: aborting due to previous error diff --git a/src/test/ui/typeck/assign-non-lval-mut-ref.fixed b/src/test/ui/typeck/assign-non-lval-mut-ref.fixed index 76e2afc672a..c4dadfbdfce 100644 --- a/src/test/ui/typeck/assign-non-lval-mut-ref.fixed +++ b/src/test/ui/typeck/assign-non-lval-mut-ref.fixed @@ -2,6 +2,8 @@ fn main() { let mut x = vec![1usize]; - *x.last_mut().unwrap() = 2usize; + *x.last_mut().unwrap() = 2; //~^ ERROR invalid left-hand side of assignment + *x.last_mut().unwrap() += 1; + //~^ ERROR binary assignment operation `+=` cannot be applied to type `&mut usize` } diff --git a/src/test/ui/typeck/assign-non-lval-mut-ref.rs b/src/test/ui/typeck/assign-non-lval-mut-ref.rs index ff91f2297c8..39573ddb6d0 100644 --- a/src/test/ui/typeck/assign-non-lval-mut-ref.rs +++ b/src/test/ui/typeck/assign-non-lval-mut-ref.rs @@ -2,6 +2,8 @@ fn main() { let mut x = vec![1usize]; - x.last_mut().unwrap() = 2usize; + x.last_mut().unwrap() = 2; //~^ ERROR invalid left-hand side of assignment + x.last_mut().unwrap() += 1; + //~^ ERROR binary assignment operation `+=` cannot be applied to type `&mut usize` } diff --git a/src/test/ui/typeck/assign-non-lval-mut-ref.stderr b/src/test/ui/typeck/assign-non-lval-mut-ref.stderr index 745ada5de0b..b0ca089b709 100644 --- a/src/test/ui/typeck/assign-non-lval-mut-ref.stderr +++ b/src/test/ui/typeck/assign-non-lval-mut-ref.stderr @@ -1,16 +1,30 @@ error[E0070]: invalid left-hand side of assignment --> $DIR/assign-non-lval-mut-ref.rs:5:27 | -LL | x.last_mut().unwrap() = 2usize; +LL | x.last_mut().unwrap() = 2; | --------------------- ^ | | | cannot assign to this expression | help: consider dereferencing here to assign to the mutable borrowed piece of memory | -LL | *x.last_mut().unwrap() = 2usize; +LL | *x.last_mut().unwrap() = 2; | + -error: aborting due to previous error +error[E0368]: binary assignment operation `+=` cannot be applied to type `&mut usize` + --> $DIR/assign-non-lval-mut-ref.rs:7:5 + | +LL | x.last_mut().unwrap() += 1; + | ---------------------^^^^^ + | | + | cannot use `+=` on type `&mut usize` + | +help: `+=` can be used on `usize`, you can dereference `x.last_mut().unwrap()` + | +LL | *x.last_mut().unwrap() += 1; + | + -For more information about this error, try `rustc --explain E0070`. +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0070, E0368. +For more information about an error, try `rustc --explain E0070`.