Skip to content
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

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

Closed
wants to merge 2 commits into from
Closed
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
@@ -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.
63 changes: 62 additions & 1 deletion compiler/rustc_typeck/src/check/method/probe.rs
Original file line number Diff line number Diff line change
@@ -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,
@@ -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
@@ -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<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> {
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`.