Lint overlapping assignments in MIR.

This commit is contained in:
Camille Gillot 2025-09-13 18:07:35 +00:00
parent ce6daf3d5a
commit 912785d966
4 changed files with 53 additions and 28 deletions

View file

@ -1475,3 +1475,20 @@ impl PlaceContext {
}
}
}
/// Small utility to visit places and locals without manually implementing a full visitor.
pub struct VisitPlacesWith<F>(pub F);
impl<'tcx, F> Visitor<'tcx> for VisitPlacesWith<F>
where
F: FnMut(Place<'tcx>, PlaceContext),
{
fn visit_local(&mut self, local: Local, ctxt: PlaceContext, _: Location) {
(self.0)(local.into(), ctxt);
}
fn visit_place(&mut self, place: &Place<'tcx>, ctxt: PlaceContext, location: Location) {
(self.0)(*place, ctxt);
self.visit_projection(place.as_ref(), ctxt, location);
}
}

View file

@ -141,7 +141,7 @@ use rustc_data_structures::union_find::UnionFind;
use rustc_index::bit_set::DenseBitSet;
use rustc_index::interval::SparseIntervalMatrix;
use rustc_index::{IndexVec, newtype_index};
use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
use rustc_middle::mir::visit::{MutVisitor, PlaceContext, VisitPlacesWith, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;
use rustc_mir_dataflow::impls::{DefUse, MaybeLiveLocals};
@ -503,22 +503,6 @@ impl TwoStepIndex {
}
}
struct VisitPlacesWith<F>(F);
impl<'tcx, F> Visitor<'tcx> for VisitPlacesWith<F>
where
F: FnMut(Place<'tcx>, PlaceContext),
{
fn visit_local(&mut self, local: Local, ctxt: PlaceContext, _: Location) {
(self.0)(local.into(), ctxt);
}
fn visit_place(&mut self, place: &Place<'tcx>, ctxt: PlaceContext, location: Location) {
(self.0)(*place, ctxt);
self.visit_projection(place.as_ref(), ctxt, location);
}
}
/// Add points depending on the result of the given dataflow analysis.
fn save_as_intervals<'tcx>(
elements: &DenseLocationMap,

View file

@ -6,7 +6,7 @@ use std::borrow::Cow;
use rustc_data_structures::fx::FxHashSet;
use rustc_index::bit_set::DenseBitSet;
use rustc_middle::mir::visit::{PlaceContext, Visitor};
use rustc_middle::mir::visit::{PlaceContext, VisitPlacesWith, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;
use rustc_mir_dataflow::impls::{MaybeStorageDead, MaybeStorageLive, always_storage_live_locals};
@ -79,15 +79,39 @@ impl<'a, 'tcx> Visitor<'tcx> for Lint<'a, 'tcx> {
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
match &statement.kind {
StatementKind::Assign(box (dest, rvalue)) => {
if let Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) = rvalue {
// The sides of an assignment must not alias. Currently this just checks whether
// the places are identical.
if dest == src {
self.fail(
location,
"encountered `Assign` statement with overlapping memory",
);
}
let forbid_aliasing = match rvalue {
Rvalue::Use(..)
| Rvalue::CopyForDeref(..)
| Rvalue::Repeat(..)
| Rvalue::Aggregate(..)
| Rvalue::Cast(..)
| Rvalue::ShallowInitBox(..)
| Rvalue::WrapUnsafeBinder(..) => true,
Rvalue::ThreadLocalRef(..)
| Rvalue::NullaryOp(..)
| Rvalue::UnaryOp(..)
| Rvalue::BinaryOp(..)
| Rvalue::Ref(..)
| Rvalue::RawPtr(..)
| Rvalue::Discriminant(..) => false,
};
// The sides of an assignment must not alias.
if forbid_aliasing {
VisitPlacesWith(|src: Place<'tcx>, _| {
if *dest == src
|| (dest.local == src.local
&& !dest.is_indirect()
&& !src.is_indirect())
{
self.fail(
location,
format!(
"encountered `{statement:?}` statement with overlapping memory"
),
);
}
})
.visit_rvalue(rvalue, location);
}
}
StatementKind::StorageLive(local) => {

View file

@ -13,7 +13,7 @@ pub fn main() {
let a: [u8; 1024];
{
a = a; //~ ERROR broken MIR
//~^ ERROR encountered `Assign` statement with overlapping memory
//~^ ERROR encountered `_1 = copy _1` statement with overlapping memory
Return()
}
}