about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBrian Koropoff <bkoropoff@gmail.com>2014-09-14 17:21:25 -0700
committerBrian Koropoff <bkoropoff@gmail.com>2014-09-14 20:56:07 -0700
commit83b7cb7ceb75d9fb4897d2f944baa09d63a34f31 (patch)
tree039fa224e20784e328e8b6a1a5e4948acef45260
parent21d1f4d7c0e637d8ae073798e666b880d31781b1 (diff)
downloadrust-83b7cb7ceb75d9fb4897d2f944baa09d63a34f31.tar.gz
rust-83b7cb7ceb75d9fb4897d2f944baa09d63a34f31.zip
Separate static item recursion check into its own pass
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 #17252
-rw-r--r--src/librustc/driver/driver.rs3
-rw-r--r--src/librustc/lib.rs1
-rw-r--r--src/librustc/middle/check_const.rs57
-rw-r--r--src/librustc/middle/check_static_recursion.rs109
4 files changed, 114 insertions, 56 deletions
diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs
index b80d53922f8..23c10c892a1 100644
--- a/src/librustc/driver/driver.rs
+++ b/src/librustc/driver/driver.rs
@@ -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,
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index fd643a70c7b..c6eee6569a6 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -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;
diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs
index 303961105b5..9b699a240cb 100644
--- a/src/librustc/middle/check_const.rs
+++ b/src/librustc/middle/check_const.rs
@@ -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;
 
@@ -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() {
@@ -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);
-    }
-}
diff --git a/src/librustc/middle/check_static_recursion.rs b/src/librustc/middle/check_static_recursion.rs
new file mode 100644
index 00000000000..b571a18c1ec
--- /dev/null
+++ b/src/librustc/middle/check_static_recursion.rs
@@ -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);
+    }
+}