From 59ff0c7c88d0eb556ea29e3965547fc17e51a0b5 Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Thu, 19 May 2011 17:18:53 -0700 Subject: [PATCH 1/8] Parsing and folding changes for globbed imports. --- src/comp/front/ast.rs | 1 + src/comp/front/parser.rs | 53 +++++++++++++++++++++++++++++++--------- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/src/comp/front/ast.rs b/src/comp/front/ast.rs index 6efda692913a8..7551ac899ad30 100644 --- a/src/comp/front/ast.rs +++ b/src/comp/front/ast.rs @@ -401,6 +401,7 @@ type view_item = spanned[view_item_]; tag view_item_ { view_item_use(ident, vec[@meta_item], def_id, ann); view_item_import(ident, vec[ident], def_id); + view_item_import_glob(vec[ident], def_id); view_item_export(ident); } diff --git a/src/comp/front/parser.rs b/src/comp/front/parser.rs index e56075b534fc4..4974e3c3fc497 100644 --- a/src/comp/front/parser.rs +++ b/src/comp/front/parser.rs @@ -2183,29 +2183,58 @@ fn parse_use(&parser p) -> @ast::view_item { } fn parse_rest_import_name(&parser p, ast::ident first, - option::t[ast::ident] def_ident) + option::t[ast::ident] def_ident) -> @ast::view_item { auto lo = p.get_lo_pos(); let vec[ast::ident] identifiers = [first]; - while (p.peek() != token::SEMI) { - expect(p, token::MOD_SEP); - auto i = parse_ident(p); - identifiers += [i]; + let bool glob = false; + + while (true) { + alt (p.peek()) { + case (token::SEMI) { + p.bump(); + break; + } + case (token::MOD_SEP) { + if (glob) { p.err("cannot path into a glob"); } + p.bump(); + } + case (_) { p.err("expecting '::' or ';'"); } + } + alt (p.peek()) { + case (token::IDENT(_,_)) { + identifiers += [parse_ident(p)]; + } + //the lexer can't tell the different kinds of stars apart ) : + case (token::BINOP(token::STAR)) { + glob = true; + p.bump(); + } + case (_) { p.err("expecting an identifier, or '*'"); } + } } auto hi = p.get_hi_pos(); - p.bump(); - auto defined_id; + auto import_decl; alt (def_ident) { case(some[ast::ident](?i)) { - defined_id = i; + if (glob) { + p.err("globbed imports can't be renamed"); + } + import_decl = ast::view_item_import(i, identifiers, + p.next_def_id()); } case (_) { - auto len = vec::len[ast::ident](identifiers); - defined_id = identifiers.(len - 1u); + if (glob) { + import_decl = ast::view_item_import_glob(identifiers, + p.next_def_id()); + } else { + auto len = vec::len[ast::ident](identifiers); + import_decl = ast::view_item_import(identifiers.(len - 1u), + identifiers, + p.next_def_id()); + } } } - auto import_decl = ast::view_item_import(defined_id, identifiers, - p.next_def_id()); ret @spanned(lo, hi, import_decl); } From 4032095920acbe8fa9b8bf5c888fccefd56a2184 Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Fri, 20 May 2011 15:56:23 -0700 Subject: [PATCH 2/8] Part one of adding globbing to resolve.rs: build up glob links. (Part two will follow those links if non-glob resolution fails.) --- src/comp/middle/resolve.rs | 140 +++++++++++++++++++++++++++++++------ 1 file changed, 119 insertions(+), 21 deletions(-) diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs index 2c5d4a377a0eb..9453f7da941c1 100644 --- a/src/comp/middle/resolve.rs +++ b/src/comp/middle/resolve.rs @@ -74,9 +74,18 @@ tag mod_index_entry { mie_tag_variant(@ast::item /* tag item */, uint /* variant index */); } type mod_index = hashmap[ident,list[mod_index_entry]]; -type indexed_mod = rec(option::t[ast::_mod] m, mod_index index); + +type indexed_mod = rec(option::t[ast::_mod] m, + mod_index index, vec[@i_m] glob_imports); /* native modules can't contain tags, and we don't store their ASTs because we only need to look at them to determine exports, which they can't control.*/ +// It should be safe to use index to memoize lookups of globbed names. + +//FIXME: this only exists because we can't yet write recursive types unless +//the recursion passes through a tag. +tag i_m { + i_m(indexed_mod); +} type crate_map = hashmap[uint,ast::crate_num]; @@ -116,23 +125,29 @@ fn resolve_crate(session sess, @ast::crate crate) -> def_map { ret e.def_map; } + // Locate all modules and imports and index them, so that the next passes can // resolve through them. fn map_crate(&@env e, &ast::crate c) { + + // First, find all the modules, and index the names that they contain + auto cell = @mutable nil[scope]; - auto v = rec(visit_crate_pre = bind push_env_for_crate(cell, _), - visit_crate_post = bind pop_env_for_crate(cell, _), - visit_view_item_pre = bind visit_view_item(e, cell, _), - visit_item_pre = bind visit_item(e, cell, _), - visit_item_post = bind pop_env_for_item(cell, _) - with walk::default_visitor()); + auto index_names = + rec(visit_crate_pre = bind push_env_for_crate(cell, _), + visit_crate_post = bind pop_env_for_crate(cell, _), + visit_view_item_pre = bind index_vi(e, cell, _), + visit_item_pre = bind index_i(e, cell, _), + visit_item_post = bind pop_env_for_item(cell, _) + with walk::default_visitor()); // Register the top-level mod e.mod_map.insert(-1, @rec(m=some(c.node.module), - index=index_mod(c.node.module))); - walk::walk_crate(v, c); + index=index_mod(c.node.module), + glob_imports=vec::empty[@i_m]())); + walk::walk_crate(index_names, c); - fn visit_view_item(@env e, @mutable list[scope] sc, &@ast::view_item i) { + fn index_vi(@env e, @mutable list[scope] sc, &@ast::view_item i) { alt (i.node) { case (ast::view_item_import(_, ?ids, ?defid)) { e.imports.insert(defid._1, todo(i, *sc)); @@ -140,22 +155,65 @@ fn map_crate(&@env e, &ast::crate c) { case (_) {} } } - fn visit_item(@env e, @mutable list[scope] sc, &@ast::item i) { + fn index_i(@env e, @mutable list[scope] sc, &@ast::item i) { push_env_for_item(sc, i); alt (i.node) { case (ast::item_mod(_, ?md, ?defid)) { - auto index = index_mod(md); - e.mod_map.insert(defid._1, @rec(m=some(md), - index=index)); + e.mod_map.insert(defid._1, + @rec(m=some(md), index=index_mod(md), + glob_imports=vec::empty[@i_m]())); } case (ast::item_native_mod(_, ?nmd, ?defid)) { - auto index = index_nmod(nmd); - e.mod_map.insert(defid._1, @rec(m=none[ast::_mod], - index=index)); + e.mod_map.insert(defid._1, + @rec(m=none[ast::_mod], + index=index_nmod(nmd), + glob_imports=vec::empty[@i_m]())); } case (_) {} } } + + // Next, assemble the links for globbed imports. + + let @indexed_mod cur_mod = e.mod_map.get(-1); + + cell = @mutable nil[scope]; + auto link_globs = + rec(visit_crate_pre = bind push_env_for_crate(cell, _), + visit_crate_post = bind pop_env_for_crate(cell, _), + visit_view_item_pre = bind link_glob(e, cell, cur_mod, _), + visit_item_pre = bind enter_i(e, cell, cur_mod, _), + visit_item_post = bind pop_env_for_item(cell, _) + with walk::default_visitor()); + walk::walk_crate(link_globs, c); + + fn enter_i(@env e, @mutable list[scope] sc, @indexed_mod cur_mod, + &@ast::item i) { + push_env_for_item(sc,i); + alt(i.node) { + case (ast::item_mod(_, _, ?defid)) { + cur_mod = e.mod_map.get(defid._1); + } + case (ast::item_native_mod(_, _, ?defid)) { + cur_mod = e.mod_map.get(defid._1); + } + case (_) {} + } + } + + fn link_glob(@env e, @mutable list[scope] sc, @indexed_mod cur_mod, + &@ast::view_item vi) { + alt (vi.node) { + //if it really is a glob import, that is + case (ast::view_item_import_glob(?path, _)) { + cur_mod.glob_imports += + [follow_import(*e, *sc, path, vi.span)]; + } + case (_) {} + } + + } + } fn resolve_imports(&env e) { @@ -312,6 +370,39 @@ fn pop_env_for_arm(@mutable list[scope] sc, &ast::arm p) { *sc = std::list::cdr(*sc); } + +//HERE +fn follow_import(&env e, &list[scope] sc, vec[ident] path, &span sp) + -> @i_m { + auto path_len = vec::len(path); + auto dcur = lookup_in_scope_strict(e, sc, sp, path.(0), ns_module); + auto i = 1u; + while (true) { + if (i == path_len) { break; } + dcur = lookup_in_mod_strict(e, dcur, sp, path.(i), + ns_module, outside); + i += 1u; + } + + alt (dcur) { + case (ast::def_mod(?def_id)) { + //TODO: is this sane? + ret @i_m(*e.mod_map.get(def_id._1)); + } + case (ast::def_native_mod(?def_id)) { + ret @i_m(*e.mod_map.get(def_id._1)); + } + case (_) { + e.sess.span_err(sp, str::connect(path, "::") + + " does not name a module."); + fail; + } + } +} + + + + // Import resolution fn resolve_import(&env e, &@ast::view_item it, &list[scope] sc) { @@ -719,6 +810,9 @@ fn found_view_item(&env e, @ast::view_item vi, namespace ns) case (ast::view_item_import(_, _, ?defid)) { ret lookup_import(e, defid, ns); } + case (ast::view_item_import_glob(_, ?defid)) { + ret none[def]; //TODO: think about this. Is it correct? + } } } @@ -813,12 +907,13 @@ fn lookup_in_local_native_mod(&env e, def_id defid, &ident id, namespace ns) // Module indexing -fn add_to_index[T](&hashmap[ident,list[T]] index, &ident id, &T ent) { +fn add_to_index(&hashmap[ident,list[mod_index_entry]] index, &ident id, + &mod_index_entry ent) { alt (index.find(id)) { - case (none[list[T]]) { - index.insert(id, cons(ent, @nil[T])); + case (none[list[mod_index_entry]]) { + index.insert(id, cons(ent, @nil[mod_index_entry])); } - case (some[list[T]](?prev)) { + case (some[list[mod_index_entry]](?prev)) { index.insert(id, cons(ent, @prev)); } } @@ -835,6 +930,9 @@ fn index_mod(&ast::_mod md) -> mod_index { case(ast::view_item_import(?def_ident,_,_)) { add_to_index(index, def_ident, mie_view_item(it)); } + case(ast::view_item_import_glob(?path,_)) { + //globbed imports have to be resolved lazily. + } case(ast::view_item_export(_)) {} } } From 61d331a09a2b46bb195d901b9ecd970d677901a0 Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Mon, 23 May 2011 18:50:33 -0700 Subject: [PATCH 3/8] Added filter_map. --- src/lib/vec.rs | 20 ++++++++++++++++---- src/test/run-pass/lib-vec.rs | 24 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/lib/vec.rs b/src/lib/vec.rs index f3f06a1dc85db..801894bd0cdd7 100644 --- a/src/lib/vec.rs +++ b/src/lib/vec.rs @@ -220,12 +220,24 @@ fn grow_init_fn_set[T](&array[T] v, uint index, fn()->T init_fn, &T val) { v.(index) = val; } -fn map[T, U](&option::operator[T,U] f, &array[T] v) -> vec[U] { - let vec[U] u = alloc[U](len[T](v)); + +fn map[T, U](&fn(&T) -> U f, &array[T] v) -> vec[U] { + let vec[U] res = alloc[U](len[T](v)); for (T ve in v) { - u += [f(ve)]; + res += [f(ve)]; } - ret u; + ret res; +} + +fn filter_map[T, U](&fn(&T) -> option::t[U] f, &array[T] v) -> vec[U] { + let vec[U] res = []; //TODO does this work these days? + for(T ve in v) { + alt(f(ve)) { + case (some[U](?elt)) { res += [elt]; } + case (none[U]) {} + } + } + ret res; } fn map2[T,U,V](&operator2[T,U,V] f, &array[T] v0, &array[U] v1) -> vec[V] { diff --git a/src/test/run-pass/lib-vec.rs b/src/test/run-pass/lib-vec.rs index af49eacf4c4ca..fb0b0d9a0a94c 100644 --- a/src/test/run-pass/lib-vec.rs +++ b/src/test/run-pass/lib-vec.rs @@ -56,11 +56,35 @@ fn test_map2() { } } +fn test_filter_map() { + fn halve(&int i) -> std::option::t[int] { + if(i % 2 == 0) { + ret std::option::some[int](i / 2); + } else { + ret std::option::none[int]; + } + } + fn halve_for_sure(&int i) -> int { ret i/2; } + + let vec[int] all_even = [0, 2, 8, 6]; + let vec[int] all_odd1 = [1, 7, 3]; + let vec[int] all_odd2 = []; + let vec[int] mix = [9, 2, 6, 7, 1, 0, 0, 3]; + let vec[int] mix_dest = [1, 3, 0, 0]; + + assert(std::vec::filter_map(halve, all_even) == + std::vec::map(halve_for_sure, all_even)); + assert(std::vec::filter_map(halve, all_odd1) == std::vec::empty[int]()); + assert(std::vec::filter_map(halve, all_odd2) == std::vec::empty[int]()); + assert(std::vec::filter_map(halve, mix) == mix_dest); +} + fn main() { test_init_elt(); test_init_fn(); test_slice(); test_map(); test_map2(); + test_filter_map(); } From be00e795e49cee4e157823f509ca718d51d1deb9 Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Mon, 23 May 2011 18:52:55 -0700 Subject: [PATCH 4/8] "import module::*;" now works. --- src/comp/front/ast.rs | 1 - src/comp/middle/resolve.rs | 123 ++++++++++++++----------- src/test/compile-fail/import-glob-0.rs | 24 +++++ src/test/run-pass/import-glob-0.rs | 49 ++++++++++ src/test/run-pass/import-glob-1.rs | 26 ++++++ src/test/run-pass/import-glob-crate.rs | 9 ++ 6 files changed, 177 insertions(+), 55 deletions(-) create mode 100644 src/test/compile-fail/import-glob-0.rs create mode 100644 src/test/run-pass/import-glob-0.rs create mode 100644 src/test/run-pass/import-glob-1.rs create mode 100644 src/test/run-pass/import-glob-crate.rs diff --git a/src/comp/front/ast.rs b/src/comp/front/ast.rs index 7551ac899ad30..f6ad378d5ad31 100644 --- a/src/comp/front/ast.rs +++ b/src/comp/front/ast.rs @@ -1,4 +1,3 @@ -import std::map::hashmap; import std::option; import std::str; import std::vec; diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs index 9453f7da941c1..e44ea78fc45fe 100644 --- a/src/comp/middle/resolve.rs +++ b/src/comp/middle/resolve.rs @@ -76,17 +76,11 @@ tag mod_index_entry { type mod_index = hashmap[ident,list[mod_index_entry]]; type indexed_mod = rec(option::t[ast::_mod] m, - mod_index index, vec[@i_m] glob_imports); + mod_index index, vec[def] glob_imports); /* native modules can't contain tags, and we don't store their ASTs because we only need to look at them to determine exports, which they can't control.*/ // It should be safe to use index to memoize lookups of globbed names. -//FIXME: this only exists because we can't yet write recursive types unless -//the recursion passes through a tag. -tag i_m { - i_m(indexed_mod); -} - type crate_map = hashmap[uint,ast::crate_num]; type def_map = hashmap[uint,def]; @@ -144,7 +138,7 @@ fn map_crate(&@env e, &ast::crate c) { // Register the top-level mod e.mod_map.insert(-1, @rec(m=some(c.node.module), index=index_mod(c.node.module), - glob_imports=vec::empty[@i_m]())); + glob_imports=vec::empty[def]())); walk::walk_crate(index_names, c); fn index_vi(@env e, @mutable list[scope] sc, &@ast::view_item i) { @@ -161,13 +155,13 @@ fn map_crate(&@env e, &ast::crate c) { case (ast::item_mod(_, ?md, ?defid)) { e.mod_map.insert(defid._1, @rec(m=some(md), index=index_mod(md), - glob_imports=vec::empty[@i_m]())); + glob_imports=vec::empty[def]())); } case (ast::item_native_mod(_, ?nmd, ?defid)) { e.mod_map.insert(defid._1, @rec(m=none[ast::_mod], index=index_nmod(nmd), - glob_imports=vec::empty[@i_m]())); + glob_imports=vec::empty[def]())); } case (_) {} } @@ -370,10 +364,8 @@ fn pop_env_for_arm(@mutable list[scope] sc, &ast::arm p) { *sc = std::list::cdr(*sc); } - -//HERE fn follow_import(&env e, &list[scope] sc, vec[ident] path, &span sp) - -> @i_m { + -> def { auto path_len = vec::len(path); auto dcur = lookup_in_scope_strict(e, sc, sp, path.(0), ns_module); auto i = 1u; @@ -385,13 +377,8 @@ fn follow_import(&env e, &list[scope] sc, vec[ident] path, &span sp) } alt (dcur) { - case (ast::def_mod(?def_id)) { - //TODO: is this sane? - ret @i_m(*e.mod_map.get(def_id._1)); - } - case (ast::def_native_mod(?def_id)) { - ret @i_m(*e.mod_map.get(def_id._1)); - } + case (ast::def_mod(?def_id)) { ret dcur; } + case (ast::def_native_mod(?def_id)) { ret dcur; } case (_) { e.sess.span_err(sp, str::connect(path, "::") + " does not name a module."); @@ -430,9 +417,12 @@ fn resolve_import(&env e, &@ast::view_item it, &list[scope] sc) { while (true) { if (i == n_idents - 1u) { register(e, defid, it.span, end_id, - lookup_in_mod(e, dcur, end_id, ns_value, outside), - lookup_in_mod(e, dcur, end_id, ns_type, outside), - lookup_in_mod(e, dcur, end_id, ns_module, outside)); + lookup_in_mod(e, dcur, it.span, end_id, ns_value, + outside), + lookup_in_mod(e, dcur, it.span, end_id, ns_type, + outside), + lookup_in_mod(e, dcur, it.span, end_id, ns_module, + outside)); break; } else { dcur = lookup_in_mod_strict(e, dcur, it.span, ids.(i), @@ -526,12 +516,12 @@ fn def_is_obj_field(&def d) -> bool { fn lookup_in_scope(&env e, list[scope] sc, &span sp, &ident id, namespace ns) -> option::t[def] { - fn in_scope(&env e, &ident id, &scope s, namespace ns) + fn in_scope(&env e, &span sp, &ident id, &scope s, namespace ns) -> option::t[def] { alt (s) { case (scope_crate(?c)) { auto defid = tup(ast::local_crate, -1); - ret lookup_in_local_mod(e, defid, id, ns, inside); + ret lookup_in_local_mod(e, defid, sp, id, ns, inside); } case (scope_item(?it)) { alt (it.node) { @@ -547,10 +537,10 @@ fn lookup_in_scope(&env e, list[scope] sc, &span sp, &ident id, namespace ns) } } case (ast::item_mod(_, _, ?defid)) { - ret lookup_in_local_mod(e, defid, id, ns, inside); + ret lookup_in_local_mod(e, defid, sp, id, ns, inside); } case (ast::item_native_mod(_, ?m, ?defid)) { - ret lookup_in_local_native_mod(e, defid, id, ns); + ret lookup_in_local_native_mod(e, defid, sp, id, ns); } case (ast::item_ty(_, _, ?ty_params, _, _)) { if (ns == ns_type) { @@ -599,7 +589,7 @@ fn lookup_in_scope(&env e, list[scope] sc, &span sp, &ident id, namespace ns) ret none[def]; } case (cons[scope](?hd, ?tl)) { - auto fnd = in_scope(e, id, hd, ns); + auto fnd = in_scope(e, sp, id, hd, ns); if (!option::is_none(fnd)) { auto df = option::get(fnd); if ((left_fn && def_is_local(df)) || @@ -618,6 +608,7 @@ fn lookup_in_scope(&env e, list[scope] sc, &span sp, &ident id, namespace ns) } } e.sess.bug("reached unreachable code in lookup_in_scope"); // sigh + fail; } fn lookup_in_ty_params(&ident id, &vec[ast::ty_param] ty_params) @@ -764,7 +755,7 @@ fn found_def_item(&@ast::item i, namespace ns) -> option::t[def] { fn lookup_in_mod_strict(&env e, def m, &span sp, &ident id, namespace ns, dir dr) -> def { - alt (lookup_in_mod(e, m, id, ns, dr)) { + alt (lookup_in_mod(e, m, sp, id, ns, dr)) { case (none[def]) { unresolved(e, sp, id, ns_name(ns)); fail; @@ -775,10 +766,10 @@ fn lookup_in_mod_strict(&env e, def m, &span sp, &ident id, } } -fn lookup_in_mod(&env e, def m, &ident id, namespace ns, dir dr) +fn lookup_in_mod(&env e, def m, &span sp, &ident id, namespace ns, dir dr) -> option::t[def] { auto defid = ast::def_id_of_def(m); - if (defid._0 != ast::local_crate) { // Not in this crate + if (defid._0 != ast::local_crate) { // examining a mod. in an ext. crate auto cached = e.ext_cache.find(tup(defid,id,ns)); if (!option::is_none(cached)) { ret cached; } auto path = [id]; @@ -793,10 +784,10 @@ fn lookup_in_mod(&env e, def m, &ident id, namespace ns, dir dr) } alt (m) { case (ast::def_mod(?defid)) { - ret lookup_in_local_mod(e, defid, id, ns, dr); + ret lookup_in_local_mod(e, defid, sp, id, ns, dr); } case (ast::def_native_mod(?defid)) { - ret lookup_in_local_native_mod(e, defid, id, ns); + ret lookup_in_local_native_mod(e, defid, sp, id, ns); } } } @@ -811,7 +802,7 @@ fn found_view_item(&env e, @ast::view_item vi, namespace ns) ret lookup_import(e, defid, ns); } case (ast::view_item_import_glob(_, ?defid)) { - ret none[def]; //TODO: think about this. Is it correct? + ret none[def]; //will be handled in the fallback glob pass } } } @@ -834,29 +825,53 @@ fn lookup_import(&env e, def_id defid, namespace ns) -> option::t[def] { fail; } -fn lookup_in_local_mod(&env e, def_id defid, &ident id, namespace ns, - dir dr) -> option::t[def] { +fn lookup_in_local_mod(&env e, def_id defid, &span sp, &ident id, + namespace ns, dir dr) -> option::t[def] { auto info = e.mod_map.get(defid._1); - auto found = info.index.find(id); - if (option::is_none(found) || - (dr == outside && !ast::is_exported(id, option::get(info.m)))) { + if (dr == outside && !ast::is_exported(id, option::get(info.m))) { // if we're in a native mod, then dr==inside, so info.m is some _mod - ret none[def]; + ret none[def]; // name is not visible } - auto lst = option::get(found); - while (true) { - alt (lst) { - case (nil[mod_index_entry]) { - ret none[def]; - } - case (cons[mod_index_entry](?hd, ?tl)) { - auto found = lookup_in_mie(e, hd, ns); - if (!option::is_none(found)) { ret found; } - lst = *tl; + alt(info.index.find(id)) { + case (none[list[mod_index_entry]]) { } + case (some[list[mod_index_entry]](?lst)) { + while (true) { + alt (lst) { + case (nil[mod_index_entry]) { break; } + case (cons[mod_index_entry](?hd, ?tl)) { + auto found = lookup_in_mie(e, hd, ns); + if (!option::is_none(found)) { ret found; } + lst = *tl; + } + } } } } - e.sess.bug("reached unreachable code in lookup_in_regular_mod"); // sigh + // not local or explicitly imported; try globs: + ret lookup_glob_in_mod(e, info, sp, id, ns, dr); +} + +fn lookup_glob_in_mod(&env e, @indexed_mod info, &span sp, &ident id, + namespace ns, dir dr) -> option::t[def] { + fn l_i_m(&env e, &def d, &span sp, &ident id, namespace ns, dir dr) + -> option::t[def] { + ret lookup_in_mod(e, d, sp, id, ns, dr); + } + auto matches = vec::filter_map + (bind l_i_m(e, _, sp, id, ns, dr), info.glob_imports); + if (vec::len(matches) == 0u) { + ret none[def]; + } else if (vec::len(matches) == 1u){ + ret some[def](matches.(0)); + } else { + for (def d in matches) { + e.sess.span_note(sp, "'" + id + "' is defined at " + + util::common::istr(ast::def_id_of_def(d)._1)); + } + e.sess.span_err(sp, "'" + id + "' is glob-imported from" + + " multiple different modules."); + fail; + } } fn lookup_in_mie(&env e, &mod_index_entry mie, namespace ns) @@ -899,9 +914,9 @@ fn lookup_in_mie(&env e, &mod_index_entry mie, namespace ns) ret none[def]; } -fn lookup_in_local_native_mod(&env e, def_id defid, &ident id, namespace ns) - -> option::t[def] { - ret lookup_in_local_mod(e, defid, id, ns, inside); +fn lookup_in_local_native_mod(&env e, def_id defid, &span sp, &ident id, + namespace ns) -> option::t[def] { + ret lookup_in_local_mod(e, defid, sp, id, ns, inside); } diff --git a/src/test/compile-fail/import-glob-0.rs b/src/test/compile-fail/import-glob-0.rs new file mode 100644 index 0000000000000..259212f7e1a2e --- /dev/null +++ b/src/test/compile-fail/import-glob-0.rs @@ -0,0 +1,24 @@ +mod module_of_many_things { + export f1, f2, f4; + fn f1() { + log "f1"; + } + fn f2() { + log "f2"; + } + fn f3() { + log "f3"; + } + fn f4() { + log "f4"; + } +} + +import module_of_many_things::*; + +fn main() { + f1(); + f2(); + f3(); + f4(); +} \ No newline at end of file diff --git a/src/test/run-pass/import-glob-0.rs b/src/test/run-pass/import-glob-0.rs new file mode 100644 index 0000000000000..d52c24641adaa --- /dev/null +++ b/src/test/run-pass/import-glob-0.rs @@ -0,0 +1,49 @@ +import module_of_many_things::*; +import dug::too::greedily::and::too::deep::*; + +mod module_of_many_things { + export f1; + export f2; + export f4; + fn f1() { + log "f1"; + } + fn f2() { + log "f2"; + } + fn f3() { + log "f3"; + } + fn f4() { + log "f4"; + } +} + +mod dug { + mod too { + mod greedily { + mod and { + mod too { + mod deep { + fn nameless_fear() { + log "Boo!"; + } + fn also_redstone() { + log "Whatever."; + } + fn f1() {} + } + } + } + } + } +} + + +fn main() { + f1(); + f2(); + f4(); + nameless_fear(); + also_redstone(); +} \ No newline at end of file diff --git a/src/test/run-pass/import-glob-1.rs b/src/test/run-pass/import-glob-1.rs new file mode 100644 index 0000000000000..d02ca19f6d5e8 --- /dev/null +++ b/src/test/run-pass/import-glob-1.rs @@ -0,0 +1,26 @@ +import a1:b1::word_traveler; + +mod a1 { // + mod b1 { // + import a2::b1::*; // <-\ + } // | + mod b2 { // | + import a2::b2::*; // <-\ -\ | + } // | | | +} // | | | + // | | | +mod a2 { // | | | + native mod b1 { // | | | + import a1::b2::*; // | <-/ -/ + } // | + mod b2 { // | + fn word_traveler() { // | + log "ahoy!"; // -/ + } // + } // +} // + + +fn main() { + word_traveler(); +} \ No newline at end of file diff --git a/src/test/run-pass/import-glob-crate.rs b/src/test/run-pass/import-glob-crate.rs new file mode 100644 index 0000000000000..8c852f413f5b5 --- /dev/null +++ b/src/test/run-pass/import-glob-crate.rs @@ -0,0 +1,9 @@ + +use std; +import std::vec::*; + +fn main() { + auto v = empty[int](); + v += [4,2]; + assert(reversed(v) == [2,4]); +} \ No newline at end of file From ae56cb1d49c5b0def4c27df8328aff829d347681 Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Wed, 25 May 2011 09:54:43 -0700 Subject: [PATCH 5/8] Add list function 'has'. --- src/lib/list.rs | 13 +++++++++++++ src/test/run-pass/lib-list.rs | 12 ++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/lib/list.rs b/src/lib/list.rs index 885d65faf226d..a97a19cd9c64d 100644 --- a/src/lib/list.rs +++ b/src/lib/list.rs @@ -52,6 +52,19 @@ fn find[T,U](&list[T] ls, } } +fn has[T](&list[T] ls, &T elt) -> bool { + alt(ls) { + case (cons[T](?hd, ?tl)) { + if (elt == hd) { + ret true; + } else { + be has(*tl, elt); + } + } + case (nil[T]) { ret false; } + } +} + fn length[T](&list[T] ls) -> uint { fn count[T](&T t, &uint u) -> uint { ret u + 1u; diff --git a/src/test/run-pass/lib-list.rs b/src/test/run-pass/lib-list.rs index caf80e84edebc..0cd1ae1a9f558 100644 --- a/src/test/run-pass/lib-list.rs +++ b/src/test/run-pass/lib-list.rs @@ -43,6 +43,17 @@ fn test_find_fail() { assert (res == option::none[int]); } +fn test_has() { + auto l = from_vec([5, 8 ,6]); + auto empty = list::nil[int]; + + assert (list::has(l, 5)); + assert (!list::has(l, 7)); + assert (list::has(l, 8)); + + assert (!list::has(empty, 5)); +} + fn test_length() { auto l = from_vec([0, 1, 2]); assert (list::length(l) == 3u); @@ -54,4 +65,5 @@ fn main() { test_find_success(); test_find_fail(); test_length(); + test_has(); } From 632bc1d3925259209e9142cb865686dc505f5592 Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Wed, 25 May 2011 10:10:30 -0700 Subject: [PATCH 6/8] Fix bugs: make sure glob imports show up in the right module, and make sure that circular glob imports don't diverge. --- src/comp/middle/resolve.rs | 157 ++++++++++-------- src/test/compile-fail/import-glob-0.rs | 12 +- src/test/compile-fail/import-glob-circular.rs | 29 ++++ src/test/run-pass/import-glob-0.rs | 1 - src/test/run-pass/import-glob-1.rs | 2 +- src/test/run-pass/import-glob-circular.rs | 47 ++++++ 6 files changed, 178 insertions(+), 70 deletions(-) create mode 100644 src/test/compile-fail/import-glob-circular.rs create mode 100644 src/test/run-pass/import-glob-circular.rs diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs index e44ea78fc45fe..f0ae03925aa56 100644 --- a/src/comp/middle/resolve.rs +++ b/src/comp/middle/resolve.rs @@ -12,6 +12,7 @@ import util::common::new_str_hash; import util::common::span; import middle::tstate::ann::ts_ann; import std::map::hashmap; +import std::list; import std::list::list; import std::list::nil; import std::list::cons; @@ -168,40 +169,43 @@ fn map_crate(&@env e, &ast::crate c) { } // Next, assemble the links for globbed imports. - - let @indexed_mod cur_mod = e.mod_map.get(-1); - cell = @mutable nil[scope]; - auto link_globs = + auto link_globs = rec(visit_crate_pre = bind push_env_for_crate(cell, _), visit_crate_post = bind pop_env_for_crate(cell, _), - visit_view_item_pre = bind link_glob(e, cell, cur_mod, _), - visit_item_pre = bind enter_i(e, cell, cur_mod, _), + visit_view_item_pre = bind link_glob(e, cell, _), + visit_item_pre = bind push_env_for_item(cell, _), visit_item_post = bind pop_env_for_item(cell, _) with walk::default_visitor()); walk::walk_crate(link_globs, c); - - fn enter_i(@env e, @mutable list[scope] sc, @indexed_mod cur_mod, - &@ast::item i) { - push_env_for_item(sc,i); - alt(i.node) { - case (ast::item_mod(_, _, ?defid)) { - cur_mod = e.mod_map.get(defid._1); - } - case (ast::item_native_mod(_, _, ?defid)) { - cur_mod = e.mod_map.get(defid._1); + + fn link_glob(@env e, @mutable list[scope] sc, &@ast::view_item vi) { + fn find_mod(@env e, list[scope] sc) -> @indexed_mod { + alt (sc) { + case (cons[scope](scope_item(?i), ?tl)) { + alt(i.node) { + case (ast::item_mod(_, _, ?defid)) { + ret e.mod_map.get(defid._1); + } + case (ast::item_native_mod(_, _, ?defid)) { + ret e.mod_map.get(defid._1); + } + case (_) { + be find_mod(e, *tl); + } + } + } + case (_) { + ret e.mod_map.get(-1); //top-level + } } - case (_) {} } - } - - fn link_glob(@env e, @mutable list[scope] sc, @indexed_mod cur_mod, - &@ast::view_item vi) { + alt (vi.node) { //if it really is a glob import, that is case (ast::view_item_import_glob(?path, _)) { - cur_mod.glob_imports += - [follow_import(*e, *sc, path, vi.span)]; + find_mod(e, *sc).glob_imports + += [follow_import(*e, *sc, path, vi.span)]; } case (_) {} } @@ -518,10 +522,13 @@ fn lookup_in_scope(&env e, list[scope] sc, &span sp, &ident id, namespace ns) -> option::t[def] { fn in_scope(&env e, &span sp, &ident id, &scope s, namespace ns) -> option::t[def] { + //not recursing through globs + let list[def] no_m = nil[def]; + alt (s) { case (scope_crate(?c)) { auto defid = tup(ast::local_crate, -1); - ret lookup_in_local_mod(e, defid, sp, id, ns, inside); + ret lookup_in_local_mod(e, defid, no_m, sp, id, ns, inside); } case (scope_item(?it)) { alt (it.node) { @@ -537,10 +544,12 @@ fn lookup_in_scope(&env e, list[scope] sc, &span sp, &ident id, namespace ns) } } case (ast::item_mod(_, _, ?defid)) { - ret lookup_in_local_mod(e, defid, sp, id, ns, inside); + ret lookup_in_local_mod(e, defid, no_m, sp, + id, ns, inside); } case (ast::item_native_mod(_, ?m, ?defid)) { - ret lookup_in_local_native_mod(e, defid, sp, id, ns); + ret lookup_in_local_native_mod(e, defid, no_m, + sp, id, ns); } case (ast::item_ty(_, _, ?ty_params, _, _)) { if (ns == ns_type) { @@ -768,26 +777,44 @@ fn lookup_in_mod_strict(&env e, def m, &span sp, &ident id, fn lookup_in_mod(&env e, def m, &span sp, &ident id, namespace ns, dir dr) -> option::t[def] { - auto defid = ast::def_id_of_def(m); - if (defid._0 != ast::local_crate) { // examining a mod. in an ext. crate - auto cached = e.ext_cache.find(tup(defid,id,ns)); - if (!option::is_none(cached)) { ret cached; } - auto path = [id]; - if (defid._1 != -1) { - path = e.ext_map.get(defid) + path; - } - auto fnd = lookup_external(e, defid._0, path, ns); - if (!option::is_none(fnd)) { - e.ext_cache.insert(tup(defid,id,ns), option::get(fnd)); - } - ret fnd; - } + be lookup_in_mod_recursively(e, cons[def](m, @nil[def]), sp, id, ns, dr); +} + +// this list is simply the stack of glob imports we have passed through +// (preventing cyclic glob imports from diverging) +fn lookup_in_mod_recursively(&env e, list[def] m, &span sp, &ident id, + namespace ns, dir dr) -> option::t[def] { alt (m) { - case (ast::def_mod(?defid)) { - ret lookup_in_local_mod(e, defid, sp, id, ns, dr); + case (cons[def](?mod_def, ?tl)) { + if (list::has(*tl, mod_def)) { + ret none[def]; // import glob cycle detected; we're done + } + auto defid = ast::def_id_of_def(mod_def); + if (defid._0 != ast::local_crate) { + // examining a module in an external crate + auto cached = e.ext_cache.find(tup(defid,id,ns)); + if (!option::is_none(cached)) { ret cached; } + auto path = [id]; + if (defid._1 != -1) { + path = e.ext_map.get(defid) + path; + } + auto fnd = lookup_external(e, defid._0, path, ns); + if (!option::is_none(fnd)) { + e.ext_cache.insert(tup(defid,id,ns), option::get(fnd)); + } + ret fnd; + } + alt (mod_def) { + case (ast::def_mod(?defid)) { + ret lookup_in_local_mod(e, defid, m, sp, id, ns, dr); + } + case (ast::def_native_mod(?defid)) { + ret lookup_in_local_native_mod(e, defid, m, sp, id, ns); + } + } } - case (ast::def_native_mod(?defid)) { - ret lookup_in_local_native_mod(e, defid, sp, id, ns); + case (_) { + e.sess.bug("lookup_in_mod_recursively needs a module"); fail; } } } @@ -825,8 +852,14 @@ fn lookup_import(&env e, def_id defid, namespace ns) -> option::t[def] { fail; } -fn lookup_in_local_mod(&env e, def_id defid, &span sp, &ident id, - namespace ns, dir dr) -> option::t[def] { + +fn lookup_in_local_native_mod(&env e, def_id defid, list[def] m, &span sp, + &ident id, namespace ns) -> option::t[def] { + ret lookup_in_local_mod(e, defid, m, sp, id, ns, inside); +} + +fn lookup_in_local_mod(&env e, def_id defid, list[def] m, &span sp, + &ident id, namespace ns, dir dr) -> option::t[def] { auto info = e.mod_map.get(defid._1); if (dr == outside && !ast::is_exported(id, option::get(info.m))) { // if we're in a native mod, then dr==inside, so info.m is some _mod @@ -848,26 +881,24 @@ fn lookup_in_local_mod(&env e, def_id defid, &span sp, &ident id, } } // not local or explicitly imported; try globs: - ret lookup_glob_in_mod(e, info, sp, id, ns, dr); + ret lookup_glob_in_mod(e, info, m, sp, id, ns, dr); } -fn lookup_glob_in_mod(&env e, @indexed_mod info, &span sp, &ident id, - namespace ns, dir dr) -> option::t[def] { - fn l_i_m(&env e, &def d, &span sp, &ident id, namespace ns, dir dr) - -> option::t[def] { - ret lookup_in_mod(e, d, sp, id, ns, dr); +fn lookup_glob_in_mod(&env e, @indexed_mod info, list[def] m, &span sp, + &ident id, namespace ns, dir dr) -> option::t[def] { + fn l_i_m_r(&env e, list[def] prev_ms, &def m, &span sp, &ident id, + namespace ns, dir dr) -> option::t[def] { + be lookup_in_mod_recursively(e, cons[def](m, @prev_ms), + sp, id, ns, dr); } - auto matches = vec::filter_map - (bind l_i_m(e, _, sp, id, ns, dr), info.glob_imports); + auto matches = vec::filter_map[def, def] + (bind l_i_m_r(e, m, _, sp, id, ns, dr), + info.glob_imports); if (vec::len(matches) == 0u) { ret none[def]; } else if (vec::len(matches) == 1u){ ret some[def](matches.(0)); } else { - for (def d in matches) { - e.sess.span_note(sp, "'" + id + "' is defined at " + - util::common::istr(ast::def_id_of_def(d)._1)); - } e.sess.span_err(sp, "'" + id + "' is glob-imported from" + " multiple different modules."); fail; @@ -914,10 +945,6 @@ fn lookup_in_mie(&env e, &mod_index_entry mie, namespace ns) ret none[def]; } -fn lookup_in_local_native_mod(&env e, def_id defid, &span sp, &ident id, - namespace ns) -> option::t[def] { - ret lookup_in_local_mod(e, defid, sp, id, ns, inside); -} // Module indexing @@ -945,9 +972,8 @@ fn index_mod(&ast::_mod md) -> mod_index { case(ast::view_item_import(?def_ident,_,_)) { add_to_index(index, def_ident, mie_view_item(it)); } - case(ast::view_item_import_glob(?path,_)) { - //globbed imports have to be resolved lazily. - } + //globbed imports have to be resolved lazily. + case(ast::view_item_import_glob(_,_)) {} case(ast::view_item_export(_)) {} } } @@ -995,6 +1021,7 @@ fn index_nmod(&ast::native_mod md) -> mod_index { case(ast::view_item_import(?def_ident,_,_)) { add_to_index(index, def_ident, mie_view_item(it)); } + case(ast::view_item_import_glob(_,_)) {} case(ast::view_item_export(_)) {} } } diff --git a/src/test/compile-fail/import-glob-0.rs b/src/test/compile-fail/import-glob-0.rs index 259212f7e1a2e..63bc0b57ad213 100644 --- a/src/test/compile-fail/import-glob-0.rs +++ b/src/test/compile-fail/import-glob-0.rs @@ -1,5 +1,12 @@ +// error-pattern: unresolved name + +import module_of_many_things::*; + mod module_of_many_things { - export f1, f2, f4; + export f1; + export f2; + export f4; + fn f1() { log "f1"; } @@ -14,11 +21,10 @@ mod module_of_many_things { } } -import module_of_many_things::*; fn main() { f1(); f2(); - f3(); + f999(); // 'export' currently doesn't work? f4(); } \ No newline at end of file diff --git a/src/test/compile-fail/import-glob-circular.rs b/src/test/compile-fail/import-glob-circular.rs new file mode 100644 index 0000000000000..9e9b5e3c9db5c --- /dev/null +++ b/src/test/compile-fail/import-glob-circular.rs @@ -0,0 +1,29 @@ + +// error-pattern: unresolved name +mod circ1 { + import circ1::*; + fn f1() { + log "f1"; + } + fn common() -> uint { + ret 0u; + } +} + +mod circ2 { + import circ2::*; + fn f2() { + log "f2"; + } + fn common() -> uint { + ret 1u; + } +} + +mod test { + import circ1::*; + + fn test() { + f1066(); + } +} \ No newline at end of file diff --git a/src/test/run-pass/import-glob-0.rs b/src/test/run-pass/import-glob-0.rs index d52c24641adaa..33ca7cabd0eea 100644 --- a/src/test/run-pass/import-glob-0.rs +++ b/src/test/run-pass/import-glob-0.rs @@ -31,7 +31,6 @@ mod dug { fn also_redstone() { log "Whatever."; } - fn f1() {} } } } diff --git a/src/test/run-pass/import-glob-1.rs b/src/test/run-pass/import-glob-1.rs index d02ca19f6d5e8..e02be57dcabf7 100644 --- a/src/test/run-pass/import-glob-1.rs +++ b/src/test/run-pass/import-glob-1.rs @@ -1,4 +1,4 @@ -import a1:b1::word_traveler; +import a1::b1::word_traveler; mod a1 { // mod b1 { // diff --git a/src/test/run-pass/import-glob-circular.rs b/src/test/run-pass/import-glob-circular.rs new file mode 100644 index 0000000000000..ba786178b61db --- /dev/null +++ b/src/test/run-pass/import-glob-circular.rs @@ -0,0 +1,47 @@ +import test1::*; +import test2::*; + +mod circ1 { + import circ1::*; + fn f1() { + log "f1"; + } + fn common() -> uint { + ret 0u; + } +} + +mod circ2 { + import circ2::*; + fn f2() { + log "f2"; + } + fn common() -> uint { + ret 1u; + } +} + +mod test1 { + import circ1::*; + fn test1() { + f1(); + f2(); + assert(common() == 0u); + } +} + +mod test2 { + import circ2::*; + fn test2() { + f1(); + f2(); + assert(common() == 1u); + } +} + + + +fn main() { + test1(); + test2(); +} \ No newline at end of file From 4761585f656d8a04172e171de6f731582f2f37fb Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Wed, 25 May 2011 11:55:48 -0700 Subject: [PATCH 7/8] Add a map from def_id s to items in resolve::env. Use it to provide better error messages in the event of glob-importing the same name multiple times. --- src/comp/middle/resolve.rs | 25 +++++++++++++- src/test/compile-fail/import-glob-multiple.rs | 34 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 src/test/compile-fail/import-glob-multiple.rs diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs index f0ae03925aa56..e14f1719e8640 100644 --- a/src/comp/middle/resolve.rs +++ b/src/comp/middle/resolve.rs @@ -88,6 +88,7 @@ type def_map = hashmap[uint,def]; type env = rec(crate_map crate_map, def_map def_map, + hashmap[def_id,@ast::item] ast_map, hashmap[ast::def_num,import_state] imports, hashmap[ast::def_num,@indexed_mod] mod_map, hashmap[def_id,vec[ident]] ext_map, @@ -107,6 +108,7 @@ tag namespace { fn resolve_crate(session sess, @ast::crate crate) -> def_map { auto e = @rec(crate_map = new_uint_hash[ast::crate_num](), def_map = new_uint_hash[def](), + ast_map = new_def_hash[@ast::item](), imports = new_int_hash[import_state](), mod_map = new_int_hash[@indexed_mod](), ext_map = new_def_hash[vec[ident]](), @@ -157,14 +159,31 @@ fn map_crate(&@env e, &ast::crate c) { e.mod_map.insert(defid._1, @rec(m=some(md), index=index_mod(md), glob_imports=vec::empty[def]())); + e.ast_map.insert(defid, i); } case (ast::item_native_mod(_, ?nmd, ?defid)) { e.mod_map.insert(defid._1, @rec(m=none[ast::_mod], index=index_nmod(nmd), glob_imports=vec::empty[def]())); + e.ast_map.insert(defid, i); + } + case (ast::item_const(_, _, _, ?defid, _)) { + e.ast_map.insert(defid, i); + } + case (ast::item_fn(_, _, _, ?defid, _)) { + e.ast_map.insert(defid, i); + } + case (ast::item_ty(_, _, _, ?defid, _)) { + e.ast_map.insert(defid, i); + } + case (ast::item_tag(_, _, _, ?defid, _)) { + e.ast_map.insert(defid, i); + } + case (ast::item_obj(_, _, _, ?obj_def_ids, _)) { + e.ast_map.insert(obj_def_ids.ty, i); + e.ast_map.insert(obj_def_ids.ctor, i); } - case (_) {} } } @@ -899,6 +918,10 @@ fn lookup_glob_in_mod(&env e, @indexed_mod info, list[def] m, &span sp, } else if (vec::len(matches) == 1u){ ret some[def](matches.(0)); } else { + for (def match in matches) { + e.sess.span_note(e.ast_map.get(ast::def_id_of_def(match)).span, + "'" + id + "' is defined here."); + } e.sess.span_err(sp, "'" + id + "' is glob-imported from" + " multiple different modules."); fail; diff --git a/src/test/compile-fail/import-glob-multiple.rs b/src/test/compile-fail/import-glob-multiple.rs new file mode 100644 index 0000000000000..c4064ac5da7dd --- /dev/null +++ b/src/test/compile-fail/import-glob-multiple.rs @@ -0,0 +1,34 @@ +// error-pattern: common2 + +import mod1::*; +import mod2::*; + +mod mod1 { + fn f1() { + log "f1"; + } + fn common1() { + log "common" + } + fn common2() { + log "common" + } +} + +mod mod2 { + fn f2() { + log "f1"; + } + fn common1() { + log "common" + } + fn common2() { + log "common" + } +} + + + +fn main() { + common2(); +} \ No newline at end of file From 14f69e65192009329ffd1321334aea0b1c4db996 Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Thu, 26 May 2011 17:33:33 -0700 Subject: [PATCH 8/8] Handle circularity in glob imports in a more elegant fashion. --- src/comp/middle/resolve.rs | 162 ++++++++++-------- src/test/compile-fail/import-glob-multiple.rs | 2 +- src/test/run-pass/import-glob-circular.rs | 32 ++-- 3 files changed, 108 insertions(+), 88 deletions(-) diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs index e14f1719e8640..aad180c60f8d3 100644 --- a/src/comp/middle/resolve.rs +++ b/src/comp/middle/resolve.rs @@ -43,7 +43,7 @@ tag scope { } tag import_state { - todo(@ast::view_item, list[scope]); + todo(@ast::view_item, list[scope]); // only used for explicit imports resolving(span); resolved(option::t[def] /* value */, option::t[def] /* type */, @@ -77,7 +77,8 @@ tag mod_index_entry { type mod_index = hashmap[ident,list[mod_index_entry]]; type indexed_mod = rec(option::t[ast::_mod] m, - mod_index index, vec[def] glob_imports); + mod_index index, vec[def] glob_imports, + hashmap[str,import_state] glob_imported_names); /* native modules can't contain tags, and we don't store their ASTs because we only need to look at them to determine exports, which they can't control.*/ // It should be safe to use index to memoize lookups of globbed names. @@ -141,7 +142,9 @@ fn map_crate(&@env e, &ast::crate c) { // Register the top-level mod e.mod_map.insert(-1, @rec(m=some(c.node.module), index=index_mod(c.node.module), - glob_imports=vec::empty[def]())); + glob_imports=vec::empty[def](), + glob_imported_names + =new_str_hash[import_state]())); walk::walk_crate(index_names, c); fn index_vi(@env e, @mutable list[scope] sc, &@ast::view_item i) { @@ -158,14 +161,18 @@ fn map_crate(&@env e, &ast::crate c) { case (ast::item_mod(_, ?md, ?defid)) { e.mod_map.insert(defid._1, @rec(m=some(md), index=index_mod(md), - glob_imports=vec::empty[def]())); + glob_imports=vec::empty[def](), + glob_imported_names + =new_str_hash[import_state]())); e.ast_map.insert(defid, i); } case (ast::item_native_mod(_, ?nmd, ?defid)) { e.mod_map.insert(defid._1, @rec(m=none[ast::_mod], index=index_nmod(nmd), - glob_imports=vec::empty[def]())); + glob_imports=vec::empty[def](), + glob_imported_names + =new_str_hash[import_state]())); e.ast_map.insert(defid, i); } case (ast::item_const(_, _, _, ?defid, _)) { @@ -542,12 +549,11 @@ fn lookup_in_scope(&env e, list[scope] sc, &span sp, &ident id, namespace ns) fn in_scope(&env e, &span sp, &ident id, &scope s, namespace ns) -> option::t[def] { //not recursing through globs - let list[def] no_m = nil[def]; alt (s) { case (scope_crate(?c)) { auto defid = tup(ast::local_crate, -1); - ret lookup_in_local_mod(e, defid, no_m, sp, id, ns, inside); + ret lookup_in_local_mod(e, defid, sp, id, ns, inside); } case (scope_item(?it)) { alt (it.node) { @@ -563,12 +569,10 @@ fn lookup_in_scope(&env e, list[scope] sc, &span sp, &ident id, namespace ns) } } case (ast::item_mod(_, _, ?defid)) { - ret lookup_in_local_mod(e, defid, no_m, sp, - id, ns, inside); + ret lookup_in_local_mod(e, defid, sp, id, ns, inside); } case (ast::item_native_mod(_, ?m, ?defid)) { - ret lookup_in_local_native_mod(e, defid, no_m, - sp, id, ns); + ret lookup_in_local_native_mod(e, defid, sp, id, ns); } case (ast::item_ty(_, _, ?ty_params, _, _)) { if (ns == ns_type) { @@ -796,44 +800,27 @@ fn lookup_in_mod_strict(&env e, def m, &span sp, &ident id, fn lookup_in_mod(&env e, def m, &span sp, &ident id, namespace ns, dir dr) -> option::t[def] { - be lookup_in_mod_recursively(e, cons[def](m, @nil[def]), sp, id, ns, dr); -} - -// this list is simply the stack of glob imports we have passed through -// (preventing cyclic glob imports from diverging) -fn lookup_in_mod_recursively(&env e, list[def] m, &span sp, &ident id, - namespace ns, dir dr) -> option::t[def] { + auto defid = ast::def_id_of_def(m); + if (defid._0 != ast::local_crate) { + // examining a module in an external crate + auto cached = e.ext_cache.find(tup(defid,id,ns)); + if (!option::is_none(cached)) { ret cached; } + auto path = [id]; + if (defid._1 != -1) { + path = e.ext_map.get(defid) + path; + } + auto fnd = lookup_external(e, defid._0, path, ns); + if (!option::is_none(fnd)) { + e.ext_cache.insert(tup(defid,id,ns), option::get(fnd)); + } + ret fnd; + } alt (m) { - case (cons[def](?mod_def, ?tl)) { - if (list::has(*tl, mod_def)) { - ret none[def]; // import glob cycle detected; we're done - } - auto defid = ast::def_id_of_def(mod_def); - if (defid._0 != ast::local_crate) { - // examining a module in an external crate - auto cached = e.ext_cache.find(tup(defid,id,ns)); - if (!option::is_none(cached)) { ret cached; } - auto path = [id]; - if (defid._1 != -1) { - path = e.ext_map.get(defid) + path; - } - auto fnd = lookup_external(e, defid._0, path, ns); - if (!option::is_none(fnd)) { - e.ext_cache.insert(tup(defid,id,ns), option::get(fnd)); - } - ret fnd; - } - alt (mod_def) { - case (ast::def_mod(?defid)) { - ret lookup_in_local_mod(e, defid, m, sp, id, ns, dr); - } - case (ast::def_native_mod(?defid)) { - ret lookup_in_local_native_mod(e, defid, m, sp, id, ns); - } - } + case (ast::def_mod(?defid)) { + ret lookup_in_local_mod(e, defid, sp, id, ns, dr); } - case (_) { - e.sess.bug("lookup_in_mod_recursively needs a module"); fail; + case (ast::def_native_mod(?defid)) { + ret lookup_in_local_native_mod(e, defid, sp, id, ns); } } } @@ -872,18 +859,18 @@ fn lookup_import(&env e, def_id defid, namespace ns) -> option::t[def] { } -fn lookup_in_local_native_mod(&env e, def_id defid, list[def] m, &span sp, +fn lookup_in_local_native_mod(&env e, def_id defid, &span sp, &ident id, namespace ns) -> option::t[def] { - ret lookup_in_local_mod(e, defid, m, sp, id, ns, inside); + ret lookup_in_local_mod(e, defid, sp, id, ns, inside); } -fn lookup_in_local_mod(&env e, def_id defid, list[def] m, &span sp, +fn lookup_in_local_mod(&env e, def_id defid, &span sp, &ident id, namespace ns, dir dr) -> option::t[def] { auto info = e.mod_map.get(defid._1); - if (dr == outside && !ast::is_exported(id, option::get(info.m))) { - // if we're in a native mod, then dr==inside, so info.m is some _mod - ret none[def]; // name is not visible - } + if (dr == outside && !ast::is_exported(id, option::get(info.m))) { + // if we're in a native mod, then dr==inside, so info.m is some _mod + ret none[def]; // name is not visible + } alt(info.index.find(id)) { case (none[list[mod_index_entry]]) { } case (some[list[mod_index_entry]](?lst)) { @@ -900,31 +887,56 @@ fn lookup_in_local_mod(&env e, def_id defid, list[def] m, &span sp, } } // not local or explicitly imported; try globs: - ret lookup_glob_in_mod(e, info, m, sp, id, ns, dr); + ret lookup_glob_in_mod(e, info, sp, id, ns, dr); } -fn lookup_glob_in_mod(&env e, @indexed_mod info, list[def] m, &span sp, - &ident id, namespace ns, dir dr) -> option::t[def] { - fn l_i_m_r(&env e, list[def] prev_ms, &def m, &span sp, &ident id, - namespace ns, dir dr) -> option::t[def] { - be lookup_in_mod_recursively(e, cons[def](m, @prev_ms), - sp, id, ns, dr); +fn lookup_glob_in_mod(&env e, @indexed_mod info, &span sp, + &ident id, namespace wanted_ns, dir dr) + -> option::t[def] { + fn per_ns(&env e, @indexed_mod info, &span sp, &ident id, + namespace ns, dir dr) -> option::t[def] { + fn l_i_m_r(&env e, &def m, &span sp, &ident id, + namespace ns, dir dr) -> option::t[def] { + be lookup_in_mod(e, m, sp, id, ns, dr); + } + + auto matches = vec::filter_map[def, def] + (bind l_i_m_r(e, _, sp, id, ns, dr), + info.glob_imports); + if (vec::len(matches) == 0u) { + ret none[def]; + } else if (vec::len(matches) == 1u){ + ret some[def](matches.(0)); + } else { + for (def match in matches) { + e.sess.span_note(e.ast_map.get + (ast::def_id_of_def(match)).span, + "'" + id + "' is defined here."); + } + e.sess.span_err(sp, "'" + id + "' is glob-imported from" + + " multiple different modules."); + fail; + } } - auto matches = vec::filter_map[def, def] - (bind l_i_m_r(e, m, _, sp, id, ns, dr), - info.glob_imports); - if (vec::len(matches) == 0u) { - ret none[def]; - } else if (vec::len(matches) == 1u){ - ret some[def](matches.(0)); - } else { - for (def match in matches) { - e.sess.span_note(e.ast_map.get(ast::def_id_of_def(match)).span, - "'" + id + "' is defined here."); + // since we don't know what names we have in advance, + // absence takes the place of todo() + if(!info.glob_imported_names.contains_key(id)) { + info.glob_imported_names.insert(id, resolving(sp)); + auto val = per_ns(e, info, sp, id, ns_value, dr); + auto typ = per_ns(e, info, sp, id, ns_type, dr); + auto md = per_ns(e, info, sp, id, ns_module, dr); + info.glob_imported_names.insert(id, resolved(val, typ, md)); + } + alt (info.glob_imported_names.get(id)) { + case (todo(_,_)) { e.sess.bug("Shouldn't've put a todo in."); } + case (resolving(?sp)) { + ret none[def]; //circularity is okay in import globs + } + case (resolved(?val, ?typ, ?md)) { + ret alt (wanted_ns) { case (ns_value) { val } + case (ns_type) { typ } + case (ns_module) { md } }; } - e.sess.span_err(sp, "'" + id + "' is glob-imported from" + - " multiple different modules."); - fail; } } diff --git a/src/test/compile-fail/import-glob-multiple.rs b/src/test/compile-fail/import-glob-multiple.rs index c4064ac5da7dd..5e4a4227d6abb 100644 --- a/src/test/compile-fail/import-glob-multiple.rs +++ b/src/test/compile-fail/import-glob-multiple.rs @@ -1,4 +1,4 @@ -// error-pattern: common2 +// error-pattern:common2 import mod1::*; import mod2::*; diff --git a/src/test/run-pass/import-glob-circular.rs b/src/test/run-pass/import-glob-circular.rs index ba786178b61db..921d449af1305 100644 --- a/src/test/run-pass/import-glob-circular.rs +++ b/src/test/run-pass/import-glob-circular.rs @@ -3,39 +3,47 @@ import test2::*; mod circ1 { import circ1::*; - fn f1() { - log "f1"; + fn f1() -> uint { + ret 1u } fn common() -> uint { - ret 0u; + ret 1u; } } mod circ2 { import circ2::*; - fn f2() { - log "f2"; + fn f2() -> uint { + ret 2u; } fn common() -> uint { - ret 1u; + ret 2u; } } mod test1 { import circ1::*; fn test1() { - f1(); - f2(); - assert(common() == 0u); + assert(f1() == 1u); + //make sure that cached lookups work... + assert(f1() == 1u); + assert(f2() == 2u); + assert(f2() == 2u); + assert(common() == 1u); + assert(common() == 1u); } } mod test2 { import circ2::*; fn test2() { - f1(); - f2(); - assert(common() == 1u); + assert(f1() == 1u); + //make sure that cached lookups work... + assert(f1() == 1u); + assert(f2() == 2u); + assert(f2() == 2u); + assert(common() == 2u); + assert(common() == 2u); } }