diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 0e1dbc53806ff..536dec8442d84 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -831,9 +831,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs: &'tcx hir::Expr<'tcx>, err_code: &'static str, op_span: Span, - ) { + ) -> Result<(), DiagnosticBuilder<'tcx>> { if lhs.is_syntactic_place_expr() { - return; + return Ok(()); } // FIXME: Make this use SessionDiagnostic once error codes can be dynamically set. @@ -885,7 +885,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - err.emit(); + Err(err) } // A generic function for checking the 'then' and 'else' clauses in an 'if' @@ -1025,14 +1025,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return self.tcx.ty_error(); } - self.check_lhs_assignable(lhs, "E0070", *span); + let mut assignable_err = self.check_lhs_assignable(lhs, "E0070", *span); let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace); - let rhs_ty = self.check_expr_coercable_to_type(&rhs, lhs_ty, Some(lhs)); + let rhs_ty = self.check_expr_with_hint(&rhs, lhs_ty); + + let (mut coerce_ty, mut coerce_err) = + self.demand_coerce_diag(&rhs, rhs_ty, lhs_ty, Some(&lhs), AllowTwoPhase::No); + + // If we either have a bad LHS type, or can't coerce the RHS into the LHS, it may be due + // to a missing deref on the LHS. + if assignable_err.is_err() || coerce_err.is_some() { + for (deref_ty, steps) in self.autoderef(lhs.span, lhs_ty) { + // Check if we can deref the LHS into something that the RHS can coerce into + if self.can_coerce(rhs_ty, deref_ty) { + coerce_ty = lhs_ty; + let span = lhs.span.shrink_to_lo(); + let msg = "consider dereferencing here to assign a value to the left-hand side"; + let suggest = "*".repeat(steps); + // We don't need to emit both an LHS error and a coercion error, + // so prefer emitting the LHS error over the coercion error + match (&mut assignable_err, &mut coerce_err) { + (Err(err), Some(coerce_err)) => { + // Cancel the coercion error if we have both + coerce_err.cancel(); + err.span_suggestion_verbose( + span, + msg, + suggest, + Applicability::MaybeIncorrect, + ); + err.emit(); + } + (Err(err), None) | (Ok(()), Some(err)) => { + err.span_suggestion_verbose( + span, + msg, + suggest, + Applicability::MaybeIncorrect, + ); + err.emit(); + } + (Ok(()), None) => unreachable!(), + } + break; + } + } + } + + // Emit any leftover errors if we still have them after checking for deref + if let Some(mut err) = coerce_err { + if !err.cancelled() { + err.emit(); + } + } + if let Err(mut err) = assignable_err { + if !err.cancelled() { + err.emit(); + } + } self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized); - if lhs_ty.references_error() || rhs_ty.references_error() { + if lhs_ty.references_error() || coerce_ty.references_error() { self.tcx.ty_error() } else { self.tcx.mk_unit() diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs index 74516acbfcff3..f8e8eb46b1913 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -42,7 +42,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return_ty }; - self.check_lhs_assignable(lhs, "E0067", op.span); + if let Err(mut err) = self.check_lhs_assignable(lhs, "E0067", op.span) { + err.emit(); + } ty } diff --git a/src/test/ui/typeck/bad-lhs-should-deref.rs b/src/test/ui/typeck/bad-lhs-should-deref.rs new file mode 100644 index 0000000000000..c62f654e73267 --- /dev/null +++ b/src/test/ui/typeck/bad-lhs-should-deref.rs @@ -0,0 +1,15 @@ +fn mut_ref() -> &'static mut Box { + todo!(); +} + +fn main() { + mut_ref() = 1; + //~^ ERROR invalid left-hand side of assignment + //~| HELP consider dereferencing here to assign a value to the left-hand side + + let x = Box::new(1); + x = 2; + //~^ ERROR mismatched types + //~| HELP store this in the heap by calling `Box::new` + //~| HELP consider dereferencing here to assign a value to the left-hand side +} diff --git a/src/test/ui/typeck/bad-lhs-should-deref.stderr b/src/test/ui/typeck/bad-lhs-should-deref.stderr new file mode 100644 index 0000000000000..caedc23019239 --- /dev/null +++ b/src/test/ui/typeck/bad-lhs-should-deref.stderr @@ -0,0 +1,37 @@ +error[E0070]: invalid left-hand side of assignment + --> $DIR/bad-lhs-should-deref.rs:6:15 + | +LL | mut_ref() = 1; + | --------- ^ + | | + | cannot assign to this expression + | +help: consider dereferencing here to assign a value to the left-hand side + | +LL | **mut_ref() = 1; + | ++ + +error[E0308]: mismatched types + --> $DIR/bad-lhs-should-deref.rs:11:9 + | +LL | let x = Box::new(1); + | ----------- expected due to this value +LL | x = 2; + | ^ expected struct `Box`, found integer + | + = note: expected struct `Box<{integer}>` + found type `{integer}` + = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html +help: store this in the heap by calling `Box::new` + | +LL | x = Box::new(2); + | +++++++++ + +help: consider dereferencing here to assign a value to the left-hand side + | +LL | *x = 2; + | + + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0070, E0308. +For more information about an error, try `rustc --explain E0070`.