about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-05-28 03:38:28 +0000
committerbors <bors@rust-lang.org>2021-05-28 03:38:28 +0000
commit8eef79ca9a2ff47db25905e8c21e2ffba1d353cb (patch)
tree481025eed32323eae2af23740e150e7933e6639a
parentdf3d86b535182cd52927405a4316ad688d2240f8 (diff)
parent71d1b2a009aa36422573d5ad4f74711b871df53e (diff)
downloadrust-8eef79ca9a2ff47db25905e8c21e2ffba1d353cb.tar.gz
rust-8eef79ca9a2ff47db25905e8c21e2ffba1d353cb.zip
Auto merge of #84968 - FabianWolff:master, r=estebank
Fix incorrect suggestions for E0605

Fixes #84598. Here is a simplified version of the problem presented in issue #84598:

```Rust
#![allow(unused_variables)]
#![allow(dead_code)]

trait T { fn t(&self) -> i32; }

unsafe fn foo(t: *mut dyn T) {
    (t as &dyn T).t();
}

fn main() {}
```

The current output is:
```
error[E0605]: non-primitive cast: `*mut (dyn T + 'static)` as `&dyn T`
 --> src/main.rs:7:5
  |
7 |     (t as &dyn T).t();
  |     ^^^^^^^^^^^^^ invalid cast
  |
help: borrow the value for the cast to be valid
  |
7 |     (&t as &dyn T).t();
  |      ^
```

This is incorrect, though: The cast will _not_ be valid when writing `&t` instead of `t`:
```
error[E0277]: the trait bound `*mut (dyn T + 'static): T` is not satisfied
 --> t4.rs:7:6
  |
7 |     (&t as &dyn T).t();
  |      ^^ the trait `T` is not implemented for `*mut (dyn T + 'static)`
  |
  = note: required for the cast to the object type `dyn T`
```

The correct suggestion is `&*t`, which I have implemented in this pull request. Of course, this suggestion will always require an unsafe block, but arguably, that's what the user really wants if they're trying to cast a pointer to a reference.

In any case, claiming that the cast will be valid after implementing the suggestion is overly optimistic, as the coercion logic doesn't seem to resolve all nested obligations, i.e. the cast may still be invalid after implementing the suggestion. I have therefore rephrased the suggestion slightly ("consider borrowing the value" instead of "borrow the value for the cast to be valid").

Additionally, I have fixed another incorrect suggestion not mentioned in #84598, which relates to casting immutable references to mutable ones:

```rust
fn main() {
    let mut x = 0;
    let m = &x as &mut i32;
}
```
currently leads to
```
error[E0605]: non-primitive cast: `&i32` as `&mut i32`
 --> t5.rs:3:13
  |
3 |     let m = &x as &mut i32;
  |             ^^^^^^^^^^^^^^ invalid cast
  |
help: borrow the value for the cast to be valid
  |
3 |     let m = &mut &x as &mut i32;
  |             ^^^^
```
which is obviously incorrect:
```
error[E0596]: cannot borrow data in a `&` reference as mutable
 --> t5.rs:3:13
  |
3 |     let m = &mut &x as &mut i32;
  |             ^^^^^^^ cannot borrow as mutable
```
I've changed the suggestion to a note explaining the problem:
```
error[E0605]: non-primitive cast: `&i32` as `&mut i32`
 --> t5.rs:3:13
  |
3 |     let m = &x as &mut i32;
  |             ^^^^^^^^^^^^^^ invalid cast
  |
note: this reference is immutable
 --> t5.rs:3:13
  |
3 |     let m = &x as &mut i32;
  |             ^^
note: trying to cast to a mutable reference type
 --> t5.rs:3:19
  |
3 |     let m = &x as &mut i32;
  |                   ^^^^^^^^
```
In this example, it would have been even nicer to suggest replacing `&x` with `&mut x`, but this would be much more complex because we would have to take apart the expression to be cast (currently, we only look at its type), and `&x` could be stored in a variable, where such a suggestion would not even be directly applicable:
```rust
fn main() {
    let mut x = 0;
    let r = &x;
    let m = r as &mut i32;
}
```
My solution covers this case, too.
-rw-r--r--compiler/rustc_typeck/src/check/cast.rs62
-rw-r--r--src/test/ui/cast/issue-84213.stderr4
-rw-r--r--src/test/ui/error-codes/E0605.stderr7
-rw-r--r--src/test/ui/issues/issue-22289.stderr2
-rw-r--r--src/test/ui/issues/issue-22312.stderr2
-rw-r--r--src/test/ui/issues/issue-2995.stderr7
-rw-r--r--src/test/ui/mismatched_types/cast-rfc0401.stderr7
7 files changed, 74 insertions, 17 deletions
diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs
index b760a54f08c..3cbc3d231f8 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 1b71d4db511..90cfa263c52 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 43269c095d6..6314e7a3a8a 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 60027853609..f90e89efb4a 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 823ffc6de6d..47ee544c02a 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<usize, Output = <Self as
 LL |         let indexer = &(*self as &dyn Index<usize, Output = <Self as Index<usize>>::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<usize, Output = <Self as Index<usize>>::Output>);
    |                         ^
diff --git a/src/test/ui/issues/issue-2995.stderr b/src/test/ui/issues/issue-2995.stderr
index 9f5968399a3..b08fe8c7352 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 388c978d038..6a97d1ee3b8 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