]> git.lizzy.rs Git - rust.git/blobdiff - compiler/rustc_const_eval/src/transform/validate.rs
Document wf constraints on control flow in cleanup blocks
[rust.git] / compiler / rustc_const_eval / src / transform / validate.rs
index 94e1b95a0eb3c9501a33f4d3bafecd82c632bd9f..9f429d3a7d9842680e6ccbaf5b13423443a52b49 100644 (file)
@@ -1,6 +1,8 @@
 //! Validates the MIR to ensure that invariants are upheld.
 
-use rustc_data_structures::fx::FxHashSet;
+use std::collections::hash_map::Entry;
+
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_index::bit_set::BitSet;
 use rustc_infer::traits::Reveal;
 use rustc_middle::mir::interpret::Scalar;
@@ -18,7 +20,7 @@
 use rustc_mir_dataflow::{Analysis, ResultsCursor};
 use rustc_target::abi::{Size, VariantIdx};
 
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 enum EdgeKind {
     Unwind,
     Normal,
@@ -57,7 +59,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
             .iterate_to_fixpoint()
             .into_results_cursor(body);
 
-        TypeChecker {
+        let mut checker = TypeChecker {
             when: &self.when,
             body,
             tcx,
@@ -67,8 +69,9 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
             storage_liveness,
             place_cache: Vec::new(),
             value_cache: Vec::new(),
-        }
-        .visit_body(body);
+        };
+        checker.visit_body(body);
+        checker.check_cleanup_control_flow();
     }
 }
 
@@ -134,6 +137,55 @@ fn check_edge(&self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) {
         }
     }
 
+    fn check_cleanup_control_flow(&self) {
+        let doms = self.body.basic_blocks.dominators();
+        let mut post_contract_node = FxHashMap::default();
+        let mut get_post_contract_node = |mut bb| {
+            if let Some(res) = post_contract_node.get(&bb) {
+                return *res;
+            }
+            let mut dom_path = vec![];
+            while self.body.basic_blocks[bb].is_cleanup {
+                dom_path.push(bb);
+                bb = doms.immediate_dominator(bb);
+            }
+            let root = *dom_path.last().unwrap();
+            for bb in dom_path {
+                post_contract_node.insert(bb, root);
+            }
+            root
+        };
+
+        let mut parent = FxHashMap::default();
+        for (bb, bb_data) in self.body.basic_blocks.iter_enumerated() {
+            if !bb_data.is_cleanup || !self.reachable_blocks.contains(bb) {
+                continue;
+            }
+            let bb = get_post_contract_node(bb);
+            for s in bb_data.terminator().successors() {
+                let s = get_post_contract_node(s);
+                if s == bb {
+                    continue;
+                }
+                match parent.entry(bb) {
+                    Entry::Vacant(e) => {
+                        e.insert(s);
+                    }
+                    Entry::Occupied(e) if s != *e.get() => self.fail(
+                        Location { block: bb, statement_index: 0 },
+                        format!(
+                            "Cleanup control flow violation: The blocks dominated by {:?} have edges to both {:?} and {:?}",
+                            bb,
+                            s,
+                            *e.get()
+                        )
+                    ),
+                    Entry::Occupied(_) => (),
+                }
+            }
+        }
+    }
+
     /// Check if src can be assigned into dest.
     /// This is not precise, it will accept some incorrect assignments.
     fn mir_assign_valid_types(&self, src: Ty<'tcx>, dest: Ty<'tcx>) -> bool {