forked from rust-lang/rust
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rollup merge of rust-lang#119077 - tmiasko:lint, r=cjgillot Separate MIR lints from validation Add a MIR lint pass, enabled with -Zlint-mir, which identifies undefined or likely erroneous behaviour. The initial implementation mostly migrates existing checks of this nature from MIR validator, where they did not belong (those checks have false positives and there is nothing inherently invalid about MIR with undefined behaviour). Fixes rust-lang#104736 Fixes rust-lang#104843 Fixes rust-lang#116079 Fixes rust-lang#116736 Fixes rust-lang#118990
- Loading branch information
Showing
13 changed files
with
196 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
//! This pass statically detects code which has undefined behaviour or is likely to be erroneous. | ||
//! It can be used to locate problems in MIR building or optimizations. It assumes that all code | ||
//! can be executed, so it has false positives. | ||
use rustc_index::bit_set::BitSet; | ||
use rustc_middle::mir::visit::{PlaceContext, Visitor}; | ||
use rustc_middle::mir::*; | ||
use rustc_middle::ty::TyCtxt; | ||
use rustc_mir_dataflow::impls::{MaybeStorageDead, MaybeStorageLive}; | ||
use rustc_mir_dataflow::storage::always_storage_live_locals; | ||
use rustc_mir_dataflow::{Analysis, ResultsCursor}; | ||
use std::borrow::Cow; | ||
|
||
pub fn lint_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, when: String) { | ||
let reachable_blocks = traversal::reachable_as_bitset(body); | ||
let always_live_locals = &always_storage_live_locals(body); | ||
|
||
let maybe_storage_live = MaybeStorageLive::new(Cow::Borrowed(always_live_locals)) | ||
.into_engine(tcx, body) | ||
.iterate_to_fixpoint() | ||
.into_results_cursor(body); | ||
|
||
let maybe_storage_dead = MaybeStorageDead::new(Cow::Borrowed(always_live_locals)) | ||
.into_engine(tcx, body) | ||
.iterate_to_fixpoint() | ||
.into_results_cursor(body); | ||
|
||
Lint { | ||
tcx, | ||
when, | ||
body, | ||
is_fn_like: tcx.def_kind(body.source.def_id()).is_fn_like(), | ||
always_live_locals, | ||
reachable_blocks, | ||
maybe_storage_live, | ||
maybe_storage_dead, | ||
} | ||
.visit_body(body); | ||
} | ||
|
||
struct Lint<'a, 'tcx> { | ||
tcx: TyCtxt<'tcx>, | ||
when: String, | ||
body: &'a Body<'tcx>, | ||
is_fn_like: bool, | ||
always_live_locals: &'a BitSet<Local>, | ||
reachable_blocks: BitSet<BasicBlock>, | ||
maybe_storage_live: ResultsCursor<'a, 'tcx, MaybeStorageLive<'a>>, | ||
maybe_storage_dead: ResultsCursor<'a, 'tcx, MaybeStorageDead<'a>>, | ||
} | ||
|
||
impl<'a, 'tcx> Lint<'a, 'tcx> { | ||
#[track_caller] | ||
fn fail(&self, location: Location, msg: impl AsRef<str>) { | ||
let span = self.body.source_info(location).span; | ||
self.tcx.sess.dcx().span_delayed_bug( | ||
span, | ||
format!( | ||
"broken MIR in {:?} ({}) at {:?}:\n{}", | ||
self.body.source.instance, | ||
self.when, | ||
location, | ||
msg.as_ref() | ||
), | ||
); | ||
} | ||
} | ||
|
||
impl<'a, 'tcx> Visitor<'tcx> for Lint<'a, 'tcx> { | ||
fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) { | ||
if self.reachable_blocks.contains(location.block) && context.is_use() { | ||
self.maybe_storage_dead.seek_after_primary_effect(location); | ||
if self.maybe_storage_dead.get().contains(local) { | ||
self.fail(location, format!("use of local {local:?}, which has no storage here")); | ||
} | ||
} | ||
} | ||
|
||
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { | ||
match statement.kind { | ||
StatementKind::StorageLive(local) => { | ||
if self.reachable_blocks.contains(location.block) { | ||
self.maybe_storage_live.seek_before_primary_effect(location); | ||
if self.maybe_storage_live.get().contains(local) { | ||
self.fail( | ||
location, | ||
format!("StorageLive({local:?}) which already has storage here"), | ||
); | ||
} | ||
} | ||
} | ||
_ => {} | ||
} | ||
|
||
self.super_statement(statement, location); | ||
} | ||
|
||
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { | ||
match terminator.kind { | ||
TerminatorKind::Return => { | ||
if self.is_fn_like && self.reachable_blocks.contains(location.block) { | ||
self.maybe_storage_live.seek_after_primary_effect(location); | ||
for local in self.maybe_storage_live.get().iter() { | ||
if !self.always_live_locals.contains(local) { | ||
self.fail( | ||
location, | ||
format!( | ||
"local {local:?} still has storage when returning from function" | ||
), | ||
); | ||
} | ||
} | ||
} | ||
} | ||
_ => {} | ||
} | ||
|
||
self.super_terminator(terminator, location); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
// compile-flags: -Zlint-mir=no | ||
// unit-test: ReferencePropagation | ||
// needs-unwind | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// compile-flags: -Zlint-mir --crate-type=lib -Ztreat-err-as-bug | ||
// failure-status: 101 | ||
// dont-check-compiler-stderr | ||
// regex-error-pattern: use of local .*, which has no storage here | ||
#![feature(custom_mir, core_intrinsics)] | ||
extern crate core; | ||
use core::intrinsics::mir::*; | ||
|
||
#[custom_mir(dialect = "built")] | ||
pub fn f(a: bool) { | ||
mir!( | ||
let b: (); | ||
{ | ||
match a { true => bb1, _ => bb2 } | ||
} | ||
bb1 = { | ||
StorageLive(b); | ||
Goto(bb3) | ||
} | ||
bb2 = { | ||
Goto(bb3) | ||
} | ||
bb3 = { | ||
b = (); | ||
RET = b; | ||
StorageDead(b); | ||
Return() | ||
} | ||
) | ||
} |
2 changes: 1 addition & 1 deletion
2
tests/ui/mir/validate/storage-live.rs → tests/ui/mir/lint/storage-live.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
Oops, something went wrong.