Skip to content

Commit 77723d2

Browse files
committed
Implement an iterator for walking types rather than the old callback code.
1 parent 39d7402 commit 77723d2

File tree

4 files changed

+218
-48
lines changed

4 files changed

+218
-48
lines changed

src/librustc/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ pub mod middle {
9898
pub mod traits;
9999
pub mod ty;
100100
pub mod ty_fold;
101+
pub mod ty_walk;
101102
pub mod weak_lang_items;
102103
}
103104

src/librustc/middle/ty.rs

Lines changed: 51 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ use middle::subst::{mod, Subst, Substs, VecPerParamSpace};
5959
use middle::traits;
6060
use middle::ty;
6161
use middle::ty_fold::{mod, TypeFoldable, TypeFolder};
62+
use middle::ty_walk::TypeWalker;
6263
use util::ppaux::{note_and_explain_region, bound_region_ptr_to_string};
6364
use util::ppaux::{trait_store_to_string, ty_to_string};
6465
use util::ppaux::{Repr, UserString};
@@ -2806,55 +2807,59 @@ pub fn mk_param_from_def<'tcx>(cx: &ctxt<'tcx>, def: &TypeParameterDef) -> Ty<'t
28062807

28072808
pub fn mk_open<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { mk_t(cx, ty_open(ty)) }
28082809

2809-
pub fn walk_ty<'tcx, F>(ty: Ty<'tcx>, mut f: F) where
2810-
F: FnMut(Ty<'tcx>),
2810+
impl<'tcx> TyS<'tcx> {
2811+
/// Iterator that walks `self` and any types reachable from
2812+
/// `self`, in depth-first order. Note that just walks the types
2813+
/// that appear in `self`, it does not descend into the fields of
2814+
/// structs or variants. For example:
2815+
///
2816+
/// ```notrust
2817+
/// int => { int }
2818+
/// Foo<Bar<int>> => { Foo<Bar<int>>, Bar<int>, int }
2819+
/// [int] => { [int], int }
2820+
/// ```
2821+
pub fn walk(&'tcx self) -> TypeWalker<'tcx> {
2822+
TypeWalker::new(self)
2823+
}
2824+
2825+
/// Iterator that walks types reachable from `self`, in
2826+
/// depth-first order. Note that this is a shallow walk. For
2827+
/// example:
2828+
///
2829+
/// ```notrust
2830+
/// int => { }
2831+
/// Foo<Bar<int>> => { Bar<int>, int }
2832+
/// [int] => { int }
2833+
/// ```
2834+
pub fn walk_children(&'tcx self) -> TypeWalker<'tcx> {
2835+
// Walks type reachable from `self` but not `self
2836+
let mut walker = self.walk();
2837+
let r = walker.next();
2838+
assert_eq!(r, Some(self));
2839+
walker
2840+
}
2841+
}
2842+
2843+
pub fn walk_ty<'tcx, F>(ty_root: Ty<'tcx>, mut f: F)
2844+
where F: FnMut(Ty<'tcx>),
28112845
{
2812-
maybe_walk_ty(ty, |ty| { f(ty); true });
2846+
for ty in ty_root.walk() {
2847+
f(ty);
2848+
}
28132849
}
28142850

2815-
pub fn maybe_walk_ty<'tcx, F>(ty: Ty<'tcx>, mut f: F) where F: FnMut(Ty<'tcx>) -> bool {
2816-
// FIXME(#19596) This is a workaround, but there should be a better way to do this
2817-
fn maybe_walk_ty_<'tcx, F>(ty: Ty<'tcx>, f: &mut F) where F: FnMut(Ty<'tcx>) -> bool {
2818-
if !(*f)(ty) {
2819-
return;
2820-
}
2821-
match ty.sty {
2822-
ty_bool | ty_char | ty_int(_) | ty_uint(_) | ty_float(_) |
2823-
ty_str | ty_infer(_) | ty_param(_) | ty_err => {}
2824-
ty_uniq(ty) | ty_vec(ty, _) | ty_open(ty) => maybe_walk_ty_(ty, f),
2825-
ty_ptr(ref tm) | ty_rptr(_, ref tm) => {
2826-
maybe_walk_ty_(tm.ty, f);
2827-
}
2828-
ty_trait(box TyTrait { ref principal, .. }) => {
2829-
for subty in principal.0.substs.types.iter() {
2830-
maybe_walk_ty_(*subty, f);
2831-
}
2832-
}
2833-
ty_projection(ProjectionTy { ref trait_ref, .. }) => {
2834-
for subty in trait_ref.substs.types.iter() {
2835-
maybe_walk_ty_(*subty, f);
2836-
}
2837-
}
2838-
ty_enum(_, ref substs) |
2839-
ty_struct(_, ref substs) |
2840-
ty_unboxed_closure(_, _, ref substs) => {
2841-
for subty in substs.types.iter() {
2842-
maybe_walk_ty_(*subty, f);
2843-
}
2844-
}
2845-
ty_tup(ref ts) => { for tt in ts.iter() { maybe_walk_ty_(*tt, f); } }
2846-
ty_bare_fn(_, ref ft) => {
2847-
for a in ft.sig.0.inputs.iter() { maybe_walk_ty_(*a, f); }
2848-
if let ty::FnConverging(output) = ft.sig.0.output {
2849-
maybe_walk_ty_(output, f);
2850-
}
2851-
}
2852-
ty_closure(ref ft) => {
2853-
for a in ft.sig.0.inputs.iter() { maybe_walk_ty_(*a, f); }
2854-
if let ty::FnConverging(output) = ft.sig.0.output {
2855-
maybe_walk_ty_(output, f);
2856-
}
2857-
}
2851+
/// Walks `ty` and any types appearing within `ty`, invoking the
2852+
/// callback `f` on each type. If the callback returns false, then the
2853+
/// children of the current type are ignored.
2854+
///
2855+
/// Note: prefer `ty.walk()` where possible.
2856+
pub fn maybe_walk_ty<'tcx,F>(ty_root: Ty<'tcx>, mut f: F)
2857+
where F : FnMut(Ty<'tcx>) -> bool
2858+
{
2859+
let mut walker = ty_root.walk();
2860+
while let Some(ty) = walker.next() {
2861+
if !f(ty) {
2862+
walker.skip_current_subtree();
28582863
}
28592864
}
28602865

src/librustc/middle/ty_walk.rs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Copyright 2012-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+
//! An iterator over the type substructure.
12+
13+
use middle::ty::{mod, Ty};
14+
use std::iter::Iterator;
15+
16+
pub struct TypeWalker<'tcx> {
17+
stack: Vec<Ty<'tcx>>,
18+
last_subtree: uint,
19+
}
20+
21+
impl<'tcx> TypeWalker<'tcx> {
22+
pub fn new(ty: Ty<'tcx>) -> TypeWalker<'tcx> {
23+
TypeWalker { stack: vec!(ty), last_subtree: 1, }
24+
}
25+
26+
fn push_subtypes(&mut self, parent_ty: Ty<'tcx>) {
27+
match parent_ty.sty {
28+
ty::ty_bool | ty::ty_char | ty::ty_int(_) | ty::ty_uint(_) | ty::ty_float(_) |
29+
ty::ty_str | ty::ty_infer(_) | ty::ty_param(_) | ty::ty_err => {
30+
}
31+
ty::ty_uniq(ty) | ty::ty_vec(ty, _) | ty::ty_open(ty) => {
32+
self.stack.push(ty);
33+
}
34+
ty::ty_ptr(ref mt) | ty::ty_rptr(_, ref mt) => {
35+
self.stack.push(mt.ty);
36+
}
37+
ty::ty_projection(ref data) => {
38+
self.push_reversed(data.trait_ref.substs.types.as_slice());
39+
}
40+
ty::ty_trait(box ty::TyTrait { ref principal, .. }) => {
41+
self.push_reversed(principal.substs().types.as_slice());
42+
}
43+
ty::ty_enum(_, ref substs) |
44+
ty::ty_struct(_, ref substs) |
45+
ty::ty_unboxed_closure(_, _, ref substs) => {
46+
self.push_reversed(substs.types.as_slice());
47+
}
48+
ty::ty_tup(ref ts) => {
49+
self.push_reversed(ts.as_slice());
50+
}
51+
ty::ty_bare_fn(_, ref ft) => {
52+
self.push_sig_subtypes(&ft.sig);
53+
}
54+
ty::ty_closure(ref ft) => {
55+
self.push_sig_subtypes(&ft.sig);
56+
}
57+
}
58+
}
59+
60+
fn push_sig_subtypes(&mut self, sig: &ty::PolyFnSig<'tcx>) {
61+
match sig.0.output {
62+
ty::FnConverging(output) => { self.stack.push(output); }
63+
ty::FnDiverging => { }
64+
}
65+
self.push_reversed(sig.0.inputs.as_slice());
66+
}
67+
68+
fn push_reversed(&mut self, tys: &[Ty<'tcx>]) {
69+
// We push slices on the stack in reverse order so as to
70+
// maintain a pre-order traversal. As of the time of this
71+
// writing, the fact that the traversal is pre-order is not
72+
// known to be significant to any code, but it seems like the
73+
// natural order one would expect (basically, the order of the
74+
// types as they are written).
75+
for &ty in tys.iter().rev() {
76+
self.stack.push(ty);
77+
}
78+
}
79+
80+
/// Skips the subtree of types corresponding to the last type
81+
/// returned by `next()`.
82+
///
83+
/// Example: Imagine you are walking `Foo<Bar<int>, uint>`.
84+
///
85+
/// ```rust
86+
/// let mut iter: TypeWalker = ...;
87+
/// iter.next(); // yields Foo
88+
/// iter.next(); // yields Bar<int>
89+
/// iter.skip_current_subtree(); // skips int
90+
/// iter.next(); // yields uint
91+
/// ```
92+
pub fn skip_current_subtree(&mut self) {
93+
self.stack.truncate(self.last_subtree);
94+
}
95+
}
96+
97+
impl<'tcx> Iterator<Ty<'tcx>> for TypeWalker<'tcx> {
98+
fn next(&mut self) -> Option<Ty<'tcx>> {
99+
debug!("next(): stack={}", self.stack);
100+
match self.stack.pop() {
101+
None => {
102+
return None;
103+
}
104+
Some(ty) => {
105+
self.last_subtree = self.stack.len();
106+
self.push_subtypes(ty);
107+
debug!("next: stack={}", self.stack);
108+
Some(ty)
109+
}
110+
}
111+
}
112+
}

src/librustc_driver/test.rs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@ use syntax::codemap::{Span, CodeMap, DUMMY_SP};
3434
use syntax::diagnostic::{Level, RenderSpan, Bug, Fatal, Error, Warning, Note, Help};
3535
use syntax::parse::token;
3636

37-
use arena::TypedArena;
38-
3937
struct Env<'a, 'tcx: 'a> {
4038
infcx: &'a infer::InferCtxt<'a, 'tcx>,
4139
}
@@ -831,3 +829,57 @@ fn subst_region_renumber_region() {
831829
assert_eq!(t_substituted, t_expected);
832830
})
833831
}
832+
833+
#[test]
834+
fn walk_ty() {
835+
test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
836+
let tcx = env.infcx.tcx;
837+
let int_ty = tcx.types.int;
838+
let uint_ty = tcx.types.uint;
839+
let tup1_ty = ty::mk_tup(tcx, vec!(int_ty, uint_ty, int_ty, uint_ty));
840+
let tup2_ty = ty::mk_tup(tcx, vec!(tup1_ty, tup1_ty, uint_ty));
841+
let uniq_ty = ty::mk_uniq(tcx, tup2_ty);
842+
let walked: Vec<_> = uniq_ty.walk().collect();
843+
assert_eq!(vec!(uniq_ty,
844+
tup2_ty,
845+
tup1_ty, int_ty, uint_ty, int_ty, uint_ty,
846+
tup1_ty, int_ty, uint_ty, int_ty, uint_ty,
847+
uint_ty),
848+
walked);
849+
})
850+
}
851+
852+
#[test]
853+
fn walk_ty_skip_subtree() {
854+
test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
855+
let tcx = env.infcx.tcx;
856+
let int_ty = tcx.types.int;
857+
let uint_ty = tcx.types.uint;
858+
let tup1_ty = ty::mk_tup(tcx, vec!(int_ty, uint_ty, int_ty, uint_ty));
859+
let tup2_ty = ty::mk_tup(tcx, vec!(tup1_ty, tup1_ty, uint_ty));
860+
let uniq_ty = ty::mk_uniq(tcx, tup2_ty);
861+
862+
// types we expect to see (in order), plus a boolean saying
863+
// whether to skip the subtree.
864+
let mut expected = vec!((uniq_ty, false),
865+
(tup2_ty, false),
866+
(tup1_ty, false),
867+
(int_ty, false),
868+
(uint_ty, false),
869+
(int_ty, false),
870+
(uint_ty, false),
871+
(tup1_ty, true), // skip the int/uint/int/uint
872+
(uint_ty, false));
873+
expected.reverse();
874+
875+
let mut walker = uniq_ty.walk();
876+
while let Some(t) = walker.next() {
877+
debug!("walked to {}", t);
878+
let (expected_ty, skip) = expected.pop().unwrap();
879+
assert_eq!(t, expected_ty);
880+
if skip { walker.skip_current_subtree(); }
881+
}
882+
883+
assert!(expected.is_empty());
884+
})
885+
}

0 commit comments

Comments
 (0)