// Copyright 2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! An experimental pass that scources for `#[rustc_mir]` attributes, //! builds the resulting MIR, and dumps it out into a file for inspection. //! //! The attribute formats that are currently accepted are: //! //! - `#[rustc_mir(graphviz="file.gv")]` //! - `#[rustc_mir(pretty="file.mir")]` use build; use rustc::dep_graph::DepNode; use rustc::mir::repr::Mir; use rustc::mir::transform::MirSource; use rustc::mir::visit::MutVisitor; use pretty; use hair::cx::Cx; use rustc::mir::mir_map::MirMap; use rustc::infer::InferCtxtBuilder; use rustc::traits::ProjectionMode; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::Substs; use rustc::util::nodemap::NodeMap; use rustc::hir; use rustc::hir::intravisit::{self, FnKind, Visitor}; use syntax::ast; use syntax::codemap::Span; use std::mem; pub fn build_mir_for_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MirMap<'tcx> { let mut map = MirMap { map: NodeMap(), }; { let mut dump = BuildMir { tcx: tcx, map: &mut map, }; tcx.visit_all_items_in_krate(DepNode::MirMapConstruction, &mut dump); } map } /// A pass to lift all the types and substitutions in a Mir /// to the global tcx. Sadly, we don't have a "folder" that /// can change 'tcx so we have to transmute afterwards. struct GlobalizeMir<'a, 'gcx: 'a> { tcx: TyCtxt<'a, 'gcx, 'gcx>, span: Span } impl<'a, 'gcx: 'tcx, 'tcx> MutVisitor<'tcx> for GlobalizeMir<'a, 'gcx> { fn visit_ty(&mut self, ty: &mut Ty<'tcx>) { if let Some(lifted) = self.tcx.lift(ty) { *ty = lifted; } else { span_bug!(self.span, "found type `{:?}` with inference types/regions in MIR", ty); } } fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>) { if let Some(lifted) = self.tcx.lift(substs) { *substs = lifted; } else { span_bug!(self.span, "found substs `{:?}` with inference types/regions in MIR", substs); } } } /////////////////////////////////////////////////////////////////////////// // BuildMir -- walks a crate, looking for fn items and methods to build MIR from struct BuildMir<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, map: &'a mut MirMap<'tcx>, } /// Helper type of a temporary returned by BuildMir::cx(...). /// Necessary because we can't write the following bound: /// F: for<'b, 'tcx> where 'gcx: 'tcx FnOnce(Cx<'b, 'gcx, 'tcx>). struct CxBuilder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { src: MirSource, infcx: InferCtxtBuilder<'a, 'gcx, 'tcx>, map: &'a mut MirMap<'gcx>, } impl<'a, 'gcx, 'tcx> BuildMir<'a, 'gcx> { fn cx<'b>(&'b mut self, src: MirSource) -> CxBuilder<'b, 'gcx, 'tcx> { let param_env = ty::ParameterEnvironment::for_item(self.tcx, src.item_id()); CxBuilder { src: src, infcx: self.tcx.infer_ctxt(None, Some(param_env), ProjectionMode::AnyFinal), map: self.map } } } impl<'a, 'gcx, 'tcx> CxBuilder<'a, 'gcx, 'tcx> { fn build(&'tcx mut self, f: F) where F: for<'b> FnOnce(Cx<'b, 'gcx, 'tcx>) -> (Mir<'tcx>, build::ScopeAuxiliaryVec) { let src = self.src; let mir = self.infcx.enter(|infcx| { let (mut mir, scope_auxiliary) = f(Cx::new(&infcx, src)); // Convert the Mir to global types. let mut globalizer = GlobalizeMir { tcx: infcx.tcx.global_tcx(), span: mir.span }; globalizer.visit_mir(&mut mir); let mir = unsafe { mem::transmute::>(mir) }; pretty::dump_mir(infcx.tcx.global_tcx(), "mir_map", &0, src, &mir, Some(&scope_auxiliary)); mir }); assert!(self.map.map.insert(src.item_id(), mir).is_none()) } } impl<'a, 'gcx> BuildMir<'a, 'gcx> { fn build_const_integer(&mut self, expr: &'gcx hir::Expr) { // FIXME(eddyb) Closures should have separate // function definition IDs and expression IDs. // Type-checking should not let closures get // this far in an integer constant position. if let hir::ExprClosure(..) = expr.node { return; } self.cx(MirSource::Const(expr.id)).build(|cx| { build::construct_const(cx, expr.id, expr) }); } } impl<'a, 'tcx> Visitor<'tcx> for BuildMir<'a, 'tcx> { // Const and static items. fn visit_item(&mut self, item: &'tcx hir::Item) { match item.node { hir::ItemConst(_, ref expr) => { self.cx(MirSource::Const(item.id)).build(|cx| { build::construct_const(cx, item.id, expr) }); } hir::ItemStatic(_, m, ref expr) => { self.cx(MirSource::Static(item.id, m)).build(|cx| { build::construct_const(cx, item.id, expr) }); } _ => {} } intravisit::walk_item(self, item); } // Trait associated const defaults. fn visit_trait_item(&mut self, item: &'tcx hir::TraitItem) { if let hir::ConstTraitItem(_, Some(ref expr)) = item.node { self.cx(MirSource::Const(item.id)).build(|cx| { build::construct_const(cx, item.id, expr) }); } intravisit::walk_trait_item(self, item); } // Impl associated const. fn visit_impl_item(&mut self, item: &'tcx hir::ImplItem) { if let hir::ImplItemKind::Const(_, ref expr) = item.node { self.cx(MirSource::Const(item.id)).build(|cx| { build::construct_const(cx, item.id, expr) }); } intravisit::walk_impl_item(self, item); } // Repeat counts, i.e. [expr; constant]. fn visit_expr(&mut self, expr: &'tcx hir::Expr) { if let hir::ExprRepeat(_, ref count) = expr.node { self.build_const_integer(count); } intravisit::walk_expr(self, expr); } // Array lengths, i.e. [T; constant]. fn visit_ty(&mut self, ty: &'tcx hir::Ty) { if let hir::TyFixedLengthVec(_, ref length) = ty.node { self.build_const_integer(length); } intravisit::walk_ty(self, ty); } // Enum variant discriminant values. fn visit_variant(&mut self, v: &'tcx hir::Variant, g: &'tcx hir::Generics, item_id: ast::NodeId) { if let Some(ref expr) = v.node.disr_expr { self.build_const_integer(expr); } intravisit::walk_variant(self, v, g, item_id); } fn visit_fn(&mut self, fk: FnKind<'tcx>, decl: &'tcx hir::FnDecl, body: &'tcx hir::Block, span: Span, id: ast::NodeId) { // fetch the fully liberated fn signature (that is, all bound // types/lifetimes replaced) let fn_sig = match self.tcx.tables.borrow().liberated_fn_sigs.get(&id) { Some(f) => f.clone(), None => { span_bug!(span, "no liberated fn sig for {:?}", id); } }; let implicit_argument = if let FnKind::Closure(..) = fk { Some((closure_self_ty(self.tcx, id, body.id), None)) } else { None }; let explicit_arguments = decl.inputs .iter() .enumerate() .map(|(index, arg)| { (fn_sig.inputs[index], Some(&*arg.pat)) }); let arguments = implicit_argument.into_iter().chain(explicit_arguments); self.cx(MirSource::Fn(id)).build(|cx| { build::construct_fn(cx, id, arguments, fn_sig.output, body) }); intravisit::walk_fn(self, fk, decl, body, span); } } fn closure_self_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, closure_expr_id: ast::NodeId, body_id: ast::NodeId) -> Ty<'tcx> { let closure_ty = tcx.node_id_to_type(closure_expr_id); // We're just hard-coding the idea that the signature will be // &self or &mut self and hence will have a bound region with // number 0, hokey. let region = ty::Region::ReFree(ty::FreeRegion { scope: tcx.region_maps.item_extent(body_id), bound_region: ty::BoundRegion::BrAnon(0), }); let region = tcx.mk_region(region); match tcx.closure_kind(tcx.map.local_def_id(closure_expr_id)) { ty::ClosureKind::Fn => tcx.mk_ref(region, ty::TypeAndMut { ty: closure_ty, mutbl: hir::MutImmutable }), ty::ClosureKind::FnMut => tcx.mk_ref(region, ty::TypeAndMut { ty: closure_ty, mutbl: hir::MutMutable }), ty::ClosureKind::FnOnce => closure_ty } }