|
3 | 3 | use super::{MirPass, MirSource};
|
4 | 4 | use rustc_middle::mir::visit::Visitor;
|
5 | 5 | use 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}, |
8 | 11 | };
|
9 | 12 | use rustc_span::{def_id::DefId, Span, DUMMY_SP};
|
10 | 13 |
|
@@ -38,6 +41,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
38 | 41 | &format!("broken MIR in {:?} ({}): {}", self.def_id, self.when, msg.as_ref()),
|
39 | 42 | );
|
40 | 43 | }
|
| 44 | + |
| 45 | + fn check_bb(&self, span: Span, bb: BasicBlock) { |
| 46 | + if self.body.basic_blocks().get(bb).is_none() { |
| 47 | + self.fail(span, format!("encountered jump to invalid basic block {:?}", bb)) |
| 48 | + } |
| 49 | + } |
41 | 50 | }
|
42 | 51 |
|
43 | 52 | impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
@@ -77,4 +86,94 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
77 | 86 | }
|
78 | 87 | }
|
79 | 88 | }
|
| 89 | + |
| 90 | + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, _location: Location) { |
| 91 | + match &terminator.kind { |
| 92 | + TerminatorKind::Goto { target } => { |
| 93 | + self.check_bb(terminator.source_info.span, *target); |
| 94 | + } |
| 95 | + TerminatorKind::SwitchInt { targets, .. } => { |
| 96 | + if targets.is_empty() { |
| 97 | + self.fail( |
| 98 | + terminator.source_info.span, |
| 99 | + "encountered `SwitchInt` terminator with no target to jump to", |
| 100 | + ); |
| 101 | + } |
| 102 | + for target in targets { |
| 103 | + self.check_bb(terminator.source_info.span, *target); |
| 104 | + } |
| 105 | + } |
| 106 | + TerminatorKind::Drop { target, unwind, .. } => { |
| 107 | + self.check_bb(terminator.source_info.span, *target); |
| 108 | + if let Some(unwind) = unwind { |
| 109 | + self.check_bb(terminator.source_info.span, *unwind); |
| 110 | + } |
| 111 | + } |
| 112 | + TerminatorKind::DropAndReplace { target, unwind, .. } => { |
| 113 | + self.check_bb(terminator.source_info.span, *target); |
| 114 | + if let Some(unwind) = unwind { |
| 115 | + self.check_bb(terminator.source_info.span, *unwind); |
| 116 | + } |
| 117 | + } |
| 118 | + TerminatorKind::Call { func, destination, cleanup, .. } => { |
| 119 | + let func_ty = func.ty(&self.body.local_decls, self.tcx); |
| 120 | + match func_ty.kind { |
| 121 | + ty::FnPtr(..) | ty::FnDef(..) => {} |
| 122 | + _ => self.fail( |
| 123 | + terminator.source_info.span, |
| 124 | + format!("encountered non-callable type {} in `Call` terminator", func_ty), |
| 125 | + ), |
| 126 | + } |
| 127 | + if let Some((_, target)) = destination { |
| 128 | + self.check_bb(terminator.source_info.span, *target); |
| 129 | + } |
| 130 | + if let Some(cleanup) = cleanup { |
| 131 | + self.check_bb(terminator.source_info.span, *cleanup); |
| 132 | + } |
| 133 | + } |
| 134 | + TerminatorKind::Assert { cond, target, cleanup, .. } => { |
| 135 | + let cond_ty = cond.ty(&self.body.local_decls, self.tcx); |
| 136 | + if cond_ty != self.tcx.types.bool { |
| 137 | + self.fail( |
| 138 | + terminator.source_info.span, |
| 139 | + format!( |
| 140 | + "encountered non-boolean condition of type {} in `Assert` terminator", |
| 141 | + cond_ty |
| 142 | + ), |
| 143 | + ); |
| 144 | + } |
| 145 | + self.check_bb(terminator.source_info.span, *target); |
| 146 | + if let Some(cleanup) = cleanup { |
| 147 | + self.check_bb(terminator.source_info.span, *cleanup); |
| 148 | + } |
| 149 | + } |
| 150 | + TerminatorKind::Yield { resume, drop, .. } => { |
| 151 | + self.check_bb(terminator.source_info.span, *resume); |
| 152 | + if let Some(drop) = drop { |
| 153 | + self.check_bb(terminator.source_info.span, *drop); |
| 154 | + } |
| 155 | + } |
| 156 | + TerminatorKind::FalseEdges { real_target, imaginary_target } => { |
| 157 | + self.check_bb(terminator.source_info.span, *real_target); |
| 158 | + self.check_bb(terminator.source_info.span, *imaginary_target); |
| 159 | + } |
| 160 | + TerminatorKind::FalseUnwind { real_target, unwind } => { |
| 161 | + self.check_bb(terminator.source_info.span, *real_target); |
| 162 | + if let Some(unwind) = unwind { |
| 163 | + self.check_bb(terminator.source_info.span, *unwind); |
| 164 | + } |
| 165 | + } |
| 166 | + TerminatorKind::InlineAsm { destination, .. } => { |
| 167 | + if let Some(destination) = destination { |
| 168 | + self.check_bb(terminator.source_info.span, *destination); |
| 169 | + } |
| 170 | + } |
| 171 | + // Nothing to validate for these. |
| 172 | + TerminatorKind::Resume |
| 173 | + | TerminatorKind::Abort |
| 174 | + | TerminatorKind::Return |
| 175 | + | TerminatorKind::Unreachable |
| 176 | + | TerminatorKind::GeneratorDrop => {} |
| 177 | + } |
| 178 | + } |
80 | 179 | }
|
0 commit comments