-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Avoid Operand::Copy
with &mut T
#72093
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,6 +39,7 @@ pub mod simplify_branches; | |
pub mod simplify_try; | ||
pub mod uninhabited_enum_branching; | ||
pub mod unreachable_prop; | ||
pub mod validate; | ||
|
||
pub(crate) fn provide(providers: &mut Providers<'_>) { | ||
self::check_unsafety::provide(providers); | ||
|
@@ -147,12 +148,18 @@ pub fn run_passes( | |
passes: &[&[&dyn MirPass<'tcx>]], | ||
) { | ||
let phase_index = mir_phase.phase_index(); | ||
let source = MirSource { instance, promoted }; | ||
let validate = tcx.sess.opts.debugging_opts.validate_mir; | ||
|
||
if body.phase >= mir_phase { | ||
return; | ||
} | ||
|
||
let source = MirSource { instance, promoted }; | ||
if validate { | ||
validate::Validator { when: format!("input to phase {:?}", mir_phase) } | ||
.run_pass(tcx, source, body); | ||
} | ||
|
||
let mut index = 0; | ||
let mut run_pass = |pass: &dyn MirPass<'tcx>| { | ||
let run_hooks = |body: &_, index, is_after| { | ||
|
@@ -169,6 +176,11 @@ pub fn run_passes( | |
pass.run_pass(tcx, source, body); | ||
run_hooks(body, index, true); | ||
|
||
if validate { | ||
validate::Validator { when: format!("after {} in phase {:?}", pass.name(), mir_phase) } | ||
.run_pass(tcx, source, body); | ||
} | ||
|
||
index += 1; | ||
}; | ||
|
||
|
@@ -179,6 +191,11 @@ pub fn run_passes( | |
} | ||
|
||
body.phase = mir_phase; | ||
|
||
if mir_phase == MirPhase::Optimized { | ||
validate::Validator { when: format!("end of phase {:?}", mir_phase) } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So we run it here even without debug assertions? Isn't that expensive? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The pass does very little work, so it's unlikely to be an issue There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That is something I want to change.^^ #72796 does some non-trivial type folding. |
||
.run_pass(tcx, source, body); | ||
} | ||
} | ||
|
||
fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> ConstQualifs { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
//! Validates the MIR to ensure that invariants are upheld. | ||
|
||
use super::{MirPass, MirSource}; | ||
use rustc_middle::mir::visit::Visitor; | ||
use rustc_middle::{ | ||
mir::{Body, Location, Operand, Rvalue, Statement, StatementKind}, | ||
ty::{ParamEnv, TyCtxt}, | ||
}; | ||
use rustc_span::{def_id::DefId, Span, DUMMY_SP}; | ||
|
||
pub struct Validator { | ||
/// Describes at which point in the pipeline this validation is happening. | ||
pub when: String, | ||
} | ||
|
||
impl<'tcx> MirPass<'tcx> for Validator { | ||
fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { | ||
let def_id = source.def_id(); | ||
let param_env = tcx.param_env(def_id); | ||
TypeChecker { when: &self.when, def_id, body, tcx, param_env }.visit_body(body); | ||
} | ||
} | ||
|
||
struct TypeChecker<'a, 'tcx> { | ||
when: &'a str, | ||
def_id: DefId, | ||
body: &'a Body<'tcx>, | ||
tcx: TyCtxt<'tcx>, | ||
param_env: ParamEnv<'tcx>, | ||
} | ||
|
||
impl<'a, 'tcx> TypeChecker<'a, 'tcx> { | ||
fn fail(&self, span: Span, msg: impl AsRef<str>) { | ||
// We use `delay_span_bug` as we might see broken MIR when other errors have already | ||
// occurred. | ||
self.tcx.sess.diagnostic().delay_span_bug( | ||
span, | ||
&format!("broken MIR in {:?} ({}): {}", self.def_id, self.when, msg.as_ref()), | ||
); | ||
} | ||
} | ||
|
||
impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { | ||
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { | ||
// `Operand::Copy` is only supposed to be used with `Copy` types. | ||
if let Operand::Copy(place) = operand { | ||
let ty = place.ty(&self.body.local_decls, self.tcx).ty; | ||
|
||
if !ty.is_copy_modulo_regions(self.tcx, self.param_env, DUMMY_SP) { | ||
self.fail( | ||
DUMMY_SP, | ||
format!("`Operand::Copy` with non-`Copy` type {} at {:?}", ty, location), | ||
); | ||
} | ||
} | ||
|
||
self.super_operand(operand, location); | ||
} | ||
|
||
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { | ||
// The sides of an assignment must not alias. Currently this just checks whether the places | ||
// are identical. | ||
if let StatementKind::Assign(box (dest, rvalue)) = &statement.kind { | ||
match rvalue { | ||
Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) => { | ||
if dest == src { | ||
self.fail( | ||
DUMMY_SP, | ||
format!( | ||
"encountered `Assign` statement with overlapping memory at {:?}", | ||
location | ||
), | ||
); | ||
} | ||
} | ||
_ => {} | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a mir-opt test showing this