Skip to content

Commit

Permalink
Suggest deref when coercing ty::Ref to ty::RawPtr
Browse files Browse the repository at this point in the history
  • Loading branch information
ldm0 committed Apr 29, 2020
1 parent a9340b1 commit a985879
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 4 deletions.
6 changes: 3 additions & 3 deletions src/librustc_typeck/check/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
use smallvec::{smallvec, SmallVec};
use std::ops::Deref;

struct Coerce<'a, 'tcx> {
pub struct Coerce<'a, 'tcx> {
fcx: &'a FnCtxt<'a, 'tcx>,
cause: ObligationCause<'tcx>,
use_lub: bool,
Expand Down Expand Up @@ -124,15 +124,15 @@ fn success<'tcx>(
}

impl<'f, 'tcx> Coerce<'f, 'tcx> {
fn new(
pub fn new(
fcx: &'f FnCtxt<'f, 'tcx>,
cause: ObligationCause<'tcx>,
allow_two_phase: AllowTwoPhase,
) -> Self {
Coerce { fcx, cause, allow_two_phase, use_lub: false }
}

fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
pub fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
self.commit_if_ok(|_| {
if self.use_lub {
self.at(&self.cause, self.fcx.param_env).lub(b, a)
Expand Down
38 changes: 37 additions & 1 deletion src/librustc_typeck/check/demand.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::check::coercion::Coerce;
use crate::check::FnCtxt;
use rustc_infer::infer::InferOk;
use rustc_trait_selection::infer::InferCtxtExt as _;
Expand All @@ -8,8 +9,9 @@ use rustc_ast::util::parser::PREC_POSTFIX;
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::{is_range_literal, Node};
use rustc_middle::traits::ObligationCauseCode;
use rustc_middle::ty::adjustment::AllowTwoPhase;
use rustc_middle::ty::{self, AssocItem, Ty};
use rustc_middle::ty::{self, AssocItem, Ty, TypeAndMut};
use rustc_span::symbol::sym;
use rustc_span::Span;

Expand Down Expand Up @@ -539,6 +541,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return Some((sp, "consider removing the borrow", code));
}
}
(
_,
&ty::RawPtr(TypeAndMut { ty: _, mutbl: hir::Mutability::Not }),
&ty::Ref(_, _, hir::Mutability::Not),
) => {
let cause = self.cause(rustc_span::DUMMY_SP, ObligationCauseCode::ExprAssignable);
// We don't ever need two-phase here since we throw out the result of the coercion
let coerce = Coerce::new(self, cause, AllowTwoPhase::No);

if let Some(steps) =
coerce.autoderef(sp, checked_ty).skip(1).find_map(|(referent_ty, steps)| {
coerce
.unify(
coerce.tcx.mk_ptr(ty::TypeAndMut {
mutbl: hir::Mutability::Not,
ty: referent_ty,
}),
expected,
)
.ok()
.map(|_| steps)
})
{
// The pointer type implements `Copy` trait so the suggestion is always valid.
if let Ok(code) = sm.span_to_snippet(sp) {
if code.starts_with('&') {
let derefs = "*".repeat(steps - 1);
let message = "consider dereferencing the reference";
let suggestion = format!("&{}{}", derefs, code[1..].to_string());
return Some((sp, message, suggestion));
}
}
}
}
_ if sp == expr.span && !is_macro => {
// Check for `Deref` implementations by constructing a predicate to
// prove: `<T as Deref>::Output == U`
Expand Down
17 changes: 17 additions & 0 deletions src/test/ui/issues/issue-32122-1.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// run-rustfix
use std::ops::Deref;

struct Foo(u8);

impl Deref for Foo {
type Target = u8;
fn deref(&self) -> &Self::Target {
&self.0
}
}

fn main() {
let a = Foo(0);
// Should suggest `&*` when coercing &ty to *const ty
let _: *const u8 = &*a; //~ ERROR mismatched types
}
17 changes: 17 additions & 0 deletions src/test/ui/issues/issue-32122-1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// run-rustfix
use std::ops::Deref;

struct Foo(u8);

impl Deref for Foo {
type Target = u8;
fn deref(&self) -> &Self::Target {
&self.0
}
}

fn main() {
let a = Foo(0);
// Should suggest `&*` when coercing &ty to *const ty
let _: *const u8 = &a; //~ ERROR mismatched types
}
16 changes: 16 additions & 0 deletions src/test/ui/issues/issue-32122-1.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error[E0308]: mismatched types
--> $DIR/issue-32122-1.rs:16:24
|
LL | let _: *const u8 = &a;
| --------- ^^
| | |
| | expected `u8`, found struct `Foo`
| | help: consider dereferencing the reference: `&*a`
| expected due to this
|
= note: expected raw pointer `*const u8`
found reference `&Foo`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
28 changes: 28 additions & 0 deletions src/test/ui/issues/issue-32122-2.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// run-rustfix
use std::ops::Deref;
struct Bar(u8);
struct Foo(Bar);
struct Emm(Foo);
impl Deref for Bar{
type Target = u8;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Deref for Foo {
type Target = Bar;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Deref for Emm {
type Target = Foo;
fn deref(&self) -> &Self::Target {
&self.0
}
}
fn main() {
let a = Emm(Foo(Bar(0)));
// Should suggest `&***` even when deref is pretty deep
let _: *const u8 = &***a; //~ ERROR mismatched types
}
28 changes: 28 additions & 0 deletions src/test/ui/issues/issue-32122-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// run-rustfix
use std::ops::Deref;
struct Bar(u8);
struct Foo(Bar);
struct Emm(Foo);
impl Deref for Bar{
type Target = u8;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Deref for Foo {
type Target = Bar;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Deref for Emm {
type Target = Foo;
fn deref(&self) -> &Self::Target {
&self.0
}
}
fn main() {
let a = Emm(Foo(Bar(0)));
// Should suggest `&***` even when deref is pretty deep
let _: *const u8 = &a; //~ ERROR mismatched types
}
16 changes: 16 additions & 0 deletions src/test/ui/issues/issue-32122-2.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error[E0308]: mismatched types
--> $DIR/issue-32122-2.rs:27:24
|
LL | let _: *const u8 = &a;
| --------- ^^
| | |
| | expected `u8`, found struct `Emm`
| | help: consider dereferencing the reference: `&***a`
| expected due to this
|
= note: expected raw pointer `*const u8`
found reference `&Emm`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.

0 comments on commit a985879

Please sign in to comment.