diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_typeck/src/check/method/confirm.rs
index e5f19281b07..7a80524f1b7 100644
--- a/compiler/rustc_typeck/src/check/method/confirm.rs
+++ b/compiler/rustc_typeck/src/check/method/confirm.rs
@@ -155,32 +155,46 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
         let mut target =
             self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
 
-        if let Some(mutbl) = pick.autoref {
-            let region = self.next_region_var(infer::Autoref(self.span, pick.item));
-            target = self.tcx.mk_ref(region, ty::TypeAndMut { mutbl, ty: target });
-            let mutbl = match mutbl {
-                hir::Mutability::Not => AutoBorrowMutability::Not,
-                hir::Mutability::Mut => AutoBorrowMutability::Mut {
-                    // Method call receivers are the primary use case
-                    // for two-phase borrows.
-                    allow_two_phase_borrow: AllowTwoPhase::Yes,
-                },
-            };
-            adjustments
-                .push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), target });
+        match &pick.autoref_or_ptr_adjustment {
+            Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => {
+                let region = self.next_region_var(infer::Autoref(self.span, pick.item));
+                target = self.tcx.mk_ref(region, ty::TypeAndMut { mutbl: *mutbl, ty: target });
+                let mutbl = match mutbl {
+                    hir::Mutability::Not => AutoBorrowMutability::Not,
+                    hir::Mutability::Mut => AutoBorrowMutability::Mut {
+                        // Method call receivers are the primary use case
+                        // for two-phase borrows.
+                        allow_two_phase_borrow: AllowTwoPhase::Yes,
+                    },
+                };
+                adjustments.push(Adjustment {
+                    kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
+                    target,
+                });
 
-            if let Some(unsize_target) = pick.unsize {
-                target = self
-                    .tcx
-                    .mk_ref(region, ty::TypeAndMut { mutbl: mutbl.into(), ty: unsize_target });
-                adjustments.push(Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target });
+                if let Some(unsize_target) = unsize {
+                    target = self
+                        .tcx
+                        .mk_ref(region, ty::TypeAndMut { mutbl: mutbl.into(), ty: unsize_target });
+                    adjustments
+                        .push(Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target });
+                }
             }
-        } else {
-            // No unsizing should be performed without autoref (at
-            // least during method dispach). This is because we
-            // currently only unsize `[T;N]` to `[T]`, and naturally
-            // that must occur being a reference.
-            assert!(pick.unsize.is_none());
+            Some(probe::AutorefOrPtrAdjustment::ToConstPtr) => {
+                target = match target.kind() {
+                    ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => {
+                        assert_eq!(*mutbl, hir::Mutability::Mut);
+                        self.tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty })
+                    }
+                    other => panic!("Cannot adjust receiver type {:?} to const ptr", other),
+                };
+
+                adjustments.push(Adjustment {
+                    kind: Adjust::Pointer(PointerCast::MutToConstPointer),
+                    target,
+                });
+            }
+            None => {}
         }
 
         self.register_predicates(autoderef.into_obligations());
diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs
index 38ff88553e8..42d7f4c988d 100644
--- a/compiler/rustc_typeck/src/check/method/probe.rs
+++ b/compiler/rustc_typeck/src/check/method/probe.rs
@@ -154,6 +154,42 @@ enum ProbeResult {
     Match,
 }
 
+/// When adjusting a receiver we often want to do one of
+///
+/// - Add a `&` (or `&mut`), converting the recevier from `T` to `&T` (or `&mut T`)
+/// - If the receiver has type `*mut T`, convert it to `*const T`
+///
+/// This type tells us which one to do.
+///
+/// Note that in principle we could do both at the same time. For example, when the receiver has
+/// type `T`, we could autoref it to `&T`, then convert to `*const T`. Or, when it has type `*mut
+/// T`, we could convert it to `*const T`, then autoref to `&*const T`. However, currently we do
+/// (at most) one of these. Either the receiver has type `T` and we convert it to `&T` (or with
+/// `mut`), or it has type `*mut T` and we convert it to `*const T`.
+#[derive(Debug, PartialEq, Clone)]
+pub enum AutorefOrPtrAdjustment<'tcx> {
+    /// Receiver has type `T`, add `&` or `&mut` (it `T` is `mut`), and maybe also "unsize" it.
+    /// Unsizing is used to convert a `[T; N]` to `[T]`, which only makes sense when autorefing.
+    Autoref {
+        mutbl: hir::Mutability,
+
+        /// Indicates that the source expression should be "unsized" to a target type. This should
+        /// probably eventually go away in favor of just coercing method receivers.
+        unsize: Option<Ty<'tcx>>,
+    },
+    /// Receiver has type `*mut T`, convert to `*const T`
+    ToConstPtr,
+}
+
+impl<'tcx> AutorefOrPtrAdjustment<'tcx> {
+    fn get_unsize(&self) -> Option<Ty<'tcx>> {
+        match self {
+            AutorefOrPtrAdjustment::Autoref { mutbl: _, unsize } => unsize.clone(),
+            AutorefOrPtrAdjustment::ToConstPtr => None,
+        }
+    }
+}
+
 #[derive(Debug, PartialEq, Clone)]
 pub struct Pick<'tcx> {
     pub item: ty::AssocItem,
@@ -165,17 +201,9 @@ pub struct Pick<'tcx> {
     ///     A = expr | *expr | **expr | ...
     pub autoderefs: usize,
 
-    /// Indicates that an autoref is applied after the optional autoderefs
-    ///
-    ///     B = A | &A | &mut A
-    pub autoref: Option<hir::Mutability>,
-
-    /// Indicates that the source expression should be "unsized" to a
-    /// target type. This should probably eventually go away in favor
-    /// of just coercing method receivers.
-    ///
-    ///     C = B | unsize(B)
-    pub unsize: Option<Ty<'tcx>>,
+    /// Indicates that we want to add an autoref (and maybe also unsize it), or if the receiver is
+    /// `*mut T`, convert it to `*const T`.
+    pub autoref_or_ptr_adjustment: Option<AutorefOrPtrAdjustment<'tcx>>,
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]
@@ -714,6 +742,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
 
         debug!("assemble_inherent_impl_probe {:?}", impl_def_id);
 
+        let (impl_ty, impl_substs) = self.impl_ty_and_substs(impl_def_id);
+        let impl_ty = impl_ty.subst(self.tcx, impl_substs);
+
         for item in self.impl_or_trait_item(impl_def_id) {
             if !self.has_applicable_self(&item) {
                 // No receiver declared. Not a candidate.
@@ -721,9 +752,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                 continue;
             }
 
-            let (impl_ty, impl_substs) = self.impl_ty_and_substs(impl_def_id);
-            let impl_ty = impl_ty.subst(self.tcx, impl_substs);
-
             // Determine the receiver type that the method itself expects.
             let xform_tys = self.xform_self_ty(&item, impl_ty, impl_substs);
 
@@ -1086,6 +1114,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                 self.pick_by_value_method(step, self_ty).or_else(|| {
                     self.pick_autorefd_method(step, self_ty, hir::Mutability::Not)
                         .or_else(|| self.pick_autorefd_method(step, self_ty, hir::Mutability::Mut))
+                        .or_else(|| self.pick_const_ptr_method(step, self_ty))
                 })
             })
             .next()
@@ -1113,7 +1142,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                 // Insert a `&*` or `&mut *` if this is a reference type:
                 if let ty::Ref(_, _, mutbl) = *step.self_ty.value.value.kind() {
                     pick.autoderefs += 1;
-                    pick.autoref = Some(mutbl);
+                    pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref {
+                        mutbl,
+                        unsize: pick.autoref_or_ptr_adjustment.and_then(|a| a.get_unsize()),
+                    })
                 }
 
                 pick
@@ -1136,8 +1168,39 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         self.pick_method(autoref_ty).map(|r| {
             r.map(|mut pick| {
                 pick.autoderefs = step.autoderefs;
-                pick.autoref = Some(mutbl);
-                pick.unsize = step.unsize.then_some(self_ty);
+                pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref {
+                    mutbl,
+                    unsize: step.unsize.then_some(self_ty),
+                });
+                pick
+            })
+        })
+    }
+
+    /// If `self_ty` is `*mut T` then this picks `*const T` methods. The reason why we have a
+    /// special case for this is because going from `*mut T` to `*const T` with autoderefs and
+    /// autorefs would require dereferencing the pointer, which is not safe.
+    fn pick_const_ptr_method(
+        &mut self,
+        step: &CandidateStep<'tcx>,
+        self_ty: Ty<'tcx>,
+    ) -> Option<PickResult<'tcx>> {
+        // Don't convert an unsized reference to ptr
+        if step.unsize {
+            return None;
+        }
+
+        let ty = match self_ty.kind() {
+            ty::RawPtr(ty::TypeAndMut { ty, mutbl: hir::Mutability::Mut }) => ty,
+            _ => return None,
+        };
+
+        let const_self_ty = ty::TypeAndMut { ty, mutbl: hir::Mutability::Not };
+        let const_ptr_ty = self.tcx.mk_ptr(const_self_ty);
+        self.pick_method(const_ptr_ty).map(|r| {
+            r.map(|mut pick| {
+                pick.autoderefs = step.autoderefs;
+                pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::ToConstPtr);
                 pick
             })
         })
@@ -1510,8 +1573,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
             kind: TraitPick,
             import_ids: probes[0].0.import_ids.clone(),
             autoderefs: 0,
-            autoref: None,
-            unsize: None,
+            autoref_or_ptr_adjustment: None,
         })
     }
 
@@ -1748,8 +1810,7 @@ impl<'tcx> Candidate<'tcx> {
             },
             import_ids: self.import_ids.clone(),
             autoderefs: 0,
-            autoref: None,
-            unsize: None,
+            autoref_or_ptr_adjustment: None,
         }
     }
 }
diff --git a/src/test/mir-opt/receiver-ptr-mutability.rs b/src/test/mir-opt/receiver-ptr-mutability.rs
new file mode 100644
index 00000000000..8e2ff0451c6
--- /dev/null
+++ b/src/test/mir-opt/receiver-ptr-mutability.rs
@@ -0,0 +1,20 @@
+// EMIT_MIR receiver_ptr_mutability.main.mir_map.0.mir
+
+#![feature(arbitrary_self_types)]
+
+struct Test {}
+
+impl Test {
+    fn x(self: *const Self) {
+        println!("x called");
+    }
+}
+
+fn main() {
+    let ptr: *mut Test = std::ptr::null_mut();
+    ptr.x();
+
+    // Test autoderefs
+    let ptr_ref: &&&&*mut Test = &&&&ptr;
+    ptr_ref.x();
+}
diff --git a/src/test/mir-opt/receiver_ptr_mutability.main.mir_map.0.mir b/src/test/mir-opt/receiver_ptr_mutability.main.mir_map.0.mir
new file mode 100644
index 00000000000..d2d96ff468d
--- /dev/null
+++ b/src/test/mir-opt/receiver_ptr_mutability.main.mir_map.0.mir
@@ -0,0 +1,96 @@
+// MIR for `main` 0 mir_map
+
+| User Type Annotations
+| 0: Canonical { max_universe: U0, variables: [], value: Ty(*mut Test) } at $DIR/receiver-ptr-mutability.rs:14:14: 14:23
+| 1: Canonical { max_universe: U0, variables: [], value: Ty(*mut Test) } at $DIR/receiver-ptr-mutability.rs:14:14: 14:23
+| 2: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }], value: Ty(&&&&*mut Test) } at $DIR/receiver-ptr-mutability.rs:18:18: 18:31
+| 3: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }], value: Ty(&&&&*mut Test) } at $DIR/receiver-ptr-mutability.rs:18:18: 18:31
+|
+fn main() -> () {
+    let mut _0: ();                      // return place in scope 0 at $DIR/receiver-ptr-mutability.rs:13:11: 13:11
+    let _1: *mut Test as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/receiver-ptr-mutability.rs:14:9: 14:12
+    let _2: ();                          // in scope 0 at $DIR/receiver-ptr-mutability.rs:15:5: 15:12
+    let mut _3: *const Test;             // in scope 0 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
+    let mut _4: *mut Test;               // in scope 0 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
+    let _6: &&&&*mut Test;               // in scope 0 at $DIR/receiver-ptr-mutability.rs:18:34: 18:41
+    let _7: &&&*mut Test;                // in scope 0 at $DIR/receiver-ptr-mutability.rs:18:35: 18:41
+    let _8: &&*mut Test;                 // in scope 0 at $DIR/receiver-ptr-mutability.rs:18:36: 18:41
+    let _9: &*mut Test;                  // in scope 0 at $DIR/receiver-ptr-mutability.rs:18:37: 18:41
+    let _10: ();                         // in scope 0 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
+    let mut _11: *const Test;            // in scope 0 at $DIR/receiver-ptr-mutability.rs:19:5: 19:12
+    let mut _12: *mut Test;              // in scope 0 at $DIR/receiver-ptr-mutability.rs:19:5: 19:12
+    scope 1 {
+        debug ptr => _1;                 // in scope 1 at $DIR/receiver-ptr-mutability.rs:14:9: 14:12
+        let _5: &&&&*mut Test as UserTypeProjection { base: UserType(2), projs: [] }; // in scope 1 at $DIR/receiver-ptr-mutability.rs:18:9: 18:16
+        scope 2 {
+            debug ptr_ref => _5;         // in scope 2 at $DIR/receiver-ptr-mutability.rs:18:9: 18:16
+        }
+    }
+
+    bb0: {
+        StorageLive(_1);                 // scope 0 at $DIR/receiver-ptr-mutability.rs:14:9: 14:12
+        _1 = null_mut::<Test>() -> [return: bb1, unwind: bb4]; // scope 0 at $DIR/receiver-ptr-mutability.rs:14:26: 14:46
+                                         // mir::Constant
+                                         // + span: $DIR/receiver-ptr-mutability.rs:14:26: 14:44
+                                         // + literal: Const { ty: fn() -> *mut Test {std::ptr::null_mut::<Test>}, val: Value(Scalar(<ZST>)) }
+    }
+
+    bb1: {
+        FakeRead(ForLet, _1);            // scope 0 at $DIR/receiver-ptr-mutability.rs:14:9: 14:12
+        AscribeUserType(_1, o, UserTypeProjection { base: UserType(1), projs: [] }); // scope 0 at $DIR/receiver-ptr-mutability.rs:14:14: 14:23
+        StorageLive(_2);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:12
+        StorageLive(_3);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
+        StorageLive(_4);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
+        _4 = _1;                         // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
+        _3 = move _4 as *const Test (Pointer(MutToConstPointer)); // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
+        StorageDead(_4);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:15:7: 15:8
+        _2 = Test::x(move _3) -> [return: bb2, unwind: bb4]; // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:12
+                                         // mir::Constant
+                                         // + span: $DIR/receiver-ptr-mutability.rs:15:9: 15:10
+                                         // + literal: Const { ty: fn(*const Test) {Test::x}, val: Value(Scalar(<ZST>)) }
+    }
+
+    bb2: {
+        StorageDead(_3);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:15:11: 15:12
+        StorageDead(_2);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:15:12: 15:13
+        StorageLive(_5);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:18:9: 18:16
+        StorageLive(_6);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:18:34: 18:41
+        StorageLive(_7);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:18:35: 18:41
+        StorageLive(_8);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:18:36: 18:41
+        StorageLive(_9);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:18:37: 18:41
+        _9 = &_1;                        // scope 1 at $DIR/receiver-ptr-mutability.rs:18:37: 18:41
+        _8 = &_9;                        // scope 1 at $DIR/receiver-ptr-mutability.rs:18:36: 18:41
+        _7 = &_8;                        // scope 1 at $DIR/receiver-ptr-mutability.rs:18:35: 18:41
+        _6 = &_7;                        // scope 1 at $DIR/receiver-ptr-mutability.rs:18:34: 18:41
+        _5 = &(*_6);                     // scope 1 at $DIR/receiver-ptr-mutability.rs:18:34: 18:41
+        FakeRead(ForLet, _5);            // scope 1 at $DIR/receiver-ptr-mutability.rs:18:9: 18:16
+        AscribeUserType(_5, o, UserTypeProjection { base: UserType(3), projs: [] }); // scope 1 at $DIR/receiver-ptr-mutability.rs:18:18: 18:31
+        StorageDead(_6);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:18:41: 18:42
+        StorageLive(_10);                // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
+        StorageLive(_11);                // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:12
+        StorageLive(_12);                // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:12
+        _12 = (*(*(*(*_5))));            // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:12
+        _11 = move _12 as *const Test (Pointer(MutToConstPointer)); // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:12
+        StorageDead(_12);                // scope 2 at $DIR/receiver-ptr-mutability.rs:19:11: 19:12
+        _10 = Test::x(move _11) -> [return: bb3, unwind: bb4]; // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
+                                         // mir::Constant
+                                         // + span: $DIR/receiver-ptr-mutability.rs:19:13: 19:14
+                                         // + literal: Const { ty: fn(*const Test) {Test::x}, val: Value(Scalar(<ZST>)) }
+    }
+
+    bb3: {
+        StorageDead(_11);                // scope 2 at $DIR/receiver-ptr-mutability.rs:19:15: 19:16
+        StorageDead(_10);                // scope 2 at $DIR/receiver-ptr-mutability.rs:19:16: 19:17
+        _0 = const ();                   // scope 0 at $DIR/receiver-ptr-mutability.rs:13:11: 20:2
+        StorageDead(_9);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:20:1: 20:2
+        StorageDead(_8);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:20:1: 20:2
+        StorageDead(_7);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:20:1: 20:2
+        StorageDead(_5);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:20:1: 20:2
+        StorageDead(_1);                 // scope 0 at $DIR/receiver-ptr-mutability.rs:20:1: 20:2
+        return;                          // scope 0 at $DIR/receiver-ptr-mutability.rs:20:2: 20:2
+    }
+
+    bb4 (cleanup): {
+        resume;                          // scope 0 at $DIR/receiver-ptr-mutability.rs:13:1: 20:2
+    }
+}