Add new Deinit statement kind

This commit is contained in:
Jakob Degen 2022-04-05 17:14:59 -04:00
parent d00e77078c
commit 9b6b1a625b
27 changed files with 141 additions and 66 deletions

View File

@ -386,6 +386,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
mir::StatementKind::FakeRead(..)
| mir::StatementKind::SetDiscriminant { .. }
| mir::StatementKind::Deinit(..)
| mir::StatementKind::StorageLive(..)
| mir::StatementKind::Retag { .. }
| mir::StatementKind::AscribeUserType(..)

View File

@ -63,9 +63,6 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
StatementKind::FakeRead(box (_, _)) => {
// Only relevant for initialized/liveness/safety checks.
}
StatementKind::SetDiscriminant { place, variant_index: _ } => {
self.mutate_place(location, **place, Shallow(None));
}
StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping {
ref src,
ref dst,
@ -91,6 +88,9 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
LocalMutationIsAllowed::Yes,
);
}
StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => {
bug!("Statement not allowed in this MIR phase")
}
}
self.super_statement(statement, location);

View File

@ -626,9 +626,6 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx
flow_state,
);
}
StatementKind::SetDiscriminant { place, variant_index: _ } => {
self.mutate_place(location, (**place, span), Shallow(None), flow_state);
}
StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping {
..
}) => {
@ -654,6 +651,9 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx
flow_state,
);
}
StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => {
bug!("Statement not allowed in this MIR phase")
}
}
}

View File

@ -1303,28 +1303,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
);
}
}
StatementKind::SetDiscriminant { ref place, variant_index } => {
let place_type = place.ty(body, tcx).ty;
let adt = match place_type.kind() {
ty::Adt(adt, _) if adt.is_enum() => adt,
_ => {
span_bug!(
stmt.source_info.span,
"bad set discriminant ({:?} = {:?}): lhs is not an enum",
place,
variant_index
);
}
};
if variant_index.as_usize() >= adt.variants().len() {
span_bug!(
stmt.source_info.span,
"bad set discriminant ({:?} = {:?}): value of of range",
place,
variant_index
);
};
}
StatementKind::AscribeUserType(box (ref place, ref projection), variance) => {
let place_ty = place.ty(body, tcx).ty;
if let Err(terr) = self.relate_type_and_user_type(
@ -1358,6 +1336,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
| StatementKind::Retag { .. }
| StatementKind::Coverage(..)
| StatementKind::Nop => {}
StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => {
bug!("Statement not allowed in this MIR phase")
}
}
}

View File

@ -772,6 +772,7 @@ fn codegen_stmt<'tcx>(
}
StatementKind::StorageLive(_)
| StatementKind::StorageDead(_)
| StatementKind::Deinit(_)
| StatementKind::Nop
| StatementKind::FakeRead(..)
| StatementKind::Retag { .. }

View File

@ -518,6 +518,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>(
StatementKind::Assign(_)
| StatementKind::FakeRead(_)
| StatementKind::SetDiscriminant { .. }
| StatementKind::Deinit(_)
| StatementKind::StorageLive(_)
| StatementKind::StorageDead(_)
| StatementKind::Retag(_, _)

View File

@ -48,6 +48,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
.codegen_set_discr(&mut bx, variant_index);
bx
}
mir::StatementKind::Deinit(..) => {
// For now, don't codegen this to anything. In the future it may be worth
// experimenting with what kind of information we can emit to LLVM without hurting
// perf here
bx
}
mir::StatementKind::StorageLive(local) => {
if let LocalRef::Place(cg_place) = self.locals[local] {
cg_place.storage_live(&mut bx);

View File

@ -890,6 +890,11 @@ impl<'tcx, 'a, Tag: Provenance, Extra> AllocRefMut<'a, 'tcx, Tag, Extra> {
) -> InterpResult<'tcx> {
self.write_scalar(alloc_range(offset, self.tcx.data_layout().pointer_size), val)
}
/// Mark the entire referenced range as uninitalized
pub fn write_uninit(&mut self) {
self.alloc.mark_init(self.range, false);
}
}
impl<'tcx, 'a, Tag: Provenance, Extra> AllocRef<'a, 'tcx, Tag, Extra> {

View File

@ -791,6 +791,42 @@ where
}
}
pub fn write_uninit(&mut self, dest: &PlaceTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> {
let mplace = match dest.place {
Place::Ptr(mplace) => MPlaceTy { mplace, layout: dest.layout },
Place::Local { frame, local } => {
match M::access_local_mut(self, frame, local)? {
Ok(local) => match dest.layout.abi {
Abi::Scalar(_) => {
*local = LocalValue::Live(Operand::Immediate(Immediate::Scalar(
ScalarMaybeUninit::Uninit,
)));
return Ok(());
}
Abi::ScalarPair(..) => {
*local = LocalValue::Live(Operand::Immediate(Immediate::ScalarPair(
ScalarMaybeUninit::Uninit,
ScalarMaybeUninit::Uninit,
)));
return Ok(());
}
_ => self.force_allocation(dest)?,
},
Err(mplace) => {
// The local is in memory, go on below.
MPlaceTy { mplace, layout: dest.layout }
}
}
}
};
let Some(mut alloc) = self.get_place_alloc_mut(&mplace)? else {
// Zero-sized access
return Ok(());
};
alloc.write_uninit();
Ok(())
}
/// Copies the data from an operand to a place. This does not support transmuting!
/// Use `copy_op_transmute` if the layouts could disagree.
#[inline(always)]

View File

@ -90,6 +90,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
self.write_discriminant(*variant_index, &dest)?;
}
Deinit(place) => {
let dest = self.eval_place(**place)?;
self.write_uninit(&dest)?;
}
// Mark locals as alive
StorageLive(local) => {
self.storage_live(*local)?;

View File

@ -692,6 +692,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
match statement.kind {
StatementKind::Assign(..)
| StatementKind::SetDiscriminant { .. }
| StatementKind::Deinit(..)
| StatementKind::FakeRead(..)
| StatementKind::StorageLive(_)
| StatementKind::StorageDead(_)

View File

@ -346,9 +346,24 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
self.fail(location, format!("bad arg ({:?} != usize)", op_cnt_ty))
}
}
StatementKind::SetDiscriminant { .. } => {
if self.mir_phase < MirPhase::DropsLowered {
self.fail(location, "`SetDiscriminant` is not allowed until drop elaboration");
StatementKind::SetDiscriminant { place, .. } => {
if self.mir_phase < MirPhase::Deaggregated {
self.fail(location, "`SetDiscriminant`is not allowed until deaggregation");
}
let pty = place.ty(&self.body.local_decls, self.tcx).ty.kind();
if !matches!(pty, ty::Adt(..) | ty::Generator(..) | ty::Opaque(..)) {
self.fail(
location,
format!(
"`SetDiscriminant` is only allowed on ADTs and generators, not {:?}",
pty
),
);
}
}
StatementKind::Deinit(..) => {
if self.mir_phase < MirPhase::Deaggregated {
self.fail(location, "`Deinit`is not allowed until deaggregation");
}
}
StatementKind::Retag(_, _) => {

View File

@ -14,22 +14,26 @@ use std::iter::TrustedLen;
/// (lhs as Variant).field1 = arg1;
/// discriminant(lhs) = variant_index; // If lhs is an enum or generator.
pub fn expand_aggregate<'tcx>(
mut lhs: Place<'tcx>,
orig_lhs: Place<'tcx>,
operands: impl Iterator<Item = (Operand<'tcx>, Ty<'tcx>)> + TrustedLen,
kind: AggregateKind<'tcx>,
source_info: SourceInfo,
tcx: TyCtxt<'tcx>,
) -> impl Iterator<Item = Statement<'tcx>> + TrustedLen {
let mut lhs = orig_lhs;
let mut set_discriminant = None;
let active_field_index = match kind {
AggregateKind::Adt(adt_did, variant_index, _, _, active_field_index) => {
let adt_def = tcx.adt_def(adt_did);
if adt_def.is_enum() {
set_discriminant = Some(Statement {
kind: StatementKind::SetDiscriminant { place: Box::new(lhs), variant_index },
kind: StatementKind::SetDiscriminant {
place: Box::new(orig_lhs),
variant_index,
},
source_info,
});
lhs = tcx.mk_place_downcast(lhs, adt_def, variant_index);
lhs = tcx.mk_place_downcast(orig_lhs, adt_def, variant_index);
}
active_field_index
}
@ -38,7 +42,7 @@ pub fn expand_aggregate<'tcx>(
// variant 0 (Unresumed).
let variant_index = VariantIdx::new(0);
set_discriminant = Some(Statement {
kind: StatementKind::SetDiscriminant { place: Box::new(lhs), variant_index },
kind: StatementKind::SetDiscriminant { place: Box::new(orig_lhs), variant_index },
source_info,
});
@ -50,27 +54,24 @@ pub fn expand_aggregate<'tcx>(
_ => None,
};
operands
.enumerate()
.map(move |(i, (op, ty))| {
let lhs_field = if let AggregateKind::Array(_) = kind {
let offset = u64::try_from(i).unwrap();
tcx.mk_place_elem(
lhs,
ProjectionElem::ConstantIndex {
offset,
min_length: offset + 1,
from_end: false,
},
)
} else {
let field = Field::new(active_field_index.unwrap_or(i));
tcx.mk_place_field(lhs, field, ty)
};
Statement {
source_info,
kind: StatementKind::Assign(Box::new((lhs_field, Rvalue::Use(op)))),
}
})
let operands = operands.enumerate().map(move |(i, (op, ty))| {
let lhs_field = if let AggregateKind::Array(_) = kind {
let offset = u64::try_from(i).unwrap();
tcx.mk_place_elem(
lhs,
ProjectionElem::ConstantIndex { offset, min_length: offset + 1, from_end: false },
)
} else {
let field = Field::new(active_field_index.unwrap_or(i));
tcx.mk_place_field(lhs, field, ty)
};
Statement {
source_info,
kind: StatementKind::Assign(Box::new((lhs_field, Rvalue::Use(op)))),
}
});
[Statement { source_info, kind: StatementKind::Deinit(Box::new(orig_lhs)) }]
.into_iter()
.chain(operands)
.chain(set_discriminant)
}

View File

@ -1590,6 +1590,8 @@ pub enum StatementKind<'tcx> {
/// Write the discriminant for a variant to the enum Place.
SetDiscriminant { place: Box<Place<'tcx>>, variant_index: VariantIdx },
Deinit(Box<Place<'tcx>>),
/// Start a live range for the storage of the local.
StorageLive(Local),
@ -1739,6 +1741,7 @@ impl Debug for Statement<'_> {
SetDiscriminant { ref place, variant_index } => {
write!(fmt, "discriminant({:?}) = {:?}", place, variant_index)
}
Deinit(ref place) => write!(fmt, "Deinit({:?})", place),
AscribeUserType(box (ref place, ref c_ty), ref variance) => {
write!(fmt, "AscribeUserType({:?}, {:?}, {:?})", place, variance, c_ty)
}

View File

@ -243,6 +243,7 @@ pub fn statement_kind_name(statement: &Statement<'_>) -> &'static str {
Assign(..) => "Assign",
FakeRead(..) => "FakeRead",
SetDiscriminant { .. } => "SetDiscriminant",
Deinit(..) => "Deinit",
StorageLive(..) => "StorageLive",
StorageDead(..) => "StorageDead",
Retag(..) => "Retag",

View File

@ -399,6 +399,13 @@ macro_rules! make_mir_visitor {
location
);
}
StatementKind::Deinit(place) => {
self.visit_place(
place,
PlaceContext::MutatingUse(MutatingUseContext::Store),
location
)
}
StatementKind::StorageLive(local) => {
self.visit_local(
local,

View File

@ -131,7 +131,8 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
// If a place is assigned to in a statement, it needs storage for that statement.
StatementKind::Assign(box (place, _))
| StatementKind::SetDiscriminant { box place, .. } => {
| StatementKind::SetDiscriminant { box place, .. }
| StatementKind::Deinit(box place) => {
trans.gen(place.local);
}

View File

@ -296,10 +296,10 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
StatementKind::StorageDead(local) => {
self.gather_move(Place::from(*local));
}
StatementKind::SetDiscriminant { .. } => {
StatementKind::SetDiscriminant { .. } | StatementKind::Deinit(..) => {
span_bug!(
stmt.source_info.span,
"SetDiscriminant should not exist during borrowck"
"SetDiscriminant/Deinit should not exist during borrowck"
);
}
StatementKind::Retag { .. }

View File

@ -97,6 +97,7 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
StatementKind::Assign(..)
| StatementKind::FakeRead(..)
| StatementKind::SetDiscriminant { .. }
| StatementKind::Deinit(..)
| StatementKind::StorageLive(..)
| StatementKind::StorageDead(..)
| StatementKind::Retag { .. }

View File

@ -827,6 +827,7 @@ pub(super) fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span>
| StatementKind::CopyNonOverlapping(..)
| StatementKind::Assign(_)
| StatementKind::SetDiscriminant { .. }
| StatementKind::Deinit(..)
| StatementKind::Retag(_, _)
| StatementKind::AscribeUserType(_, _) => {
Some(statement.source_info.span)

View File

@ -530,6 +530,7 @@ impl<'a> Conflicts<'a> {
StatementKind::Assign(_) => {}
StatementKind::SetDiscriminant { .. }
| StatementKind::Deinit(..)
| StatementKind::StorageLive(..)
| StatementKind::StorageDead(..)
| StatementKind::Retag(..)

View File

@ -1441,6 +1441,7 @@ impl<'tcx> Visitor<'tcx> for EnsureGeneratorFieldAssignmentsNeverAlias<'_> {
StatementKind::FakeRead(..)
| StatementKind::SetDiscriminant { .. }
| StatementKind::Deinit(..)
| StatementKind::StorageLive(_)
| StatementKind::StorageDead(_)
| StatementKind::Retag(..)

View File

@ -50,6 +50,7 @@ impl RemoveNoopLandingPads {
StatementKind::Assign { .. }
| StatementKind::SetDiscriminant { .. }
| StatementKind::Deinit(..)
| StatementKind::CopyNonOverlapping(..)
| StatementKind::Retag { .. } => {
return false;

View File

@ -21,7 +21,9 @@ impl<'tcx> MirPass<'tcx> for RemoveZsts {
let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
for block in basic_blocks.iter_mut() {
for statement in block.statements.iter_mut() {
if let StatementKind::Assign(box (place, _)) = statement.kind {
if let StatementKind::Assign(box (place, _)) | StatementKind::Deinit(box place) =
statement.kind
{
let place_ty = place.ty(local_decls, tcx).ty;
if !maybe_zst(place_ty) {
continue;

View File

@ -242,6 +242,7 @@ fn is_likely_const<'tcx>(mut tracked_place: Place<'tcx>, block: &BasicBlockData<
// These statements have no influence on the place
// we are interested in
StatementKind::FakeRead(_)
| StatementKind::Deinit(_)
| StatementKind::StorageLive(_)
| StatementKind::Retag(_, _)
| StatementKind::AscribeUserType(_, _)
@ -308,6 +309,7 @@ fn find_determining_place<'tcx>(
// These statements have no influence on the place
// we are interested in
StatementKind::FakeRead(_)
| StatementKind::Deinit(_)
| StatementKind::StorageLive(_)
| StatementKind::StorageDead(_)
| StatementKind::Retag(_, _)

View File

@ -498,7 +498,8 @@ impl<'tcx> Visitor<'tcx> for UsedLocals {
self.visit_rvalue(rvalue, location);
}
StatementKind::SetDiscriminant { ref place, variant_index: _ } => {
StatementKind::SetDiscriminant { ref place, variant_index: _ }
| StatementKind::Deinit(ref place) => {
self.visit_lhs(place, location);
}
}
@ -534,9 +535,8 @@ fn remove_unused_definitions(used_locals: &mut UsedLocals, body: &mut Body<'_>)
}
StatementKind::Assign(box (place, _)) => used_locals.is_used(place.local),
StatementKind::SetDiscriminant { ref place, .. } => {
used_locals.is_used(place.local)
}
StatementKind::SetDiscriminant { ref place, .. }
| StatementKind::Deinit(ref place) => used_locals.is_used(place.local),
_ => true,
};

View File

@ -211,7 +211,8 @@ fn check_statement<'tcx>(
StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body),
// just an assignment
StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body),
StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) =>
check_place(tcx, **place, span, body),
StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { dst, src, count }) => {
check_operand(tcx, dst, span, body)?;