about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBrian Anderson <banderson@mozilla.com>2012-04-04 16:12:57 -0700
committerBrian Anderson <banderson@mozilla.com>2012-04-04 16:12:57 -0700
commit9aa7241f058b1061f877841c2ec5487211e54215 (patch)
tree9e7812e2ad6f987af75b3a6fab436a9016d32620
parent38ed2ea0968db8dbcebb1d1c650b8693ffb8a1dd (diff)
downloadrust-9aa7241f058b1061f877841c2ec5487211e54215.tar.gz
rust-9aa7241f058b1061f877841c2ec5487211e54215.zip
rustc: Don't allow recursive constants
-rw-r--r--src/rustc/driver/driver.rs2
-rw-r--r--src/rustc/middle/check_const.rs72
-rw-r--r--src/test/compile-fail/const-recursive.rs6
3 files changed, 74 insertions, 6 deletions
diff --git a/src/rustc/driver/driver.rs b/src/rustc/driver/driver.rs
index 1b0b4a52b40..b467c396563 100644
--- a/src/rustc/driver/driver.rs
+++ b/src/rustc/driver/driver.rs
@@ -149,7 +149,7 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg,
         time(time_passes, "typechecking",
              bind typeck::check_crate(ty_cx, impl_map, crate));
     time(time_passes, "const checking",
-         bind middle::check_const::check_crate(sess, crate, def_map,
+         bind middle::check_const::check_crate(sess, crate, ast_map, def_map,
                                                method_map, ty_cx));
 
     if upto == cu_typeck { ret {crate: crate, tcx: some(ty_cx)}; }
diff --git a/src/rustc/middle/check_const.rs b/src/rustc/middle/check_const.rs
index caae792581b..29a1864c024 100644
--- a/src/rustc/middle/check_const.rs
+++ b/src/rustc/middle/check_const.rs
@@ -3,10 +3,11 @@ import syntax::{visit, ast_util};
 import driver::session::session;
 import std::map::hashmap;
 
-fn check_crate(sess: session, crate: @crate, def_map: resolve::def_map,
+fn check_crate(sess: session, crate: @crate, ast_map: ast_map::map,
+               def_map: resolve::def_map,
                 method_map: typeck::method_map, tcx: ty::ctxt) {
     visit::visit_crate(*crate, false, visit::mk_vt(@{
-        visit_item: check_item,
+        visit_item: check_item(sess, ast_map, def_map, _, _, _),
         visit_pat: check_pat,
         visit_expr: bind check_expr(sess, def_map, method_map, tcx, _, _, _)
         with *visit::default_visitor()
@@ -14,9 +15,13 @@ fn check_crate(sess: session, crate: @crate, def_map: resolve::def_map,
     sess.abort_if_errors();
 }
 
-fn check_item(it: @item, &&_is_const: bool, v: visit::vt<bool>) {
+fn check_item(sess: session, ast_map: ast_map::map, def_map: resolve::def_map,
+              it: @item, &&_is_const: bool, v: visit::vt<bool>) {
     alt it.node {
-      item_const(_, ex) { v.visit_expr(ex, true, v); }
+      item_const(_, ex) {
+        v.visit_expr(ex, true, v);
+        check_item_recursion(sess, ast_map, def_map, it);
+      }
       item_enum(vs, _) {
         for var in vs {
             option::with_option_do(var.node.disr_expr) {|ex|
@@ -73,7 +78,7 @@ fn check_expr(sess: session, def_map: resolve::def_map,
                               "` in a constant expression");
             }
           }
-          expr_path(path) {
+          expr_path(_) {
             alt def_map.find(e.id) {
               some(def_const(def_id)) {
                 if !ast_util::is_local(def_id) {
@@ -115,6 +120,63 @@ fn check_expr(sess: session, def_map: resolve::def_map,
     visit::visit_expr(e, is_const, v);
 }
 
+// Make sure a const item doesn't recursively refer to itself
+// FIXME: Should use the dependency graph when it's available
+fn check_item_recursion(sess: session, ast_map: ast_map::map,
+                        def_map: resolve::def_map, it: @item) {
+
+    type env = {
+        root_it: @item,
+        sess: session,
+        ast_map: ast_map::map,
+        def_map: resolve::def_map,
+        idstack: @mut [node_id],
+    };
+
+    let env = {
+        root_it: it,
+        sess: sess,
+        ast_map: ast_map,
+        def_map: def_map,
+        idstack: @mut []
+    };
+
+    let visitor = visit::mk_vt(@{
+        visit_item: visit_item,
+        visit_expr: visit_expr
+        with *visit::default_visitor()
+    });
+    visitor.visit_item(it, env, visitor);
+
+    fn visit_item(it: @item, &&env: env, v: visit::vt<env>) {
+        if (*env.idstack).contains(it.id) {
+            env.sess.span_fatal(env.root_it.span, "recursive constant");
+        }
+        vec::push(*env.idstack, it.id);
+        visit::visit_item(it, env, v);
+        vec::pop(*env.idstack);
+    }
+
+    fn visit_expr(e: @expr, &&env: env, v: visit::vt<env>) {
+        alt e.node {
+          expr_path(path) {
+            alt env.def_map.find(e.id) {
+              some(def_const(def_id)) {
+                alt check env.ast_map.get(def_id.node) {
+                  ast_map::node_item(it, _) {
+                    v.visit_item(it, env, v);
+                  }
+                }
+              }
+              _ { }
+            }
+          }
+          _ { }
+        }
+        visit::visit_expr(e, env, v);
+    }
+}
+
 // Local Variables:
 // mode: rust
 // fill-column: 78;
diff --git a/src/test/compile-fail/const-recursive.rs b/src/test/compile-fail/const-recursive.rs
new file mode 100644
index 00000000000..e242948b322
--- /dev/null
+++ b/src/test/compile-fail/const-recursive.rs
@@ -0,0 +1,6 @@
+// error-pattern: recursive constant
+const a: int = b;
+const b: int = a;
+
+fn main() {
+}
\ No newline at end of file