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:
James Miller 2014-12-16 12:21:08 +13:00
parent 22a9f250b5
commit eee209d9e2
5 changed files with 99 additions and 44 deletions

View file

@ -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;
}
}

View file

@ -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

View file

@ -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);

View file

@ -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 {

View file

@ -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> {