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};
19 use rustc::mir::tcx::LvalueTy;
20 use rustc::mir::transform::{MirPass, MirSource, Pass};
21 use rustc::mir::visit::Visitor;
24 use syntax_pos::{Span, DUMMY_SP};
26 use rustc_data_structures::indexed_vec::Idx;
28 fn mirbug(tcx: TyCtxt, span: Span, msg: &str) {
29 tcx.sess.diagnostic().span_bug(span, msg);
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)*)))
39 macro_rules! span_mirbug_and_err {
40 ($context:expr, $elem:expr, $($message:tt)*) => ({
42 span_mirbug!($context, $elem, $($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 let rval_ty = rvalue.ty(self.mir, self.tcx());
86 self.sanitize_type(rvalue, rval_ty);
89 fn visit_mir(&mut self, mir: &Mir<'tcx>) {
90 self.sanitize_type(&"return type", mir.return_ty);
91 for local_decl in &mir.local_decls {
92 self.sanitize_type(local_decl, local_decl.ty);
94 if self.errors_reported {
101 impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
102 fn new(cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
107 errors_reported: false
111 fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> {
115 fn sanitize_type(&mut self, parent: &fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> {
116 if ty.needs_infer() || ty.has_escaping_regions() || ty.references_error() {
117 span_mirbug_and_err!(self, parent, "bad type {:?}", ty)
123 fn sanitize_lvalue(&mut self, lvalue: &Lvalue<'tcx>, location: Location) -> LvalueTy<'tcx> {
124 debug!("sanitize_lvalue: {:?}", lvalue);
126 Lvalue::Local(index) => LvalueTy::Ty { ty: self.mir.local_decls[index].ty },
127 Lvalue::Static(box Static { def_id, ty: sty }) => {
128 let sty = self.sanitize_type(lvalue, sty);
129 let ty = self.tcx().item_type(def_id);
130 let ty = self.cx.normalize(&ty);
131 if let Err(terr) = self.cx.eq_types(self.last_span, ty, sty) {
133 self, lvalue, "bad static type ({:?}: {:?}): {:?}",
136 LvalueTy::Ty { ty: sty }
139 Lvalue::Projection(ref proj) => {
140 let base_ty = self.sanitize_lvalue(&proj.base, location);
141 if let LvalueTy::Ty { ty } = base_ty {
142 if ty.references_error() {
143 assert!(self.errors_reported);
144 return LvalueTy::Ty { ty: self.tcx().types.err };
147 self.sanitize_projection(base_ty, &proj.elem, lvalue, location)
152 fn sanitize_projection(&mut self,
153 base: LvalueTy<'tcx>,
154 pi: &LvalueElem<'tcx>,
155 lvalue: &Lvalue<'tcx>,
158 debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, lvalue);
159 let tcx = self.tcx();
160 let base_ty = base.to_ty(tcx);
161 let span = self.last_span;
163 ProjectionElem::Deref => {
164 let deref_ty = base_ty.builtin_deref(true, ty::LvaluePreference::NoPreference);
166 ty: deref_ty.map(|t| t.ty).unwrap_or_else(|| {
167 span_mirbug_and_err!(
168 self, lvalue, "deref of non-pointer {:?}", base_ty)
172 ProjectionElem::Index(ref i) => {
173 self.visit_operand(i, location);
174 let index_ty = i.ty(self.mir, tcx);
175 if index_ty != tcx.types.usize {
177 ty: span_mirbug_and_err!(self, i, "index by non-usize {:?}", i)
181 ty: base_ty.builtin_index().unwrap_or_else(|| {
182 span_mirbug_and_err!(
183 self, lvalue, "index of non-array {:?}", base_ty)
188 ProjectionElem::ConstantIndex { .. } => {
189 // consider verifying in-bounds
191 ty: base_ty.builtin_index().unwrap_or_else(|| {
192 span_mirbug_and_err!(
193 self, lvalue, "index of non-array {:?}", base_ty)
197 ProjectionElem::Subslice { from, to } => {
199 ty: match base_ty.sty {
200 ty::TyArray(inner, size) => {
201 let min_size = (from as usize) + (to as usize);
202 if let Some(rest_size) = size.checked_sub(min_size) {
203 tcx.mk_array(inner, rest_size)
205 span_mirbug_and_err!(
206 self, lvalue, "taking too-small slice of {:?}", base_ty)
209 ty::TySlice(..) => base_ty,
211 span_mirbug_and_err!(
212 self, lvalue, "slice of non-array {:?}", base_ty)
217 ProjectionElem::Downcast(adt_def1, index) =>
219 ty::TyAdt(adt_def, substs) if adt_def.is_enum() && adt_def == adt_def1 => {
220 if index >= adt_def.variants.len() {
222 ty: span_mirbug_and_err!(
225 "cast to variant #{:?} but enum only has {:?}",
227 adt_def.variants.len())
238 ty: span_mirbug_and_err!(
239 self, lvalue, "can't downcast {:?} as {:?}",
243 ProjectionElem::Field(field, fty) => {
244 let fty = self.sanitize_type(lvalue, fty);
245 match self.field_ty(lvalue, base, field) {
247 if let Err(terr) = self.cx.eq_types(span, ty, fty) {
249 self, lvalue, "bad field access ({:?}: {:?}): {:?}",
253 Err(FieldAccessError::OutOfRange { field_count }) => {
255 self, lvalue, "accessed field #{} but variant only has {}",
256 field.index(), field_count)
259 LvalueTy::Ty { ty: fty }
264 fn error(&mut self) -> Ty<'tcx> {
265 self.errors_reported = true;
269 fn field_ty(&mut self,
271 base_ty: LvalueTy<'tcx>,
273 -> Result<Ty<'tcx>, FieldAccessError>
275 let tcx = self.tcx();
277 let (variant, substs) = match base_ty {
278 LvalueTy::Downcast { adt_def, substs, variant_index } => {
279 (&adt_def.variants[variant_index], substs)
281 LvalueTy::Ty { ty } => match ty.sty {
282 ty::TyAdt(adt_def, substs) if adt_def.is_univariant() => {
283 (&adt_def.variants[0], substs)
285 ty::TyClosure(def_id, substs) => {
286 return match substs.upvar_tys(def_id, tcx).nth(field.index()) {
288 None => Err(FieldAccessError::OutOfRange {
289 field_count: substs.upvar_tys(def_id, tcx).count()
293 ty::TyTuple(tys, _) => {
294 return match tys.get(field.index()) {
296 None => Err(FieldAccessError::OutOfRange {
297 field_count: tys.len()
301 _ => return Ok(span_mirbug_and_err!(
302 self, parent, "can't project out of {:?}", base_ty))
306 if let Some(field) = variant.fields.get(field.index()) {
307 Ok(self.cx.normalize(&field.ty(tcx, substs)))
309 Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() })
314 pub struct TypeChecker<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
315 infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
316 fulfillment_cx: traits::FulfillmentContext<'tcx>,
318 body_id: ast::NodeId,
321 impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
322 fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, body_id: ast::NodeId) -> Self {
325 fulfillment_cx: traits::FulfillmentContext::new(),
331 fn misc(&self, span: Span) -> traits::ObligationCause<'tcx> {
332 traits::ObligationCause::misc(span, self.body_id)
335 pub fn register_infer_ok_obligations<T>(&mut self, infer_ok: InferOk<'tcx, T>) -> T {
336 for obligation in infer_ok.obligations {
337 self.fulfillment_cx.register_predicate_obligation(self.infcx, obligation);
342 fn sub_types(&mut self, sup: Ty<'tcx>, sub: Ty<'tcx>)
343 -> infer::UnitResult<'tcx>
345 self.infcx.sub_types(false, &self.misc(self.last_span), sup, sub)
346 .map(|ok| self.register_infer_ok_obligations(ok))
349 fn eq_types(&mut self, span: Span, a: Ty<'tcx>, b: Ty<'tcx>)
350 -> infer::UnitResult<'tcx>
352 self.infcx.eq_types(false, &self.misc(span), a, b)
353 .map(|ok| self.register_infer_ok_obligations(ok))
356 fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> {
360 fn check_stmt(&mut self, mir: &Mir<'tcx>, stmt: &Statement<'tcx>) {
361 debug!("check_stmt: {:?}", stmt);
362 let tcx = self.tcx();
364 StatementKind::Assign(ref lv, ref rv) => {
365 let lv_ty = lv.ty(mir, tcx).to_ty(tcx);
366 let rv_ty = rv.ty(mir, tcx);
367 if let Err(terr) = self.sub_types(rv_ty, lv_ty) {
368 span_mirbug!(self, stmt, "bad assignment ({:?} = {:?}): {:?}",
372 StatementKind::SetDiscriminant{ ref lvalue, variant_index } => {
373 let lvalue_type = lvalue.ty(mir, tcx).to_ty(tcx);
374 let adt = match lvalue_type.sty {
375 TypeVariants::TyAdt(adt, _) if adt.is_enum() => adt,
377 span_bug!(stmt.source_info.span,
378 "bad set discriminant ({:?} = {:?}): lhs is not an enum",
383 if variant_index >= adt.variants.len() {
384 span_bug!(stmt.source_info.span,
385 "bad set discriminant ({:?} = {:?}): value of of range",
390 StatementKind::StorageLive(ref lv) |
391 StatementKind::StorageDead(ref lv) => {
393 Lvalue::Local(_) => {}
395 span_mirbug!(self, stmt, "bad lvalue: expected local");
399 StatementKind::InlineAsm { .. } |
400 StatementKind::Nop => {}
404 fn check_terminator(&mut self,
406 term: &Terminator<'tcx>) {
407 debug!("check_terminator: {:?}", term);
408 let tcx = self.tcx();
410 TerminatorKind::Goto { .. } |
411 TerminatorKind::Resume |
412 TerminatorKind::Return |
413 TerminatorKind::Unreachable |
414 TerminatorKind::Drop { .. } => {
415 // no checks needed for these
419 TerminatorKind::DropAndReplace {
424 let lv_ty = location.ty(mir, tcx).to_ty(tcx);
425 let rv_ty = value.ty(mir, tcx);
426 if let Err(terr) = self.sub_types(rv_ty, lv_ty) {
427 span_mirbug!(self, term, "bad DropAndReplace ({:?} = {:?}): {:?}",
431 TerminatorKind::SwitchInt { ref discr, switch_ty, .. } => {
432 let discr_ty = discr.ty(mir, tcx);
433 if let Err(terr) = self.sub_types(discr_ty, switch_ty) {
434 span_mirbug!(self, term, "bad SwitchInt ({:?} on {:?}): {:?}",
435 switch_ty, discr_ty, terr);
437 if !switch_ty.is_integral() && !switch_ty.is_char() &&
440 span_mirbug!(self, term, "bad SwitchInt discr ty {:?}",switch_ty);
442 // FIXME: check the values
444 TerminatorKind::Call { ref func, ref args, ref destination, .. } => {
445 let func_ty = func.ty(mir, tcx);
446 debug!("check_terminator: call, func_ty={:?}", func_ty);
447 let sig = match func_ty.sty {
448 ty::TyFnDef(.., sig) | ty::TyFnPtr(sig) => sig,
450 span_mirbug!(self, term, "call to non-function {:?}", func_ty);
454 let sig = tcx.erase_late_bound_regions(&sig);
455 let sig = self.normalize(&sig);
456 self.check_call_dest(mir, term, &sig, destination);
458 if self.is_box_free(func) {
459 self.check_box_free_inputs(mir, term, &sig, args);
461 self.check_call_inputs(mir, term, &sig, args);
464 TerminatorKind::Assert { ref cond, ref msg, .. } => {
465 let cond_ty = cond.ty(mir, tcx);
466 if cond_ty != tcx.types.bool {
467 span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty);
470 if let AssertMessage::BoundsCheck { ref len, ref index } = *msg {
471 if len.ty(mir, tcx) != tcx.types.usize {
472 span_mirbug!(self, len, "bounds-check length non-usize {:?}", len)
474 if index.ty(mir, tcx) != tcx.types.usize {
475 span_mirbug!(self, index, "bounds-check index non-usize {:?}", index)
482 fn check_call_dest(&mut self,
484 term: &Terminator<'tcx>,
485 sig: &ty::FnSig<'tcx>,
486 destination: &Option<(Lvalue<'tcx>, BasicBlock)>) {
487 let tcx = self.tcx();
489 Some((ref dest, _)) => {
490 let dest_ty = dest.ty(mir, tcx).to_ty(tcx);
491 if let Err(terr) = self.sub_types(sig.output(), dest_ty) {
492 span_mirbug!(self, term,
493 "call dest mismatch ({:?} <- {:?}): {:?}",
494 dest_ty, sig.output(), terr);
498 // FIXME(canndrew): This is_never should probably be an is_uninhabited
499 if !sig.output().is_never() {
500 span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
506 fn check_call_inputs(&mut self,
508 term: &Terminator<'tcx>,
509 sig: &ty::FnSig<'tcx>,
510 args: &[Operand<'tcx>])
512 debug!("check_call_inputs({:?}, {:?})", sig, args);
513 if args.len() < sig.inputs().len() ||
514 (args.len() > sig.inputs().len() && !sig.variadic) {
515 span_mirbug!(self, term, "call to {:?} with wrong # of args", sig);
517 for (n, (fn_arg, op_arg)) in sig.inputs().iter().zip(args).enumerate() {
518 let op_arg_ty = op_arg.ty(mir, self.tcx());
519 if let Err(terr) = self.sub_types(op_arg_ty, fn_arg) {
520 span_mirbug!(self, term, "bad arg #{:?} ({:?} <- {:?}): {:?}",
521 n, fn_arg, op_arg_ty, terr);
526 fn is_box_free(&self, operand: &Operand<'tcx>) -> bool {
528 &Operand::Constant(Constant {
529 literal: Literal::Item { def_id, .. }, ..
531 Some(def_id) == self.tcx().lang_items.box_free_fn()
537 fn check_box_free_inputs(&mut self,
539 term: &Terminator<'tcx>,
540 sig: &ty::FnSig<'tcx>,
541 args: &[Operand<'tcx>])
543 debug!("check_box_free_inputs");
545 // box_free takes a Box as a pointer. Allow for that.
547 if sig.inputs().len() != 1 {
548 span_mirbug!(self, term, "box_free should take 1 argument");
552 let pointee_ty = match sig.inputs()[0].sty {
553 ty::TyRawPtr(mt) => mt.ty,
555 span_mirbug!(self, term, "box_free should take a raw ptr");
561 span_mirbug!(self, term, "box_free called with wrong # of args");
565 let ty = args[0].ty(mir, self.tcx());
566 let arg_ty = match ty.sty {
567 ty::TyRawPtr(mt) => mt.ty,
568 ty::TyAdt(def, _) if def.is_box() => ty.boxed_ty(),
570 span_mirbug!(self, term, "box_free called with bad arg ty");
575 if let Err(terr) = self.sub_types(arg_ty, pointee_ty) {
576 span_mirbug!(self, term, "bad box_free arg ({:?} <- {:?}): {:?}",
577 pointee_ty, arg_ty, terr);
581 fn check_iscleanup(&mut self, mir: &Mir<'tcx>, block: &BasicBlockData<'tcx>)
583 let is_cleanup = block.is_cleanup;
584 self.last_span = block.terminator().source_info.span;
585 match block.terminator().kind {
586 TerminatorKind::Goto { target } =>
587 self.assert_iscleanup(mir, block, target, is_cleanup),
588 TerminatorKind::SwitchInt { ref targets, .. } => {
589 for target in targets {
590 self.assert_iscleanup(mir, block, *target, is_cleanup);
593 TerminatorKind::Resume => {
595 span_mirbug!(self, block, "resume on non-cleanup block!")
598 TerminatorKind::Return => {
600 span_mirbug!(self, block, "return on cleanup block")
603 TerminatorKind::Unreachable => {}
604 TerminatorKind::Drop { target, unwind, .. } |
605 TerminatorKind::DropAndReplace { target, unwind, .. } |
606 TerminatorKind::Assert { target, cleanup: unwind, .. } => {
607 self.assert_iscleanup(mir, block, target, is_cleanup);
608 if let Some(unwind) = unwind {
610 span_mirbug!(self, block, "unwind on cleanup block")
612 self.assert_iscleanup(mir, block, unwind, true);
615 TerminatorKind::Call { ref destination, cleanup, .. } => {
616 if let &Some((_, target)) = destination {
617 self.assert_iscleanup(mir, block, target, is_cleanup);
619 if let Some(cleanup) = cleanup {
621 span_mirbug!(self, block, "cleanup on cleanup block")
623 self.assert_iscleanup(mir, block, cleanup, true);
629 fn assert_iscleanup(&mut self,
635 if mir[bb].is_cleanup != iscleanuppad {
636 span_mirbug!(self, ctxt, "cleanuppad mismatch: {:?} should be {:?}",
641 fn typeck_mir(&mut self, mir: &Mir<'tcx>) {
642 self.last_span = mir.span;
643 debug!("run_on_mir: {:?}", mir.span);
644 for block in mir.basic_blocks() {
645 for stmt in &block.statements {
646 if stmt.source_info.span != DUMMY_SP {
647 self.last_span = stmt.source_info.span;
649 self.check_stmt(mir, stmt);
652 self.check_terminator(mir, block.terminator());
653 self.check_iscleanup(mir, block);
658 fn normalize<T>(&mut self, value: &T) -> T
659 where T: fmt::Debug + TypeFoldable<'tcx>
661 let mut selcx = traits::SelectionContext::new(self.infcx);
662 let cause = traits::ObligationCause::misc(self.last_span, ast::CRATE_NODE_ID);
663 let traits::Normalized { value, obligations } =
664 traits::normalize(&mut selcx, cause, value);
666 debug!("normalize: value={:?} obligations={:?}",
670 let mut fulfill_cx = &mut self.fulfillment_cx;
671 for obligation in obligations {
672 fulfill_cx.register_predicate_obligation(self.infcx, obligation);
678 fn verify_obligations(&mut self, mir: &Mir<'tcx>) {
679 self.last_span = mir.span;
680 if let Err(e) = self.fulfillment_cx.select_all_or_error(self.infcx) {
681 span_mirbug!(self, "", "errors selecting obligation: {:?}",
687 pub struct TypeckMir;
690 pub fn new() -> Self {
695 impl<'tcx> MirPass<'tcx> for TypeckMir {
696 fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
697 src: MirSource, mir: &mut Mir<'tcx>) {
698 debug!("run_pass: {}", tcx.node_path_str(src.item_id()));
700 if tcx.sess.err_count() > 0 {
701 // compiling a broken program can obviously result in a
702 // broken MIR, so try not to report duplicate errors.
705 let param_env = ty::ParameterEnvironment::for_item(tcx, src.item_id());
706 tcx.infer_ctxt(param_env, Reveal::UserFacing).enter(|infcx| {
707 let mut checker = TypeChecker::new(&infcx, src.item_id());
709 let mut verifier = TypeVerifier::new(&mut checker, mir);
710 verifier.visit_mir(mir);
711 if verifier.errors_reported {
712 // don't do further checks to avoid ICEs
716 checker.typeck_mir(mir);
717 checker.verify_obligations(mir);
722 impl Pass for TypeckMir {