Only count nested returns when the outer return is reachable
This narrows the definition of nested returns such that only when the outer return has a chance of being executed (due to the inner return being conditional) do we mark the function as having nested returns. Fixes #19684
This commit is contained in:
parent
22a9f250b5
commit
eee209d9e2
5 changed files with 99 additions and 44 deletions
|
|
@ -49,4 +49,11 @@ impl CFG {
|
|||
blk: &ast::Block) -> CFG {
|
||||
construct::construct(tcx, blk)
|
||||
}
|
||||
|
||||
pub fn node_is_reachable(&self, id: ast::NodeId) -> bool {
|
||||
for node in self.graph.depth_traverse(self.entry) {
|
||||
if node.id == id { return true }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
use std::fmt::{Formatter, Error, Show};
|
||||
use std::uint;
|
||||
use std::collections::BitvSet;
|
||||
|
||||
pub struct Graph<N,E> {
|
||||
nodes: Vec<Node<N>> ,
|
||||
|
|
@ -294,6 +295,42 @@ impl<N,E> Graph<N,E> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn depth_traverse<'a>(&'a self, start: NodeIndex) -> DepthFirstTraversal<'a, N, E> {
|
||||
DepthFirstTraversal {
|
||||
graph: self,
|
||||
stack: vec![start],
|
||||
visited: BitvSet::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DepthFirstTraversal<'g, N:'g, E:'g> {
|
||||
graph: &'g Graph<N, E>,
|
||||
stack: Vec<NodeIndex>,
|
||||
visited: BitvSet
|
||||
}
|
||||
|
||||
impl<'g, N, E> Iterator<&'g N> for DepthFirstTraversal<'g, N, E> {
|
||||
fn next(&mut self) -> Option<&'g N> {
|
||||
while self.stack.len() > 0 {
|
||||
let idx = self.stack.pop().unwrap();
|
||||
if self.visited.contains(&idx.node_id()) {
|
||||
continue;
|
||||
}
|
||||
self.visited.insert(idx.node_id());
|
||||
self.graph.each_outgoing_edge(idx, |_, e| -> bool {
|
||||
if !self.visited.contains(&e.target().node_id()) {
|
||||
self.stack.push(e.target());
|
||||
}
|
||||
true
|
||||
});
|
||||
|
||||
return Some(self.graph.node_data(idx));
|
||||
}
|
||||
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn each_edge_index<F>(max_edge_index: EdgeIndex, mut f: F) where
|
||||
|
|
|
|||
|
|
@ -472,7 +472,7 @@ pub fn list_metadata(sess: &Session, path: &Path,
|
|||
/// The diagnostic emitter yielded to the procedure should be used for reporting
|
||||
/// errors of the compiler.
|
||||
pub fn monitor<F:FnOnce()+Send>(f: F) {
|
||||
static STACK_SIZE: uint = 32000000; // 32MB
|
||||
static STACK_SIZE: uint = 8 * 1024 * 1024; // 8MB
|
||||
|
||||
let (tx, rx) = channel();
|
||||
let w = io::ChanWriter::new(tx);
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ use llvm::{BasicBlockRef, Linkage, ValueRef, Vector, get_param};
|
|||
use llvm;
|
||||
use metadata::{csearch, encoder, loader};
|
||||
use middle::astencode;
|
||||
use middle::cfg;
|
||||
use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem};
|
||||
use middle::subst;
|
||||
use middle::weak_lang_items;
|
||||
|
|
@ -1305,47 +1306,33 @@ pub fn make_return_slot_pointer<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
|
|||
}
|
||||
}
|
||||
|
||||
struct CheckForNestedReturnsVisitor {
|
||||
struct FindNestedReturn {
|
||||
found: bool,
|
||||
in_return: bool
|
||||
}
|
||||
|
||||
impl CheckForNestedReturnsVisitor {
|
||||
fn explicit() -> CheckForNestedReturnsVisitor {
|
||||
CheckForNestedReturnsVisitor { found: false, in_return: false }
|
||||
}
|
||||
fn implicit() -> CheckForNestedReturnsVisitor {
|
||||
CheckForNestedReturnsVisitor { found: false, in_return: true }
|
||||
impl FindNestedReturn {
|
||||
fn new() -> FindNestedReturn {
|
||||
FindNestedReturn { found: false }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> Visitor<'v> for CheckForNestedReturnsVisitor {
|
||||
impl<'v> Visitor<'v> for FindNestedReturn {
|
||||
fn visit_expr(&mut self, e: &ast::Expr) {
|
||||
match e.node {
|
||||
ast::ExprRet(..) => {
|
||||
if self.in_return {
|
||||
self.found = true;
|
||||
} else {
|
||||
self.in_return = true;
|
||||
visit::walk_expr(self, e);
|
||||
self.in_return = false;
|
||||
}
|
||||
self.found = true;
|
||||
}
|
||||
_ => visit::walk_expr(self, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn has_nested_returns(tcx: &ty::ctxt, id: ast::NodeId) -> bool {
|
||||
match tcx.map.find(id) {
|
||||
fn build_cfg(tcx: &ty::ctxt, id: ast::NodeId) -> (ast::NodeId, Option<cfg::CFG>) {
|
||||
let blk = match tcx.map.find(id) {
|
||||
Some(ast_map::NodeItem(i)) => {
|
||||
match i.node {
|
||||
ast::ItemFn(_, _, _, _, ref blk) => {
|
||||
let mut explicit = CheckForNestedReturnsVisitor::explicit();
|
||||
let mut implicit = CheckForNestedReturnsVisitor::implicit();
|
||||
visit::walk_item(&mut explicit, &*i);
|
||||
visit::walk_expr_opt(&mut implicit, &blk.expr);
|
||||
explicit.found || implicit.found
|
||||
blk
|
||||
}
|
||||
_ => tcx.sess.bug("unexpected item variant in has_nested_returns")
|
||||
}
|
||||
|
|
@ -1355,11 +1342,7 @@ fn has_nested_returns(tcx: &ty::ctxt, id: ast::NodeId) -> bool {
|
|||
ast::ProvidedMethod(ref m) => {
|
||||
match m.node {
|
||||
ast::MethDecl(_, _, _, _, _, _, ref blk, _) => {
|
||||
let mut explicit = CheckForNestedReturnsVisitor::explicit();
|
||||
let mut implicit = CheckForNestedReturnsVisitor::implicit();
|
||||
visit::walk_method_helper(&mut explicit, &**m);
|
||||
visit::walk_expr_opt(&mut implicit, &blk.expr);
|
||||
explicit.found || implicit.found
|
||||
blk
|
||||
}
|
||||
ast::MethMac(_) => tcx.sess.bug("unexpanded macro")
|
||||
}
|
||||
|
|
@ -1379,11 +1362,7 @@ fn has_nested_returns(tcx: &ty::ctxt, id: ast::NodeId) -> bool {
|
|||
ast::MethodImplItem(ref m) => {
|
||||
match m.node {
|
||||
ast::MethDecl(_, _, _, _, _, _, ref blk, _) => {
|
||||
let mut explicit = CheckForNestedReturnsVisitor::explicit();
|
||||
let mut implicit = CheckForNestedReturnsVisitor::implicit();
|
||||
visit::walk_method_helper(&mut explicit, &**m);
|
||||
visit::walk_expr_opt(&mut implicit, &blk.expr);
|
||||
explicit.found || implicit.found
|
||||
blk
|
||||
}
|
||||
ast::MethMac(_) => tcx.sess.bug("unexpanded macro")
|
||||
}
|
||||
|
|
@ -1397,24 +1376,47 @@ fn has_nested_returns(tcx: &ty::ctxt, id: ast::NodeId) -> bool {
|
|||
Some(ast_map::NodeExpr(e)) => {
|
||||
match e.node {
|
||||
ast::ExprClosure(_, _, _, ref blk) => {
|
||||
let mut explicit = CheckForNestedReturnsVisitor::explicit();
|
||||
let mut implicit = CheckForNestedReturnsVisitor::implicit();
|
||||
visit::walk_expr(&mut explicit, e);
|
||||
visit::walk_expr_opt(&mut implicit, &blk.expr);
|
||||
explicit.found || implicit.found
|
||||
blk
|
||||
}
|
||||
_ => tcx.sess.bug("unexpected expr variant in has_nested_returns")
|
||||
}
|
||||
}
|
||||
|
||||
Some(ast_map::NodeVariant(..)) | Some(ast_map::NodeStructCtor(..)) => false,
|
||||
Some(ast_map::NodeVariant(..)) | Some(ast_map::NodeStructCtor(..)) => return (ast::DUMMY_NODE_ID, None),
|
||||
|
||||
// glue, shims, etc
|
||||
None if id == ast::DUMMY_NODE_ID => false,
|
||||
None if id == ast::DUMMY_NODE_ID => return (ast::DUMMY_NODE_ID, None),
|
||||
|
||||
_ => tcx.sess.bug(format!("unexpected variant in has_nested_returns: {}",
|
||||
tcx.map.path_to_string(id)).as_slice())
|
||||
};
|
||||
|
||||
(blk.id, Some(cfg::CFG::new(tcx, &**blk)))
|
||||
}
|
||||
|
||||
fn has_nested_returns(tcx: &ty::ctxt, cfg: &cfg::CFG, blk_id: ast::NodeId) -> bool {
|
||||
for n in cfg.graph.depth_traverse(cfg.entry) {
|
||||
match tcx.map.find(n.id) {
|
||||
Some(ast_map::NodeExpr(ex)) => {
|
||||
if let ast::ExprRet(Some(ref ret_expr)) = ex.node {
|
||||
let mut visitor = FindNestedReturn::new();
|
||||
visit::walk_expr(&mut visitor, &**ret_expr);
|
||||
if visitor.found {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(ast_map::NodeBlock(blk)) if blk.id == blk_id => {
|
||||
let mut visitor = FindNestedReturn::new();
|
||||
visit::walk_expr_opt(&mut visitor, &blk.expr);
|
||||
if visitor.found {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// NB: must keep 4 fns in sync:
|
||||
|
|
@ -1453,7 +1455,12 @@ pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
|
|||
ty::FnDiverging => false
|
||||
};
|
||||
let debug_context = debuginfo::create_function_debug_context(ccx, id, param_substs, llfndecl);
|
||||
let nested_returns = has_nested_returns(ccx.tcx(), id);
|
||||
let (blk_id, cfg) = build_cfg(ccx.tcx(), id);
|
||||
let nested_returns = if let Some(ref cfg) = cfg {
|
||||
has_nested_returns(ccx.tcx(), cfg, blk_id)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let mut fcx = FunctionContext {
|
||||
llfn: llfndecl,
|
||||
|
|
@ -1472,7 +1479,8 @@ pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
|
|||
block_arena: block_arena,
|
||||
ccx: ccx,
|
||||
debug_context: debug_context,
|
||||
scopes: RefCell::new(Vec::new())
|
||||
scopes: RefCell::new(Vec::new()),
|
||||
cfg: cfg
|
||||
};
|
||||
|
||||
if has_env {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ use session::Session;
|
|||
use llvm;
|
||||
use llvm::{ValueRef, BasicBlockRef, BuilderRef, ContextRef};
|
||||
use llvm::{True, False, Bool};
|
||||
use middle::cfg;
|
||||
use middle::def;
|
||||
use middle::infer;
|
||||
use middle::lang_items::LangItem;
|
||||
|
|
@ -264,6 +265,8 @@ pub struct FunctionContext<'a, 'tcx: 'a> {
|
|||
|
||||
// Cleanup scopes.
|
||||
pub scopes: RefCell<Vec<cleanup::CleanupScope<'a, 'tcx>>>,
|
||||
|
||||
pub cfg: Option<cfg::CFG>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> FunctionContext<'a, 'tcx> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue