33use super :: { MirPass , MirSource } ;
44use rustc_middle:: mir:: visit:: Visitor ;
55use rustc_middle:: {
6- mir:: { Body , Location , Operand , Rvalue , Statement , StatementKind } ,
7- ty:: { ParamEnv , TyCtxt } ,
6+ mir:: {
7+ BasicBlock , Body , Location , Operand , Rvalue , Statement , StatementKind , Terminator ,
8+ TerminatorKind ,
9+ } ,
10+ ty:: { self , ParamEnv , TyCtxt } ,
811} ;
9- use rustc_span:: { def_id:: DefId , Span , DUMMY_SP } ;
12+ use rustc_span:: def_id:: DefId ;
1013
1114pub struct Validator {
1215 /// Describes at which point in the pipeline this validation is happening.
@@ -30,27 +33,38 @@ struct TypeChecker<'a, 'tcx> {
3033}
3134
3235impl < ' a , ' tcx > TypeChecker < ' a , ' tcx > {
33- fn fail ( & self , span : Span , msg : impl AsRef < str > ) {
36+ fn fail ( & self , location : Location , msg : impl AsRef < str > ) {
37+ let span = self . body . source_info ( location) . span ;
3438 // We use `delay_span_bug` as we might see broken MIR when other errors have already
3539 // occurred.
3640 self . tcx . sess . diagnostic ( ) . delay_span_bug (
3741 span,
38- & format ! ( "broken MIR in {:?} ({}): {}" , self . def_id, self . when, msg. as_ref( ) ) ,
42+ & format ! (
43+ "broken MIR in {:?} ({}) at {:?}:\n {}" ,
44+ self . def_id,
45+ self . when,
46+ location,
47+ msg. as_ref( )
48+ ) ,
3949 ) ;
4050 }
51+
52+ fn check_bb ( & self , location : Location , bb : BasicBlock ) {
53+ if self . body . basic_blocks ( ) . get ( bb) . is_none ( ) {
54+ self . fail ( location, format ! ( "encountered jump to invalid basic block {:?}" , bb) )
55+ }
56+ }
4157}
4258
4359impl < ' a , ' tcx > Visitor < ' tcx > for TypeChecker < ' a , ' tcx > {
4460 fn visit_operand ( & mut self , operand : & Operand < ' tcx > , location : Location ) {
4561 // `Operand::Copy` is only supposed to be used with `Copy` types.
4662 if let Operand :: Copy ( place) = operand {
4763 let ty = place. ty ( & self . body . local_decls , self . tcx ) . ty ;
64+ let span = self . body . source_info ( location) . span ;
4865
49- if !ty. is_copy_modulo_regions ( self . tcx , self . param_env , DUMMY_SP ) {
50- self . fail (
51- DUMMY_SP ,
52- format ! ( "`Operand::Copy` with non-`Copy` type {} at {:?}" , ty, location) ,
53- ) ;
66+ if !ty. is_copy_modulo_regions ( self . tcx , self . param_env , span) {
67+ self . fail ( location, format ! ( "`Operand::Copy` with non-`Copy` type {}" , ty) ) ;
5468 }
5569 }
5670
@@ -65,16 +79,107 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
6579 Rvalue :: Use ( Operand :: Copy ( src) | Operand :: Move ( src) ) => {
6680 if dest == src {
6781 self . fail (
68- DUMMY_SP ,
69- format ! (
70- "encountered `Assign` statement with overlapping memory at {:?}" ,
71- location
72- ) ,
82+ location,
83+ "encountered `Assign` statement with overlapping memory" ,
7384 ) ;
7485 }
7586 }
7687 _ => { }
7788 }
7889 }
7990 }
91+
92+ fn visit_terminator ( & mut self , terminator : & Terminator < ' tcx > , location : Location ) {
93+ match & terminator. kind {
94+ TerminatorKind :: Goto { target } => {
95+ self . check_bb ( location, * target) ;
96+ }
97+ TerminatorKind :: SwitchInt { targets, values, .. } => {
98+ if targets. len ( ) != values. len ( ) + 1 {
99+ self . fail (
100+ location,
101+ format ! (
102+ "encountered `SwitchInt` terminator with {} values, but {} targets (should be values+1)" ,
103+ values. len( ) ,
104+ targets. len( ) ,
105+ ) ,
106+ ) ;
107+ }
108+ for target in targets {
109+ self . check_bb ( location, * target) ;
110+ }
111+ }
112+ TerminatorKind :: Drop { target, unwind, .. } => {
113+ self . check_bb ( location, * target) ;
114+ if let Some ( unwind) = unwind {
115+ self . check_bb ( location, * unwind) ;
116+ }
117+ }
118+ TerminatorKind :: DropAndReplace { target, unwind, .. } => {
119+ self . check_bb ( location, * target) ;
120+ if let Some ( unwind) = unwind {
121+ self . check_bb ( location, * unwind) ;
122+ }
123+ }
124+ TerminatorKind :: Call { func, destination, cleanup, .. } => {
125+ let func_ty = func. ty ( & self . body . local_decls , self . tcx ) ;
126+ match func_ty. kind {
127+ ty:: FnPtr ( ..) | ty:: FnDef ( ..) => { }
128+ _ => self . fail (
129+ location,
130+ format ! ( "encountered non-callable type {} in `Call` terminator" , func_ty) ,
131+ ) ,
132+ }
133+ if let Some ( ( _, target) ) = destination {
134+ self . check_bb ( location, * target) ;
135+ }
136+ if let Some ( cleanup) = cleanup {
137+ self . check_bb ( location, * cleanup) ;
138+ }
139+ }
140+ TerminatorKind :: Assert { cond, target, cleanup, .. } => {
141+ let cond_ty = cond. ty ( & self . body . local_decls , self . tcx ) ;
142+ if cond_ty != self . tcx . types . bool {
143+ self . fail (
144+ location,
145+ format ! (
146+ "encountered non-boolean condition of type {} in `Assert` terminator" ,
147+ cond_ty
148+ ) ,
149+ ) ;
150+ }
151+ self . check_bb ( location, * target) ;
152+ if let Some ( cleanup) = cleanup {
153+ self . check_bb ( location, * cleanup) ;
154+ }
155+ }
156+ TerminatorKind :: Yield { resume, drop, .. } => {
157+ self . check_bb ( location, * resume) ;
158+ if let Some ( drop) = drop {
159+ self . check_bb ( location, * drop) ;
160+ }
161+ }
162+ TerminatorKind :: FalseEdges { real_target, imaginary_target } => {
163+ self . check_bb ( location, * real_target) ;
164+ self . check_bb ( location, * imaginary_target) ;
165+ }
166+ TerminatorKind :: FalseUnwind { real_target, unwind } => {
167+ self . check_bb ( location, * real_target) ;
168+ if let Some ( unwind) = unwind {
169+ self . check_bb ( location, * unwind) ;
170+ }
171+ }
172+ TerminatorKind :: InlineAsm { destination, .. } => {
173+ if let Some ( destination) = destination {
174+ self . check_bb ( location, * destination) ;
175+ }
176+ }
177+ // Nothing to validate for these.
178+ TerminatorKind :: Resume
179+ | TerminatorKind :: Abort
180+ | TerminatorKind :: Return
181+ | TerminatorKind :: Unreachable
182+ | TerminatorKind :: GeneratorDrop => { }
183+ }
184+ }
80185}
0 commit comments