Skip to content

Commit 83b7cb7

Browse files
committed
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
1 parent 21d1f4d commit 83b7cb7

File tree

4 files changed

+114
-56
lines changed

4 files changed

+114
-56
lines changed

src/librustc/driver/driver.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,9 @@ pub fn phase_3_run_analysis_passes<'tcx>(sess: Session,
394394
let stability_index = time(time_passes, "stability index", (), |_|
395395
stability::Index::build(krate));
396396

397+
time(time_passes, "static item recursion checking", (), |_|
398+
middle::check_static_recursion::check_crate(&sess, krate, &def_map, &ast_map));
399+
397400
let ty_cx = ty::mk_ctxt(sess,
398401
type_arena,
399402
def_map,

src/librustc/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ pub mod middle {
8080
pub mod borrowck;
8181
pub mod cfg;
8282
pub mod check_const;
83+
pub mod check_static_recursion;
8384
pub mod check_loop;
8485
pub mod check_match;
8586
pub mod check_rvalues;

src/librustc/middle/check_const.rs

Lines changed: 1 addition & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,13 @@
99
// except according to those terms.
1010

1111

12-
use driver::session::Session;
1312
use middle::def::*;
14-
use middle::resolve;
1513
use middle::ty;
1614
use middle::typeck;
1715
use util::ppaux;
1816

1917
use syntax::ast::*;
20-
use syntax::{ast_util, ast_map};
18+
use syntax::ast_util;
2119
use syntax::visit::Visitor;
2220
use syntax::visit;
2321

@@ -63,7 +61,6 @@ fn check_item(v: &mut CheckCrateVisitor, it: &Item) {
6361
match it.node {
6462
ItemStatic(_, _, ref ex) => {
6563
v.inside_const(|v| v.visit_expr(&**ex));
66-
check_item_recursion(&v.tcx.sess, &v.tcx.map, &v.tcx.def_map, it);
6764
}
6865
ItemEnum(ref enum_definition, _) => {
6966
for var in (*enum_definition).variants.iter() {
@@ -214,55 +211,3 @@ fn check_expr(v: &mut CheckCrateVisitor, e: &Expr) {
214211
}
215212
visit::walk_expr(v, e);
216213
}
217-
218-
struct CheckItemRecursionVisitor<'a, 'ast: 'a> {
219-
root_it: &'a Item,
220-
sess: &'a Session,
221-
ast_map: &'a ast_map::Map<'ast>,
222-
def_map: &'a resolve::DefMap,
223-
idstack: Vec<NodeId>
224-
}
225-
226-
// Make sure a const item doesn't recursively refer to itself
227-
// FIXME: Should use the dependency graph when it's available (#1356)
228-
pub fn check_item_recursion<'a>(sess: &'a Session,
229-
ast_map: &'a ast_map::Map,
230-
def_map: &'a resolve::DefMap,
231-
it: &'a Item) {
232-
233-
let mut visitor = CheckItemRecursionVisitor {
234-
root_it: it,
235-
sess: sess,
236-
ast_map: ast_map,
237-
def_map: def_map,
238-
idstack: Vec::new()
239-
};
240-
visitor.visit_item(it);
241-
}
242-
243-
impl<'a, 'ast, 'v> Visitor<'v> for CheckItemRecursionVisitor<'a, 'ast> {
244-
fn visit_item(&mut self, it: &Item) {
245-
if self.idstack.iter().any(|x| x == &(it.id)) {
246-
self.sess.span_fatal(self.root_it.span, "recursive constant");
247-
}
248-
self.idstack.push(it.id);
249-
visit::walk_item(self, it);
250-
self.idstack.pop();
251-
}
252-
253-
fn visit_expr(&mut self, e: &Expr) {
254-
match e.node {
255-
ExprPath(..) => {
256-
match self.def_map.borrow().find(&e.id) {
257-
Some(&DefStatic(def_id, _)) if
258-
ast_util::is_local(def_id) => {
259-
self.visit_item(&*self.ast_map.expect_item(def_id.node));
260-
}
261-
_ => ()
262-
}
263-
},
264-
_ => ()
265-
}
266-
visit::walk_expr(self, e);
267-
}
268-
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// This compiler pass detects static items that refer to themselves
12+
// recursively.
13+
14+
use driver::session::Session;
15+
use middle::resolve;
16+
use middle::def::DefStatic;
17+
18+
use syntax::ast::{Crate, Expr, ExprPath, Item, ItemStatic, NodeId};
19+
use syntax::{ast_util, ast_map};
20+
use syntax::visit::Visitor;
21+
use syntax::visit;
22+
23+
struct CheckCrateVisitor<'a, 'ast: 'a> {
24+
sess: &'a Session,
25+
def_map: &'a resolve::DefMap,
26+
ast_map: &'a ast_map::Map<'ast>
27+
}
28+
29+
impl<'v, 'a, 'ast> Visitor<'v> for CheckCrateVisitor<'a, 'ast> {
30+
fn visit_item(&mut self, i: &Item) {
31+
check_item(self, i);
32+
}
33+
}
34+
35+
pub fn check_crate<'ast>(sess: &Session,
36+
krate: &Crate,
37+
def_map: &resolve::DefMap,
38+
ast_map: &ast_map::Map<'ast>) {
39+
let mut visitor = CheckCrateVisitor {
40+
sess: sess,
41+
def_map: def_map,
42+
ast_map: ast_map
43+
};
44+
visit::walk_crate(&mut visitor, krate);
45+
sess.abort_if_errors();
46+
}
47+
48+
fn check_item(v: &mut CheckCrateVisitor, it: &Item) {
49+
match it.node {
50+
ItemStatic(_, _, ref ex) => {
51+
check_item_recursion(v.sess, v.ast_map, v.def_map, it);
52+
visit::walk_expr(v, &**ex)
53+
},
54+
_ => visit::walk_item(v, it)
55+
}
56+
}
57+
58+
struct CheckItemRecursionVisitor<'a, 'ast: 'a> {
59+
root_it: &'a Item,
60+
sess: &'a Session,
61+
ast_map: &'a ast_map::Map<'ast>,
62+
def_map: &'a resolve::DefMap,
63+
idstack: Vec<NodeId>
64+
}
65+
66+
// Make sure a const item doesn't recursively refer to itself
67+
// FIXME: Should use the dependency graph when it's available (#1356)
68+
pub fn check_item_recursion<'a>(sess: &'a Session,
69+
ast_map: &'a ast_map::Map,
70+
def_map: &'a resolve::DefMap,
71+
it: &'a Item) {
72+
73+
let mut visitor = CheckItemRecursionVisitor {
74+
root_it: it,
75+
sess: sess,
76+
ast_map: ast_map,
77+
def_map: def_map,
78+
idstack: Vec::new()
79+
};
80+
visitor.visit_item(it);
81+
}
82+
83+
impl<'a, 'ast, 'v> Visitor<'v> for CheckItemRecursionVisitor<'a, 'ast> {
84+
fn visit_item(&mut self, it: &Item) {
85+
if self.idstack.iter().any(|x| x == &(it.id)) {
86+
self.sess.span_err(self.root_it.span, "recursive constant");
87+
return;
88+
}
89+
self.idstack.push(it.id);
90+
visit::walk_item(self, it);
91+
self.idstack.pop();
92+
}
93+
94+
fn visit_expr(&mut self, e: &Expr) {
95+
match e.node {
96+
ExprPath(..) => {
97+
match self.def_map.borrow().find(&e.id) {
98+
Some(&DefStatic(def_id, _)) if
99+
ast_util::is_local(def_id) => {
100+
self.visit_item(&*self.ast_map.expect_item(def_id.node));
101+
}
102+
_ => ()
103+
}
104+
},
105+
_ => ()
106+
}
107+
visit::walk_expr(self, e);
108+
}
109+
}

0 commit comments

Comments
 (0)