Improve predecessor detection.

It is necessary to detect whether we are making the first
assignment into a union. This is checked by looking at the moves and
checking if there are any from locations earlier in the control flow
graph.

This commit improves the detection of this by switching from a naive
method that compared only the statement and basic block indices with
a more robust method that looks at the predecessors of a location.
This commit is contained in:
David Wood 2018-11-05 15:14:28 +01:00
parent 8e7786a2d9
commit 1672c27a7d
No known key found for this signature in database
GPG Key ID: 01760B4F9F53F154
2 changed files with 39 additions and 6 deletions

View File

@ -20,6 +20,7 @@
use mir::visit::MirVisitable;
use rustc_apfloat::ieee::{Double, Single};
use rustc_apfloat::Float;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::graph::dominators::{dominators, Dominators};
use rustc_data_structures::graph::{self, GraphPredecessors, GraphSuccessors};
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
@ -2706,6 +2707,36 @@ pub fn successor_within_block(&self) -> Location {
}
}
/// Returns `true` if `other` is earlier in the control flow graph than `self`.
pub fn is_predecessor_of<'tcx>(&self, other: Location, mir: &Mir<'tcx>) -> bool {
// If we are in the same block as the other location and are an earlier statement
// then we are a predecessor of `other`.
if self.block == other.block && self.statement_index < other.statement_index {
return true;
}
// If we're in another block, then we want to check that block is a predecessor of `other`.
let mut queue: Vec<BasicBlock> = mir.predecessors_for(other.block).clone();
let mut visited = FxHashSet::default();
while let Some(block) = queue.pop() {
// If we haven't visited this block before, then make sure we visit it's predecessors.
if visited.insert(block) {
queue.append(&mut mir.predecessors_for(block).clone());
} else {
continue;
}
// If we found the block that `self` is in, then we are a predecessor of `other` (since
// we found that block by looking at the predecessors of `other`).
if self.block == block {
return true;
}
}
false
}
pub fn dominates(&self, other: Location, dominators: &Dominators<BasicBlock>) -> bool {
if self.block == other.block {
self.statement_index <= other.statement_index

View File

@ -1784,12 +1784,14 @@ fn check_parent_of_field<'cx, 'gcx, 'tcx>(
// of the union - we should error in that case.
let tcx = this.infcx.tcx;
if let ty::TyKind::Adt(def, _) = base.ty(this.mir, tcx).to_ty(tcx).sty {
let moved_before_this = this.move_data.path_map[mpi].iter().any(|moi| {
this.move_data.moves[*moi].source < context.loc
});
if def.is_union() && moved_before_this {
return;
if def.is_union() {
if this.move_data.path_map[mpi].iter().any(|moi| {
this.move_data.moves[*moi].source.is_predecessor_of(
context.loc, this.mir,
)
}) {
return;
}
}
}