Clarify which kinds of MIR are allowed during which phases.
This enhances documentation with these details and extends the validator to check these requirements more thoroughly. As a part of this, we add a new `Deaggregated` phase, and rename other phases so that their names more naturally correspond to what they represent.
This commit is contained in:
parent
547369d3d8
commit
fe40240e4d
8 changed files with 98 additions and 52 deletions
|
@ -42,7 +42,7 @@ pub struct PromoteTemps<'tcx> {
|
||||||
|
|
||||||
impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> {
|
impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> {
|
||||||
fn phase_change(&self) -> Option<MirPhase> {
|
fn phase_change(&self) -> Option<MirPhase> {
|
||||||
Some(MirPhase::ConstPromotion)
|
Some(MirPhase::ConstsPromoted)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||||
|
|
|
@ -266,22 +266,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The deaggregator currently does not deaggreagate arrays.
|
Rvalue::Aggregate(agg_kind, _) => {
|
||||||
// So for now, we ignore them here.
|
let disallowed = match **agg_kind {
|
||||||
Rvalue::Aggregate(box AggregateKind::Array { .. }, _) => {}
|
AggregateKind::Array(..) => false,
|
||||||
// All other aggregates must be gone after some phases.
|
AggregateKind::Generator(..) => {
|
||||||
Rvalue::Aggregate(box kind, _) => {
|
self.mir_phase >= MirPhase::GeneratorsLowered
|
||||||
if self.mir_phase > MirPhase::DropLowering
|
}
|
||||||
&& !matches!(kind, AggregateKind::Generator(..))
|
_ => self.mir_phase >= MirPhase::Deaggregated,
|
||||||
{
|
};
|
||||||
// Generators persist until the state machine transformation, but all
|
if disallowed {
|
||||||
// other aggregates must have been lowered.
|
|
||||||
self.fail(
|
|
||||||
location,
|
|
||||||
format!("{:?} have been lowered to field assignments", rvalue),
|
|
||||||
)
|
|
||||||
} else if self.mir_phase > MirPhase::GeneratorLowering {
|
|
||||||
// No more aggregates after drop and generator lowering.
|
|
||||||
self.fail(
|
self.fail(
|
||||||
location,
|
location,
|
||||||
format!("{:?} have been lowered to field assignments", rvalue),
|
format!("{:?} have been lowered to field assignments", rvalue),
|
||||||
|
@ -289,7 +282,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Rvalue::Ref(_, BorrowKind::Shallow, _) => {
|
Rvalue::Ref(_, BorrowKind::Shallow, _) => {
|
||||||
if self.mir_phase > MirPhase::DropLowering {
|
if self.mir_phase >= MirPhase::DropsLowered {
|
||||||
self.fail(
|
self.fail(
|
||||||
location,
|
location,
|
||||||
"`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase",
|
"`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase",
|
||||||
|
@ -300,7 +293,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StatementKind::AscribeUserType(..) => {
|
StatementKind::AscribeUserType(..) => {
|
||||||
if self.mir_phase > MirPhase::DropLowering {
|
if self.mir_phase >= MirPhase::DropsLowered {
|
||||||
self.fail(
|
self.fail(
|
||||||
location,
|
location,
|
||||||
"`AscribeUserType` should have been removed after drop lowering phase",
|
"`AscribeUserType` should have been removed after drop lowering phase",
|
||||||
|
@ -308,7 +301,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StatementKind::FakeRead(..) => {
|
StatementKind::FakeRead(..) => {
|
||||||
if self.mir_phase > MirPhase::DropLowering {
|
if self.mir_phase >= MirPhase::DropsLowered {
|
||||||
self.fail(
|
self.fail(
|
||||||
location,
|
location,
|
||||||
"`FakeRead` should have been removed after drop lowering phase",
|
"`FakeRead` should have been removed after drop lowering phase",
|
||||||
|
@ -351,10 +344,18 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||||
self.fail(location, format!("bad arg ({:?} != usize)", op_cnt_ty))
|
self.fail(location, format!("bad arg ({:?} != usize)", op_cnt_ty))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StatementKind::SetDiscriminant { .. }
|
StatementKind::SetDiscriminant { .. } => {
|
||||||
| StatementKind::StorageLive(..)
|
if self.mir_phase < MirPhase::DropsLowered {
|
||||||
|
self.fail(location, "`SetDiscriminant` is not allowed until drop elaboration");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StatementKind::Retag(_, _) => {
|
||||||
|
// FIXME(JakobDegen) The validator should check that `self.mir_phase <
|
||||||
|
// DropsLowered`. However, this causes ICEs with generation of drop shims, which
|
||||||
|
// seem to fail to set their `MirPhase` correctly.
|
||||||
|
}
|
||||||
|
StatementKind::StorageLive(..)
|
||||||
| StatementKind::StorageDead(..)
|
| StatementKind::StorageDead(..)
|
||||||
| StatementKind::Retag(_, _)
|
|
||||||
| StatementKind::Coverage(_)
|
| StatementKind::Coverage(_)
|
||||||
| StatementKind::Nop => {}
|
| StatementKind::Nop => {}
|
||||||
}
|
}
|
||||||
|
@ -424,10 +425,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TerminatorKind::DropAndReplace { target, unwind, .. } => {
|
TerminatorKind::DropAndReplace { target, unwind, .. } => {
|
||||||
if self.mir_phase > MirPhase::DropLowering {
|
if self.mir_phase >= MirPhase::DropsLowered {
|
||||||
self.fail(
|
self.fail(
|
||||||
location,
|
location,
|
||||||
"`DropAndReplace` is not permitted to exist after drop elaboration",
|
"`DropAndReplace` should have been removed during drop elaboration",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
self.check_edge(location, *target, EdgeKind::Normal);
|
self.check_edge(location, *target, EdgeKind::Normal);
|
||||||
|
@ -494,7 +495,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TerminatorKind::Yield { resume, drop, .. } => {
|
TerminatorKind::Yield { resume, drop, .. } => {
|
||||||
if self.mir_phase > MirPhase::GeneratorLowering {
|
if self.mir_phase >= MirPhase::GeneratorsLowered {
|
||||||
self.fail(location, "`Yield` should have been replaced by generator lowering");
|
self.fail(location, "`Yield` should have been replaced by generator lowering");
|
||||||
}
|
}
|
||||||
self.check_edge(location, *resume, EdgeKind::Normal);
|
self.check_edge(location, *resume, EdgeKind::Normal);
|
||||||
|
@ -503,10 +504,22 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TerminatorKind::FalseEdge { real_target, imaginary_target } => {
|
TerminatorKind::FalseEdge { real_target, imaginary_target } => {
|
||||||
|
if self.mir_phase >= MirPhase::DropsLowered {
|
||||||
|
self.fail(
|
||||||
|
location,
|
||||||
|
"`FalseEdge` should have been removed after drop elaboration",
|
||||||
|
);
|
||||||
|
}
|
||||||
self.check_edge(location, *real_target, EdgeKind::Normal);
|
self.check_edge(location, *real_target, EdgeKind::Normal);
|
||||||
self.check_edge(location, *imaginary_target, EdgeKind::Normal);
|
self.check_edge(location, *imaginary_target, EdgeKind::Normal);
|
||||||
}
|
}
|
||||||
TerminatorKind::FalseUnwind { real_target, unwind } => {
|
TerminatorKind::FalseUnwind { real_target, unwind } => {
|
||||||
|
if self.mir_phase >= MirPhase::DropsLowered {
|
||||||
|
self.fail(
|
||||||
|
location,
|
||||||
|
"`FalseUnwind` should have been removed after drop elaboration",
|
||||||
|
);
|
||||||
|
}
|
||||||
self.check_edge(location, *real_target, EdgeKind::Normal);
|
self.check_edge(location, *real_target, EdgeKind::Normal);
|
||||||
if let Some(unwind) = unwind {
|
if let Some(unwind) = unwind {
|
||||||
self.check_edge(location, *unwind, EdgeKind::Unwind);
|
self.check_edge(location, *unwind, EdgeKind::Unwind);
|
||||||
|
@ -520,12 +533,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||||
self.check_edge(location, *cleanup, EdgeKind::Unwind);
|
self.check_edge(location, *cleanup, EdgeKind::Unwind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
TerminatorKind::GeneratorDrop => {
|
||||||
|
if self.mir_phase >= MirPhase::GeneratorsLowered {
|
||||||
|
self.fail(
|
||||||
|
location,
|
||||||
|
"`GeneratorDrop` should have been replaced by generator lowering",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
// Nothing to validate for these.
|
// Nothing to validate for these.
|
||||||
TerminatorKind::Resume
|
TerminatorKind::Resume
|
||||||
| TerminatorKind::Abort
|
| TerminatorKind::Abort
|
||||||
| TerminatorKind::Return
|
| TerminatorKind::Return
|
||||||
| TerminatorKind::Unreachable
|
| TerminatorKind::Unreachable => {}
|
||||||
| TerminatorKind::GeneratorDrop => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.super_terminator(terminator, location);
|
self.super_terminator(terminator, location);
|
||||||
|
|
|
@ -127,14 +127,11 @@ pub trait MirPass<'tcx> {
|
||||||
/// These phases all describe dialects of MIR. Since all MIR uses the same datastructures, the
|
/// These phases all describe dialects of MIR. Since all MIR uses the same datastructures, the
|
||||||
/// dialects forbid certain variants or values in certain phases.
|
/// dialects forbid certain variants or values in certain phases.
|
||||||
///
|
///
|
||||||
/// Note: Each phase's validation checks all invariants of the *previous* phases' dialects. A phase
|
|
||||||
/// that changes the dialect documents what invariants must be upheld *after* that phase finishes.
|
|
||||||
///
|
|
||||||
/// Warning: ordering of variants is significant.
|
/// Warning: ordering of variants is significant.
|
||||||
#[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
#[derive(HashStable)]
|
#[derive(HashStable)]
|
||||||
pub enum MirPhase {
|
pub enum MirPhase {
|
||||||
Build = 0,
|
Built = 0,
|
||||||
// FIXME(oli-obk): it's unclear whether we still need this phase (and its corresponding query).
|
// FIXME(oli-obk): it's unclear whether we still need this phase (and its corresponding query).
|
||||||
// We used to have this for pre-miri MIR based const eval.
|
// We used to have this for pre-miri MIR based const eval.
|
||||||
Const = 1,
|
Const = 1,
|
||||||
|
@ -142,17 +139,32 @@ pub enum MirPhase {
|
||||||
/// by creating a new MIR body per promoted element. After this phase (and thus the termination
|
/// by creating a new MIR body per promoted element. After this phase (and thus the termination
|
||||||
/// of the `mir_promoted` query), these promoted elements are available in the `promoted_mir`
|
/// of the `mir_promoted` query), these promoted elements are available in the `promoted_mir`
|
||||||
/// query.
|
/// query.
|
||||||
ConstPromotion = 2,
|
ConstsPromoted = 2,
|
||||||
/// After this phase
|
/// Beginning with this phase, the following variants are disallowed:
|
||||||
/// * the only `AggregateKind`s allowed are `Array` and `Generator`,
|
/// * [`TerminatorKind::DropAndReplace`](terminator::TerminatorKind::DropAndReplace)
|
||||||
/// * `DropAndReplace` is gone for good
|
/// * [`TerminatorKind::FalseUnwind`](terminator::TerminatorKind::FalseUnwind)
|
||||||
/// * `Drop` now uses explicit drop flags visible in the MIR and reaching a `Drop` terminator
|
/// * [`TerminatorKind::FalseEdge`](terminator::TerminatorKind::FalseEdge)
|
||||||
/// means that the auto-generated drop glue will be invoked.
|
/// * [`StatementKind::FakeRead`]
|
||||||
DropLowering = 3,
|
/// * [`StatementKind::AscribeUserType`]
|
||||||
/// After this phase, generators are explicit state machines (no more `Yield`).
|
/// * [`Rvalue::Ref`] with `BorrowKind::Shallow`
|
||||||
/// `AggregateKind::Generator` is gone for good.
|
///
|
||||||
GeneratorLowering = 4,
|
/// And the following variant is allowed:
|
||||||
Optimization = 5,
|
/// * [`StatementKind::Retag`]
|
||||||
|
///
|
||||||
|
/// Furthermore, `Drop` now uses explicit drop flags visible in the MIR and reaching a `Drop`
|
||||||
|
/// terminator means that the auto-generated drop glue will be invoked.
|
||||||
|
DropsLowered = 3,
|
||||||
|
/// Beginning with this phase, the following variant is disallowed:
|
||||||
|
/// * [`Rvalue::Aggregate`] for any `AggregateKind` except `Array`
|
||||||
|
///
|
||||||
|
/// And the following variant is allowed:
|
||||||
|
/// * [`StatementKind::SetDiscriminant`]
|
||||||
|
Deaggregated = 4,
|
||||||
|
/// Beginning with this phase, the following variants are disallowed:
|
||||||
|
/// * [`TerminatorKind::Yield`](terminator::TerminatorKind::Yield)
|
||||||
|
/// * [`TerminatorKind::GeneratorDrop](terminator::TerminatorKind::GeneratorDrop)
|
||||||
|
GeneratorsLowered = 5,
|
||||||
|
Optimized = 6,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MirPhase {
|
impl MirPhase {
|
||||||
|
@ -311,7 +323,7 @@ impl<'tcx> Body<'tcx> {
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut body = Body {
|
let mut body = Body {
|
||||||
phase: MirPhase::Build,
|
phase: MirPhase::Built,
|
||||||
source,
|
source,
|
||||||
basic_blocks,
|
basic_blocks,
|
||||||
source_scopes,
|
source_scopes,
|
||||||
|
@ -346,7 +358,7 @@ impl<'tcx> Body<'tcx> {
|
||||||
/// crate.
|
/// crate.
|
||||||
pub fn new_cfg_only(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) -> Self {
|
pub fn new_cfg_only(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) -> Self {
|
||||||
let mut body = Body {
|
let mut body = Body {
|
||||||
phase: MirPhase::Build,
|
phase: MirPhase::Built,
|
||||||
source: MirSource::item(DefId::local(CRATE_DEF_INDEX)),
|
source: MirSource::item(DefId::local(CRATE_DEF_INDEX)),
|
||||||
basic_blocks,
|
basic_blocks,
|
||||||
source_scopes: IndexVec::new(),
|
source_scopes: IndexVec::new(),
|
||||||
|
@ -1541,6 +1553,11 @@ impl Statement<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The various kinds of statements that can appear in MIR.
|
||||||
|
///
|
||||||
|
/// Not all of these are allowed at every [`MirPhase`]. Check the documentation there to see which
|
||||||
|
/// ones you do not have to worry about. The MIR validator will generally enforce such restrictions,
|
||||||
|
/// causing an ICE if they are violated.
|
||||||
#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)]
|
#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)]
|
||||||
pub enum StatementKind<'tcx> {
|
pub enum StatementKind<'tcx> {
|
||||||
/// Write the RHS Rvalue to the LHS Place.
|
/// Write the RHS Rvalue to the LHS Place.
|
||||||
|
@ -2223,6 +2240,11 @@ impl<'tcx> Operand<'tcx> {
|
||||||
/// Rvalues
|
/// Rvalues
|
||||||
|
|
||||||
#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
|
#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
|
||||||
|
/// The various kinds of rvalues that can appear in MIR.
|
||||||
|
///
|
||||||
|
/// Not all of these are allowed at every [`MirPhase`]. Check the documentation there to see which
|
||||||
|
/// ones you do not have to worry about. The MIR validator will generally enforce such restrictions,
|
||||||
|
/// causing an ICE if they are violated.
|
||||||
pub enum Rvalue<'tcx> {
|
pub enum Rvalue<'tcx> {
|
||||||
/// x (either a move or copy, depending on type of x)
|
/// x (either a move or copy, depending on type of x)
|
||||||
Use(Operand<'tcx>),
|
Use(Operand<'tcx>),
|
||||||
|
|
|
@ -6,6 +6,10 @@ use rustc_middle::ty::TyCtxt;
|
||||||
pub struct Deaggregator;
|
pub struct Deaggregator;
|
||||||
|
|
||||||
impl<'tcx> MirPass<'tcx> for Deaggregator {
|
impl<'tcx> MirPass<'tcx> for Deaggregator {
|
||||||
|
fn phase_change(&self) -> Option<MirPhase> {
|
||||||
|
Some(MirPhase::Deaggregated)
|
||||||
|
}
|
||||||
|
|
||||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||||
let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
|
let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
|
||||||
let local_decls = &*local_decls;
|
let local_decls = &*local_decls;
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub struct ElaborateDrops;
|
||||||
|
|
||||||
impl<'tcx> MirPass<'tcx> for ElaborateDrops {
|
impl<'tcx> MirPass<'tcx> for ElaborateDrops {
|
||||||
fn phase_change(&self) -> Option<MirPhase> {
|
fn phase_change(&self) -> Option<MirPhase> {
|
||||||
Some(MirPhase::DropLowering)
|
Some(MirPhase::DropsLowered)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||||
|
|
|
@ -1235,7 +1235,7 @@ fn create_cases<'tcx>(
|
||||||
|
|
||||||
impl<'tcx> MirPass<'tcx> for StateTransform {
|
impl<'tcx> MirPass<'tcx> for StateTransform {
|
||||||
fn phase_change(&self) -> Option<MirPhase> {
|
fn phase_change(&self) -> Option<MirPhase> {
|
||||||
Some(MirPhase::GeneratorLowering)
|
Some(MirPhase::GeneratorsLowered)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||||
|
|
|
@ -341,7 +341,7 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -
|
||||||
pm::run_passes(
|
pm::run_passes(
|
||||||
tcx,
|
tcx,
|
||||||
&mut body,
|
&mut body,
|
||||||
&[&const_prop::ConstProp, &marker::PhaseChange(MirPhase::Optimization)],
|
&[&const_prop::ConstProp, &marker::PhaseChange(MirPhase::Optimized)],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -398,7 +398,7 @@ fn mir_drops_elaborated_and_const_checked<'tcx>(
|
||||||
}
|
}
|
||||||
|
|
||||||
run_post_borrowck_cleanup_passes(tcx, &mut body);
|
run_post_borrowck_cleanup_passes(tcx, &mut body);
|
||||||
assert!(body.phase == MirPhase::DropLowering);
|
assert!(body.phase == MirPhase::Deaggregated);
|
||||||
tcx.alloc_steal_mir(body)
|
tcx.alloc_steal_mir(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,7 +458,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(body.phase == MirPhase::GeneratorLowering);
|
assert!(body.phase == MirPhase::GeneratorsLowered);
|
||||||
|
|
||||||
// The main optimizations that we do on MIR.
|
// The main optimizations that we do on MIR.
|
||||||
pm::run_passes(
|
pm::run_passes(
|
||||||
|
@ -495,7 +495,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||||
&deduplicate_blocks::DeduplicateBlocks,
|
&deduplicate_blocks::DeduplicateBlocks,
|
||||||
// Some cleanup necessary at least for LLVM and potentially other codegen backends.
|
// Some cleanup necessary at least for LLVM and potentially other codegen backends.
|
||||||
&add_call_guards::CriticalCallEdges,
|
&add_call_guards::CriticalCallEdges,
|
||||||
&marker::PhaseChange(MirPhase::Optimization),
|
&marker::PhaseChange(MirPhase::Optimized),
|
||||||
// Dump the end result for testing and debugging purposes.
|
// Dump the end result for testing and debugging purposes.
|
||||||
&dump_mir::Marker("PreCodegen"),
|
&dump_mir::Marker("PreCodegen"),
|
||||||
],
|
],
|
||||||
|
|
|
@ -114,7 +114,7 @@ pub fn run_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, passes: &[&dyn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if validate || body.phase == MirPhase::Optimization {
|
if validate || body.phase == MirPhase::Optimized {
|
||||||
validate_body(tcx, body, format!("end of phase transition to {:?}", body.phase));
|
validate_body(tcx, body, format!("end of phase transition to {:?}", body.phase));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue