Auto merge of #82436 - osa1:issue80258, r=nikomatsakis
Allow calling *const methods on *mut values This allows `*const` methods to be called on `*mut` values. TODOs: - [x] ~~Remove debug logs~~ Done. - [x] ~~I haven't tested, but I think this currently won't work when the `self` value has type like `&&&&& *mut X` because I don't do any autoderefs when probing. To fix this the new code in `rustc_typeck::check::method::probe` needs to reuse `pick_method` somehow as I think that's the function that autoderefs.~~ This works, because autoderefs are done before calling `pick_core`, in `method_autoderef_steps`, called by `probe_op`. - [x] ~~I should probably move the new `Pick` to `pick_autorefd_method`. If not, I should move it to its own function.~~ Done. - [ ] ~~Test this with a `Pick` with `to_ptr = true` and `unsize = true`.~~ I think this case cannot happen, because we don't have any array methods with `*mut [X]` receiver. I should confirm that this is true and document this. I've placed two assertions about this. - [x] ~~Maybe give `(Mutability, bool)` a name and fields~~ I now have a `to_const_ptr` field in `Pick`. - [x] ~~Changes in `adjust_self_ty` is quite hacky. The problem is we can't deref a pointer, and even if we don't have an adjustment to get the address of a value, so to go from `*mut` to `*const` we need a special case.~~ There's still a special case for `to_const_ptr`, but I'm not sure if we can avoid this. - [ ] Figure out how `reached_raw_pointer` stuff is used. I suspect only for error messages. Fixes #80258
This commit is contained in:
commit
f42888c15f
4 changed files with 236 additions and 45 deletions
|
@ -155,32 +155,46 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
|
||||||
let mut target =
|
let mut target =
|
||||||
self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
|
self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
|
||||||
|
|
||||||
if let Some(mutbl) = pick.autoref {
|
match &pick.autoref_or_ptr_adjustment {
|
||||||
let region = self.next_region_var(infer::Autoref(self.span, pick.item));
|
Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => {
|
||||||
target = self.tcx.mk_ref(region, ty::TypeAndMut { mutbl, ty: target });
|
let region = self.next_region_var(infer::Autoref(self.span, pick.item));
|
||||||
let mutbl = match mutbl {
|
target = self.tcx.mk_ref(region, ty::TypeAndMut { mutbl: *mutbl, ty: target });
|
||||||
hir::Mutability::Not => AutoBorrowMutability::Not,
|
let mutbl = match mutbl {
|
||||||
hir::Mutability::Mut => AutoBorrowMutability::Mut {
|
hir::Mutability::Not => AutoBorrowMutability::Not,
|
||||||
// Method call receivers are the primary use case
|
hir::Mutability::Mut => AutoBorrowMutability::Mut {
|
||||||
// for two-phase borrows.
|
// Method call receivers are the primary use case
|
||||||
allow_two_phase_borrow: AllowTwoPhase::Yes,
|
// for two-phase borrows.
|
||||||
},
|
allow_two_phase_borrow: AllowTwoPhase::Yes,
|
||||||
};
|
},
|
||||||
adjustments
|
};
|
||||||
.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), target });
|
adjustments.push(Adjustment {
|
||||||
|
kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
|
||||||
|
target,
|
||||||
|
});
|
||||||
|
|
||||||
if let Some(unsize_target) = pick.unsize {
|
if let Some(unsize_target) = unsize {
|
||||||
target = self
|
target = self
|
||||||
.tcx
|
.tcx
|
||||||
.mk_ref(region, ty::TypeAndMut { mutbl: mutbl.into(), ty: unsize_target });
|
.mk_ref(region, ty::TypeAndMut { mutbl: mutbl.into(), ty: unsize_target });
|
||||||
adjustments.push(Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target });
|
adjustments
|
||||||
|
.push(Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
Some(probe::AutorefOrPtrAdjustment::ToConstPtr) => {
|
||||||
// No unsizing should be performed without autoref (at
|
target = match target.kind() {
|
||||||
// least during method dispach). This is because we
|
ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => {
|
||||||
// currently only unsize `[T;N]` to `[T]`, and naturally
|
assert_eq!(*mutbl, hir::Mutability::Mut);
|
||||||
// that must occur being a reference.
|
self.tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty })
|
||||||
assert!(pick.unsize.is_none());
|
}
|
||||||
|
other => panic!("Cannot adjust receiver type {:?} to const ptr", other),
|
||||||
|
};
|
||||||
|
|
||||||
|
adjustments.push(Adjustment {
|
||||||
|
kind: Adjust::Pointer(PointerCast::MutToConstPointer),
|
||||||
|
target,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.register_predicates(autoderef.into_obligations());
|
self.register_predicates(autoderef.into_obligations());
|
||||||
|
|
|
@ -154,6 +154,42 @@ enum ProbeResult {
|
||||||
Match,
|
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)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct Pick<'tcx> {
|
pub struct Pick<'tcx> {
|
||||||
pub item: ty::AssocItem,
|
pub item: ty::AssocItem,
|
||||||
|
@ -165,17 +201,9 @@ pub struct Pick<'tcx> {
|
||||||
/// A = expr | *expr | **expr | ...
|
/// A = expr | *expr | **expr | ...
|
||||||
pub autoderefs: usize,
|
pub autoderefs: usize,
|
||||||
|
|
||||||
/// Indicates that an autoref is applied after the optional autoderefs
|
/// 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`.
|
||||||
/// B = A | &A | &mut A
|
pub autoref_or_ptr_adjustment: Option<AutorefOrPtrAdjustment<'tcx>>,
|
||||||
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>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
@ -714,6 +742,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
||||||
|
|
||||||
debug!("assemble_inherent_impl_probe {:?}", impl_def_id);
|
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) {
|
for item in self.impl_or_trait_item(impl_def_id) {
|
||||||
if !self.has_applicable_self(&item) {
|
if !self.has_applicable_self(&item) {
|
||||||
// No receiver declared. Not a candidate.
|
// No receiver declared. Not a candidate.
|
||||||
|
@ -721,9 +752,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
||||||
continue;
|
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.
|
// Determine the receiver type that the method itself expects.
|
||||||
let xform_tys = self.xform_self_ty(&item, impl_ty, impl_substs);
|
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_by_value_method(step, self_ty).or_else(|| {
|
||||||
self.pick_autorefd_method(step, self_ty, hir::Mutability::Not)
|
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_autorefd_method(step, self_ty, hir::Mutability::Mut))
|
||||||
|
.or_else(|| self.pick_const_ptr_method(step, self_ty))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.next()
|
.next()
|
||||||
|
@ -1113,7 +1142,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
||||||
// Insert a `&*` or `&mut *` if this is a reference type:
|
// Insert a `&*` or `&mut *` if this is a reference type:
|
||||||
if let ty::Ref(_, _, mutbl) = *step.self_ty.value.value.kind() {
|
if let ty::Ref(_, _, mutbl) = *step.self_ty.value.value.kind() {
|
||||||
pick.autoderefs += 1;
|
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
|
pick
|
||||||
|
@ -1136,8 +1168,39 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
||||||
self.pick_method(autoref_ty).map(|r| {
|
self.pick_method(autoref_ty).map(|r| {
|
||||||
r.map(|mut pick| {
|
r.map(|mut pick| {
|
||||||
pick.autoderefs = step.autoderefs;
|
pick.autoderefs = step.autoderefs;
|
||||||
pick.autoref = Some(mutbl);
|
pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref {
|
||||||
pick.unsize = step.unsize.then_some(self_ty);
|
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<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
|
pick
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1510,8 +1573,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
||||||
kind: TraitPick,
|
kind: TraitPick,
|
||||||
import_ids: probes[0].0.import_ids.clone(),
|
import_ids: probes[0].0.import_ids.clone(),
|
||||||
autoderefs: 0,
|
autoderefs: 0,
|
||||||
autoref: None,
|
autoref_or_ptr_adjustment: None,
|
||||||
unsize: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1748,8 +1810,7 @@ impl<'tcx> Candidate<'tcx> {
|
||||||
},
|
},
|
||||||
import_ids: self.import_ids.clone(),
|
import_ids: self.import_ids.clone(),
|
||||||
autoderefs: 0,
|
autoderefs: 0,
|
||||||
autoref: None,
|
autoref_or_ptr_adjustment: None,
|
||||||
unsize: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
20
src/test/mir-opt/receiver-ptr-mutability.rs
Normal file
20
src/test/mir-opt/receiver-ptr-mutability.rs
Normal file
|
@ -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();
|
||||||
|
}
|
96
src/test/mir-opt/receiver_ptr_mutability.main.mir_map.0.mir
Normal file
96
src/test/mir-opt/receiver_ptr_mutability.main.mir_map.0.mir
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue