@@ -11,6 +11,12 @@ use rustc_middle::{
1111} ;
1212use rustc_span:: def_id:: DefId ;
1313
14+ #[ derive( Copy , Clone , Debug ) ]
15+ enum EdgeKind {
16+ Unwind ,
17+ Normal ,
18+ }
19+
1420pub struct Validator {
1521 /// Describes at which point in the pipeline this validation is happening.
1622 pub when : String ,
@@ -49,8 +55,31 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
4955 ) ;
5056 }
5157
52- fn check_bb ( & self , location : Location , bb : BasicBlock ) {
53- if self . body . basic_blocks ( ) . get ( bb) . is_none ( ) {
58+ fn check_edge ( & self , location : Location , bb : BasicBlock , edge_kind : EdgeKind ) {
59+ if let Some ( bb) = self . body . basic_blocks ( ) . get ( bb) {
60+ let src = self . body . basic_blocks ( ) . get ( location. block ) . unwrap ( ) ;
61+ match ( src. is_cleanup , bb. is_cleanup , edge_kind) {
62+ // Non-cleanup blocks can jump to non-cleanup blocks along non-unwind edges
63+ ( false , false , EdgeKind :: Normal )
64+ // Non-cleanup blocks can jump to cleanup blocks along unwind edges
65+ | ( false , true , EdgeKind :: Unwind )
66+ // Cleanup blocks can jump to cleanup blocks along non-unwind edges
67+ | ( true , true , EdgeKind :: Normal ) => { }
68+ // All other jumps are invalid
69+ _ => {
70+ self . fail (
71+ location,
72+ format ! (
73+ "{:?} edge to {:?} violates unwind invariants (cleanup {:?} -> {:?})" ,
74+ edge_kind,
75+ bb,
76+ src. is_cleanup,
77+ bb. is_cleanup,
78+ )
79+ )
80+ }
81+ }
82+ } else {
5483 self . fail ( location, format ! ( "encountered jump to invalid basic block {:?}" , bb) )
5584 }
5685 }
@@ -92,7 +121,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
92121 fn visit_terminator ( & mut self , terminator : & Terminator < ' tcx > , location : Location ) {
93122 match & terminator. kind {
94123 TerminatorKind :: Goto { target } => {
95- self . check_bb ( location, * target) ;
124+ self . check_edge ( location, * target, EdgeKind :: Normal ) ;
96125 }
97126 TerminatorKind :: SwitchInt { targets, values, .. } => {
98127 if targets. len ( ) != values. len ( ) + 1 {
@@ -106,19 +135,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
106135 ) ;
107136 }
108137 for target in targets {
109- self . check_bb ( location, * target) ;
138+ self . check_edge ( location, * target, EdgeKind :: Normal ) ;
110139 }
111140 }
112141 TerminatorKind :: Drop { target, unwind, .. } => {
113- self . check_bb ( location, * target) ;
142+ self . check_edge ( location, * target, EdgeKind :: Normal ) ;
114143 if let Some ( unwind) = unwind {
115- self . check_bb ( location, * unwind) ;
144+ self . check_edge ( location, * unwind, EdgeKind :: Unwind ) ;
116145 }
117146 }
118147 TerminatorKind :: DropAndReplace { target, unwind, .. } => {
119- self . check_bb ( location, * target) ;
148+ self . check_edge ( location, * target, EdgeKind :: Normal ) ;
120149 if let Some ( unwind) = unwind {
121- self . check_bb ( location, * unwind) ;
150+ self . check_edge ( location, * unwind, EdgeKind :: Unwind ) ;
122151 }
123152 }
124153 TerminatorKind :: Call { func, destination, cleanup, .. } => {
@@ -131,10 +160,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
131160 ) ,
132161 }
133162 if let Some ( ( _, target) ) = destination {
134- self . check_bb ( location, * target) ;
163+ self . check_edge ( location, * target, EdgeKind :: Normal ) ;
135164 }
136165 if let Some ( cleanup) = cleanup {
137- self . check_bb ( location, * cleanup) ;
166+ self . check_edge ( location, * cleanup, EdgeKind :: Unwind ) ;
138167 }
139168 }
140169 TerminatorKind :: Assert { cond, target, cleanup, .. } => {
@@ -148,30 +177,30 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
148177 ) ,
149178 ) ;
150179 }
151- self . check_bb ( location, * target) ;
180+ self . check_edge ( location, * target, EdgeKind :: Normal ) ;
152181 if let Some ( cleanup) = cleanup {
153- self . check_bb ( location, * cleanup) ;
182+ self . check_edge ( location, * cleanup, EdgeKind :: Unwind ) ;
154183 }
155184 }
156185 TerminatorKind :: Yield { resume, drop, .. } => {
157- self . check_bb ( location, * resume) ;
186+ self . check_edge ( location, * resume, EdgeKind :: Normal ) ;
158187 if let Some ( drop) = drop {
159- self . check_bb ( location, * drop) ;
188+ self . check_edge ( location, * drop, EdgeKind :: Normal ) ;
160189 }
161190 }
162191 TerminatorKind :: FalseEdge { real_target, imaginary_target } => {
163- self . check_bb ( location, * real_target) ;
164- self . check_bb ( location, * imaginary_target) ;
192+ self . check_edge ( location, * real_target, EdgeKind :: Normal ) ;
193+ self . check_edge ( location, * imaginary_target, EdgeKind :: Normal ) ;
165194 }
166195 TerminatorKind :: FalseUnwind { real_target, unwind } => {
167- self . check_bb ( location, * real_target) ;
196+ self . check_edge ( location, * real_target, EdgeKind :: Normal ) ;
168197 if let Some ( unwind) = unwind {
169- self . check_bb ( location, * unwind) ;
198+ self . check_edge ( location, * unwind, EdgeKind :: Unwind ) ;
170199 }
171200 }
172201 TerminatorKind :: InlineAsm { destination, .. } => {
173202 if let Some ( destination) = destination {
174- self . check_bb ( location, * destination) ;
203+ self . check_edge ( location, * destination, EdgeKind :: Normal ) ;
175204 }
176205 }
177206 // Nothing to validate for these.
0 commit comments