Skip to content

Commit

Permalink
Separate static item recursion check into its own pass
Browse files Browse the repository at this point in the history
This new pass is run before type checking so that recursive items
are detected beforehand.  This prevents going into an infinite
recursion during type checking when a recursive item is used in
an array type.

As a bonus, use `span_err` instead of `span_fatal` so multiple
errors can be reported.

Closes issue rust-lang#17252
  • Loading branch information
bkoropoff committed Sep 15, 2014
1 parent 21d1f4d commit 83b7cb7
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 56 deletions.
3 changes: 3 additions & 0 deletions src/librustc/driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,9 @@ pub fn phase_3_run_analysis_passes<'tcx>(sess: Session,
let stability_index = time(time_passes, "stability index", (), |_|
stability::Index::build(krate));

time(time_passes, "static item recursion checking", (), |_|
middle::check_static_recursion::check_crate(&sess, krate, &def_map, &ast_map));

let ty_cx = ty::mk_ctxt(sess,
type_arena,
def_map,
Expand Down
1 change: 1 addition & 0 deletions src/librustc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ pub mod middle {
pub mod borrowck;
pub mod cfg;
pub mod check_const;
pub mod check_static_recursion;
pub mod check_loop;
pub mod check_match;
pub mod check_rvalues;
Expand Down
57 changes: 1 addition & 56 deletions src/librustc/middle/check_const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,13 @@
// except according to those terms.


use driver::session::Session;
use middle::def::*;
use middle::resolve;
use middle::ty;
use middle::typeck;
use util::ppaux;

use syntax::ast::*;
use syntax::{ast_util, ast_map};
use syntax::ast_util;
use syntax::visit::Visitor;
use syntax::visit;

Expand Down Expand Up @@ -63,7 +61,6 @@ fn check_item(v: &mut CheckCrateVisitor, it: &Item) {
match it.node {
ItemStatic(_, _, ref ex) => {
v.inside_const(|v| v.visit_expr(&**ex));
check_item_recursion(&v.tcx.sess, &v.tcx.map, &v.tcx.def_map, it);
}
ItemEnum(ref enum_definition, _) => {
for var in (*enum_definition).variants.iter() {
Expand Down Expand Up @@ -214,55 +211,3 @@ fn check_expr(v: &mut CheckCrateVisitor, e: &Expr) {
}
visit::walk_expr(v, e);
}

struct CheckItemRecursionVisitor<'a, 'ast: 'a> {
root_it: &'a Item,
sess: &'a Session,
ast_map: &'a ast_map::Map<'ast>,
def_map: &'a resolve::DefMap,
idstack: Vec<NodeId>
}

// Make sure a const item doesn't recursively refer to itself
// FIXME: Should use the dependency graph when it's available (#1356)
pub fn check_item_recursion<'a>(sess: &'a Session,
ast_map: &'a ast_map::Map,
def_map: &'a resolve::DefMap,
it: &'a Item) {

let mut visitor = CheckItemRecursionVisitor {
root_it: it,
sess: sess,
ast_map: ast_map,
def_map: def_map,
idstack: Vec::new()
};
visitor.visit_item(it);
}

impl<'a, 'ast, 'v> Visitor<'v> for CheckItemRecursionVisitor<'a, 'ast> {
fn visit_item(&mut self, it: &Item) {
if self.idstack.iter().any(|x| x == &(it.id)) {
self.sess.span_fatal(self.root_it.span, "recursive constant");
}
self.idstack.push(it.id);
visit::walk_item(self, it);
self.idstack.pop();
}

fn visit_expr(&mut self, e: &Expr) {
match e.node {
ExprPath(..) => {
match self.def_map.borrow().find(&e.id) {
Some(&DefStatic(def_id, _)) if
ast_util::is_local(def_id) => {
self.visit_item(&*self.ast_map.expect_item(def_id.node));
}
_ => ()
}
},
_ => ()
}
visit::walk_expr(self, e);
}
}
109 changes: 109 additions & 0 deletions src/librustc/middle/check_static_recursion.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright 2014 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// This compiler pass detects static items that refer to themselves
// recursively.

use driver::session::Session;
use middle::resolve;
use middle::def::DefStatic;

use syntax::ast::{Crate, Expr, ExprPath, Item, ItemStatic, NodeId};
use syntax::{ast_util, ast_map};
use syntax::visit::Visitor;
use syntax::visit;

struct CheckCrateVisitor<'a, 'ast: 'a> {
sess: &'a Session,
def_map: &'a resolve::DefMap,
ast_map: &'a ast_map::Map<'ast>
}

impl<'v, 'a, 'ast> Visitor<'v> for CheckCrateVisitor<'a, 'ast> {
fn visit_item(&mut self, i: &Item) {
check_item(self, i);
}
}

pub fn check_crate<'ast>(sess: &Session,
krate: &Crate,
def_map: &resolve::DefMap,
ast_map: &ast_map::Map<'ast>) {
let mut visitor = CheckCrateVisitor {
sess: sess,
def_map: def_map,
ast_map: ast_map
};
visit::walk_crate(&mut visitor, krate);
sess.abort_if_errors();
}

fn check_item(v: &mut CheckCrateVisitor, it: &Item) {
match it.node {
ItemStatic(_, _, ref ex) => {
check_item_recursion(v.sess, v.ast_map, v.def_map, it);
visit::walk_expr(v, &**ex)
},
_ => visit::walk_item(v, it)
}
}

struct CheckItemRecursionVisitor<'a, 'ast: 'a> {
root_it: &'a Item,
sess: &'a Session,
ast_map: &'a ast_map::Map<'ast>,
def_map: &'a resolve::DefMap,
idstack: Vec<NodeId>
}

// Make sure a const item doesn't recursively refer to itself
// FIXME: Should use the dependency graph when it's available (#1356)
pub fn check_item_recursion<'a>(sess: &'a Session,
ast_map: &'a ast_map::Map,
def_map: &'a resolve::DefMap,
it: &'a Item) {

let mut visitor = CheckItemRecursionVisitor {
root_it: it,
sess: sess,
ast_map: ast_map,
def_map: def_map,
idstack: Vec::new()
};
visitor.visit_item(it);
}

impl<'a, 'ast, 'v> Visitor<'v> for CheckItemRecursionVisitor<'a, 'ast> {
fn visit_item(&mut self, it: &Item) {
if self.idstack.iter().any(|x| x == &(it.id)) {
self.sess.span_err(self.root_it.span, "recursive constant");
return;
}
self.idstack.push(it.id);
visit::walk_item(self, it);
self.idstack.pop();
}

fn visit_expr(&mut self, e: &Expr) {
match e.node {
ExprPath(..) => {
match self.def_map.borrow().find(&e.id) {
Some(&DefStatic(def_id, _)) if
ast_util::is_local(def_id) => {
self.visit_item(&*self.ast_map.expect_item(def_id.node));
}
_ => ()
}
},
_ => ()
}
visit::walk_expr(self, e);
}
}

0 comments on commit 83b7cb7

Please sign in to comment.