about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <dylan.dpc@gmail.com>2020-04-30 14:07:53 +0200
committerGitHub <noreply@github.com>2020-04-30 14:07:53 +0200
commit58d955e6cc84b92a6013a3e170e076f65a0c1b18 (patch)
tree311eed86e28394afb831c12b9943f32f602f7076
parent4e6772b52bc9832bb47bedba61abdcfb4ba0128e (diff)
parenta9858791134253d004971664fd7e9bd9b0983723 (diff)
downloadrust-58d955e6cc84b92a6013a3e170e076f65a0c1b18.tar.gz
rust-58d955e6cc84b92a6013a3e170e076f65a0c1b18.zip
Rollup merge of #71540 - ldm0:ref2ptr, r=oli-obk
Suggest deref when coercing `ty::Ref` to `ty::RawPtr`

Fixes #32122

Currently we do autoderef when casting `ty::Ref` ->`ty::Ref`, but we don't autoderef when casting `ty::Ref` -> `ty::RawPtr`. This PR make the compiler suggests deref when coercing `ty::Ref` to `ty::RawPtr`
-rw-r--r--src/librustc_typeck/check/coercion.rs8
-rw-r--r--src/librustc_typeck/check/demand.rs40
-rw-r--r--src/librustc_typeck/check/expr.rs2
-rw-r--r--src/librustc_typeck/check/mod.rs2
-rw-r--r--src/test/ui/issues/issue-32122-1.fixed17
-rw-r--r--src/test/ui/issues/issue-32122-1.rs17
-rw-r--r--src/test/ui/issues/issue-32122-1.stderr16
-rw-r--r--src/test/ui/issues/issue-32122-2.fixed28
-rw-r--r--src/test/ui/issues/issue-32122-2.rs28
-rw-r--r--src/test/ui/issues/issue-32122-2.stderr16
10 files changed, 166 insertions, 8 deletions
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index 3d665123f67..5d1a1a16485 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -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,
@@ -124,7 +124,7 @@ 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,
@@ -132,7 +132,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         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)
@@ -771,10 +771,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
             ty::RawPtr(mt) => (false, mt),
             _ => return self.unify_and(a, b, identity),
         };
+        coerce_mutbls(mt_a.mutbl, mutbl_b)?;
 
         // Check that the types which they point at are compatible.
         let a_unsafe = self.tcx.mk_ptr(ty::TypeAndMut { mutbl: mutbl_b, ty: mt_a.ty });
-        coerce_mutbls(mt_a.mutbl, mutbl_b)?;
         // Although references and unsafe ptrs have the same
         // representation, we still register an Adjust::DerefRef so that
         // regionck knows that the region for `a` must be valid here.
diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs
index aa36bec6e1e..65ef9cad874 100644
--- a/src/librustc_typeck/check/demand.rs
+++ b/src/librustc_typeck/check/demand.rs
@@ -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 _;
@@ -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;
 
@@ -25,7 +27,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) {
         self.annotate_expected_due_to_let_ty(err, expr);
         self.suggest_compatible_variants(err, expr, expected, expr_ty);
-        self.suggest_ref_or_into(err, expr, expected, expr_ty);
+        self.suggest_deref_ref_or_into(err, expr, expected, expr_ty);
         if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) {
             return;
         }
@@ -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`
diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs
index d287589789e..92ddfbff824 100644
--- a/src/librustc_typeck/check/expr.rs
+++ b/src/librustc_typeck/check/expr.rs
@@ -86,7 +86,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) {
             let expr = expr.peel_drop_temps();
-            self.suggest_ref_or_into(&mut err, expr, expected_ty, ty);
+            self.suggest_deref_ref_or_into(&mut err, expr, expected_ty, ty);
             extend_err(&mut err);
             // Error possibly reported in `check_assign` so avoid emitting error again.
             err.emit_unless(self.is_assign_to_bool(expr, expected_ty));
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 5877c6d269a..adbab3d4cb6 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -5029,7 +5029,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         false
     }
 
-    pub fn suggest_ref_or_into(
+    pub fn suggest_deref_ref_or_into(
         &self,
         err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'_>,
diff --git a/src/test/ui/issues/issue-32122-1.fixed b/src/test/ui/issues/issue-32122-1.fixed
new file mode 100644
index 00000000000..4fc5f64ff9a
--- /dev/null
+++ b/src/test/ui/issues/issue-32122-1.fixed
@@ -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
+}
diff --git a/src/test/ui/issues/issue-32122-1.rs b/src/test/ui/issues/issue-32122-1.rs
new file mode 100644
index 00000000000..3c4859f07a2
--- /dev/null
+++ b/src/test/ui/issues/issue-32122-1.rs
@@ -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
+}
diff --git a/src/test/ui/issues/issue-32122-1.stderr b/src/test/ui/issues/issue-32122-1.stderr
new file mode 100644
index 00000000000..313de275c53
--- /dev/null
+++ b/src/test/ui/issues/issue-32122-1.stderr
@@ -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`.
diff --git a/src/test/ui/issues/issue-32122-2.fixed b/src/test/ui/issues/issue-32122-2.fixed
new file mode 100644
index 00000000000..cee0e592976
--- /dev/null
+++ b/src/test/ui/issues/issue-32122-2.fixed
@@ -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
+}
diff --git a/src/test/ui/issues/issue-32122-2.rs b/src/test/ui/issues/issue-32122-2.rs
new file mode 100644
index 00000000000..39e9df4224e
--- /dev/null
+++ b/src/test/ui/issues/issue-32122-2.rs
@@ -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
+}
diff --git a/src/test/ui/issues/issue-32122-2.stderr b/src/test/ui/issues/issue-32122-2.stderr
new file mode 100644
index 00000000000..959a49507e4
--- /dev/null
+++ b/src/test/ui/issues/issue-32122-2.stderr
@@ -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`.