diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs index b760a54f08c76..3cbc3d231f847 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_typeck/src/check/cast.rs @@ -35,6 +35,7 @@ use crate::type_error_struct; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported}; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; +use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::cast::{CastKind, CastTy}; use rustc_middle::ty::error::TypeError; @@ -347,15 +348,52 @@ impl<'a, 'tcx> CastCheck<'tcx> { fcx.ty_to_string(self.cast_ty) ); let mut sugg = None; + let mut sugg_mutref = false; if let ty::Ref(reg, _, mutbl) = *self.cast_ty.kind() { - if fcx - .try_coerce( - self.expr, - fcx.tcx.mk_ref(reg, TypeAndMut { ty: self.expr_ty, mutbl }), - self.cast_ty, - AllowTwoPhase::No, - ) - .is_ok() + if let ty::RawPtr(TypeAndMut { ty: expr_ty, .. }) = *self.expr_ty.kind() { + if fcx + .try_coerce( + self.expr, + fcx.tcx.mk_ref( + &ty::RegionKind::ReErased, + TypeAndMut { ty: expr_ty, mutbl }, + ), + self.cast_ty, + AllowTwoPhase::No, + ) + .is_ok() + { + sugg = Some(format!("&{}*", mutbl.prefix_str())); + } + } else if let ty::Ref(expr_reg, expr_ty, expr_mutbl) = *self.expr_ty.kind() { + if expr_mutbl == Mutability::Not + && mutbl == Mutability::Mut + && fcx + .try_coerce( + self.expr, + fcx.tcx.mk_ref( + expr_reg, + TypeAndMut { ty: expr_ty, mutbl: Mutability::Mut }, + ), + self.cast_ty, + AllowTwoPhase::No, + ) + .is_ok() + { + sugg_mutref = true; + } + } + + if !sugg_mutref + && sugg == None + && fcx + .try_coerce( + self.expr, + fcx.tcx.mk_ref(reg, TypeAndMut { ty: self.expr_ty, mutbl }), + self.cast_ty, + AllowTwoPhase::No, + ) + .is_ok() { sugg = Some(format!("&{}", mutbl.prefix_str())); } @@ -375,11 +413,15 @@ impl<'a, 'tcx> CastCheck<'tcx> { sugg = Some(format!("&{}", mutbl.prefix_str())); } } - if let Some(sugg) = sugg { + if sugg_mutref { + err.span_label(self.span, "invalid cast"); + err.span_note(self.expr.span, "this reference is immutable"); + err.span_note(self.cast_span, "trying to cast to a mutable reference type"); + } else if let Some(sugg) = sugg { err.span_label(self.span, "invalid cast"); err.span_suggestion_verbose( self.expr.span.shrink_to_lo(), - "borrow the value for the cast to be valid", + "consider borrowing the value", sugg, Applicability::MachineApplicable, ); diff --git a/src/test/ui/cast/issue-84213.stderr b/src/test/ui/cast/issue-84213.stderr index a76aac580131e..3c43427fa178b 100644 --- a/src/test/ui/cast/issue-84213.stderr +++ b/src/test/ui/cast/issue-84213.stderr @@ -4,7 +4,7 @@ error[E0605]: non-primitive cast: `Something` as `*const Something` LL | let _pointer_to_something = something as *const Something; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast | -help: borrow the value for the cast to be valid +help: consider borrowing the value | LL | let _pointer_to_something = &something as *const Something; | ^ @@ -15,7 +15,7 @@ error[E0605]: non-primitive cast: `Something` as `*mut Something` LL | let _mut_pointer_to_something = something as *mut Something; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast | -help: borrow the value for the cast to be valid +help: consider borrowing the value | LL | let _mut_pointer_to_something = &mut something as *mut Something; | ^^^^ diff --git a/src/test/ui/error-codes/E0605.stderr b/src/test/ui/error-codes/E0605.stderr index 43269c095d6dd..6314e7a3a8a78 100644 --- a/src/test/ui/error-codes/E0605.stderr +++ b/src/test/ui/error-codes/E0605.stderr @@ -8,7 +8,12 @@ error[E0605]: non-primitive cast: `*const u8` as `&u8` --> $DIR/E0605.rs:6:5 | LL | v as &u8; - | ^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object + | ^^^^^^^^ invalid cast + | +help: consider borrowing the value + | +LL | &*v as &u8; + | ^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-22289.stderr b/src/test/ui/issues/issue-22289.stderr index 600278536093b..f90e89efb4a8c 100644 --- a/src/test/ui/issues/issue-22289.stderr +++ b/src/test/ui/issues/issue-22289.stderr @@ -4,7 +4,7 @@ error[E0605]: non-primitive cast: `i32` as `&(dyn Any + 'static)` LL | 0 as &dyn std::any::Any; | ^^^^^^^^^^^^^^^^^^^^^^^ invalid cast | -help: borrow the value for the cast to be valid +help: consider borrowing the value | LL | &0 as &dyn std::any::Any; | ^ diff --git a/src/test/ui/issues/issue-22312.stderr b/src/test/ui/issues/issue-22312.stderr index 823ffc6de6dc2..47ee544c02af7 100644 --- a/src/test/ui/issues/issue-22312.stderr +++ b/src/test/ui/issues/issue-22312.stderr @@ -4,7 +4,7 @@ error[E0605]: non-primitive cast: `Self` as `&dyn Index>::Output>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast | -help: borrow the value for the cast to be valid +help: consider borrowing the value | LL | let indexer = &(&*self as &dyn Index>::Output>); | ^ diff --git a/src/test/ui/issues/issue-2995.stderr b/src/test/ui/issues/issue-2995.stderr index 9f5968399a37d..b08fe8c7352a9 100644 --- a/src/test/ui/issues/issue-2995.stderr +++ b/src/test/ui/issues/issue-2995.stderr @@ -2,7 +2,12 @@ error[E0605]: non-primitive cast: `*const isize` as `&isize` --> $DIR/issue-2995.rs:2:22 | LL | let _q: &isize = p as &isize; - | ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object + | ^^^^^^^^^^^ invalid cast + | +help: consider borrowing the value + | +LL | let _q: &isize = &*p as &isize; + | ^^ error: aborting due to previous error diff --git a/src/test/ui/mismatched_types/cast-rfc0401.stderr b/src/test/ui/mismatched_types/cast-rfc0401.stderr index 388c978d03853..6a97d1ee3b813 100644 --- a/src/test/ui/mismatched_types/cast-rfc0401.stderr +++ b/src/test/ui/mismatched_types/cast-rfc0401.stderr @@ -24,7 +24,12 @@ error[E0605]: non-primitive cast: `*const u8` as `&u8` --> $DIR/cast-rfc0401.rs:29:13 | LL | let _ = v as &u8; - | ^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object + | ^^^^^^^^ invalid cast + | +help: consider borrowing the value + | +LL | let _ = &*v as &u8; + | ^^ error[E0605]: non-primitive cast: `*const u8` as `E` --> $DIR/cast-rfc0401.rs:30:13