diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_typeck/src/check/method/confirm.rs index e5f19281b0773..7a80524f1b79a 100644 --- a/compiler/rustc_typeck/src/check/method/confirm.rs +++ b/compiler/rustc_typeck/src/check/method/confirm.rs @@ -155,32 +155,46 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { let mut target = self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false)); - if let Some(mutbl) = pick.autoref { - let region = self.next_region_var(infer::Autoref(self.span, pick.item)); - target = self.tcx.mk_ref(region, ty::TypeAndMut { mutbl, ty: target }); - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // Method call receivers are the primary use case - // for two-phase borrows. - allow_two_phase_borrow: AllowTwoPhase::Yes, - }, - }; - adjustments - .push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), target }); - - if let Some(unsize_target) = pick.unsize { - target = self - .tcx - .mk_ref(region, ty::TypeAndMut { mutbl: mutbl.into(), ty: unsize_target }); - adjustments.push(Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target }); + match &pick.autoref_or_ptr_adjustment { + Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => { + let region = self.next_region_var(infer::Autoref(self.span, pick.item)); + target = self.tcx.mk_ref(region, ty::TypeAndMut { mutbl: *mutbl, ty: target }); + let mutbl = match mutbl { + hir::Mutability::Not => AutoBorrowMutability::Not, + hir::Mutability::Mut => AutoBorrowMutability::Mut { + // Method call receivers are the primary use case + // for two-phase borrows. + allow_two_phase_borrow: AllowTwoPhase::Yes, + }, + }; + adjustments.push(Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), + target, + }); + + if let Some(unsize_target) = unsize { + target = self + .tcx + .mk_ref(region, ty::TypeAndMut { mutbl: mutbl.into(), ty: unsize_target }); + adjustments + .push(Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target }); + } + } + Some(probe::AutorefOrPtrAdjustment::ToConstPtr) => { + target = match target.kind() { + ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => { + assert_eq!(*mutbl, hir::Mutability::Mut); + self.tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty }) + } + other => panic!("Cannot adjust receiver type {:?} to const ptr", other), + }; + + adjustments.push(Adjustment { + kind: Adjust::Pointer(PointerCast::MutToConstPointer), + target, + }); } - } else { - // No unsizing should be performed without autoref (at - // least during method dispach). This is because we - // currently only unsize `[T;N]` to `[T]`, and naturally - // that must occur being a reference. - assert!(pick.unsize.is_none()); + None => {} } self.register_predicates(autoderef.into_obligations()); diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs index 38ff88553e8d1..42d7f4c988db7 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -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>, + }, + /// Receiver has type `*mut T`, convert to `*const T` + ToConstPtr, +} + +impl<'tcx> AutorefOrPtrAdjustment<'tcx> { + fn get_unsize(&self) -> Option> { + 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, - - /// 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>, + /// 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>, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -714,6 +742,9 @@ 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. @@ -721,9 +752,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { 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. + fn pick_const_ptr_method( + &mut self, + step: &CandidateStep<'tcx>, + self_ty: Ty<'tcx>, + ) -> Option> { + // 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, } } } diff --git a/src/test/mir-opt/receiver-ptr-mutability.rs b/src/test/mir-opt/receiver-ptr-mutability.rs new file mode 100644 index 0000000000000..8e2ff0451c63e --- /dev/null +++ b/src/test/mir-opt/receiver-ptr-mutability.rs @@ -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(); +} diff --git a/src/test/mir-opt/receiver_ptr_mutability.main.mir_map.0.mir b/src/test/mir-opt/receiver_ptr_mutability.main.mir_map.0.mir new file mode 100644 index 0000000000000..d2d96ff468dfc --- /dev/null +++ b/src/test/mir-opt/receiver_ptr_mutability.main.mir_map.0.mir @@ -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::() -> [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::}, val: Value(Scalar()) } + } + + 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()) } + } + + 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()) } + } + + 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 + } +}