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.
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.
11 //! This pass type-checks the MIR to ensure it is not broken.
12 #![allow(unreachable_code)]
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::repr::*;
19 use rustc::mir::tcx::LvalueTy;
20 use rustc::mir::transform::{MirPass, MirSource, Pass};
21 use rustc::mir::visit::{self, Visitor};
23 use syntax_pos::{Span, DUMMY_SP};
25 use rustc_data_structures::indexed_vec::Idx;
27 macro_rules! span_mirbug {
28 ($context:expr, $elem:expr, $($message:tt)*) => ({
29 $context.tcx().sess.span_warn(
31 &format!("broken MIR ({:?}): {}", $elem, format!($($message)*))
36 macro_rules! span_mirbug_and_err {
37 ($context:expr, $elem:expr, $($message:tt)*) => ({
39 $context.tcx().sess.span_warn(
41 &format!("broken MIR ({:?}): {:?}", $elem, format!($($message)*))
48 enum FieldAccessError {
49 OutOfRange { field_count: usize }
52 /// Verifies that MIR types are sane to not crash further checks.
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
57 struct TypeVerifier<'a, 'b: 'a, 'gcx: 'b+'tcx, 'tcx: 'b> {
58 cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>,
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;
71 fn visit_lvalue(&mut self,
72 lvalue: &Lvalue<'tcx>,
73 _context: visit::LvalueContext,
75 self.sanitize_lvalue(lvalue, location);
78 fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
79 self.super_constant(constant, location);
80 self.sanitize_type(constant, constant.ty);
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);
90 fn visit_mir(&mut self, mir: &Mir<'tcx>) {
91 self.sanitize_type(&"return type", mir.return_ty);
92 for var_decl in &mir.var_decls {
93 self.sanitize_type(var_decl, var_decl.ty);
95 for (n, arg_decl) in mir.arg_decls.iter().enumerate() {
96 self.sanitize_type(&(n, arg_decl), arg_decl.ty);
98 for (n, tmp_decl) in mir.temp_decls.iter().enumerate() {
99 self.sanitize_type(&(n, tmp_decl), tmp_decl.ty);
101 if self.errors_reported {
108 impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
109 fn new(cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
114 errors_reported: false
118 fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> {
122 fn sanitize_type(&mut self, parent: &fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> {
123 if ty.needs_infer() || ty.has_escaping_regions() || ty.references_error() {
124 span_mirbug_and_err!(self, parent, "bad type {:?}", ty)
130 fn sanitize_lvalue(&mut self, lvalue: &Lvalue<'tcx>, location: Location) -> LvalueTy<'tcx> {
131 debug!("sanitize_lvalue: {:?}", lvalue);
133 Lvalue::Var(index) => LvalueTy::Ty { ty: self.mir.var_decls[index].ty },
134 Lvalue::Temp(index) => LvalueTy::Ty { ty: self.mir.temp_decls[index].ty },
135 Lvalue::Arg(index) => LvalueTy::Ty { ty: self.mir.arg_decls[index].ty },
136 Lvalue::Static(def_id) =>
137 LvalueTy::Ty { ty: self.tcx().lookup_item_type(def_id).ty },
138 Lvalue::ReturnPointer => {
139 LvalueTy::Ty { ty: self.mir.return_ty }
141 Lvalue::Projection(ref proj) => {
142 let base_ty = self.sanitize_lvalue(&proj.base, location);
143 if let LvalueTy::Ty { ty } = base_ty {
144 if ty.references_error() {
145 assert!(self.errors_reported);
146 return LvalueTy::Ty { ty: self.tcx().types.err };
149 self.sanitize_projection(base_ty, &proj.elem, lvalue, location)
154 fn sanitize_projection(&mut self,
155 base: LvalueTy<'tcx>,
156 pi: &LvalueElem<'tcx>,
157 lvalue: &Lvalue<'tcx>,
160 debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, lvalue);
161 let tcx = self.tcx();
162 let base_ty = base.to_ty(tcx);
163 let span = self.last_span;
165 ProjectionElem::Deref => {
166 let deref_ty = base_ty.builtin_deref(true, ty::LvaluePreference::NoPreference);
168 ty: deref_ty.map(|t| t.ty).unwrap_or_else(|| {
169 span_mirbug_and_err!(
170 self, lvalue, "deref of non-pointer {:?}", base_ty)
174 ProjectionElem::Index(ref i) => {
175 self.visit_operand(i, location);
176 let index_ty = i.ty(self.mir, tcx);
177 if index_ty != tcx.types.usize {
179 ty: span_mirbug_and_err!(self, i, "index by non-usize {:?}", i)
183 ty: base_ty.builtin_index().unwrap_or_else(|| {
184 span_mirbug_and_err!(
185 self, lvalue, "index of non-array {:?}", base_ty)
190 ProjectionElem::ConstantIndex { .. } => {
191 // consider verifying in-bounds
193 ty: base_ty.builtin_index().unwrap_or_else(|| {
194 span_mirbug_and_err!(
195 self, lvalue, "index of non-array {:?}", base_ty)
199 ProjectionElem::Subslice { from, to } => {
201 ty: match base_ty.sty {
202 ty::TyArray(inner, size) => {
203 let min_size = (from as usize) + (to as usize);
204 if let Some(rest_size) = size.checked_sub(min_size) {
205 tcx.mk_array(inner, rest_size)
207 span_mirbug_and_err!(
208 self, lvalue, "taking too-small slice of {:?}", base_ty)
211 ty::TySlice(..) => base_ty,
213 span_mirbug_and_err!(
214 self, lvalue, "slice of non-array {:?}", base_ty)
219 ProjectionElem::Downcast(adt_def1, index) =>
221 ty::TyEnum(adt_def, substs) if adt_def == adt_def1 => {
222 if index >= adt_def.variants.len() {
224 ty: span_mirbug_and_err!(
227 "cast to variant #{:?} but enum only has {:?}",
229 adt_def.variants.len())
240 ty: span_mirbug_and_err!(
241 self, lvalue, "can't downcast {:?} as {:?}",
245 ProjectionElem::Field(field, fty) => {
246 let fty = self.sanitize_type(lvalue, fty);
247 match self.field_ty(lvalue, base, field) {
249 if let Err(terr) = self.cx.eq_types(span, ty, fty) {
251 self, lvalue, "bad field access ({:?}: {:?}): {:?}",
255 Err(FieldAccessError::OutOfRange { field_count }) => {
257 self, lvalue, "accessed field #{} but variant only has {}",
258 field.index(), field_count)
261 LvalueTy::Ty { ty: fty }
266 fn error(&mut self) -> Ty<'tcx> {
267 self.errors_reported = true;
271 fn field_ty(&mut self,
273 base_ty: LvalueTy<'tcx>,
275 -> Result<Ty<'tcx>, FieldAccessError>
277 let tcx = self.tcx();
279 let (variant, substs) = match base_ty {
280 LvalueTy::Downcast { adt_def, substs, variant_index } => {
281 (&adt_def.variants[variant_index], substs)
283 LvalueTy::Ty { ty } => match ty.sty {
284 ty::TyStruct(adt_def, substs) | ty::TyEnum(adt_def, substs)
285 if adt_def.is_univariant() => {
286 (&adt_def.variants[0], substs)
288 ty::TyTuple(tys) | ty::TyClosure(_, ty::ClosureSubsts {
291 return match tys.get(field.index()) {
293 None => Err(FieldAccessError::OutOfRange {
294 field_count: tys.len()
298 _ => return Ok(span_mirbug_and_err!(
299 self, parent, "can't project out of {:?}", base_ty))
303 if let Some(field) = variant.fields.get(field.index()) {
304 Ok(self.cx.normalize(&field.ty(tcx, substs)))
306 Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() })
311 pub struct TypeChecker<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
312 infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
313 fulfillment_cx: traits::FulfillmentContext<'tcx>,
317 impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
318 fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self {
321 fulfillment_cx: traits::FulfillmentContext::new(),
326 fn sub_types(&self, span: Span, sup: Ty<'tcx>, sub: Ty<'tcx>)
327 -> infer::UnitResult<'tcx>
329 self.infcx.sub_types(false, infer::TypeOrigin::Misc(span), sup, sub)
330 // FIXME(#32730) propagate obligations
331 .map(|InferOk { obligations, .. }| assert!(obligations.is_empty()))
334 fn eq_types(&self, span: Span, a: Ty<'tcx>, b: Ty<'tcx>)
335 -> infer::UnitResult<'tcx>
337 self.infcx.eq_types(false, infer::TypeOrigin::Misc(span), a, b)
338 // FIXME(#32730) propagate obligations
339 .map(|InferOk { obligations, .. }| assert!(obligations.is_empty()))
342 fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> {
346 fn check_stmt(&mut self, mir: &Mir<'tcx>, stmt: &Statement<'tcx>) {
347 debug!("check_stmt: {:?}", stmt);
348 let tcx = self.tcx();
350 StatementKind::Assign(ref lv, ref rv) => {
351 let lv_ty = lv.ty(mir, tcx).to_ty(tcx);
352 let rv_ty = rv.ty(mir, tcx);
353 if let Some(rv_ty) = rv_ty {
354 if let Err(terr) = self.sub_types(self.last_span, rv_ty, lv_ty) {
355 span_mirbug!(self, stmt, "bad assignment ({:?} = {:?}): {:?}",
358 // FIXME: rvalue with undeterminable type - e.g. inline
362 StatementKind::SetDiscriminant{ ref lvalue, variant_index } => {
363 let lvalue_type = lvalue.ty(mir, tcx).to_ty(tcx);
364 let adt = match lvalue_type.sty {
365 TypeVariants::TyEnum(adt, _) => adt,
367 span_bug!(stmt.source_info.span,
368 "bad set discriminant ({:?} = {:?}): lhs is not an enum",
373 if variant_index >= adt.variants.len() {
374 span_bug!(stmt.source_info.span,
375 "bad set discriminant ({:?} = {:?}): value of of range",
380 StatementKind::StorageLive(ref lv) |
381 StatementKind::StorageDead(ref lv) => {
383 Lvalue::Temp(_) | Lvalue::Var(_) => {}
385 span_mirbug!(self, stmt, "bad lvalue: expected temp or var");
392 fn check_terminator(&mut self,
394 term: &Terminator<'tcx>) {
395 debug!("check_terminator: {:?}", term);
396 let tcx = self.tcx();
398 TerminatorKind::Goto { .. } |
399 TerminatorKind::Resume |
400 TerminatorKind::Return |
401 TerminatorKind::Unreachable |
402 TerminatorKind::Drop { .. } => {
403 // no checks needed for these
407 TerminatorKind::DropAndReplace {
412 let lv_ty = location.ty(mir, tcx).to_ty(tcx);
413 let rv_ty = value.ty(mir, tcx);
414 if let Err(terr) = self.sub_types(self.last_span, rv_ty, lv_ty) {
415 span_mirbug!(self, term, "bad DropAndReplace ({:?} = {:?}): {:?}",
420 TerminatorKind::If { ref cond, .. } => {
421 let cond_ty = cond.ty(mir, tcx);
425 span_mirbug!(self, term, "bad If ({:?}, not bool", cond_ty);
429 TerminatorKind::SwitchInt { ref discr, switch_ty, .. } => {
430 let discr_ty = discr.ty(mir, tcx).to_ty(tcx);
431 if let Err(terr) = self.sub_types(self.last_span, discr_ty, switch_ty) {
432 span_mirbug!(self, term, "bad SwitchInt ({:?} on {:?}): {:?}",
433 switch_ty, discr_ty, terr);
435 if !switch_ty.is_integral() && !switch_ty.is_char() &&
438 span_mirbug!(self, term, "bad SwitchInt discr ty {:?}",switch_ty);
440 // FIXME: check the values
442 TerminatorKind::Switch { ref discr, adt_def, ref targets } => {
443 let discr_ty = discr.ty(mir, tcx).to_ty(tcx);
446 if def == adt_def && adt_def.variants.len() == targets.len()
449 span_mirbug!(self, term, "bad Switch ({:?} on {:?})",
454 TerminatorKind::Call { ref func, ref args, ref destination, .. } => {
455 let func_ty = func.ty(mir, tcx);
456 debug!("check_terminator: call, func_ty={:?}", func_ty);
457 let func_ty = match func_ty.sty {
458 ty::TyFnDef(_, _, func_ty) | ty::TyFnPtr(func_ty) => func_ty,
460 span_mirbug!(self, term, "call to non-function {:?}", func_ty);
464 let sig = tcx.erase_late_bound_regions(&func_ty.sig);
465 let sig = self.normalize(&sig);
466 self.check_call_dest(mir, term, &sig, destination);
468 if self.is_box_free(func) {
469 self.check_box_free_inputs(mir, term, &sig, args);
471 self.check_call_inputs(mir, term, &sig, args);
474 TerminatorKind::Assert { ref cond, ref msg, .. } => {
475 let cond_ty = cond.ty(mir, tcx);
476 if cond_ty != tcx.types.bool {
477 span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty);
480 if let AssertMessage::BoundsCheck { ref len, ref index } = *msg {
481 if len.ty(mir, tcx) != tcx.types.usize {
482 span_mirbug!(self, len, "bounds-check length non-usize {:?}", len)
484 if index.ty(mir, tcx) != tcx.types.usize {
485 span_mirbug!(self, index, "bounds-check index non-usize {:?}", index)
492 fn check_call_dest(&self,
494 term: &Terminator<'tcx>,
495 sig: &ty::FnSig<'tcx>,
496 destination: &Option<(Lvalue<'tcx>, BasicBlock)>) {
497 let tcx = self.tcx();
499 Some((ref dest, _)) => {
500 let dest_ty = dest.ty(mir, tcx).to_ty(tcx);
501 if let Err(terr) = self.sub_types(self.last_span, sig.output, dest_ty) {
502 span_mirbug!(self, term,
503 "call dest mismatch ({:?} <- {:?}): {:?}",
504 dest_ty, sig.output, terr);
508 // FIXME(canndrew): This is_never should probably be an is_uninhabited
509 if !sig.output.is_never() {
510 span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
516 fn check_call_inputs(&self,
518 term: &Terminator<'tcx>,
519 sig: &ty::FnSig<'tcx>,
520 args: &[Operand<'tcx>])
522 debug!("check_call_inputs({:?}, {:?})", sig, args);
523 if args.len() < sig.inputs.len() ||
524 (args.len() > sig.inputs.len() && !sig.variadic) {
525 span_mirbug!(self, term, "call to {:?} with wrong # of args", sig);
527 for (n, (fn_arg, op_arg)) in sig.inputs.iter().zip(args).enumerate() {
528 let op_arg_ty = op_arg.ty(mir, self.tcx());
529 if let Err(terr) = self.sub_types(self.last_span, op_arg_ty, fn_arg) {
530 span_mirbug!(self, term, "bad arg #{:?} ({:?} <- {:?}): {:?}",
531 n, fn_arg, op_arg_ty, terr);
536 fn is_box_free(&self, operand: &Operand<'tcx>) -> bool {
538 &Operand::Constant(Constant {
539 literal: Literal::Item { def_id, .. }, ..
541 Some(def_id) == self.tcx().lang_items.box_free_fn()
547 fn check_box_free_inputs(&self,
549 term: &Terminator<'tcx>,
550 sig: &ty::FnSig<'tcx>,
551 args: &[Operand<'tcx>])
553 debug!("check_box_free_inputs");
555 // box_free takes a Box as a pointer. Allow for that.
557 if sig.inputs.len() != 1 {
558 span_mirbug!(self, term, "box_free should take 1 argument");
562 let pointee_ty = match sig.inputs[0].sty {
563 ty::TyRawPtr(mt) => mt.ty,
565 span_mirbug!(self, term, "box_free should take a raw ptr");
571 span_mirbug!(self, term, "box_free called with wrong # of args");
575 let arg_ty = match args[0].ty(mir, self.tcx()).sty {
576 ty::TyRawPtr(mt) => mt.ty,
579 span_mirbug!(self, term, "box_free called with bad arg ty");
584 if let Err(terr) = self.sub_types(self.last_span, arg_ty, pointee_ty) {
585 span_mirbug!(self, term, "bad box_free arg ({:?} <- {:?}): {:?}",
586 pointee_ty, arg_ty, terr);
590 fn check_iscleanup(&mut self, mir: &Mir<'tcx>, block: &BasicBlockData<'tcx>)
592 let is_cleanup = block.is_cleanup;
593 self.last_span = block.terminator().source_info.span;
594 match block.terminator().kind {
595 TerminatorKind::Goto { target } =>
596 self.assert_iscleanup(mir, block, target, is_cleanup),
597 TerminatorKind::If { targets: (on_true, on_false), .. } => {
598 self.assert_iscleanup(mir, block, on_true, is_cleanup);
599 self.assert_iscleanup(mir, block, on_false, is_cleanup);
601 TerminatorKind::Switch { ref targets, .. } |
602 TerminatorKind::SwitchInt { ref targets, .. } => {
603 for target in targets {
604 self.assert_iscleanup(mir, block, *target, is_cleanup);
607 TerminatorKind::Resume => {
609 span_mirbug!(self, block, "resume on non-cleanup block!")
612 TerminatorKind::Return => {
614 span_mirbug!(self, block, "return on cleanup block")
617 TerminatorKind::Unreachable => {}
618 TerminatorKind::Drop { target, unwind, .. } |
619 TerminatorKind::DropAndReplace { target, unwind, .. } |
620 TerminatorKind::Assert { target, cleanup: unwind, .. } => {
621 self.assert_iscleanup(mir, block, target, is_cleanup);
622 if let Some(unwind) = unwind {
624 span_mirbug!(self, block, "unwind on cleanup block")
626 self.assert_iscleanup(mir, block, unwind, true);
629 TerminatorKind::Call { ref destination, cleanup, .. } => {
630 if let &Some((_, target)) = destination {
631 self.assert_iscleanup(mir, block, target, is_cleanup);
633 if let Some(cleanup) = cleanup {
635 span_mirbug!(self, block, "cleanup on cleanup block")
637 self.assert_iscleanup(mir, block, cleanup, true);
643 fn assert_iscleanup(&mut self,
649 if mir[bb].is_cleanup != iscleanuppad {
650 span_mirbug!(self, ctxt, "cleanuppad mismatch: {:?} should be {:?}",
655 fn typeck_mir(&mut self, mir: &Mir<'tcx>) {
656 self.last_span = mir.span;
657 debug!("run_on_mir: {:?}", mir.span);
658 for block in mir.basic_blocks() {
659 for stmt in &block.statements {
660 if stmt.source_info.span != DUMMY_SP {
661 self.last_span = stmt.source_info.span;
663 self.check_stmt(mir, stmt);
666 self.check_terminator(mir, block.terminator());
667 self.check_iscleanup(mir, block);
672 fn normalize<T>(&mut self, value: &T) -> T
673 where T: fmt::Debug + TypeFoldable<'tcx>
675 let mut selcx = traits::SelectionContext::new(self.infcx);
676 let cause = traits::ObligationCause::misc(self.last_span, 0);
677 let traits::Normalized { value, obligations } =
678 traits::normalize(&mut selcx, cause, value);
680 debug!("normalize: value={:?} obligations={:?}",
684 let mut fulfill_cx = &mut self.fulfillment_cx;
685 for obligation in obligations {
686 fulfill_cx.register_predicate_obligation(self.infcx, obligation);
692 fn verify_obligations(&mut self, mir: &Mir<'tcx>) {
693 self.last_span = mir.span;
694 if let Err(e) = self.fulfillment_cx.select_all_or_error(self.infcx) {
695 span_mirbug!(self, "", "errors selecting obligation: {:?}",
701 pub struct TypeckMir;
704 pub fn new() -> Self {
709 impl<'tcx> MirPass<'tcx> for TypeckMir {
710 fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
711 src: MirSource, mir: &mut Mir<'tcx>) {
712 if tcx.sess.err_count() > 0 {
713 // compiling a broken program can obviously result in a
714 // broken MIR, so try not to report duplicate errors.
717 let param_env = ty::ParameterEnvironment::for_item(tcx, src.item_id());
718 tcx.infer_ctxt(None, Some(param_env), Reveal::NotSpecializable).enter(|infcx| {
719 let mut checker = TypeChecker::new(&infcx);
721 let mut verifier = TypeVerifier::new(&mut checker, mir);
722 verifier.visit_mir(mir);
723 if verifier.errors_reported {
724 // don't do further checks to avoid ICEs
728 checker.typeck_mir(mir);
729 checker.verify_obligations(mir);
734 impl Pass for TypeckMir {