diff --git a/src/librustc/mir/mir_map.rs b/src/librustc/mir/mir_map.rs index aa24f6d8979..82bd2caaac1 100644 --- a/src/librustc/mir/mir_map.rs +++ b/src/librustc/mir/mir_map.rs @@ -12,6 +12,7 @@ use util::nodemap::NodeMap; use mir::repr::Mir; use mir::transform::MirPass; use middle::ty; +use middle::infer; pub struct MirMap<'tcx> { pub map: NodeMap>, @@ -19,9 +20,14 @@ pub struct MirMap<'tcx> { impl<'tcx> MirMap<'tcx> { pub fn run_passes(&mut self, passes: &mut [Box], tcx: &ty::ctxt<'tcx>) { - for (_, ref mut mir) in &mut self.map { + if passes.is_empty() { return; } + + for (&id, mir) in &mut self.map { + let param_env = ty::ParameterEnvironment::for_item(tcx, id); + let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env)); + for pass in &mut *passes { - pass.run_on_mir(mir, tcx) + pass.run_on_mir(mir, &infcx) } } } diff --git a/src/librustc/mir/transform.rs b/src/librustc/mir/transform.rs index 0f18d8d2821..cc417f5a99e 100644 --- a/src/librustc/mir/transform.rs +++ b/src/librustc/mir/transform.rs @@ -9,8 +9,8 @@ // except according to those terms. use mir::repr::Mir; -use middle::ty::ctxt; +use middle::infer::InferCtxt; pub trait MirPass { - fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, tcx: &ctxt<'tcx>); + fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>, infcx: &InferCtxt<'a, 'tcx>); } diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 68d85dc8394..70c33540121 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -149,11 +149,11 @@ impl<'a, 'm, 'tcx> Visitor<'tcx> for InnerDump<'a,'m,'tcx> { match build_mir(Cx::new(&infcx), implicit_arg_tys, id, span, decl, body) { Ok(mut mir) => { - clear_dead_blocks::ClearDeadBlocks::new().run_on_mir(&mut mir, self.tcx); - type_check::TypeckMir::new(&infcx).run_on_mir(&mut mir, self.tcx); - no_landing_pads::NoLandingPads.run_on_mir(&mut mir, self.tcx); + clear_dead_blocks::ClearDeadBlocks::new().run_on_mir(&mut mir, &infcx); + type_check::TypeckMir::new().run_on_mir(&mut mir, &infcx); + no_landing_pads::NoLandingPads.run_on_mir(&mut mir, &infcx); if self.tcx.sess.opts.mir_opt_level > 0 { - simplify_cfg::SimplifyCfg::new().run_on_mir(&mut mir, self.tcx); + simplify_cfg::SimplifyCfg::new().run_on_mir(&mut mir, &infcx); } let meta_item_list = self.attr .iter() diff --git a/src/librustc_mir/transform/clear_dead_blocks.rs b/src/librustc_mir/transform/clear_dead_blocks.rs index 88c0116f26d..2c08b6b0b81 100644 --- a/src/librustc_mir/transform/clear_dead_blocks.rs +++ b/src/librustc_mir/transform/clear_dead_blocks.rs @@ -15,7 +15,7 @@ //! This pass does not renumber or remove the blocks, to have the //! MIR better match the source. -use rustc::middle::ty; +use rustc::middle::infer; use rustc::mir::repr::*; use rustc::mir::transform::MirPass; @@ -56,7 +56,8 @@ impl ClearDeadBlocks { } impl MirPass for ClearDeadBlocks { - fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, _tcx: &ty::ctxt<'tcx>) { + fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>, _: &infer::InferCtxt<'a, 'tcx>) + { self.clear_dead_blocks(mir); } } diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index b927ab489f1..a82d1fc5399 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -16,18 +16,13 @@ use rustc::middle::ty; use rustc::mir::repr::*; use rustc::mir::visit::MutVisitor; use rustc::mir::mir_map::MirMap; -use rustc::mir::transform::MirPass; pub fn erase_regions<'tcx>(tcx: &ty::ctxt<'tcx>, mir_map: &mut MirMap<'tcx>) { - let mut eraser = EraseRegions; - for (_, mir) in &mut mir_map.map { - eraser.run_on_mir(mir, tcx); + EraseRegionsVisitor::new(tcx).visit_mir(mir); } } -pub struct EraseRegions; - struct EraseRegionsVisitor<'a, 'tcx: 'a> { tcx: &'a ty::ctxt<'tcx>, } @@ -58,12 +53,6 @@ impl<'a, 'tcx> EraseRegionsVisitor<'a, 'tcx> { } } -impl MirPass for EraseRegions { - fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, tcx: &ty::ctxt<'tcx>) { - EraseRegionsVisitor::new(tcx).visit_mir(mir); - } -} - impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> { fn visit_mir(&mut self, mir: &mut Mir<'tcx>) { self.erase_regions_return_ty(&mut mir.return_ty); diff --git a/src/librustc_mir/transform/no_landing_pads.rs b/src/librustc_mir/transform/no_landing_pads.rs index d0ea9f10f2e..e2c93bd4e87 100644 --- a/src/librustc_mir/transform/no_landing_pads.rs +++ b/src/librustc_mir/transform/no_landing_pads.rs @@ -11,7 +11,7 @@ //! This pass removes the unwind branch of all the terminators when the no-landing-pads option is //! specified. -use rustc::middle::ty; +use rustc::middle::infer; use rustc::mir::repr::*; use rustc::mir::visit::MutVisitor; use rustc::mir::transform::MirPass; @@ -41,8 +41,9 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads { } impl MirPass for NoLandingPads { - fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, tcx: &ty::ctxt<'tcx>) { - if tcx.sess.no_landing_pads() { + fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>, + infcx: &infer::InferCtxt<'a, 'tcx>) { + if infcx.tcx.sess.no_landing_pads() { self.visit_mir(mir); } } diff --git a/src/librustc_mir/transform/simplify_cfg.rs b/src/librustc_mir/transform/simplify_cfg.rs index 17c5b5f7c3c..16d12324202 100644 --- a/src/librustc_mir/transform/simplify_cfg.rs +++ b/src/librustc_mir/transform/simplify_cfg.rs @@ -9,6 +9,7 @@ // except according to those terms. use rustc::middle::const_eval::ConstVal; +use rustc::middle::infer; use rustc::mir::repr::*; use transform::util; use rustc::mir::transform::MirPass; @@ -119,7 +120,7 @@ impl SimplifyCfg { } impl MirPass for SimplifyCfg { - fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, _: &::rustc::middle::ty::ctxt<'tcx>) { + fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>, _: &infer::InferCtxt<'a, 'tcx>) { let mut changed = true; while changed { changed = self.simplify_branches(mir); diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 010e3274253..4e94c76c7e8 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -11,7 +11,7 @@ //! This pass type-checks the MIR to ensure it is not broken. #![allow(unreachable_code)] -use rustc::middle::infer; +use rustc::middle::infer::{self, InferCtxt}; use rustc::middle::traits; use rustc::middle::ty::{self, Ty}; use rustc::middle::ty::fold::TypeFoldable; @@ -35,7 +35,7 @@ macro_rules! span_mirbug { macro_rules! span_mirbug_and_err { ($context:expr, $elem:expr, $($message:tt)*) => ({ { - $context.tcx().sess.span_bug( + $context.tcx().sess.span_warn( $context.last_span, &format!("broken MIR ({:?}): {:?}", $elem, format!($($message)*)) ); @@ -50,14 +50,14 @@ enum FieldAccessError { /// Verifies that MIR types are sane to not crash further /// checks. -struct TypeVerifier<'a, 'tcx: 'a> { - infcx: &'a infer::InferCtxt<'a, 'tcx>, +struct TypeVerifier<'a, 'b: 'a, 'tcx: 'b> { + cx: &'a mut TypeChecker<'b, 'tcx>, mir: &'a Mir<'tcx>, last_span: Span, errors_reported: bool } -impl<'a, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'tcx> { +impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { fn visit_span(&mut self, span: &Span) { if *span != DUMMY_SP { self.last_span = *span; @@ -100,10 +100,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'tcx> { } } -impl<'a, 'tcx> TypeVerifier<'a, 'tcx> { - fn new(infcx: &'a infer::InferCtxt<'a, 'tcx>, mir: &'a Mir<'tcx>) -> Self { +impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { + fn new(cx: &'a mut TypeChecker<'b, 'tcx>, mir: &'a Mir<'tcx>) -> Self { TypeVerifier { - infcx: infcx, + cx: cx, mir: mir, last_span: mir.span, errors_reported: false @@ -111,7 +111,11 @@ impl<'a, 'tcx> TypeVerifier<'a, 'tcx> { } fn tcx(&self) -> &'a ty::ctxt<'tcx> { - self.infcx.tcx + self.cx.infcx.tcx + } + + fn infcx(&self) -> &'a InferCtxt<'a, 'tcx> { + self.cx.infcx } fn sanitize_type(&mut self, parent: &fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> { @@ -163,6 +167,7 @@ impl<'a, 'tcx> TypeVerifier<'a, 'tcx> { debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, lvalue); let tcx = self.tcx(); let base_ty = base.to_ty(tcx); + let span = self.last_span; match *pi { ProjectionElem::Deref => { let deref_ty = base_ty.builtin_deref(true, ty::LvaluePreference::NoPreference); @@ -227,7 +232,7 @@ impl<'a, 'tcx> TypeVerifier<'a, 'tcx> { let fty = self.sanitize_type(lvalue, fty); match self.field_ty(lvalue, base, field) { Ok(ty) => { - if let Err(terr) = infer::can_mk_subty(self.infcx, ty, fty) { + if let Err(terr) = self.cx.mk_eqty(span, ty, fty) { span_mirbug!( self, lvalue, "bad field access ({:?}: {:?}): {:?}", ty, fty, terr); @@ -282,14 +287,15 @@ impl<'a, 'tcx> TypeVerifier<'a, 'tcx> { }; if let Some(field) = variant.fields.get(field.index()) { - Ok(self.normalize(parent, field.ty(tcx, substs))) + Ok(self.normalize(field.ty(tcx, substs))) } else { Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() }) } } - fn normalize(&mut self, parent: &fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> { - let mut selcx = traits::SelectionContext::new(&self.infcx); + fn normalize(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + let infcx = self.infcx(); + let mut selcx = traits::SelectionContext::new(infcx); let cause = traits::ObligationCause::misc(self.last_span, 0); let traits::Normalized { value: ty, obligations } = traits::normalize(&mut selcx, cause, &ty); @@ -298,33 +304,44 @@ impl<'a, 'tcx> TypeVerifier<'a, 'tcx> { ty, obligations); - let mut fulfill_cx = self.infcx.fulfillment_cx.borrow_mut(); + let mut fulfill_cx = &mut self.cx.fulfillment_cx; for obligation in obligations { - fulfill_cx.register_predicate_obligation(&self.infcx, obligation); + fulfill_cx.register_predicate_obligation(infcx, obligation); } - match infer::drain_fulfillment_cx(&self.infcx, &mut fulfill_cx, &ty) { - Ok(ty) => ty, - Err(e) => { - span_mirbug_and_err!(self, parent, "trait fulfillment failed: {:?}", e) - } - } + ty } } -pub struct TypeckMir<'a, 'tcx: 'a> { - infcx: &'a infer::InferCtxt<'a, 'tcx>, +pub struct TypeChecker<'a, 'tcx: 'a> { + infcx: &'a InferCtxt<'a, 'tcx>, + fulfillment_cx: traits::FulfillmentContext<'tcx>, last_span: Span } -impl<'a, 'tcx> TypeckMir<'a, 'tcx> { - pub fn new(infcx: &'a infer::InferCtxt<'a, 'tcx>) -> Self { - TypeckMir { +impl<'a, 'tcx> TypeChecker<'a, 'tcx> { + fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Self { + TypeChecker { infcx: infcx, + fulfillment_cx: traits::FulfillmentContext::new(), last_span: DUMMY_SP } } + fn mk_subty(&self, span: Span, sup: Ty<'tcx>, sub: Ty<'tcx>) + -> infer::UnitResult<'tcx> + { + infer::mk_subty(self.infcx, false, infer::TypeOrigin::Misc(span), + sup, sub) + } + + fn mk_eqty(&self, span: Span, a: Ty<'tcx>, b: Ty<'tcx>) + -> infer::UnitResult<'tcx> + { + infer::mk_eqty(self.infcx, false, infer::TypeOrigin::Misc(span), + a, b) + } + fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.infcx.tcx } @@ -337,7 +354,7 @@ impl<'a, 'tcx> TypeckMir<'a, 'tcx> { let lv_ty = mir.lvalue_ty(tcx, lv).to_ty(tcx); let rv_ty = mir.rvalue_ty(tcx, rv); if let Some(rv_ty) = rv_ty { - if let Err(terr) = infer::can_mk_subty(self.infcx, rv_ty, lv_ty) { + if let Err(terr) = self.mk_subty(self.last_span, rv_ty, lv_ty) { span_mirbug!(self, stmt, "bad assignment ({:?} = {:?}): {:?}", lv_ty, rv_ty, terr); } @@ -358,7 +375,10 @@ impl<'a, 'tcx> TypeckMir<'a, 'tcx> { Terminator::Goto { .. } | Terminator::Resume | Terminator::Return | - Terminator::Drop { .. } => {} + Terminator::Drop { .. } => { + // no checks needed for these + } + Terminator::If { ref cond, .. } => { let cond_ty = mir.operand_ty(tcx, cond); match cond_ty.sty { @@ -370,15 +390,23 @@ impl<'a, 'tcx> TypeckMir<'a, 'tcx> { } Terminator::SwitchInt { ref discr, switch_ty, .. } => { let discr_ty = mir.lvalue_ty(tcx, discr).to_ty(tcx); - if let Err(terr) = infer::can_mk_subty(self.infcx, discr_ty, switch_ty) { + if let Err(terr) = self.mk_subty(self.last_span, discr_ty, switch_ty) { span_mirbug!(self, term, "bad SwitchInt ({:?} on {:?}): {:?}", switch_ty, discr_ty, terr); } + if !switch_ty.is_integral() && !switch_ty.is_char() && + !switch_ty.is_bool() + { + span_mirbug!(self, term, "bad SwitchInt discr ty {:?}",switch_ty); + } + // FIXME: check the values } - Terminator::Switch { ref discr, adt_def, .. } => { + Terminator::Switch { ref discr, adt_def, ref targets } => { let discr_ty = mir.lvalue_ty(tcx, discr).to_ty(tcx); match discr_ty.sty { - ty::TyEnum(def, _) if def == adt_def => {}, + ty::TyEnum(def, _) + if def == adt_def && adt_def.variants.len() == targets.len() + => {}, _ => { span_mirbug!(self, term, "bad Switch ({:?} on {:?})", adt_def, discr_ty); @@ -419,7 +447,7 @@ impl<'a, 'tcx> TypeckMir<'a, 'tcx> { } (&Some((ref dest, _)), ty::FnConverging(ty)) => { let dest_ty = mir.lvalue_ty(tcx, dest).to_ty(tcx); - if let Err(terr) = infer::can_mk_subty(self.infcx, ty, dest_ty) { + if let Err(terr) = self.mk_subty(self.last_span, ty, dest_ty) { span_mirbug!(self, term, "call dest mismatch ({:?} <- {:?}): {:?}", dest_ty, ty, terr); @@ -445,7 +473,7 @@ impl<'a, 'tcx> TypeckMir<'a, 'tcx> { } for (n, (fn_arg, op_arg)) in sig.inputs.iter().zip(args).enumerate() { let op_arg_ty = mir.operand_ty(self.tcx(), op_arg); - if let Err(terr) = infer::can_mk_subty(self.infcx, op_arg_ty, fn_arg) { + if let Err(terr) = self.mk_subty(self.last_span, op_arg_ty, fn_arg) { span_mirbug!(self, term, "bad arg #{:?} ({:?} <- {:?}): {:?}", n, fn_arg, op_arg_ty, terr); } @@ -500,7 +528,7 @@ impl<'a, 'tcx> TypeckMir<'a, 'tcx> { } }; - if let Err(terr) = infer::can_mk_subty(self.infcx, arg_ty, pointee_ty) { + if let Err(terr) = self.mk_subty(self.last_span, arg_ty, pointee_ty) { span_mirbug!(self, term, "bad box_free arg ({:?} <- {:?}): {:?}", pointee_ty, arg_ty, terr); } @@ -522,28 +550,45 @@ impl<'a, 'tcx> TypeckMir<'a, 'tcx> { } } } + + fn verify_obligations(&mut self, mir: &Mir<'tcx>) { + self.last_span = mir.span; + if let Err(e) = self.fulfillment_cx.select_all_or_error(self.infcx) { + span_mirbug!(self, "", "errors selecting obligation: {:?}", + e); + } + } } -impl<'a, 'tcx> MirPass for TypeckMir<'a, 'tcx> { - fn run_on_mir<'tcx_>(&mut self, - mir: &mut Mir<'tcx_>, - _tcx: &ty::ctxt<'tcx_>) { - // FIXME: pass param_env to run_on_mir - let mir: &mut Mir<'tcx> = unsafe { ::std::mem::transmute(mir) }; +pub struct TypeckMir; - if self.tcx().sess.err_count() > 0 { +impl TypeckMir { + pub fn new() -> Self { + TypeckMir + } +} + +impl MirPass for TypeckMir { + fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>, infcx: &InferCtxt<'a, 'tcx>) + { + if infcx.tcx.sess.err_count() > 0 { // compiling a broken program can obviously result in a // broken MIR, so try not to report duplicate errors. return; } - let mut type_verifier = TypeVerifier::new(self.infcx, mir); - type_verifier.visit_mir(mir); + let mut checker = TypeChecker::new(infcx); - if type_verifier.errors_reported { - return; + { + let mut verifier = TypeVerifier::new(&mut checker, mir); + verifier.visit_mir(mir); + if verifier.errors_reported { + // don't do further checks to avoid ICEs + return; + } } - self.typeck_mir(mir); + checker.typeck_mir(mir); + checker.verify_obligations(mir); } } diff --git a/src/test/auxiliary/dummy_mir_pass.rs b/src/test/auxiliary/dummy_mir_pass.rs index df9ea4ab012..16ef965e0db 100644 --- a/src/test/auxiliary/dummy_mir_pass.rs +++ b/src/test/auxiliary/dummy_mir_pass.rs @@ -21,17 +21,14 @@ extern crate syntax; use rustc::mir::transform::MirPass; use rustc::mir::repr::{Mir, Literal}; use rustc::mir::visit::MutVisitor; -use rustc::middle::ty; +use rustc::middle::infer::InferCtxt; use rustc::middle::const_eval::ConstVal; -use rustc::lint::{LateContext, LintContext, LintPass, LateLintPass, LateLintPassObject, LintArray}; use rustc_plugin::Registry; -use rustc_front::hir; -use syntax::attr; struct Pass; impl MirPass for Pass { - fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, tcx: &ty::ctxt<'tcx>) { + fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>, _: &InferCtxt<'a, 'tcx>) { Visitor.visit_mir(mir) } }