about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_typeck/check/coercion.rs15
-rw-r--r--src/librustc_typeck/check/demand.rs125
-rw-r--r--src/librustc_typeck/check/mod.rs4
-rw-r--r--src/test/ui/issues/issue-32122-1.stderr2
-rw-r--r--src/test/ui/issues/issue-32122-2.stderr2
-rw-r--r--src/test/ui/issues/issue-71676-1.fixed54
-rw-r--r--src/test/ui/issues/issue-71676-1.rs54
-rw-r--r--src/test/ui/issues/issue-71676-1.stderr55
-rw-r--r--src/test/ui/issues/issue-71676-2.rs43
-rw-r--r--src/test/ui/issues/issue-71676-2.stderr16
10 files changed, 323 insertions, 47 deletions
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index e3b16eaaef2..c336ec13479 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;
 
-pub struct Coerce<'a, 'tcx> {
+struct Coerce<'a, 'tcx> {
     fcx: &'a FnCtxt<'a, 'tcx>,
     cause: ObligationCause<'tcx>,
     use_lub: bool,
@@ -126,7 +126,7 @@ fn success<'tcx>(
 }
 
 impl<'f, 'tcx> Coerce<'f, 'tcx> {
-    pub fn new(
+    fn new(
         fcx: &'f FnCtxt<'f, 'tcx>,
         cause: ObligationCause<'tcx>,
         allow_two_phase: AllowTwoPhase,
@@ -134,7 +134,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         Coerce { fcx, cause, allow_two_phase, use_lub: false }
     }
 
-    pub fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
+    fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
         debug!("unify(a: {:?}, b: {:?}, use_lub: {})", a, b, self.use_lub);
         self.commit_if_ok(|_| {
             if self.use_lub {
@@ -831,6 +831,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.probe(|_| coerce.coerce(source, target)).is_ok()
     }
 
+    pub fn deref_steps(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> Option<usize> {
+        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);
+        coerce
+            .autoderef(rustc_span::DUMMY_SP, expr_ty)
+            .find_map(|(ty, steps)| coerce.unify(ty, target).ok().map(|_| steps))
+    }
+
     /// Given some expressions, their known unified type and another expression,
     /// tries to unify the types, potentially inserting coercions on any of the
     /// provided expressions and returns their LUB (aka "common supertype").
diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs
index 65ef9cad874..2c4ab40ccd4 100644
--- a/src/librustc_typeck/check/demand.rs
+++ b/src/librustc_typeck/check/demand.rs
@@ -1,4 +1,3 @@
-use crate::check::coercion::Coerce;
 use crate::check::FnCtxt;
 use rustc_infer::infer::InferOk;
 use rustc_trait_selection::infer::InferCtxtExt as _;
@@ -9,7 +8,6 @@ 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, TypeAndMut};
 use rustc_span::symbol::sym;
@@ -376,7 +374,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expr: &hir::Expr<'_>,
         checked_ty: Ty<'tcx>,
         expected: Ty<'tcx>,
-    ) -> Option<(Span, &'static str, String)> {
+    ) -> Option<(Span, &'static str, String, Applicability)> {
         let sm = self.sess().source_map();
         let sp = expr.span;
         if sm.is_imported(sp) {
@@ -395,16 +393,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // `ExprKind::DropTemps` is semantically irrelevant for these suggestions.
         let expr = expr.peel_drop_temps();
 
+        let remove_prefix = |s: String, prefix: &str| {
+            if s.starts_with(prefix) { Some(s[prefix.len()..].to_string()) } else { None }
+        };
+
         match (&expr.kind, &expected.kind, &checked_ty.kind) {
             (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (&exp.kind, &check.kind) {
                 (&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => {
                     if let hir::ExprKind::Lit(_) = expr.kind {
                         if let Ok(src) = sm.span_to_snippet(sp) {
-                            if src.starts_with("b\"") {
+                            if let Some(src) = remove_prefix(src, "b\"") {
                                 return Some((
                                     sp,
                                     "consider removing the leading `b`",
-                                    src[1..].to_string(),
+                                    format!("\"{}", src),
+                                    Applicability::MachineApplicable,
                                 ));
                             }
                         }
@@ -413,11 +416,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 (&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => {
                     if let hir::ExprKind::Lit(_) = expr.kind {
                         if let Ok(src) = sm.span_to_snippet(sp) {
-                            if src.starts_with('"') {
+                            if let Some(src) = remove_prefix(src, "\"") {
                                 return Some((
                                     sp,
                                     "consider adding a leading `b`",
-                                    format!("b{}", src),
+                                    format!("b\"{}", src),
+                                    Applicability::MachineApplicable,
                                 ));
                             }
                         }
@@ -470,7 +474,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         let sugg_expr = if needs_parens { format!("({})", src) } else { src };
 
                         if let Some(sugg) = self.can_use_as_ref(expr) {
-                            return Some(sugg);
+                            return Some((
+                                sugg.0,
+                                sugg.1,
+                                sugg.2,
+                                Applicability::MachineApplicable,
+                            ));
                         }
                         let field_name = if is_struct_pat_shorthand_field {
                             format!("{}: ", sugg_expr)
@@ -495,6 +504,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                         "consider dereferencing here to assign to the mutable \
                                          borrowed piece of memory",
                                         format!("*{}", src),
+                                        Applicability::MachineApplicable,
                                     ));
                                 }
                             }
@@ -505,11 +515,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 sp,
                                 "consider mutably borrowing here",
                                 format!("{}&mut {}", field_name, sugg_expr),
+                                Applicability::MachineApplicable,
                             ),
                             hir::Mutability::Not => (
                                 sp,
                                 "consider borrowing here",
                                 format!("{}&{}", field_name, sugg_expr),
+                                Applicability::MachineApplicable,
                             ),
                         });
                     }
@@ -526,51 +538,84 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // We have `&T`, check if what was expected was `T`. If so,
                 // we may want to suggest removing a `&`.
                 if sm.is_imported(expr.span) {
-                    if let Ok(code) = sm.span_to_snippet(sp) {
-                        if code.starts_with('&') {
+                    if let Ok(src) = sm.span_to_snippet(sp) {
+                        if let Some(src) = remove_prefix(src, "&") {
                             return Some((
                                 sp,
                                 "consider removing the borrow",
-                                code[1..].to_string(),
+                                src,
+                                Applicability::MachineApplicable,
                             ));
                         }
                     }
                     return None;
                 }
                 if let Ok(code) = sm.span_to_snippet(expr.span) {
-                    return Some((sp, "consider removing the borrow", code));
+                    return Some((
+                        sp,
+                        "consider removing the borrow",
+                        code,
+                        Applicability::MachineApplicable,
+                    ));
                 }
             }
             (
                 _,
-                &ty::RawPtr(TypeAndMut { ty: _, mutbl: hir::Mutability::Not }),
-                &ty::Ref(_, _, hir::Mutability::Not),
+                &ty::RawPtr(TypeAndMut { ty: ty_b, mutbl: mutbl_b }),
+                &ty::Ref(_, ty_a, mutbl_a),
             ) => {
-                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 let Some(steps) = self.deref_steps(ty_a, ty_b) {
+                    // Only suggest valid if dereferencing needed.
+                    if steps > 0 {
+                        // The pointer type implements `Copy` trait so the suggestion is always valid.
+                        if let Ok(src) = sm.span_to_snippet(sp) {
+                            let derefs = "*".repeat(steps);
+                            match mutbl_b {
+                                hir::Mutability::Mut => match mutbl_a {
+                                    hir::Mutability::Mut => {
+                                        if let Some(src) = remove_prefix(src, "&mut ") {
+                                            return Some((
+                                                sp,
+                                                "consider dereferencing",
+                                                format!("&mut {}{}", derefs, src),
+                                                Applicability::MachineApplicable,
+                                            ));
+                                        }
+                                    }
+                                    hir::Mutability::Not => {
+                                        if let Some(src) = remove_prefix(src, "&") {
+                                            return Some((
+                                                sp,
+                                                "consider dereferencing",
+                                                format!("&mut {}{}", derefs, src),
+                                                Applicability::Unspecified,
+                                            ));
+                                        }
+                                    }
+                                },
+                                hir::Mutability::Not => match mutbl_a {
+                                    hir::Mutability::Mut => {
+                                        if let Some(src) = remove_prefix(src, "&mut ") {
+                                            return Some((
+                                                sp,
+                                                "consider dereferencing",
+                                                format!("&{}{}", derefs, src),
+                                                Applicability::MachineApplicable,
+                                            ));
+                                        }
+                                    }
+                                    hir::Mutability::Not => {
+                                        if let Some(src) = remove_prefix(src, "&") {
+                                            return Some((
+                                                sp,
+                                                "consider dereferencing",
+                                                format!("&{}{}", derefs, src),
+                                                Applicability::MachineApplicable,
+                                            ));
+                                        }
+                                    }
+                                },
+                            }
                         }
                     }
                 }
@@ -616,7 +661,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         } else {
                             format!("*{}", code)
                         };
-                        return Some((sp, message, suggestion));
+                        return Some((sp, message, suggestion, Applicability::MachineApplicable));
                     }
                 }
             }
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index adbab3d4cb6..68817d3fe0f 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -5036,8 +5036,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
     ) {
-        if let Some((sp, msg, suggestion)) = self.check_ref(expr, found, expected) {
-            err.span_suggestion(sp, msg, suggestion, Applicability::MachineApplicable);
+        if let Some((sp, msg, suggestion, applicability)) = self.check_ref(expr, found, expected) {
+            err.span_suggestion(sp, msg, suggestion, applicability);
         } else if let (ty::FnDef(def_id, ..), true) =
             (&found.kind, self.suggest_fn_call(err, expr, expected, found))
         {
diff --git a/src/test/ui/issues/issue-32122-1.stderr b/src/test/ui/issues/issue-32122-1.stderr
index 313de275c53..dfbd3223efc 100644
--- a/src/test/ui/issues/issue-32122-1.stderr
+++ b/src/test/ui/issues/issue-32122-1.stderr
@@ -5,7 +5,7 @@ LL |     let _: *const u8 = &a;
    |            ---------   ^^
    |            |           |
    |            |           expected `u8`, found struct `Foo`
-   |            |           help: consider dereferencing the reference: `&*a`
+   |            |           help: consider dereferencing: `&*a`
    |            expected due to this
    |
    = note: expected raw pointer `*const u8`
diff --git a/src/test/ui/issues/issue-32122-2.stderr b/src/test/ui/issues/issue-32122-2.stderr
index 959a49507e4..2e199e2a19f 100644
--- a/src/test/ui/issues/issue-32122-2.stderr
+++ b/src/test/ui/issues/issue-32122-2.stderr
@@ -5,7 +5,7 @@ LL |     let _: *const u8 = &a;
    |            ---------   ^^
    |            |           |
    |            |           expected `u8`, found struct `Emm`
-   |            |           help: consider dereferencing the reference: `&***a`
+   |            |           help: consider dereferencing: `&***a`
    |            expected due to this
    |
    = note: expected raw pointer `*const u8`
diff --git a/src/test/ui/issues/issue-71676-1.fixed b/src/test/ui/issues/issue-71676-1.fixed
new file mode 100644
index 00000000000..d2be0db95fb
--- /dev/null
+++ b/src/test/ui/issues/issue-71676-1.fixed
@@ -0,0 +1,54 @@
+// run-rustfix
+use std::ops::Deref;
+use std::ops::DerefMut;
+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
+    }
+}
+impl DerefMut for Bar{
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+impl DerefMut for Foo {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+impl DerefMut for Emm {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+fn main() {
+    // Suggest dereference with arbitrary mutability
+    let a = Emm(Foo(Bar(0)));
+    let _: *const u8 = &***a; //~ ERROR mismatched types
+
+    let mut a = Emm(Foo(Bar(0)));
+    let _: *mut u8 = &mut ***a; //~ ERROR mismatched types
+
+    let a = Emm(Foo(Bar(0)));
+    let _: *const u8 = &***a; //~ ERROR mismatched types
+
+    let mut a = Emm(Foo(Bar(0)));
+    let _: *mut u8 = &mut ***a; //~ ERROR mismatched types
+
+}
diff --git a/src/test/ui/issues/issue-71676-1.rs b/src/test/ui/issues/issue-71676-1.rs
new file mode 100644
index 00000000000..c09ad6dabaa
--- /dev/null
+++ b/src/test/ui/issues/issue-71676-1.rs
@@ -0,0 +1,54 @@
+// run-rustfix
+use std::ops::Deref;
+use std::ops::DerefMut;
+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
+    }
+}
+impl DerefMut for Bar{
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+impl DerefMut for Foo {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+impl DerefMut for Emm {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+fn main() {
+    // Suggest dereference with arbitrary mutability
+    let a = Emm(Foo(Bar(0)));
+    let _: *const u8 = &a; //~ ERROR mismatched types
+
+    let mut a = Emm(Foo(Bar(0)));
+    let _: *mut u8 = &a; //~ ERROR mismatched types
+
+    let a = Emm(Foo(Bar(0)));
+    let _: *const u8 = &mut a; //~ ERROR mismatched types
+
+    let mut a = Emm(Foo(Bar(0)));
+    let _: *mut u8 = &mut a; //~ ERROR mismatched types
+
+}
diff --git a/src/test/ui/issues/issue-71676-1.stderr b/src/test/ui/issues/issue-71676-1.stderr
new file mode 100644
index 00000000000..bbabc2202dc
--- /dev/null
+++ b/src/test/ui/issues/issue-71676-1.stderr
@@ -0,0 +1,55 @@
+error[E0308]: mismatched types
+  --> $DIR/issue-71676-1.rs:43:24
+   |
+LL |     let _: *const u8 = &a;
+   |            ---------   ^^
+   |            |           |
+   |            |           expected `u8`, found struct `Emm`
+   |            |           help: consider dereferencing: `&***a`
+   |            expected due to this
+   |
+   = note: expected raw pointer `*const u8`
+                found reference `&Emm`
+
+error[E0308]: mismatched types
+  --> $DIR/issue-71676-1.rs:46:22
+   |
+LL |     let _: *mut u8 = &a;
+   |            -------   ^^
+   |            |         |
+   |            |         types differ in mutability
+   |            |         help: consider dereferencing: `&mut ***a`
+   |            expected due to this
+   |
+   = note: expected raw pointer `*mut u8`
+                found reference `&Emm`
+
+error[E0308]: mismatched types
+  --> $DIR/issue-71676-1.rs:49:24
+   |
+LL |     let _: *const u8 = &mut a;
+   |            ---------   ^^^^^^
+   |            |           |
+   |            |           expected `u8`, found struct `Emm`
+   |            |           help: consider dereferencing: `&***a`
+   |            expected due to this
+   |
+   = note:    expected raw pointer `*const u8`
+           found mutable reference `&mut Emm`
+
+error[E0308]: mismatched types
+  --> $DIR/issue-71676-1.rs:52:22
+   |
+LL |     let _: *mut u8 = &mut a;
+   |            -------   ^^^^^^
+   |            |         |
+   |            |         expected `u8`, found struct `Emm`
+   |            |         help: consider dereferencing: `&mut ***a`
+   |            expected due to this
+   |
+   = note:    expected raw pointer `*mut u8`
+           found mutable reference `&mut Emm`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/issues/issue-71676-2.rs b/src/test/ui/issues/issue-71676-2.rs
new file mode 100644
index 00000000000..d11fce567ce
--- /dev/null
+++ b/src/test/ui/issues/issue-71676-2.rs
@@ -0,0 +1,43 @@
+use std::ops::Deref;
+use std::ops::DerefMut;
+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
+    }
+}
+impl DerefMut for Bar{
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+impl DerefMut for Foo {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+impl DerefMut for Emm {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+fn main() {
+    // Should not suggest when a is immutable
+    let a = Emm(Foo(Bar(0)));
+    let _: *mut u8 = &a; //~ ERROR mismatched types
+}
diff --git a/src/test/ui/issues/issue-71676-2.stderr b/src/test/ui/issues/issue-71676-2.stderr
new file mode 100644
index 00000000000..273ae9cb4db
--- /dev/null
+++ b/src/test/ui/issues/issue-71676-2.stderr
@@ -0,0 +1,16 @@
+error[E0308]: mismatched types
+  --> $DIR/issue-71676-2.rs:42:22
+   |
+LL |     let _: *mut u8 = &a;
+   |            -------   ^^
+   |            |         |
+   |            |         types differ in mutability
+   |            |         help: consider dereferencing: `&mut ***a`
+   |            expected due to this
+   |
+   = note: expected raw pointer `*mut u8`
+                found reference `&Emm`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.