Skip to content

Include adjustments to allow unsizing coercions for raw slice pointers in receiver position #82190

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions compiler/rustc_typeck/src/check/method/confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 => {
Expand Down Expand Up @@ -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.
Expand Down
63 changes: 62 additions & 1 deletion compiler/rustc_typeck/src/check/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,17 @@ pub struct Pick<'tcx> {
pub unsize: Option<Ty<'tcx>>,
}

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,
Expand Down Expand Up @@ -1062,14 +1073,23 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
fn pick_core(&mut self) -> Option<PickResult<'tcx>> {
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()
.filter(|step| {
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
Expand All @@ -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()
Expand Down Expand Up @@ -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<PickResult<'tcx>> {
// 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> {
Expand Down
20 changes: 20 additions & 0 deletions src/test/ui/raw-ref-op/raw-ref-unsize-coercion-receiver.rs
Original file line number Diff line number Diff line change
@@ -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
}
11 changes: 11 additions & 0 deletions src/test/ui/raw-ref-op/raw-ref-unsize-coercion-receiver.stderr
Original file line number Diff line number Diff line change
@@ -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`.