-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Allow calling *const methods on *mut values #82436
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,16 +742,16 @@ 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. | ||
self.record_static_candidate(ImplSource(impl_def_id)); | ||
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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIUC, it's not so much that the pointer would be dereferenced, but more that adding an autoref would create a safe reference, which we don't want to do. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just tried this: struct Test {}
impl Test {
fn x(&self) {
println!("x called");
}
}
fn main() {
let mut test = Test {};
let mut test_mut_ref = &mut test;
test_mut_ref.x();
} the relevant, unoptimized MIR:
I thought the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're not wrong, the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this the case even when result of Instead what we do here is more like |
||
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, | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.