Auto merge of #102256 - cjgillot:let-under, r=lcnr
Introduce a no-op `PlaceMention` statement for `let _ =`. Fixes https://github.com/rust-lang/rust/issues/54003 Fixes https://github.com/rust-lang/rust/issues/80059 Split from https://github.com/rust-lang/rust/pull/101500 This PR introduces a new `PlaceMention` statement dedicated to matches that neither introduce bindings nor ascribe types. Without this, all traces of the match would vanish from MIR, making it impossible to diagnose unsafety or use in #101500. This allows to mark `let _ = <unsafe union access or dereference>` as requiring an unsafe block. Nominating for lang team, as this introduces an extra error.
This commit is contained in:
commit
d5833423a0
48 changed files with 271 additions and 19 deletions
|
@ -390,6 +390,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
|
|||
| mir::StatementKind::Deinit(..)
|
||||
| mir::StatementKind::StorageLive(..)
|
||||
| mir::StatementKind::Retag { .. }
|
||||
| mir::StatementKind::PlaceMention(..)
|
||||
| mir::StatementKind::AscribeUserType(..)
|
||||
| mir::StatementKind::Coverage(..)
|
||||
| mir::StatementKind::Intrinsic(..)
|
||||
|
|
|
@ -72,6 +72,8 @@ pub fn categorize(context: PlaceContext) -> Option<DefUse> {
|
|||
PlaceContext::MutatingUse(MutatingUseContext::Drop) =>
|
||||
Some(DefUse::Drop),
|
||||
|
||||
// This statement exists to help unsafeck. It does not require the place to be live.
|
||||
PlaceContext::NonUse(NonUseContext::PlaceMention) => None,
|
||||
// Debug info is neither def nor use.
|
||||
PlaceContext::NonUse(NonUseContext::VarDebugInfo) => None,
|
||||
|
||||
|
|
|
@ -79,6 +79,8 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
|
|||
}
|
||||
// Only relevant for mir typeck
|
||||
StatementKind::AscribeUserType(..)
|
||||
// Only relevant for unsafeck
|
||||
| StatementKind::PlaceMention(..)
|
||||
// Doesn't have any language semantics
|
||||
| StatementKind::Coverage(..)
|
||||
// Does not actually affect borrowck
|
||||
|
|
|
@ -690,6 +690,8 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx
|
|||
}
|
||||
// Only relevant for mir typeck
|
||||
StatementKind::AscribeUserType(..)
|
||||
// Only relevant for unsafeck
|
||||
| StatementKind::PlaceMention(..)
|
||||
// Doesn't have any language semantics
|
||||
| StatementKind::Coverage(..)
|
||||
// These do not actually affect borrowck
|
||||
|
|
|
@ -772,7 +772,9 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
|||
|
||||
match context {
|
||||
PlaceContext::MutatingUse(_) => ty::Invariant,
|
||||
PlaceContext::NonUse(StorageDead | StorageLive | VarDebugInfo) => ty::Invariant,
|
||||
PlaceContext::NonUse(StorageDead | StorageLive | PlaceMention | VarDebugInfo) => {
|
||||
ty::Invariant
|
||||
}
|
||||
PlaceContext::NonMutatingUse(
|
||||
Inspect | Copy | Move | SharedBorrow | ShallowBorrow | UniqueBorrow | AddressOf
|
||||
| Projection,
|
||||
|
@ -1282,6 +1284,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
| StatementKind::Retag { .. }
|
||||
| StatementKind::Coverage(..)
|
||||
| StatementKind::ConstEvalCounter
|
||||
| StatementKind::PlaceMention(..)
|
||||
| StatementKind::Nop => {}
|
||||
StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => {
|
||||
bug!("Statement not allowed in this MIR phase")
|
||||
|
|
|
@ -819,6 +819,7 @@ fn codegen_stmt<'tcx>(
|
|||
| StatementKind::Nop
|
||||
| StatementKind::FakeRead(..)
|
||||
| StatementKind::Retag { .. }
|
||||
| StatementKind::PlaceMention(..)
|
||||
| StatementKind::AscribeUserType(..) => {}
|
||||
|
||||
StatementKind::Coverage { .. } => fx.tcx.sess.fatal("-Zcoverage is unimplemented"),
|
||||
|
|
|
@ -529,6 +529,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>(
|
|||
| StatementKind::StorageDead(_)
|
||||
| StatementKind::Retag(_, _)
|
||||
| StatementKind::AscribeUserType(_, _)
|
||||
| StatementKind::PlaceMention(..)
|
||||
| StatementKind::Coverage(_)
|
||||
| StatementKind::ConstEvalCounter
|
||||
| StatementKind::Nop => {}
|
||||
|
|
|
@ -92,6 +92,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
| mir::StatementKind::Retag { .. }
|
||||
| mir::StatementKind::AscribeUserType(..)
|
||||
| mir::StatementKind::ConstEvalCounter
|
||||
| mir::StatementKind::PlaceMention(..)
|
||||
| mir::StatementKind::Nop => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
Intrinsic(box intrinsic) => self.emulate_nondiverging_intrinsic(intrinsic)?,
|
||||
|
||||
// Statements we do not track.
|
||||
AscribeUserType(..) => {}
|
||||
PlaceMention(..) | AscribeUserType(..) => {}
|
||||
|
||||
// Currently, Miri discards Coverage statements. Coverage statements are only injected
|
||||
// via an optional compile time MIR pass and have no side effects. Since Coverage
|
||||
|
|
|
@ -690,6 +690,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||
| StatementKind::StorageLive(_)
|
||||
| StatementKind::StorageDead(_)
|
||||
| StatementKind::Retag { .. }
|
||||
| StatementKind::PlaceMention(..)
|
||||
| StatementKind::AscribeUserType(..)
|
||||
| StatementKind::Coverage(..)
|
||||
| StatementKind::Intrinsic(..)
|
||||
|
|
|
@ -679,6 +679,14 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
StatementKind::PlaceMention(..) => {
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(
|
||||
location,
|
||||
"`PlaceMention` should have been removed after drop lowering phase",
|
||||
);
|
||||
}
|
||||
}
|
||||
StatementKind::AscribeUserType(..) => {
|
||||
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
|
||||
self.fail(
|
||||
|
|
|
@ -1453,6 +1453,9 @@ impl Debug for Statement<'_> {
|
|||
write!(fmt, "discriminant({:?}) = {:?}", place, variant_index)
|
||||
}
|
||||
Deinit(ref place) => write!(fmt, "Deinit({:?})", place),
|
||||
PlaceMention(ref place) => {
|
||||
write!(fmt, "PlaceMention({:?})", place)
|
||||
}
|
||||
AscribeUserType(box (ref place, ref c_ty), ref variance) => {
|
||||
write!(fmt, "AscribeUserType({:?}, {:?}, {:?})", place, variance, c_ty)
|
||||
}
|
||||
|
|
|
@ -247,6 +247,7 @@ pub fn statement_kind_name(statement: &Statement<'_>) -> &'static str {
|
|||
StorageLive(..) => "StorageLive",
|
||||
StorageDead(..) => "StorageDead",
|
||||
Retag(..) => "Retag",
|
||||
PlaceMention(..) => "PlaceMention",
|
||||
AscribeUserType(..) => "AscribeUserType",
|
||||
Coverage(..) => "Coverage",
|
||||
Intrinsic(..) => "Intrinsic",
|
||||
|
|
|
@ -325,6 +325,15 @@ pub enum StatementKind<'tcx> {
|
|||
/// Only `RetagKind::Default` and `RetagKind::FnEntry` are permitted.
|
||||
Retag(RetagKind, Box<Place<'tcx>>),
|
||||
|
||||
/// This statement exists to preserve a trace of a scrutinee matched against a wildcard binding.
|
||||
/// This is especially useful for `let _ = PLACE;` bindings that desugar to a single
|
||||
/// `PlaceMention(PLACE)`.
|
||||
///
|
||||
/// When executed at runtime this is a nop.
|
||||
///
|
||||
/// Disallowed after drop elaboration.
|
||||
PlaceMention(Box<Place<'tcx>>),
|
||||
|
||||
/// Encodes a user's type ascription. These need to be preserved
|
||||
/// intact so that NLL can respect them. For example:
|
||||
/// ```ignore (illustrative)
|
||||
|
|
|
@ -405,6 +405,13 @@ macro_rules! make_mir_visitor {
|
|||
StatementKind::Retag(kind, place) => {
|
||||
self.visit_retag($(& $mutability)? *kind, place, location);
|
||||
}
|
||||
StatementKind::PlaceMention(place) => {
|
||||
self.visit_place(
|
||||
place,
|
||||
PlaceContext::NonUse(NonUseContext::PlaceMention),
|
||||
location
|
||||
);
|
||||
}
|
||||
StatementKind::AscribeUserType(
|
||||
box (place, user_ty),
|
||||
variance
|
||||
|
@ -1288,6 +1295,8 @@ pub enum NonUseContext {
|
|||
AscribeUserTy,
|
||||
/// The data of a user variable, for debug info.
|
||||
VarDebugInfo,
|
||||
/// PlaceMention statement.
|
||||
PlaceMention,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
|
|
|
@ -90,6 +90,17 @@ impl<'tcx> CFG<'tcx> {
|
|||
self.push(block, stmt);
|
||||
}
|
||||
|
||||
pub(crate) fn push_place_mention(
|
||||
&mut self,
|
||||
block: BasicBlock,
|
||||
source_info: SourceInfo,
|
||||
place: Place<'tcx>,
|
||||
) {
|
||||
let kind = StatementKind::PlaceMention(Box::new(place));
|
||||
let stmt = Statement { source_info, kind };
|
||||
self.push(block, stmt);
|
||||
}
|
||||
|
||||
pub(crate) fn terminate(
|
||||
&mut self,
|
||||
block: BasicBlock,
|
||||
|
|
|
@ -556,6 +556,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
_ => {
|
||||
let place_builder = unpack!(block = self.as_place_builder(block, initializer));
|
||||
|
||||
if let Some(place) = place_builder.try_to_place(self) {
|
||||
let source_info = self.source_info(initializer.span);
|
||||
self.cfg.push_place_mention(block, source_info, place);
|
||||
}
|
||||
|
||||
self.place_into_pattern(block, &irrefutable_pat, place_builder, true)
|
||||
}
|
||||
}
|
||||
|
@ -576,6 +582,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
false,
|
||||
&mut [&mut candidate],
|
||||
);
|
||||
|
||||
// For matches and function arguments, the place that is being matched
|
||||
// can be set when creating the variables. But the place for
|
||||
// let PATTERN = ... might not even exist until we do the assignment.
|
||||
|
|
|
@ -263,6 +263,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
|
|||
| StatementKind::StorageDead(_)
|
||||
| StatementKind::Retag(..)
|
||||
| StatementKind::AscribeUserType(..)
|
||||
| StatementKind::PlaceMention(..)
|
||||
| StatementKind::Coverage(..)
|
||||
| StatementKind::Intrinsic(..)
|
||||
| StatementKind::ConstEvalCounter
|
||||
|
|
|
@ -139,6 +139,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
|
|||
// Nothing to do for these. Match exhaustively so this fails to compile when new
|
||||
// variants are added.
|
||||
StatementKind::AscribeUserType(..)
|
||||
| StatementKind::PlaceMention(..)
|
||||
| StatementKind::Coverage(..)
|
||||
| StatementKind::FakeRead(..)
|
||||
| StatementKind::ConstEvalCounter
|
||||
|
|
|
@ -329,6 +329,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
|||
}
|
||||
StatementKind::Retag { .. }
|
||||
| StatementKind::AscribeUserType(..)
|
||||
| StatementKind::PlaceMention(..)
|
||||
| StatementKind::Coverage(..)
|
||||
| StatementKind::Intrinsic(..)
|
||||
| StatementKind::ConstEvalCounter
|
||||
|
|
|
@ -86,6 +86,7 @@ pub trait ValueAnalysis<'tcx> {
|
|||
StatementKind::ConstEvalCounter
|
||||
| StatementKind::Nop
|
||||
| StatementKind::FakeRead(..)
|
||||
| StatementKind::PlaceMention(..)
|
||||
| StatementKind::Coverage(..)
|
||||
| StatementKind::AscribeUserType(..) => (),
|
||||
}
|
||||
|
|
|
@ -100,13 +100,16 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
|
|||
| StatementKind::StorageLive(..)
|
||||
| StatementKind::StorageDead(..)
|
||||
| StatementKind::Retag { .. }
|
||||
| StatementKind::AscribeUserType(..)
|
||||
| StatementKind::PlaceMention(..)
|
||||
| StatementKind::Coverage(..)
|
||||
| StatementKind::Intrinsic(..)
|
||||
| StatementKind::ConstEvalCounter
|
||||
| StatementKind::Nop => {
|
||||
// safe (at least as emitted during MIR construction)
|
||||
}
|
||||
// `AscribeUserType` just exists to help MIR borrowck.
|
||||
// It has no semantics, and everything is already reported by `PlaceMention`.
|
||||
StatementKind::AscribeUserType(..) => return,
|
||||
}
|
||||
self.super_statement(statement, location);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ impl<'tcx> MirPass<'tcx> for CleanupPostBorrowck {
|
|||
for statement in basic_block.statements.iter_mut() {
|
||||
match statement.kind {
|
||||
StatementKind::AscribeUserType(..)
|
||||
| StatementKind::PlaceMention(..)
|
||||
| StatementKind::Assign(box (_, Rvalue::Ref(_, BorrowKind::Shallow, _)))
|
||||
| StatementKind::FakeRead(..) => statement.make_nop(),
|
||||
_ => (),
|
||||
|
|
|
@ -832,6 +832,7 @@ pub(super) fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span>
|
|||
| StatementKind::SetDiscriminant { .. }
|
||||
| StatementKind::Deinit(..)
|
||||
| StatementKind::Retag(_, _)
|
||||
| StatementKind::PlaceMention(..)
|
||||
| StatementKind::AscribeUserType(_, _) => {
|
||||
Some(statement.source_info.span)
|
||||
}
|
||||
|
|
|
@ -56,7 +56,9 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS
|
|||
| StatementKind::ConstEvalCounter
|
||||
| StatementKind::Nop => (),
|
||||
|
||||
StatementKind::FakeRead(_) | StatementKind::AscribeUserType(_, _) => {
|
||||
StatementKind::FakeRead(_)
|
||||
| StatementKind::PlaceMention(_)
|
||||
| StatementKind::AscribeUserType(_, _) => {
|
||||
bug!("{:?} not found in this MIR phase!", &statement.kind)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -583,7 +583,9 @@ impl WriteInfo {
|
|||
| StatementKind::Coverage(_)
|
||||
| StatementKind::StorageLive(_)
|
||||
| StatementKind::StorageDead(_) => (),
|
||||
StatementKind::FakeRead(_) | StatementKind::AscribeUserType(_, _) => {
|
||||
StatementKind::FakeRead(_)
|
||||
| StatementKind::AscribeUserType(_, _)
|
||||
| StatementKind::PlaceMention(_) => {
|
||||
bug!("{:?} not found in this MIR phase", statement)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1647,6 +1647,7 @@ impl<'tcx> Visitor<'tcx> for EnsureGeneratorFieldAssignmentsNeverAlias<'_> {
|
|||
| StatementKind::StorageDead(_)
|
||||
| StatementKind::Retag(..)
|
||||
| StatementKind::AscribeUserType(..)
|
||||
| StatementKind::PlaceMention(..)
|
||||
| StatementKind::Coverage(..)
|
||||
| StatementKind::Intrinsic(..)
|
||||
| StatementKind::ConstEvalCounter
|
||||
|
|
|
@ -33,6 +33,7 @@ impl RemoveNoopLandingPads {
|
|||
StatementKind::FakeRead(..)
|
||||
| StatementKind::StorageLive(_)
|
||||
| StatementKind::StorageDead(_)
|
||||
| StatementKind::PlaceMention(..)
|
||||
| StatementKind::AscribeUserType(..)
|
||||
| StatementKind::Coverage(..)
|
||||
| StatementKind::ConstEvalCounter
|
||||
|
|
|
@ -245,6 +245,7 @@ fn is_likely_const<'tcx>(mut tracked_place: Place<'tcx>, block: &BasicBlockData<
|
|||
| StatementKind::StorageLive(_)
|
||||
| StatementKind::Retag(_, _)
|
||||
| StatementKind::AscribeUserType(_, _)
|
||||
| StatementKind::PlaceMention(..)
|
||||
| StatementKind::Coverage(_)
|
||||
| StatementKind::StorageDead(_)
|
||||
| StatementKind::Intrinsic(_)
|
||||
|
@ -315,6 +316,7 @@ fn find_determining_place<'tcx>(
|
|||
| StatementKind::StorageDead(_)
|
||||
| StatementKind::Retag(_, _)
|
||||
| StatementKind::AscribeUserType(_, _)
|
||||
| StatementKind::PlaceMention(..)
|
||||
| StatementKind::Coverage(_)
|
||||
| StatementKind::Intrinsic(_)
|
||||
| StatementKind::ConstEvalCounter
|
||||
|
|
|
@ -525,6 +525,7 @@ impl<'tcx> Visitor<'tcx> for UsedLocals {
|
|||
| StatementKind::Retag(..)
|
||||
| StatementKind::Coverage(..)
|
||||
| StatementKind::FakeRead(..)
|
||||
| StatementKind::PlaceMention(..)
|
||||
| StatementKind::AscribeUserType(..) => {
|
||||
self.super_statement(statement, location);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue