Skip to content

Commit a985879

Browse files
committed
Suggest deref when coercing ty::Ref to ty::RawPtr
1 parent a9340b1 commit a985879

File tree

8 files changed

+162
-4
lines changed

8 files changed

+162
-4
lines changed

src/librustc_typeck/check/coercion.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
7474
use smallvec::{smallvec, SmallVec};
7575
use std::ops::Deref;
7676

77-
struct Coerce<'a, 'tcx> {
77+
pub struct Coerce<'a, 'tcx> {
7878
fcx: &'a FnCtxt<'a, 'tcx>,
7979
cause: ObligationCause<'tcx>,
8080
use_lub: bool,
@@ -124,15 +124,15 @@ fn success<'tcx>(
124124
}
125125

126126
impl<'f, 'tcx> Coerce<'f, 'tcx> {
127-
fn new(
127+
pub fn new(
128128
fcx: &'f FnCtxt<'f, 'tcx>,
129129
cause: ObligationCause<'tcx>,
130130
allow_two_phase: AllowTwoPhase,
131131
) -> Self {
132132
Coerce { fcx, cause, allow_two_phase, use_lub: false }
133133
}
134134

135-
fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
135+
pub fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
136136
self.commit_if_ok(|_| {
137137
if self.use_lub {
138138
self.at(&self.cause, self.fcx.param_env).lub(b, a)

src/librustc_typeck/check/demand.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::check::coercion::Coerce;
12
use crate::check::FnCtxt;
23
use rustc_infer::infer::InferOk;
34
use rustc_trait_selection::infer::InferCtxtExt as _;
@@ -8,8 +9,9 @@ use rustc_ast::util::parser::PREC_POSTFIX;
89
use rustc_errors::{Applicability, DiagnosticBuilder};
910
use rustc_hir as hir;
1011
use rustc_hir::{is_range_literal, Node};
12+
use rustc_middle::traits::ObligationCauseCode;
1113
use rustc_middle::ty::adjustment::AllowTwoPhase;
12-
use rustc_middle::ty::{self, AssocItem, Ty};
14+
use rustc_middle::ty::{self, AssocItem, Ty, TypeAndMut};
1315
use rustc_span::symbol::sym;
1416
use rustc_span::Span;
1517

@@ -539,6 +541,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
539541
return Some((sp, "consider removing the borrow", code));
540542
}
541543
}
544+
(
545+
_,
546+
&ty::RawPtr(TypeAndMut { ty: _, mutbl: hir::Mutability::Not }),
547+
&ty::Ref(_, _, hir::Mutability::Not),
548+
) => {
549+
let cause = self.cause(rustc_span::DUMMY_SP, ObligationCauseCode::ExprAssignable);
550+
// We don't ever need two-phase here since we throw out the result of the coercion
551+
let coerce = Coerce::new(self, cause, AllowTwoPhase::No);
552+
553+
if let Some(steps) =
554+
coerce.autoderef(sp, checked_ty).skip(1).find_map(|(referent_ty, steps)| {
555+
coerce
556+
.unify(
557+
coerce.tcx.mk_ptr(ty::TypeAndMut {
558+
mutbl: hir::Mutability::Not,
559+
ty: referent_ty,
560+
}),
561+
expected,
562+
)
563+
.ok()
564+
.map(|_| steps)
565+
})
566+
{
567+
// The pointer type implements `Copy` trait so the suggestion is always valid.
568+
if let Ok(code) = sm.span_to_snippet(sp) {
569+
if code.starts_with('&') {
570+
let derefs = "*".repeat(steps - 1);
571+
let message = "consider dereferencing the reference";
572+
let suggestion = format!("&{}{}", derefs, code[1..].to_string());
573+
return Some((sp, message, suggestion));
574+
}
575+
}
576+
}
577+
}
542578
_ if sp == expr.span && !is_macro => {
543579
// Check for `Deref` implementations by constructing a predicate to
544580
// prove: `<T as Deref>::Output == U`
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// run-rustfix
2+
use std::ops::Deref;
3+
4+
struct Foo(u8);
5+
6+
impl Deref for Foo {
7+
type Target = u8;
8+
fn deref(&self) -> &Self::Target {
9+
&self.0
10+
}
11+
}
12+
13+
fn main() {
14+
let a = Foo(0);
15+
// Should suggest `&*` when coercing &ty to *const ty
16+
let _: *const u8 = &*a; //~ ERROR mismatched types
17+
}

src/test/ui/issues/issue-32122-1.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// run-rustfix
2+
use std::ops::Deref;
3+
4+
struct Foo(u8);
5+
6+
impl Deref for Foo {
7+
type Target = u8;
8+
fn deref(&self) -> &Self::Target {
9+
&self.0
10+
}
11+
}
12+
13+
fn main() {
14+
let a = Foo(0);
15+
// Should suggest `&*` when coercing &ty to *const ty
16+
let _: *const u8 = &a; //~ ERROR mismatched types
17+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/issue-32122-1.rs:16:24
3+
|
4+
LL | let _: *const u8 = &a;
5+
| --------- ^^
6+
| | |
7+
| | expected `u8`, found struct `Foo`
8+
| | help: consider dereferencing the reference: `&*a`
9+
| expected due to this
10+
|
11+
= note: expected raw pointer `*const u8`
12+
found reference `&Foo`
13+
14+
error: aborting due to previous error
15+
16+
For more information about this error, try `rustc --explain E0308`.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// run-rustfix
2+
use std::ops::Deref;
3+
struct Bar(u8);
4+
struct Foo(Bar);
5+
struct Emm(Foo);
6+
impl Deref for Bar{
7+
type Target = u8;
8+
fn deref(&self) -> &Self::Target {
9+
&self.0
10+
}
11+
}
12+
impl Deref for Foo {
13+
type Target = Bar;
14+
fn deref(&self) -> &Self::Target {
15+
&self.0
16+
}
17+
}
18+
impl Deref for Emm {
19+
type Target = Foo;
20+
fn deref(&self) -> &Self::Target {
21+
&self.0
22+
}
23+
}
24+
fn main() {
25+
let a = Emm(Foo(Bar(0)));
26+
// Should suggest `&***` even when deref is pretty deep
27+
let _: *const u8 = &***a; //~ ERROR mismatched types
28+
}

src/test/ui/issues/issue-32122-2.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// run-rustfix
2+
use std::ops::Deref;
3+
struct Bar(u8);
4+
struct Foo(Bar);
5+
struct Emm(Foo);
6+
impl Deref for Bar{
7+
type Target = u8;
8+
fn deref(&self) -> &Self::Target {
9+
&self.0
10+
}
11+
}
12+
impl Deref for Foo {
13+
type Target = Bar;
14+
fn deref(&self) -> &Self::Target {
15+
&self.0
16+
}
17+
}
18+
impl Deref for Emm {
19+
type Target = Foo;
20+
fn deref(&self) -> &Self::Target {
21+
&self.0
22+
}
23+
}
24+
fn main() {
25+
let a = Emm(Foo(Bar(0)));
26+
// Should suggest `&***` even when deref is pretty deep
27+
let _: *const u8 = &a; //~ ERROR mismatched types
28+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/issue-32122-2.rs:27:24
3+
|
4+
LL | let _: *const u8 = &a;
5+
| --------- ^^
6+
| | |
7+
| | expected `u8`, found struct `Emm`
8+
| | help: consider dereferencing the reference: `&***a`
9+
| expected due to this
10+
|
11+
= note: expected raw pointer `*const u8`
12+
found reference `&Emm`
13+
14+
error: aborting due to previous error
15+
16+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)