]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/type_check.rs
transform broken MIR warnings to hard ICEs
[rust.git] / src / librustc_mir / transform / type_check.rs
1 // Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! This pass type-checks the MIR to ensure it is not broken.
12 #![allow(unreachable_code)]
13
14 use rustc::infer::{self, InferCtxt, InferOk};
15 use rustc::traits::{self, Reveal};
16 use rustc::ty::fold::TypeFoldable;
17 use rustc::ty::{self, Ty, TyCtxt, TypeVariants};
18 use rustc::mir::*;
19 use rustc::mir::tcx::LvalueTy;
20 use rustc::mir::transform::{MirPass, MirSource, Pass};
21 use rustc::mir::visit::Visitor;
22 use std::fmt;
23 use syntax::ast;
24 use syntax_pos::{Span, DUMMY_SP};
25
26 use rustc_data_structures::indexed_vec::Idx;
27
28 fn mirbug(tcx: TyCtxt, span: Span, msg: &str) {
29     tcx.sess.diagnostic().span_bug(span, msg);
30 }
31
32 macro_rules! span_mirbug {
33     ($context:expr, $elem:expr, $($message:tt)*) => ({
34         mirbug($context.tcx(), $context.last_span,
35                &format!("broken MIR ({:?}): {}", $elem, format!($($message)*)))
36     })
37 }
38
39 macro_rules! span_mirbug_and_err {
40     ($context:expr, $elem:expr, $($message:tt)*) => ({
41         {
42             span_mirbug!($context, $elem, $($message)*);
43             $context.error()
44         }
45     })
46 }
47
48 enum FieldAccessError {
49     OutOfRange { field_count: usize }
50 }
51
52 /// Verifies that MIR types are sane to not crash further checks.
53 ///
54 /// The sanitize_XYZ methods here take an MIR object and compute its
55 /// type, calling `span_mirbug` and returning an error type if there
56 /// is a problem.
57 struct TypeVerifier<'a, 'b: 'a, 'gcx: 'b+'tcx, 'tcx: 'b> {
58     cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>,
59     mir: &'a Mir<'tcx>,
60     last_span: Span,
61     errors_reported: bool
62 }
63
64 impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> {
65     fn visit_span(&mut self, span: &Span) {
66         if *span != DUMMY_SP {
67             self.last_span = *span;
68         }
69     }
70
71     fn visit_lvalue(&mut self,
72                     lvalue: &Lvalue<'tcx>,
73                     _context: visit::LvalueContext,
74                     location: Location) {
75         self.sanitize_lvalue(lvalue, location);
76     }
77
78     fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
79         self.super_constant(constant, location);
80         self.sanitize_type(constant, constant.ty);
81     }
82
83     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
84         self.super_rvalue(rvalue, location);
85         if let Some(ty) = rvalue.ty(self.mir, self.tcx()) {
86             self.sanitize_type(rvalue, ty);
87         }
88     }
89
90     fn visit_mir(&mut self, mir: &Mir<'tcx>) {
91         self.sanitize_type(&"return type", mir.return_ty);
92         for local_decl in &mir.local_decls {
93             self.sanitize_type(local_decl, local_decl.ty);
94         }
95         if self.errors_reported {
96             return;
97         }
98         self.super_mir(mir);
99     }
100 }
101
102 impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
103     fn new(cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
104         TypeVerifier {
105             cx: cx,
106             mir: mir,
107             last_span: mir.span,
108             errors_reported: false
109         }
110     }
111
112     fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> {
113         self.cx.infcx.tcx
114     }
115
116     fn sanitize_type(&mut self, parent: &fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> {
117         if ty.needs_infer() || ty.has_escaping_regions() || ty.references_error() {
118             span_mirbug_and_err!(self, parent, "bad type {:?}", ty)
119         } else {
120             ty
121         }
122     }
123
124     fn sanitize_lvalue(&mut self, lvalue: &Lvalue<'tcx>, location: Location) -> LvalueTy<'tcx> {
125         debug!("sanitize_lvalue: {:?}", lvalue);
126         match *lvalue {
127             Lvalue::Local(index) => LvalueTy::Ty { ty: self.mir.local_decls[index].ty },
128             Lvalue::Static(box Static { def_id, ty: sty }) => {
129                 let sty = self.sanitize_type(lvalue, sty);
130                 let ty = self.tcx().item_type(def_id);
131                 let ty = self.cx.normalize(&ty);
132                 if let Err(terr) = self.cx.eq_types(self.last_span, ty, sty) {
133                     span_mirbug!(
134                         self, lvalue, "bad static type ({:?}: {:?}): {:?}",
135                         ty, sty, terr);
136                 }
137                 LvalueTy::Ty { ty: sty }
138
139             },
140             Lvalue::Projection(ref proj) => {
141                 let base_ty = self.sanitize_lvalue(&proj.base, location);
142                 if let LvalueTy::Ty { ty } = base_ty {
143                     if ty.references_error() {
144                         assert!(self.errors_reported);
145                         return LvalueTy::Ty { ty: self.tcx().types.err };
146                     }
147                 }
148                 self.sanitize_projection(base_ty, &proj.elem, lvalue, location)
149             }
150         }
151     }
152
153     fn sanitize_projection(&mut self,
154                            base: LvalueTy<'tcx>,
155                            pi: &LvalueElem<'tcx>,
156                            lvalue: &Lvalue<'tcx>,
157                            location: Location)
158                            -> LvalueTy<'tcx> {
159         debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, lvalue);
160         let tcx = self.tcx();
161         let base_ty = base.to_ty(tcx);
162         let span = self.last_span;
163         match *pi {
164             ProjectionElem::Deref => {
165                 let deref_ty = base_ty.builtin_deref(true, ty::LvaluePreference::NoPreference);
166                 LvalueTy::Ty {
167                     ty: deref_ty.map(|t| t.ty).unwrap_or_else(|| {
168                         span_mirbug_and_err!(
169                             self, lvalue, "deref of non-pointer {:?}", base_ty)
170                     })
171                 }
172             }
173             ProjectionElem::Index(ref i) => {
174                 self.visit_operand(i, location);
175                 let index_ty = i.ty(self.mir, tcx);
176                 if index_ty != tcx.types.usize {
177                     LvalueTy::Ty {
178                         ty: span_mirbug_and_err!(self, i, "index by non-usize {:?}", i)
179                     }
180                 } else {
181                     LvalueTy::Ty {
182                         ty: base_ty.builtin_index().unwrap_or_else(|| {
183                             span_mirbug_and_err!(
184                                 self, lvalue, "index of non-array {:?}", base_ty)
185                         })
186                     }
187                 }
188             }
189             ProjectionElem::ConstantIndex { .. } => {
190                 // consider verifying in-bounds
191                 LvalueTy::Ty {
192                     ty: base_ty.builtin_index().unwrap_or_else(|| {
193                         span_mirbug_and_err!(
194                             self, lvalue, "index of non-array {:?}", base_ty)
195                     })
196                 }
197             }
198             ProjectionElem::Subslice { from, to } => {
199                 LvalueTy::Ty {
200                     ty: match base_ty.sty {
201                         ty::TyArray(inner, size) => {
202                             let min_size = (from as usize) + (to as usize);
203                             if let Some(rest_size) = size.checked_sub(min_size) {
204                                 tcx.mk_array(inner, rest_size)
205                             } else {
206                                 span_mirbug_and_err!(
207                                     self, lvalue, "taking too-small slice of {:?}", base_ty)
208                             }
209                         }
210                         ty::TySlice(..) => base_ty,
211                         _ => {
212                             span_mirbug_and_err!(
213                                 self, lvalue, "slice of non-array {:?}", base_ty)
214                         }
215                     }
216                 }
217             }
218             ProjectionElem::Downcast(adt_def1, index) =>
219                 match base_ty.sty {
220                     ty::TyAdt(adt_def, substs) if adt_def.is_enum() && adt_def == adt_def1 => {
221                         if index >= adt_def.variants.len() {
222                             LvalueTy::Ty {
223                                 ty: span_mirbug_and_err!(
224                                     self,
225                                     lvalue,
226                                     "cast to variant #{:?} but enum only has {:?}",
227                                     index,
228                                     adt_def.variants.len())
229                             }
230                         } else {
231                             LvalueTy::Downcast {
232                                 adt_def: adt_def,
233                                 substs: substs,
234                                 variant_index: index
235                             }
236                         }
237                     }
238                     _ => LvalueTy::Ty {
239                         ty: span_mirbug_and_err!(
240                             self, lvalue, "can't downcast {:?} as {:?}",
241                             base_ty, adt_def1)
242                     }
243                 },
244             ProjectionElem::Field(field, fty) => {
245                 let fty = self.sanitize_type(lvalue, fty);
246                 match self.field_ty(lvalue, base, field) {
247                     Ok(ty) => {
248                         if let Err(terr) = self.cx.eq_types(span, ty, fty) {
249                             span_mirbug!(
250                                 self, lvalue, "bad field access ({:?}: {:?}): {:?}",
251                                 ty, fty, terr);
252                         }
253                     }
254                     Err(FieldAccessError::OutOfRange { field_count }) => {
255                         span_mirbug!(
256                             self, lvalue, "accessed field #{} but variant only has {}",
257                             field.index(), field_count)
258                     }
259                 }
260                 LvalueTy::Ty { ty: fty }
261             }
262         }
263     }
264
265     fn error(&mut self) -> Ty<'tcx> {
266         self.errors_reported = true;
267         self.tcx().types.err
268     }
269
270     fn field_ty(&mut self,
271                 parent: &fmt::Debug,
272                 base_ty: LvalueTy<'tcx>,
273                 field: Field)
274                 -> Result<Ty<'tcx>, FieldAccessError>
275     {
276         let tcx = self.tcx();
277
278         let (variant, substs) = match base_ty {
279             LvalueTy::Downcast { adt_def, substs, variant_index } => {
280                 (&adt_def.variants[variant_index], substs)
281             }
282             LvalueTy::Ty { ty } => match ty.sty {
283                 ty::TyAdt(adt_def, substs) if adt_def.is_univariant() => {
284                         (&adt_def.variants[0], substs)
285                     }
286                 ty::TyClosure(def_id, substs) => {
287                     return match substs.upvar_tys(def_id, tcx).nth(field.index()) {
288                         Some(ty) => Ok(ty),
289                         None => Err(FieldAccessError::OutOfRange {
290                             field_count: substs.upvar_tys(def_id, tcx).count()
291                         })
292                     }
293                 }
294                 ty::TyTuple(tys, _) => {
295                     return match tys.get(field.index()) {
296                         Some(&ty) => Ok(ty),
297                         None => Err(FieldAccessError::OutOfRange {
298                             field_count: tys.len()
299                         })
300                     }
301                 }
302                 _ => return Ok(span_mirbug_and_err!(
303                     self, parent, "can't project out of {:?}", base_ty))
304             }
305         };
306
307         if let Some(field) = variant.fields.get(field.index()) {
308             Ok(self.cx.normalize(&field.ty(tcx, substs)))
309         } else {
310             Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() })
311         }
312     }
313 }
314
315 pub struct TypeChecker<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
316     infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
317     fulfillment_cx: traits::FulfillmentContext<'tcx>,
318     last_span: Span,
319     body_id: ast::NodeId,
320 }
321
322 impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
323     fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, body_id: ast::NodeId) -> Self {
324         TypeChecker {
325             infcx: infcx,
326             fulfillment_cx: traits::FulfillmentContext::new(),
327             last_span: DUMMY_SP,
328             body_id: body_id,
329         }
330     }
331
332     fn misc(&self, span: Span) -> traits::ObligationCause<'tcx> {
333         traits::ObligationCause::misc(span, self.body_id)
334     }
335
336     pub fn register_infer_ok_obligations<T>(&mut self, infer_ok: InferOk<'tcx, T>) -> T {
337         for obligation in infer_ok.obligations {
338             self.fulfillment_cx.register_predicate_obligation(self.infcx, obligation);
339         }
340         infer_ok.value
341     }
342
343     fn sub_types(&mut self, sup: Ty<'tcx>, sub: Ty<'tcx>)
344                  -> infer::UnitResult<'tcx>
345     {
346         self.infcx.sub_types(false, &self.misc(self.last_span), sup, sub)
347             .map(|ok| self.register_infer_ok_obligations(ok))
348     }
349
350     fn eq_types(&mut self, span: Span, a: Ty<'tcx>, b: Ty<'tcx>)
351                 -> infer::UnitResult<'tcx>
352     {
353         self.infcx.eq_types(false, &self.misc(span), a, b)
354             .map(|ok| self.register_infer_ok_obligations(ok))
355     }
356
357     fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> {
358         self.infcx.tcx
359     }
360
361     fn check_stmt(&mut self, mir: &Mir<'tcx>, stmt: &Statement<'tcx>) {
362         debug!("check_stmt: {:?}", stmt);
363         let tcx = self.tcx();
364         match stmt.kind {
365             StatementKind::Assign(ref lv, ref rv) => {
366                 let lv_ty = lv.ty(mir, tcx).to_ty(tcx);
367                 let rv_ty = rv.ty(mir, tcx);
368                 if let Some(rv_ty) = rv_ty {
369                     if let Err(terr) = self.sub_types(rv_ty, lv_ty) {
370                         span_mirbug!(self, stmt, "bad assignment ({:?} = {:?}): {:?}",
371                                      lv_ty, rv_ty, terr);
372                     }
373                 }
374                 // FIXME: rvalue with undeterminable type - e.g. AggregateKind::Array branch that
375                 // returns `None`.
376             }
377             StatementKind::SetDiscriminant{ ref lvalue, variant_index } => {
378                 let lvalue_type = lvalue.ty(mir, tcx).to_ty(tcx);
379                 let adt = match lvalue_type.sty {
380                     TypeVariants::TyAdt(adt, _) if adt.is_enum() => adt,
381                     _ => {
382                         span_bug!(stmt.source_info.span,
383                                   "bad set discriminant ({:?} = {:?}): lhs is not an enum",
384                                   lvalue,
385                                   variant_index);
386                     }
387                 };
388                 if variant_index >= adt.variants.len() {
389                      span_bug!(stmt.source_info.span,
390                                "bad set discriminant ({:?} = {:?}): value of of range",
391                                lvalue,
392                                variant_index);
393                 };
394             }
395             StatementKind::StorageLive(ref lv) |
396             StatementKind::StorageDead(ref lv) => {
397                 match *lv {
398                     Lvalue::Local(_) => {}
399                     _ => {
400                         span_mirbug!(self, stmt, "bad lvalue: expected local");
401                     }
402                 }
403             }
404             StatementKind::InlineAsm { .. } |
405             StatementKind::Nop => {}
406         }
407     }
408
409     fn check_terminator(&mut self,
410                         mir: &Mir<'tcx>,
411                         term: &Terminator<'tcx>) {
412         debug!("check_terminator: {:?}", term);
413         let tcx = self.tcx();
414         match term.kind {
415             TerminatorKind::Goto { .. } |
416             TerminatorKind::Resume |
417             TerminatorKind::Return |
418             TerminatorKind::Unreachable |
419             TerminatorKind::Drop { .. } => {
420                 // no checks needed for these
421             }
422
423
424             TerminatorKind::DropAndReplace {
425                 ref location,
426                 ref value,
427                 ..
428             } => {
429                 let lv_ty = location.ty(mir, tcx).to_ty(tcx);
430                 let rv_ty = value.ty(mir, tcx);
431                 if let Err(terr) = self.sub_types(rv_ty, lv_ty) {
432                     span_mirbug!(self, term, "bad DropAndReplace ({:?} = {:?}): {:?}",
433                                  lv_ty, rv_ty, terr);
434                 }
435             }
436             TerminatorKind::SwitchInt { ref discr, switch_ty, .. } => {
437                 let discr_ty = discr.ty(mir, tcx);
438                 if let Err(terr) = self.sub_types(discr_ty, switch_ty) {
439                     span_mirbug!(self, term, "bad SwitchInt ({:?} on {:?}): {:?}",
440                                  switch_ty, discr_ty, terr);
441                 }
442                 if !switch_ty.is_integral() && !switch_ty.is_char() &&
443                     !switch_ty.is_bool()
444                 {
445                     span_mirbug!(self, term, "bad SwitchInt discr ty {:?}",switch_ty);
446                 }
447                 // FIXME: check the values
448             }
449             TerminatorKind::Call { ref func, ref args, ref destination, .. } => {
450                 let func_ty = func.ty(mir, tcx);
451                 debug!("check_terminator: call, func_ty={:?}", func_ty);
452                 let sig = match func_ty.sty {
453                     ty::TyFnDef(.., sig) | ty::TyFnPtr(sig) => sig,
454                     _ => {
455                         span_mirbug!(self, term, "call to non-function {:?}", func_ty);
456                         return;
457                     }
458                 };
459                 let sig = tcx.erase_late_bound_regions(&sig);
460                 let sig = self.normalize(&sig);
461                 self.check_call_dest(mir, term, &sig, destination);
462
463                 if self.is_box_free(func) {
464                     self.check_box_free_inputs(mir, term, &sig, args);
465                 } else {
466                     self.check_call_inputs(mir, term, &sig, args);
467                 }
468             }
469             TerminatorKind::Assert { ref cond, ref msg, .. } => {
470                 let cond_ty = cond.ty(mir, tcx);
471                 if cond_ty != tcx.types.bool {
472                     span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty);
473                 }
474
475                 if let AssertMessage::BoundsCheck { ref len, ref index } = *msg {
476                     if len.ty(mir, tcx) != tcx.types.usize {
477                         span_mirbug!(self, len, "bounds-check length non-usize {:?}", len)
478                     }
479                     if index.ty(mir, tcx) != tcx.types.usize {
480                         span_mirbug!(self, index, "bounds-check index non-usize {:?}", index)
481                     }
482                 }
483             }
484         }
485     }
486
487     fn check_call_dest(&mut self,
488                        mir: &Mir<'tcx>,
489                        term: &Terminator<'tcx>,
490                        sig: &ty::FnSig<'tcx>,
491                        destination: &Option<(Lvalue<'tcx>, BasicBlock)>) {
492         let tcx = self.tcx();
493         match *destination {
494             Some((ref dest, _)) => {
495                 let dest_ty = dest.ty(mir, tcx).to_ty(tcx);
496                 if let Err(terr) = self.sub_types(sig.output(), dest_ty) {
497                     span_mirbug!(self, term,
498                                  "call dest mismatch ({:?} <- {:?}): {:?}",
499                                  dest_ty, sig.output(), terr);
500                 }
501             },
502             None => {
503                 // FIXME(canndrew): This is_never should probably be an is_uninhabited
504                 if !sig.output().is_never() {
505                     span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
506                 }
507             },
508         }
509     }
510
511     fn check_call_inputs(&mut self,
512                          mir: &Mir<'tcx>,
513                          term: &Terminator<'tcx>,
514                          sig: &ty::FnSig<'tcx>,
515                          args: &[Operand<'tcx>])
516     {
517         debug!("check_call_inputs({:?}, {:?})", sig, args);
518         if args.len() < sig.inputs().len() ||
519            (args.len() > sig.inputs().len() && !sig.variadic) {
520             span_mirbug!(self, term, "call to {:?} with wrong # of args", sig);
521         }
522         for (n, (fn_arg, op_arg)) in sig.inputs().iter().zip(args).enumerate() {
523             let op_arg_ty = op_arg.ty(mir, self.tcx());
524             if let Err(terr) = self.sub_types(op_arg_ty, fn_arg) {
525                 span_mirbug!(self, term, "bad arg #{:?} ({:?} <- {:?}): {:?}",
526                              n, fn_arg, op_arg_ty, terr);
527             }
528         }
529     }
530
531     fn is_box_free(&self, operand: &Operand<'tcx>) -> bool {
532         match operand {
533             &Operand::Constant(Constant {
534                 literal: Literal::Item { def_id, .. }, ..
535             }) => {
536                 Some(def_id) == self.tcx().lang_items.box_free_fn()
537             }
538             _ => false,
539         }
540     }
541
542     fn check_box_free_inputs(&mut self,
543                              mir: &Mir<'tcx>,
544                              term: &Terminator<'tcx>,
545                              sig: &ty::FnSig<'tcx>,
546                              args: &[Operand<'tcx>])
547     {
548         debug!("check_box_free_inputs");
549
550         // box_free takes a Box as a pointer. Allow for that.
551
552         if sig.inputs().len() != 1 {
553             span_mirbug!(self, term, "box_free should take 1 argument");
554             return;
555         }
556
557         let pointee_ty = match sig.inputs()[0].sty {
558             ty::TyRawPtr(mt) => mt.ty,
559             _ => {
560                 span_mirbug!(self, term, "box_free should take a raw ptr");
561                 return;
562             }
563         };
564
565         if args.len() != 1 {
566             span_mirbug!(self, term, "box_free called with wrong # of args");
567             return;
568         }
569
570         let ty = args[0].ty(mir, self.tcx());
571         let arg_ty = match ty.sty {
572             ty::TyRawPtr(mt) => mt.ty,
573             ty::TyAdt(def, _) if def.is_box() => ty.boxed_ty(),
574             _ => {
575                 span_mirbug!(self, term, "box_free called with bad arg ty");
576                 return;
577             }
578         };
579
580         if let Err(terr) = self.sub_types(arg_ty, pointee_ty) {
581             span_mirbug!(self, term, "bad box_free arg ({:?} <- {:?}): {:?}",
582                          pointee_ty, arg_ty, terr);
583         }
584     }
585
586     fn check_iscleanup(&mut self, mir: &Mir<'tcx>, block: &BasicBlockData<'tcx>)
587     {
588         let is_cleanup = block.is_cleanup;
589         self.last_span = block.terminator().source_info.span;
590         match block.terminator().kind {
591             TerminatorKind::Goto { target } =>
592                 self.assert_iscleanup(mir, block, target, is_cleanup),
593             TerminatorKind::SwitchInt { ref targets, .. } => {
594                 for target in targets {
595                     self.assert_iscleanup(mir, block, *target, is_cleanup);
596                 }
597             }
598             TerminatorKind::Resume => {
599                 if !is_cleanup {
600                     span_mirbug!(self, block, "resume on non-cleanup block!")
601                 }
602             }
603             TerminatorKind::Return => {
604                 if is_cleanup {
605                     span_mirbug!(self, block, "return on cleanup block")
606                 }
607             }
608             TerminatorKind::Unreachable => {}
609             TerminatorKind::Drop { target, unwind, .. } |
610             TerminatorKind::DropAndReplace { target, unwind, .. } |
611             TerminatorKind::Assert { target, cleanup: unwind, .. } => {
612                 self.assert_iscleanup(mir, block, target, is_cleanup);
613                 if let Some(unwind) = unwind {
614                     if is_cleanup {
615                         span_mirbug!(self, block, "unwind on cleanup block")
616                     }
617                     self.assert_iscleanup(mir, block, unwind, true);
618                 }
619             }
620             TerminatorKind::Call { ref destination, cleanup, .. } => {
621                 if let &Some((_, target)) = destination {
622                     self.assert_iscleanup(mir, block, target, is_cleanup);
623                 }
624                 if let Some(cleanup) = cleanup {
625                     if is_cleanup {
626                         span_mirbug!(self, block, "cleanup on cleanup block")
627                     }
628                     self.assert_iscleanup(mir, block, cleanup, true);
629                 }
630             }
631         }
632     }
633
634     fn assert_iscleanup(&mut self,
635                         mir: &Mir<'tcx>,
636                         ctxt: &fmt::Debug,
637                         bb: BasicBlock,
638                         iscleanuppad: bool)
639     {
640         if mir[bb].is_cleanup != iscleanuppad {
641             span_mirbug!(self, ctxt, "cleanuppad mismatch: {:?} should be {:?}",
642                          bb, iscleanuppad);
643         }
644     }
645
646     fn typeck_mir(&mut self, mir: &Mir<'tcx>) {
647         self.last_span = mir.span;
648         debug!("run_on_mir: {:?}", mir.span);
649         for block in mir.basic_blocks() {
650             for stmt in &block.statements {
651                 if stmt.source_info.span != DUMMY_SP {
652                     self.last_span = stmt.source_info.span;
653                 }
654                 self.check_stmt(mir, stmt);
655             }
656
657             self.check_terminator(mir, block.terminator());
658             self.check_iscleanup(mir, block);
659         }
660     }
661
662
663     fn normalize<T>(&mut self, value: &T) -> T
664         where T: fmt::Debug + TypeFoldable<'tcx>
665     {
666         let mut selcx = traits::SelectionContext::new(self.infcx);
667         let cause = traits::ObligationCause::misc(self.last_span, ast::CRATE_NODE_ID);
668         let traits::Normalized { value, obligations } =
669             traits::normalize(&mut selcx, cause, value);
670
671         debug!("normalize: value={:?} obligations={:?}",
672                value,
673                obligations);
674
675         let mut fulfill_cx = &mut self.fulfillment_cx;
676         for obligation in obligations {
677             fulfill_cx.register_predicate_obligation(self.infcx, obligation);
678         }
679
680         value
681     }
682
683     fn verify_obligations(&mut self, mir: &Mir<'tcx>) {
684         self.last_span = mir.span;
685         if let Err(e) = self.fulfillment_cx.select_all_or_error(self.infcx) {
686             span_mirbug!(self, "", "errors selecting obligation: {:?}",
687                          e);
688         }
689     }
690 }
691
692 pub struct TypeckMir;
693
694 impl TypeckMir {
695     pub fn new() -> Self {
696         TypeckMir
697     }
698 }
699
700 impl<'tcx> MirPass<'tcx> for TypeckMir {
701     fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
702                     src: MirSource, mir: &mut Mir<'tcx>) {
703         debug!("run_pass: {}", tcx.node_path_str(src.item_id()));
704
705         if tcx.sess.err_count() > 0 {
706             // compiling a broken program can obviously result in a
707             // broken MIR, so try not to report duplicate errors.
708             return;
709         }
710         let param_env = ty::ParameterEnvironment::for_item(tcx, src.item_id());
711         tcx.infer_ctxt(param_env, Reveal::UserFacing).enter(|infcx| {
712             let mut checker = TypeChecker::new(&infcx, src.item_id());
713             {
714                 let mut verifier = TypeVerifier::new(&mut checker, mir);
715                 verifier.visit_mir(mir);
716                 if verifier.errors_reported {
717                     // don't do further checks to avoid ICEs
718                     return;
719                 }
720             }
721             checker.typeck_mir(mir);
722             checker.verify_obligations(mir);
723         });
724     }
725 }
726
727 impl Pass for TypeckMir {
728 }