Skip to content

Commit 8eef79c

Browse files
committed
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.
2 parents df3d86b + 71d1b2a commit 8eef79c

File tree

7 files changed

+74
-17
lines changed

7 files changed

+74
-17
lines changed

compiler/rustc_typeck/src/check/cast.rs

+52-10
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use crate::type_error_struct;
3535
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported};
3636
use rustc_hir as hir;
3737
use rustc_hir::lang_items::LangItem;
38+
use rustc_middle::mir::Mutability;
3839
use rustc_middle::ty::adjustment::AllowTwoPhase;
3940
use rustc_middle::ty::cast::{CastKind, CastTy};
4041
use rustc_middle::ty::error::TypeError;
@@ -347,15 +348,52 @@ impl<'a, 'tcx> CastCheck<'tcx> {
347348
fcx.ty_to_string(self.cast_ty)
348349
);
349350
let mut sugg = None;
351+
let mut sugg_mutref = false;
350352
if let ty::Ref(reg, _, mutbl) = *self.cast_ty.kind() {
351-
if fcx
352-
.try_coerce(
353-
self.expr,
354-
fcx.tcx.mk_ref(reg, TypeAndMut { ty: self.expr_ty, mutbl }),
355-
self.cast_ty,
356-
AllowTwoPhase::No,
357-
)
358-
.is_ok()
353+
if let ty::RawPtr(TypeAndMut { ty: expr_ty, .. }) = *self.expr_ty.kind() {
354+
if fcx
355+
.try_coerce(
356+
self.expr,
357+
fcx.tcx.mk_ref(
358+
&ty::RegionKind::ReErased,
359+
TypeAndMut { ty: expr_ty, mutbl },
360+
),
361+
self.cast_ty,
362+
AllowTwoPhase::No,
363+
)
364+
.is_ok()
365+
{
366+
sugg = Some(format!("&{}*", mutbl.prefix_str()));
367+
}
368+
} else if let ty::Ref(expr_reg, expr_ty, expr_mutbl) = *self.expr_ty.kind() {
369+
if expr_mutbl == Mutability::Not
370+
&& mutbl == Mutability::Mut
371+
&& fcx
372+
.try_coerce(
373+
self.expr,
374+
fcx.tcx.mk_ref(
375+
expr_reg,
376+
TypeAndMut { ty: expr_ty, mutbl: Mutability::Mut },
377+
),
378+
self.cast_ty,
379+
AllowTwoPhase::No,
380+
)
381+
.is_ok()
382+
{
383+
sugg_mutref = true;
384+
}
385+
}
386+
387+
if !sugg_mutref
388+
&& sugg == None
389+
&& fcx
390+
.try_coerce(
391+
self.expr,
392+
fcx.tcx.mk_ref(reg, TypeAndMut { ty: self.expr_ty, mutbl }),
393+
self.cast_ty,
394+
AllowTwoPhase::No,
395+
)
396+
.is_ok()
359397
{
360398
sugg = Some(format!("&{}", mutbl.prefix_str()));
361399
}
@@ -375,11 +413,15 @@ impl<'a, 'tcx> CastCheck<'tcx> {
375413
sugg = Some(format!("&{}", mutbl.prefix_str()));
376414
}
377415
}
378-
if let Some(sugg) = sugg {
416+
if sugg_mutref {
417+
err.span_label(self.span, "invalid cast");
418+
err.span_note(self.expr.span, "this reference is immutable");
419+
err.span_note(self.cast_span, "trying to cast to a mutable reference type");
420+
} else if let Some(sugg) = sugg {
379421
err.span_label(self.span, "invalid cast");
380422
err.span_suggestion_verbose(
381423
self.expr.span.shrink_to_lo(),
382-
"borrow the value for the cast to be valid",
424+
"consider borrowing the value",
383425
sugg,
384426
Applicability::MachineApplicable,
385427
);

src/test/ui/cast/issue-84213.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0605]: non-primitive cast: `Something` as `*const Something`
44
LL | let _pointer_to_something = something as *const Something;
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast
66
|
7-
help: borrow the value for the cast to be valid
7+
help: consider borrowing the value
88
|
99
LL | let _pointer_to_something = &something as *const Something;
1010
| ^
@@ -15,7 +15,7 @@ error[E0605]: non-primitive cast: `Something` as `*mut Something`
1515
LL | let _mut_pointer_to_something = something as *mut Something;
1616
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast
1717
|
18-
help: borrow the value for the cast to be valid
18+
help: consider borrowing the value
1919
|
2020
LL | let _mut_pointer_to_something = &mut something as *mut Something;
2121
| ^^^^

src/test/ui/error-codes/E0605.stderr

+6-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ error[E0605]: non-primitive cast: `*const u8` as `&u8`
88
--> $DIR/E0605.rs:6:5
99
|
1010
LL | v as &u8;
11-
| ^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
11+
| ^^^^^^^^ invalid cast
12+
|
13+
help: consider borrowing the value
14+
|
15+
LL | &*v as &u8;
16+
| ^^
1217

1318
error: aborting due to 2 previous errors
1419

src/test/ui/issues/issue-22289.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0605]: non-primitive cast: `i32` as `&(dyn Any + 'static)`
44
LL | 0 as &dyn std::any::Any;
55
| ^^^^^^^^^^^^^^^^^^^^^^^ invalid cast
66
|
7-
help: borrow the value for the cast to be valid
7+
help: consider borrowing the value
88
|
99
LL | &0 as &dyn std::any::Any;
1010
| ^

src/test/ui/issues/issue-22312.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0605]: non-primitive cast: `Self` as `&dyn Index<usize, Output = <Self as
44
LL | let indexer = &(*self as &dyn Index<usize, Output = <Self as Index<usize>>::Output>);
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast
66
|
7-
help: borrow the value for the cast to be valid
7+
help: consider borrowing the value
88
|
99
LL | let indexer = &(&*self as &dyn Index<usize, Output = <Self as Index<usize>>::Output>);
1010
| ^

src/test/ui/issues/issue-2995.stderr

+6-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ error[E0605]: non-primitive cast: `*const isize` as `&isize`
22
--> $DIR/issue-2995.rs:2:22
33
|
44
LL | let _q: &isize = p as &isize;
5-
| ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
5+
| ^^^^^^^^^^^ invalid cast
6+
|
7+
help: consider borrowing the value
8+
|
9+
LL | let _q: &isize = &*p as &isize;
10+
| ^^
611

712
error: aborting due to previous error
813

src/test/ui/mismatched_types/cast-rfc0401.stderr

+6-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,12 @@ error[E0605]: non-primitive cast: `*const u8` as `&u8`
2424
--> $DIR/cast-rfc0401.rs:29:13
2525
|
2626
LL | let _ = v as &u8;
27-
| ^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
27+
| ^^^^^^^^ invalid cast
28+
|
29+
help: consider borrowing the value
30+
|
31+
LL | let _ = &*v as &u8;
32+
| ^^
2833

2934
error[E0605]: non-primitive cast: `*const u8` as `E`
3035
--> $DIR/cast-rfc0401.rs:30:13

0 commit comments

Comments
 (0)