Skip to content

Commit 98fbc09

Browse files
committed
Allow calling *const methods on *mut values
Fixes #80258
1 parent 4b9f5cc commit 98fbc09

File tree

4 files changed

+237
-46
lines changed

4 files changed

+237
-46
lines changed

compiler/rustc_typeck/src/check/method/confirm.rs

+39-25
Original file line numberDiff line numberDiff line change
@@ -155,32 +155,46 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
155155
let mut target =
156156
self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
157157

158-
if let Some(mutbl) = pick.autoref {
159-
let region = self.next_region_var(infer::Autoref(self.span, pick.item));
160-
target = self.tcx.mk_ref(region, ty::TypeAndMut { mutbl, ty: target });
161-
let mutbl = match mutbl {
162-
hir::Mutability::Not => AutoBorrowMutability::Not,
163-
hir::Mutability::Mut => AutoBorrowMutability::Mut {
164-
// Method call receivers are the primary use case
165-
// for two-phase borrows.
166-
allow_two_phase_borrow: AllowTwoPhase::Yes,
167-
},
168-
};
169-
adjustments
170-
.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), target });
171-
172-
if let Some(unsize_target) = pick.unsize {
173-
target = self
174-
.tcx
175-
.mk_ref(region, ty::TypeAndMut { mutbl: mutbl.into(), ty: unsize_target });
176-
adjustments.push(Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target });
158+
match &pick.autoref_or_ptr_adjustment {
159+
Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => {
160+
let region = self.next_region_var(infer::Autoref(self.span, pick.item));
161+
target = self.tcx.mk_ref(region, ty::TypeAndMut { mutbl: *mutbl, ty: target });
162+
let mutbl = match mutbl {
163+
hir::Mutability::Not => AutoBorrowMutability::Not,
164+
hir::Mutability::Mut => AutoBorrowMutability::Mut {
165+
// Method call receivers are the primary use case
166+
// for two-phase borrows.
167+
allow_two_phase_borrow: AllowTwoPhase::Yes,
168+
},
169+
};
170+
adjustments.push(Adjustment {
171+
kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
172+
target,
173+
});
174+
175+
if let Some(unsize_target) = unsize {
176+
target = self
177+
.tcx
178+
.mk_ref(region, ty::TypeAndMut { mutbl: mutbl.into(), ty: unsize_target });
179+
adjustments
180+
.push(Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target });
181+
}
182+
}
183+
Some(probe::AutorefOrPtrAdjustment::ToConstPtr) => {
184+
target = match target.kind() {
185+
ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => {
186+
assert_eq!(*mutbl, hir::Mutability::Mut);
187+
self.tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty })
188+
}
189+
other => panic!("Cannot adjust receiver type {:?} to const ptr", other),
190+
};
191+
192+
adjustments.push(Adjustment {
193+
kind: Adjust::Pointer(PointerCast::MutToConstPointer),
194+
target,
195+
});
177196
}
178-
} else {
179-
// No unsizing should be performed without autoref (at
180-
// least during method dispach). This is because we
181-
// currently only unsize `[T;N]` to `[T]`, and naturally
182-
// that must occur being a reference.
183-
assert!(pick.unsize.is_none());
197+
None => {}
184198
}
185199

186200
self.register_predicates(autoderef.into_obligations());

compiler/rustc_typeck/src/check/method/probe.rs

+82-21
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,42 @@ enum ProbeResult {
154154
Match,
155155
}
156156

157+
/// When adjusting a receiver we often want to do one of
158+
///
159+
/// - Add a `&` (or `&mut`), converting the recevier from `T` to `&T` (or `&mut T`)
160+
/// - If the receiver has type `*mut T`, convert it to `*const T`
161+
///
162+
/// This type tells us which one to do.
163+
///
164+
/// Note that in principle we could do both at the same time. For example, when the receiver has
165+
/// type `T`, we could autoref it to `&T`, then convert to `*const T`. Or, when it has type `*mut
166+
/// T`, we could convert it to `*const T`, then autoref to `&*const T`. However, currently we do
167+
/// (at most) one of these. Either the receiver has type `T` and we convert it to `&T` (or with
168+
/// `mut`), or it has type `*mut T` and we convert it to `*const T`.
169+
#[derive(Debug, PartialEq, Clone)]
170+
pub enum AutorefOrPtrAdjustment<'tcx> {
171+
/// Receiver has type `T`, add `&` or `&mut` (it `T` is `mut`), and maybe also "unsize" it.
172+
/// Unsizing is used to convert a `[T; N]` to `[T]`, which only makes sense when autorefing.
173+
Autoref {
174+
mutbl: hir::Mutability,
175+
176+
/// Indicates that the source expression should be "unsized" to a target type. This should
177+
/// probably eventually go away in favor of just coercing method receivers.
178+
unsize: Option<Ty<'tcx>>,
179+
},
180+
/// Receiver has type `*mut T`, convert to `*const T`
181+
ToConstPtr,
182+
}
183+
184+
impl<'tcx> AutorefOrPtrAdjustment<'tcx> {
185+
fn get_unsize(&self) -> Option<Ty<'tcx>> {
186+
match self {
187+
AutorefOrPtrAdjustment::Autoref { mutbl: _, unsize } => unsize.clone(),
188+
AutorefOrPtrAdjustment::ToConstPtr => None,
189+
}
190+
}
191+
}
192+
157193
#[derive(Debug, PartialEq, Clone)]
158194
pub struct Pick<'tcx> {
159195
pub item: ty::AssocItem,
@@ -165,17 +201,9 @@ pub struct Pick<'tcx> {
165201
/// A = expr | *expr | **expr | ...
166202
pub autoderefs: usize,
167203

168-
/// Indicates that an autoref is applied after the optional autoderefs
169-
///
170-
/// B = A | &A | &mut A
171-
pub autoref: Option<hir::Mutability>,
172-
173-
/// Indicates that the source expression should be "unsized" to a
174-
/// target type. This should probably eventually go away in favor
175-
/// of just coercing method receivers.
176-
///
177-
/// C = B | unsize(B)
178-
pub unsize: Option<Ty<'tcx>>,
204+
/// Indicates that we want to add an autoref (and maybe also unsize it), or if the receiver is
205+
/// `*mut T`, convert it to `*const T`.
206+
pub autoref_or_ptr_adjustment: Option<AutorefOrPtrAdjustment<'tcx>>,
179207
}
180208

181209
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -714,16 +742,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
714742

715743
debug!("assemble_inherent_impl_probe {:?}", impl_def_id);
716744

745+
let (impl_ty, impl_substs) = self.impl_ty_and_substs(impl_def_id);
746+
let impl_ty = impl_ty.subst(self.tcx, impl_substs);
747+
717748
for item in self.impl_or_trait_item(impl_def_id) {
718749
if !self.has_applicable_self(&item) {
719750
// No receiver declared. Not a candidate.
720751
self.record_static_candidate(ImplSource(impl_def_id));
721752
continue;
722753
}
723754

724-
let (impl_ty, impl_substs) = self.impl_ty_and_substs(impl_def_id);
725-
let impl_ty = impl_ty.subst(self.tcx, impl_substs);
726-
727755
// Determine the receiver type that the method itself expects.
728756
let xform_tys = self.xform_self_ty(&item, impl_ty, impl_substs);
729757

@@ -1086,6 +1114,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
10861114
self.pick_by_value_method(step, self_ty).or_else(|| {
10871115
self.pick_autorefd_method(step, self_ty, hir::Mutability::Not)
10881116
.or_else(|| self.pick_autorefd_method(step, self_ty, hir::Mutability::Mut))
1117+
.or_else(|| self.pick_const_ptr_method(step, self_ty))
10891118
})
10901119
})
10911120
.next()
@@ -1113,7 +1142,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
11131142
// Insert a `&*` or `&mut *` if this is a reference type:
11141143
if let ty::Ref(_, _, mutbl) = *step.self_ty.value.value.kind() {
11151144
pick.autoderefs += 1;
1116-
pick.autoref = Some(mutbl);
1145+
pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref {
1146+
mutbl,
1147+
unsize: pick.autoref_or_ptr_adjustment.and_then(|a| a.get_unsize()),
1148+
})
11171149
}
11181150

11191151
pick
@@ -1136,8 +1168,39 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
11361168
self.pick_method(autoref_ty).map(|r| {
11371169
r.map(|mut pick| {
11381170
pick.autoderefs = step.autoderefs;
1139-
pick.autoref = Some(mutbl);
1140-
pick.unsize = step.unsize.then_some(self_ty);
1171+
pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref {
1172+
mutbl,
1173+
unsize: step.unsize.then_some(self_ty),
1174+
});
1175+
pick
1176+
})
1177+
})
1178+
}
1179+
1180+
/// If `self_ty` is `*mut T` then this picks `*const T` methods. The reason why we have a
1181+
/// special case for this is because going from `*mut T` to `*const T` with autoderefs and
1182+
/// autorefs would require dereferencing the pointer, which is not safe.
1183+
fn pick_const_ptr_method(
1184+
&mut self,
1185+
step: &CandidateStep<'tcx>,
1186+
self_ty: Ty<'tcx>,
1187+
) -> Option<PickResult<'tcx>> {
1188+
// Don't convert an unsized reference to ptr
1189+
if step.unsize {
1190+
return None;
1191+
}
1192+
1193+
let ty = match self_ty.kind() {
1194+
ty::RawPtr(ty::TypeAndMut { ty, mutbl: hir::Mutability::Mut }) => ty,
1195+
_ => return None,
1196+
};
1197+
1198+
let const_self_ty = ty::TypeAndMut { ty, mutbl: hir::Mutability::Not };
1199+
let const_ptr_ty = self.tcx.mk_ptr(const_self_ty);
1200+
self.pick_method(const_ptr_ty).map(|r| {
1201+
r.map(|mut pick| {
1202+
pick.autoderefs = step.autoderefs;
1203+
pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::ToConstPtr);
11411204
pick
11421205
})
11431206
})
@@ -1510,8 +1573,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
15101573
kind: TraitPick,
15111574
import_ids: probes[0].0.import_ids.clone(),
15121575
autoderefs: 0,
1513-
autoref: None,
1514-
unsize: None,
1576+
autoref_or_ptr_adjustment: None,
15151577
})
15161578
}
15171579

@@ -1748,8 +1810,7 @@ impl<'tcx> Candidate<'tcx> {
17481810
},
17491811
import_ids: self.import_ids.clone(),
17501812
autoderefs: 0,
1751-
autoref: None,
1752-
unsize: None,
1813+
autoref_or_ptr_adjustment: None,
17531814
}
17541815
}
17551816
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// EMIT_MIR receiver_ptr_mutability.main.mir_map.0.mir
2+
3+
#![feature(arbitrary_self_types)]
4+
5+
struct Test {}
6+
7+
impl Test {
8+
fn x(self: *const Self) {
9+
println!("x called");
10+
}
11+
}
12+
13+
fn main() {
14+
let ptr: *mut Test = std::ptr::null_mut();
15+
ptr.x();
16+
17+
// Test autoderefs
18+
let ptr_ref: &&&&*mut Test = &&&&ptr;
19+
ptr_ref.x();
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// MIR for `main` 0 mir_map
2+
3+
| User Type Annotations
4+
| 0: Canonical { max_universe: U0, variables: [], value: Ty(*mut Test) } at $DIR/receiver-ptr-mutability.rs:14:14: 14:23
5+
| 1: Canonical { max_universe: U0, variables: [], value: Ty(*mut Test) } at $DIR/receiver-ptr-mutability.rs:14:14: 14:23
6+
| 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
7+
| 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
8+
|
9+
fn main() -> () {
10+
let mut _0: (); // return place in scope 0 at $DIR/receiver-ptr-mutability.rs:13:11: 13:11
11+
let _1: *mut Test as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/receiver-ptr-mutability.rs:14:9: 14:12
12+
let _2: (); // in scope 0 at $DIR/receiver-ptr-mutability.rs:15:5: 15:12
13+
let mut _3: *const Test; // in scope 0 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
14+
let mut _4: *mut Test; // in scope 0 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
15+
let _6: &&&&*mut Test; // in scope 0 at $DIR/receiver-ptr-mutability.rs:18:34: 18:41
16+
let _7: &&&*mut Test; // in scope 0 at $DIR/receiver-ptr-mutability.rs:18:35: 18:41
17+
let _8: &&*mut Test; // in scope 0 at $DIR/receiver-ptr-mutability.rs:18:36: 18:41
18+
let _9: &*mut Test; // in scope 0 at $DIR/receiver-ptr-mutability.rs:18:37: 18:41
19+
let _10: (); // in scope 0 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
20+
let mut _11: *const Test; // in scope 0 at $DIR/receiver-ptr-mutability.rs:19:5: 19:12
21+
let mut _12: *mut Test; // in scope 0 at $DIR/receiver-ptr-mutability.rs:19:5: 19:12
22+
scope 1 {
23+
debug ptr => _1; // in scope 1 at $DIR/receiver-ptr-mutability.rs:14:9: 14:12
24+
let _5: &&&&*mut Test as UserTypeProjection { base: UserType(2), projs: [] }; // in scope 1 at $DIR/receiver-ptr-mutability.rs:18:9: 18:16
25+
scope 2 {
26+
debug ptr_ref => _5; // in scope 2 at $DIR/receiver-ptr-mutability.rs:18:9: 18:16
27+
}
28+
}
29+
30+
bb0: {
31+
StorageLive(_1); // scope 0 at $DIR/receiver-ptr-mutability.rs:14:9: 14:12
32+
_1 = null_mut::<Test>() -> [return: bb1, unwind: bb4]; // scope 0 at $DIR/receiver-ptr-mutability.rs:14:26: 14:46
33+
// mir::Constant
34+
// + span: $DIR/receiver-ptr-mutability.rs:14:26: 14:44
35+
// + literal: Const { ty: fn() -> *mut Test {std::ptr::null_mut::<Test>}, val: Value(Scalar(<ZST>)) }
36+
}
37+
38+
bb1: {
39+
FakeRead(ForLet, _1); // scope 0 at $DIR/receiver-ptr-mutability.rs:14:9: 14:12
40+
AscribeUserType(_1, o, UserTypeProjection { base: UserType(1), projs: [] }); // scope 0 at $DIR/receiver-ptr-mutability.rs:14:14: 14:23
41+
StorageLive(_2); // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:12
42+
StorageLive(_3); // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
43+
StorageLive(_4); // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
44+
_4 = _1; // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
45+
_3 = move _4 as *const Test (Pointer(MutToConstPointer)); // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
46+
StorageDead(_4); // scope 1 at $DIR/receiver-ptr-mutability.rs:15:7: 15:8
47+
_2 = Test::x(move _3) -> [return: bb2, unwind: bb4]; // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:12
48+
// mir::Constant
49+
// + span: $DIR/receiver-ptr-mutability.rs:15:9: 15:10
50+
// + literal: Const { ty: fn(*const Test) {Test::x}, val: Value(Scalar(<ZST>)) }
51+
}
52+
53+
bb2: {
54+
StorageDead(_3); // scope 1 at $DIR/receiver-ptr-mutability.rs:15:11: 15:12
55+
StorageDead(_2); // scope 1 at $DIR/receiver-ptr-mutability.rs:15:12: 15:13
56+
StorageLive(_5); // scope 1 at $DIR/receiver-ptr-mutability.rs:18:9: 18:16
57+
StorageLive(_6); // scope 1 at $DIR/receiver-ptr-mutability.rs:18:34: 18:41
58+
StorageLive(_7); // scope 1 at $DIR/receiver-ptr-mutability.rs:18:35: 18:41
59+
StorageLive(_8); // scope 1 at $DIR/receiver-ptr-mutability.rs:18:36: 18:41
60+
StorageLive(_9); // scope 1 at $DIR/receiver-ptr-mutability.rs:18:37: 18:41
61+
_9 = &_1; // scope 1 at $DIR/receiver-ptr-mutability.rs:18:37: 18:41
62+
_8 = &_9; // scope 1 at $DIR/receiver-ptr-mutability.rs:18:36: 18:41
63+
_7 = &_8; // scope 1 at $DIR/receiver-ptr-mutability.rs:18:35: 18:41
64+
_6 = &_7; // scope 1 at $DIR/receiver-ptr-mutability.rs:18:34: 18:41
65+
_5 = &(*_6); // scope 1 at $DIR/receiver-ptr-mutability.rs:18:34: 18:41
66+
FakeRead(ForLet, _5); // scope 1 at $DIR/receiver-ptr-mutability.rs:18:9: 18:16
67+
AscribeUserType(_5, o, UserTypeProjection { base: UserType(3), projs: [] }); // scope 1 at $DIR/receiver-ptr-mutability.rs:18:18: 18:31
68+
StorageDead(_6); // scope 1 at $DIR/receiver-ptr-mutability.rs:18:41: 18:42
69+
StorageLive(_10); // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
70+
StorageLive(_11); // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:12
71+
StorageLive(_12); // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:12
72+
_12 = (*(*(*(*_5)))); // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:12
73+
_11 = move _12 as *const Test (Pointer(MutToConstPointer)); // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:12
74+
StorageDead(_12); // scope 2 at $DIR/receiver-ptr-mutability.rs:19:11: 19:12
75+
_10 = Test::x(move _11) -> [return: bb3, unwind: bb4]; // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
76+
// mir::Constant
77+
// + span: $DIR/receiver-ptr-mutability.rs:19:13: 19:14
78+
// + literal: Const { ty: fn(*const Test) {Test::x}, val: Value(Scalar(<ZST>)) }
79+
}
80+
81+
bb3: {
82+
StorageDead(_11); // scope 2 at $DIR/receiver-ptr-mutability.rs:19:15: 19:16
83+
StorageDead(_10); // scope 2 at $DIR/receiver-ptr-mutability.rs:19:16: 19:17
84+
_0 = const (); // scope 0 at $DIR/receiver-ptr-mutability.rs:13:11: 20:2
85+
StorageDead(_9); // scope 1 at $DIR/receiver-ptr-mutability.rs:20:1: 20:2
86+
StorageDead(_8); // scope 1 at $DIR/receiver-ptr-mutability.rs:20:1: 20:2
87+
StorageDead(_7); // scope 1 at $DIR/receiver-ptr-mutability.rs:20:1: 20:2
88+
StorageDead(_5); // scope 1 at $DIR/receiver-ptr-mutability.rs:20:1: 20:2
89+
StorageDead(_1); // scope 0 at $DIR/receiver-ptr-mutability.rs:20:1: 20:2
90+
return; // scope 0 at $DIR/receiver-ptr-mutability.rs:20:2: 20:2
91+
}
92+
93+
bb4 (cleanup): {
94+
resume; // scope 0 at $DIR/receiver-ptr-mutability.rs:13:1: 20:2
95+
}
96+
}

0 commit comments

Comments
 (0)