diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_typeck/src/check/method/confirm.rs index e5f19281b0773..25daf02dd62c1 100644 --- a/compiler/rustc_typeck/src/check/method/confirm.rs +++ b/compiler/rustc_typeck/src/check/method/confirm.rs @@ -13,6 +13,7 @@ use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::{self, Subst, SubstsRef}; use rustc_middle::ty::{self, GenericParamDefKind, Ty}; use rustc_span::Span; +use rustc_trait_selection::autoderef::Autoderef; use rustc_trait_selection::traits; use std::ops::Deref; @@ -137,8 +138,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { ) -> Ty<'tcx> { // Commit the autoderefs by calling `autoderef` again, but this // time writing the results into the various typeck results. - let mut autoderef = - self.autoderef_overloaded_span(self.span, unadjusted_self_ty, self.call_expr.span); + let mut autoderef = self.autoderef_self_ty(unadjusted_self_ty, &pick); let (_, n) = match autoderef.nth(pick.autoderefs) { Some(n) => n, None => { @@ -191,6 +191,25 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { target } + fn autoderef_self_ty( + &'a self, + unadjusted_self_ty: Ty<'tcx>, + pick: &probe::Pick<'tcx>, + ) -> Autoderef<'a, 'tcx> { + let autoderef = + self.autoderef_overloaded_span(self.span, unadjusted_self_ty, self.call_expr.span); + + // We allow unsize coercions of raw pointers in method callee position + if let &ty::RawPtr(ty::TypeAndMut { ty: t, .. }) = unadjusted_self_ty.kind() { + if let ty::Array(_, _) = t.kind() { + if pick.unsize_raw_ptr() { + return autoderef.include_raw_pointers(); + } + } + } + autoderef + } + /// Returns a set of substitutions for the method *receiver* where all type and region /// parameters are instantiated with fresh variables. This substitution does not include any /// parameters declared on the method itself. diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs index 158c214759db2..9e0197fdcf64d 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -177,6 +177,17 @@ pub struct Pick<'tcx> { pub unsize: Option>, } +impl Pick<'tcx> { + pub(crate) fn unsize_raw_ptr(&self) -> bool { + if let Some(t) = &self.unsize { + if let &ty::Slice(_) = t.kind() { + return self.autoderefs == 1 && self.autoref.is_some(); + } + } + false + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub enum PickKind<'tcx> { InherentImplPick, @@ -1062,6 +1073,15 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn pick_core(&mut self) -> Option> { let steps = self.steps.clone(); + let allow_deref_raw_ptr = |step: &CandidateStep<'tcx>| -> bool { + // allow dereferencing of raw pointers to slices + if let &ty::Slice(_) = step.self_ty.value.value.kind() { + true + } else { + !step.from_unsafe_deref + } + }; + // find the first step that works steps .iter() @@ -1069,7 +1089,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { debug!("pick_core: step={:?}", step); // skip types that are from a type error or that would require dereferencing // a raw pointer - !step.self_ty.references_error() && !step.from_unsafe_deref + !step.self_ty.references_error() && allow_deref_raw_ptr(&step) }) .flat_map(|step| { let InferOk { value: self_ty, obligations: _ } = self @@ -1085,6 +1105,10 @@ 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(|| { + // allow unsizing of raw pointers + self.pick_raw_autorefd_slice_method(step, self_ty) + }) }) }) .next() @@ -1701,6 +1725,43 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.tcx.associated_items(def_id).in_definition_order().copied().collect() } } + fn pick_raw_autorefd_slice_method( + &mut self, + step: &CandidateStep<'tcx>, + self_ty: Ty<'tcx>, + ) -> Option> { + // To enable raw-ptr unsizing we try to autoref slices to raw pointers + debug!("pick_raw_ptr_autorefd_slice_method(step: {:?}, self_ty: {:?}", step, self_ty); + if let &ty::Slice(_) = self_ty.kind() { + let tcx = self.tcx; + + let autoref_ptr_ty = + tcx.mk_ptr(ty::TypeAndMut { ty: self_ty, mutbl: hir::Mutability::Not }); + return self + .pick_method(autoref_ptr_ty) + .map(|r| { + r.map(|mut pick| { + pick.autoderefs = step.autoderefs; + pick.autoref = Some(hir::Mutability::Not); + pick.unsize = step.unsize.then_some(self_ty); + pick + }) + }) + .or_else(|| { + let autoref_mut_ptr_ty = + tcx.mk_ptr(ty::TypeAndMut { ty: self_ty, mutbl: hir::Mutability::Mut }); + self.pick_method(autoref_mut_ptr_ty).map(|r| { + r.map(|mut pick| { + pick.autoderefs = step.autoderefs; + pick.autoref = Some(hir::Mutability::Mut); + pick.unsize = step.unsize.then_some(self_ty); + pick + }) + }) + }); + } + None + } } impl<'tcx> Candidate<'tcx> { diff --git a/src/test/ui/raw-ref-op/raw-ref-unsize-coercion-receiver.rs b/src/test/ui/raw-ref-op/raw-ref-unsize-coercion-receiver.rs new file mode 100644 index 0000000000000..11a8c868bcc99 --- /dev/null +++ b/src/test/ui/raw-ref-op/raw-ref-unsize-coercion-receiver.rs @@ -0,0 +1,20 @@ +#![feature(raw_ref_op)] + +fn main() { + let a1 = [4,5,6]; + let p1 = &raw const a1; + let _ = unsafe { p1.get_unchecked(1) }; + + let mut a2 = [4,5,6]; + let p2 = &raw mut a2; + let _ = unsafe { p2.get_unchecked(1) }; + + let mut a3 = [4,5,6]; + let p3 = &raw mut a3; + let _ = unsafe { p3.get_unchecked_mut(1) }; + + let a4 = [4,5,6]; + let p4 = &raw const a4; + let _ = unsafe { p4.get_unchecked_mut(1) }; + //~^ ERROR cannot borrow +} diff --git a/src/test/ui/raw-ref-op/raw-ref-unsize-coercion-receiver.stderr b/src/test/ui/raw-ref-op/raw-ref-unsize-coercion-receiver.stderr new file mode 100644 index 0000000000000..d857f4372ada6 --- /dev/null +++ b/src/test/ui/raw-ref-op/raw-ref-unsize-coercion-receiver.stderr @@ -0,0 +1,11 @@ +error[E0596]: cannot borrow `*p4` as mutable, as it is behind a `*const` pointer + --> $DIR/raw-ref-unsize-coercion-receiver.rs:18:20 + | +LL | let p4 = &raw const a4; + | ------------- help: consider changing this to be a mutable pointer: `&mut raw const a4` +LL | let _ = unsafe { p4.get_unchecked_mut(1) }; + | ^^ `p4` is a `*const` pointer, so the data it refers to cannot be borrowed as mutable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0596`.