mir: factor out the parts of MIR building which are not fn-specific.

This commit is contained in:
Eduard Burtescu 2016-04-15 17:11:24 +03:00
parent bbc41aa9a6
commit cde2f5f116
2 changed files with 156 additions and 180 deletions

View file

@ -10,12 +10,13 @@
use hair::cx::Cx;
use rustc::middle::region::{CodeExtent, CodeExtentData};
use rustc::ty::{self, FnOutput, Ty};
use rustc::ty::{self, Ty};
use rustc::mir::repr::*;
use rustc_data_structures::fnv::FnvHashMap;
use rustc::hir;
use rustc::hir::pat_util::pat_is_binding;
use std::ops::{Index, IndexMut};
use syntax::abi::Abi;
use syntax::ast;
use syntax::codemap::Span;
use syntax::parse::token::keywords;
@ -159,53 +160,31 @@ macro_rules! unpack {
///////////////////////////////////////////////////////////////////////////
/// the main entry point for building MIR for a function
pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
span: Span,
fn_id: ast::NodeId,
body_id: ast::NodeId,
implicit_arguments: Vec<Ty<'tcx>>,
explicit_arguments: Vec<(Ty<'tcx>, &'tcx hir::Pat)>,
return_ty: FnOutput<'tcx>,
ast_block: &'tcx hir::Block)
-> (Mir<'tcx>, ScopeAuxiliaryVec) {
pub fn construct_fn<'a, 'tcx, A>(hir: Cx<'a,'tcx>,
fn_id: ast::NodeId,
arguments: A,
return_ty: ty::FnOutput<'tcx>,
ast_block: &'tcx hir::Block)
-> (Mir<'tcx>, ScopeAuxiliaryVec)
where A: Iterator<Item=(Ty<'tcx>, Option<&'tcx hir::Pat>)>
{
let tcx = hir.tcx();
let cfg = CFG { basic_blocks: vec![] };
let span = tcx.map.span(fn_id);
let mut builder = Builder::new(hir, span);
let mut builder = Builder {
hir: hir,
cfg: cfg,
fn_span: span,
scopes: vec![],
scope_datas: vec![],
scope_auxiliary: ScopeAuxiliaryVec { vec: vec![] },
loop_scopes: vec![],
temp_decls: vec![],
var_decls: vec![],
var_indices: FnvHashMap(),
unit_temp: None,
cached_resume_block: None,
cached_return_block: None
};
assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
let mut arg_decls = None; // assigned to `Some` in closures below
let body_id = ast_block.id;
let call_site_extent =
tcx.region_maps.lookup_code_extent(
CodeExtentData::CallSiteScope { fn_id: fn_id, body_id: body_id });
let _ = builder.in_scope(call_site_extent, START_BLOCK, |builder, call_site_scope_id| {
let mut block = START_BLOCK;
let arg_extent =
tcx.region_maps.lookup_code_extent(
CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body_id });
unpack!(block = builder.in_scope(arg_extent, block, |builder, arg_scope_id| {
arg_decls = Some(unpack!(block = builder.args_and_body(block,
return_ty,
implicit_arguments,
explicit_arguments,
arg_scope_id,
ast_block)));
block.unit()
let arg_extent =
tcx.region_maps.lookup_code_extent(
CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body_id });
let mut block = START_BLOCK;
let mut arg_decls = unpack!(block = builder.in_scope(call_site_extent, block,
|builder, call_site_scope_id| {
let arg_decls = unpack!(block = builder.in_scope(arg_extent, block,
|builder, arg_scope_id| {
builder.args_and_body(block, return_ty, arguments, arg_scope_id, ast_block)
}));
let return_block = builder.return_block();
@ -213,20 +192,19 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
TerminatorKind::Goto { target: return_block });
builder.cfg.terminate(return_block, call_site_scope_id, span,
TerminatorKind::Return);
return_block.unit()
});
return_block.and(arg_decls)
}));
assert_eq!(block, builder.return_block());
assert!(
builder.cfg.basic_blocks
.iter()
.enumerate()
.all(|(index, block)| {
if block.terminator.is_none() {
bug!("no terminator on block {:?} in fn {:?}",
index, fn_id)
}
true
}));
match tcx.node_id_to_type(fn_id).sty {
ty::TyFnDef(_, _, f) if f.abi == Abi::RustCall => {
// RustCall pseudo-ABI untuples the last argument.
if let Some(arg_decl) = arg_decls.last_mut() {
arg_decl.spread = true;
}
}
_ => {}
}
// Gather the upvars of a closure, if any.
let upvar_decls: Vec<_> = tcx.with_freevars(fn_id, |freevars| {
@ -251,72 +229,98 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
}).collect()
});
(
Mir {
basic_blocks: builder.cfg.basic_blocks,
scopes: builder.scope_datas,
var_decls: builder.var_decls,
arg_decls: arg_decls.take().expect("args never built?"),
temp_decls: builder.temp_decls,
upvar_decls: upvar_decls,
return_ty: return_ty,
span: span
},
builder.scope_auxiliary,
)
builder.finish(upvar_decls, arg_decls, return_ty)
}
impl<'a,'tcx> Builder<'a,'tcx> {
fn args_and_body(&mut self,
mut block: BasicBlock,
return_ty: FnOutput<'tcx>,
implicit_arguments: Vec<Ty<'tcx>>,
explicit_arguments: Vec<(Ty<'tcx>, &'tcx hir::Pat)>,
argument_scope_id: ScopeId,
ast_block: &'tcx hir::Block)
-> BlockAnd<Vec<ArgDecl<'tcx>>>
fn new(hir: Cx<'a, 'tcx>, span: Span) -> Builder<'a, 'tcx> {
let mut builder = Builder {
hir: hir,
cfg: CFG { basic_blocks: vec![] },
fn_span: span,
scopes: vec![],
scope_datas: vec![],
scope_auxiliary: ScopeAuxiliaryVec { vec: vec![] },
loop_scopes: vec![],
temp_decls: vec![],
var_decls: vec![],
var_indices: FnvHashMap(),
unit_temp: None,
cached_resume_block: None,
cached_return_block: None
};
assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
builder
}
fn finish(self,
upvar_decls: Vec<UpvarDecl>,
arg_decls: Vec<ArgDecl<'tcx>>,
return_ty: ty::FnOutput<'tcx>)
-> (Mir<'tcx>, ScopeAuxiliaryVec) {
for (index, block) in self.cfg.basic_blocks.iter().enumerate() {
if block.terminator.is_none() {
span_bug!(self.fn_span, "no terminator on block {:?}", index);
}
}
(Mir {
basic_blocks: self.cfg.basic_blocks,
scopes: self.scope_datas,
var_decls: self.var_decls,
arg_decls: arg_decls,
temp_decls: self.temp_decls,
upvar_decls: upvar_decls,
return_ty: return_ty,
span: self.fn_span
}, self.scope_auxiliary)
}
fn args_and_body<A>(&mut self,
mut block: BasicBlock,
return_ty: ty::FnOutput<'tcx>,
arguments: A,
argument_scope_id: ScopeId,
ast_block: &'tcx hir::Block)
-> BlockAnd<Vec<ArgDecl<'tcx>>>
where A: Iterator<Item=(Ty<'tcx>, Option<&'tcx hir::Pat>)>
{
// to start, translate the argument patterns and collect the argument types.
let implicits = implicit_arguments.into_iter().map(|ty| (ty, None));
let explicits = explicit_arguments.into_iter().map(|(ty, pat)| (ty, Some(pat)));
let arg_decls =
implicits
.chain(explicits)
.enumerate()
.map(|(index, (ty, pattern))| {
let lvalue = Lvalue::Arg(index as u32);
if let Some(pattern) = pattern {
let pattern = self.hir.irrefutable_pat(pattern);
unpack!(block = self.lvalue_into_pattern(block,
argument_scope_id,
pattern,
&lvalue));
}
let arg_decls = arguments.enumerate().map(|(index, (ty, pattern))| {
let lvalue = Lvalue::Arg(index as u32);
if let Some(pattern) = pattern {
let pattern = self.hir.irrefutable_pat(pattern);
unpack!(block = self.lvalue_into_pattern(block,
argument_scope_id,
pattern,
&lvalue));
}
// Make sure we drop (parts of) the argument even when not matched on.
let argument_extent = self.scope_auxiliary[argument_scope_id].extent;
self.schedule_drop(pattern.as_ref().map_or(ast_block.span, |pat| pat.span),
argument_extent, &lvalue, ty);
// Make sure we drop (parts of) the argument even when not matched on.
let argument_extent = self.scope_auxiliary[argument_scope_id].extent;
self.schedule_drop(pattern.as_ref().map_or(ast_block.span, |pat| pat.span),
argument_extent, &lvalue, ty);
let mut name = keywords::Invalid.name();
if let Some(pat) = pattern {
if let hir::PatKind::Ident(_, ref ident, _) = pat.node {
if pat_is_binding(&self.hir.tcx().def_map.borrow(), pat) {
name = ident.node.name;
}
let mut name = keywords::Invalid.name();
if let Some(pat) = pattern {
if let hir::PatKind::Ident(_, ref ident, _) = pat.node {
if pat_is_binding(&self.hir.tcx().def_map.borrow(), pat) {
name = ident.node.name;
}
}
}
ArgDecl {
ty: ty,
spread: false,
debug_name: name
}
})
.collect();
ArgDecl {
ty: ty,
spread: false,
debug_name: name
}
}).collect();
// FIXME(#32959): temporary hack for the issue at hand
let return_is_unit = if let FnOutput::FnConverging(t) = return_ty {
let return_is_unit = if let ty::FnConverging(t) = return_ty {
t.is_nil()
} else {
false

View file

@ -28,11 +28,9 @@ use rustc::mir::mir_map::MirMap;
use rustc::infer;
use rustc::traits::ProjectionMode;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::util::common::ErrorReported;
use rustc::util::nodemap::NodeMap;
use rustc::hir;
use rustc::hir::intravisit::{self, Visitor};
use syntax::abi::Abi;
use syntax::ast;
use syntax::codemap::Span;
@ -58,6 +56,24 @@ struct BuildMir<'a, 'tcx: 'a> {
map: &'a mut MirMap<'tcx>,
}
impl<'a, 'tcx> BuildMir<'a, 'tcx> {
fn build<F>(&mut self, id: ast::NodeId, f: F)
where F: for<'b> FnOnce(Cx<'b, 'tcx>) -> (Mir<'tcx>, build::ScopeAuxiliaryVec)
{
let param_env = ty::ParameterEnvironment::for_item(self.tcx, id);
let infcx = infer::new_infer_ctxt(self.tcx,
&self.tcx.tables,
Some(param_env),
ProjectionMode::AnyFinal);
let (mir, scope_auxiliary) = f(Cx::new(&infcx));
pretty::dump_mir(self.tcx, "mir_map", &0, id, &mir, Some(&scope_auxiliary));
assert!(self.map.map.insert(id, mir).is_none())
}
}
impl<'a, 'tcx> Visitor<'tcx> for BuildMir<'a, 'tcx> {
fn visit_fn(&mut self,
fk: intravisit::FnKind<'tcx>,
@ -65,82 +81,38 @@ impl<'a, 'tcx> Visitor<'tcx> for BuildMir<'a, 'tcx> {
body: &'tcx hir::Block,
span: Span,
id: ast::NodeId) {
let implicit_arg_tys = if let intravisit::FnKind::Closure(..) = fk {
vec![closure_self_ty(&self.tcx, id, body.id)]
} else {
vec![]
// 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 param_env = ty::ParameterEnvironment::for_item(self.tcx, id);
let infcx = infer::new_infer_ctxt(self.tcx,
&self.tcx.tables,
Some(param_env),
ProjectionMode::AnyFinal);
let implicit_argument = if let intravisit::FnKind::Closure(..) = fk {
Some((closure_self_ty(&self.tcx, id, body.id), None))
} else {
None
};
match build_mir(Cx::new(&infcx), implicit_arg_tys, id, span, decl, body) {
Ok(mir) => assert!(self.map.map.insert(id, mir).is_none()),
Err(ErrorReported) => {}
}
let explicit_arguments =
decl.inputs
.iter()
.enumerate()
.map(|(index, arg)| {
(fn_sig.inputs[index], Some(&*arg.pat))
});
self.build(id, |cx| {
let arguments = implicit_argument.into_iter().chain(explicit_arguments);
build::construct_fn(cx, id, arguments, fn_sig.output, body)
});
intravisit::walk_fn(self, fk, decl, body, span);
}
}
fn build_mir<'a,'tcx:'a>(cx: Cx<'a,'tcx>,
implicit_arg_tys: Vec<Ty<'tcx>>,
fn_id: ast::NodeId,
span: Span,
decl: &'tcx hir::FnDecl,
body: &'tcx hir::Block)
-> Result<Mir<'tcx>, ErrorReported> {
// fetch the fully liberated fn signature (that is, all bound
// types/lifetimes replaced)
let fn_sig = match cx.tcx().tables.borrow().liberated_fn_sigs.get(&fn_id) {
Some(f) => f.clone(),
None => {
span_bug!(span, "no liberated fn sig for {:?}", fn_id);
}
};
let arguments =
decl.inputs
.iter()
.enumerate()
.map(|(index, arg)| {
(fn_sig.inputs[index], &*arg.pat)
})
.collect();
let (mut mir, scope_auxiliary) =
build::construct(cx,
span,
fn_id,
body.id,
implicit_arg_tys,
arguments,
fn_sig.output,
body);
match cx.tcx().node_id_to_type(fn_id).sty {
ty::TyFnDef(_, _, f) if f.abi == Abi::RustCall => {
// RustCall pseudo-ABI untuples the last argument.
if let Some(arg_decl) = mir.arg_decls.last_mut() {
arg_decl.spread = true;
}
}
_ => {}
}
pretty::dump_mir(cx.tcx(),
"mir_map",
&0,
fn_id,
&mir,
Some(&scope_auxiliary));
Ok(mir)
}
fn closure_self_ty<'a, 'tcx>(tcx: &TyCtxt<'tcx>,
closure_expr_id: ast::NodeId,
body_id: ast::NodeId)