Skip to content

Commit af6b482

Browse files
paulstansifergraydon
authored andcommitted
Handle circularity in glob imports in a more elegant fashion.
1 parent 7fe3d82 commit af6b482

File tree

3 files changed

+108
-88
lines changed

3 files changed

+108
-88
lines changed

src/comp/middle/resolve.rs

Lines changed: 87 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ tag scope {
4343
}
4444

4545
tag import_state {
46-
todo(@ast::view_item, list[scope]);
46+
todo(@ast::view_item, list[scope]); // only used for explicit imports
4747
resolving(span);
4848
resolved(option::t[def] /* value */,
4949
option::t[def] /* type */,
@@ -77,7 +77,8 @@ tag mod_index_entry {
7777
type mod_index = hashmap[ident,list[mod_index_entry]];
7878

7979
type indexed_mod = rec(option::t[ast::_mod] m,
80-
mod_index index, vec[def] glob_imports);
80+
mod_index index, vec[def] glob_imports,
81+
hashmap[str,import_state] glob_imported_names);
8182
/* native modules can't contain tags, and we don't store their ASTs because we
8283
only need to look at them to determine exports, which they can't control.*/
8384
// 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) {
141142
// Register the top-level mod
142143
e.mod_map.insert(-1, @rec(m=some(c.node.module),
143144
index=index_mod(c.node.module),
144-
glob_imports=vec::empty[def]()));
145+
glob_imports=vec::empty[def](),
146+
glob_imported_names
147+
=new_str_hash[import_state]()));
145148
walk::walk_crate(index_names, c);
146149

147150
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) {
158161
case (ast::item_mod(_, ?md, ?defid)) {
159162
e.mod_map.insert(defid._1,
160163
@rec(m=some(md), index=index_mod(md),
161-
glob_imports=vec::empty[def]()));
164+
glob_imports=vec::empty[def](),
165+
glob_imported_names
166+
=new_str_hash[import_state]()));
162167
e.ast_map.insert(defid, i);
163168
}
164169
case (ast::item_native_mod(_, ?nmd, ?defid)) {
165170
e.mod_map.insert(defid._1,
166171
@rec(m=none[ast::_mod],
167172
index=index_nmod(nmd),
168-
glob_imports=vec::empty[def]()));
173+
glob_imports=vec::empty[def](),
174+
glob_imported_names
175+
=new_str_hash[import_state]()));
169176
e.ast_map.insert(defid, i);
170177
}
171178
case (ast::item_const(_, _, _, ?defid, _)) {
@@ -542,12 +549,11 @@ fn lookup_in_scope(&env e, list[scope] sc, &span sp, &ident id, namespace ns)
542549
fn in_scope(&env e, &span sp, &ident id, &scope s, namespace ns)
543550
-> option::t[def] {
544551
//not recursing through globs
545-
let list[def] no_m = nil[def];
546552

547553
alt (s) {
548554
case (scope_crate(?c)) {
549555
auto defid = tup(ast::local_crate, -1);
550-
ret lookup_in_local_mod(e, defid, no_m, sp, id, ns, inside);
556+
ret lookup_in_local_mod(e, defid, sp, id, ns, inside);
551557
}
552558
case (scope_item(?it)) {
553559
alt (it.node) {
@@ -563,12 +569,10 @@ fn lookup_in_scope(&env e, list[scope] sc, &span sp, &ident id, namespace ns)
563569
}
564570
}
565571
case (ast::item_mod(_, _, ?defid)) {
566-
ret lookup_in_local_mod(e, defid, no_m, sp,
567-
id, ns, inside);
572+
ret lookup_in_local_mod(e, defid, sp, id, ns, inside);
568573
}
569574
case (ast::item_native_mod(_, ?m, ?defid)) {
570-
ret lookup_in_local_native_mod(e, defid, no_m,
571-
sp, id, ns);
575+
ret lookup_in_local_native_mod(e, defid, sp, id, ns);
572576
}
573577
case (ast::item_ty(_, _, ?ty_params, _, _)) {
574578
if (ns == ns_type) {
@@ -796,44 +800,27 @@ fn lookup_in_mod_strict(&env e, def m, &span sp, &ident id,
796800

797801
fn lookup_in_mod(&env e, def m, &span sp, &ident id, namespace ns, dir dr)
798802
-> option::t[def] {
799-
be lookup_in_mod_recursively(e, cons[def](m, @nil[def]), sp, id, ns, dr);
800-
}
801-
802-
// this list is simply the stack of glob imports we have passed through
803-
// (preventing cyclic glob imports from diverging)
804-
fn lookup_in_mod_recursively(&env e, list[def] m, &span sp, &ident id,
805-
namespace ns, dir dr) -> option::t[def] {
803+
auto defid = ast::def_id_of_def(m);
804+
if (defid._0 != ast::local_crate) {
805+
// examining a module in an external crate
806+
auto cached = e.ext_cache.find(tup(defid,id,ns));
807+
if (!option::is_none(cached)) { ret cached; }
808+
auto path = [id];
809+
if (defid._1 != -1) {
810+
path = e.ext_map.get(defid) + path;
811+
}
812+
auto fnd = lookup_external(e, defid._0, path, ns);
813+
if (!option::is_none(fnd)) {
814+
e.ext_cache.insert(tup(defid,id,ns), option::get(fnd));
815+
}
816+
ret fnd;
817+
}
806818
alt (m) {
807-
case (cons[def](?mod_def, ?tl)) {
808-
if (list::has(*tl, mod_def)) {
809-
ret none[def]; // import glob cycle detected; we're done
810-
}
811-
auto defid = ast::def_id_of_def(mod_def);
812-
if (defid._0 != ast::local_crate) {
813-
// examining a module in an external crate
814-
auto cached = e.ext_cache.find(tup(defid,id,ns));
815-
if (!option::is_none(cached)) { ret cached; }
816-
auto path = [id];
817-
if (defid._1 != -1) {
818-
path = e.ext_map.get(defid) + path;
819-
}
820-
auto fnd = lookup_external(e, defid._0, path, ns);
821-
if (!option::is_none(fnd)) {
822-
e.ext_cache.insert(tup(defid,id,ns), option::get(fnd));
823-
}
824-
ret fnd;
825-
}
826-
alt (mod_def) {
827-
case (ast::def_mod(?defid)) {
828-
ret lookup_in_local_mod(e, defid, m, sp, id, ns, dr);
829-
}
830-
case (ast::def_native_mod(?defid)) {
831-
ret lookup_in_local_native_mod(e, defid, m, sp, id, ns);
832-
}
833-
}
819+
case (ast::def_mod(?defid)) {
820+
ret lookup_in_local_mod(e, defid, sp, id, ns, dr);
834821
}
835-
case (_) {
836-
e.sess.bug("lookup_in_mod_recursively needs a module"); fail;
822+
case (ast::def_native_mod(?defid)) {
823+
ret lookup_in_local_native_mod(e, defid, sp, id, ns);
837824
}
838825
}
839826
}
@@ -872,18 +859,18 @@ fn lookup_import(&env e, def_id defid, namespace ns) -> option::t[def] {
872859
}
873860

874861

875-
fn lookup_in_local_native_mod(&env e, def_id defid, list[def] m, &span sp,
862+
fn lookup_in_local_native_mod(&env e, def_id defid, &span sp,
876863
&ident id, namespace ns) -> option::t[def] {
877-
ret lookup_in_local_mod(e, defid, m, sp, id, ns, inside);
864+
ret lookup_in_local_mod(e, defid, sp, id, ns, inside);
878865
}
879866

880-
fn lookup_in_local_mod(&env e, def_id defid, list[def] m, &span sp,
867+
fn lookup_in_local_mod(&env e, def_id defid, &span sp,
881868
&ident id, namespace ns, dir dr) -> option::t[def] {
882869
auto info = e.mod_map.get(defid._1);
883-
if (dr == outside && !ast::is_exported(id, option::get(info.m))) {
884-
// if we're in a native mod, then dr==inside, so info.m is some _mod
885-
ret none[def]; // name is not visible
886-
}
870+
if (dr == outside && !ast::is_exported(id, option::get(info.m))) {
871+
// if we're in a native mod, then dr==inside, so info.m is some _mod
872+
ret none[def]; // name is not visible
873+
}
887874
alt(info.index.find(id)) {
888875
case (none[list[mod_index_entry]]) { }
889876
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,
900887
}
901888
}
902889
// not local or explicitly imported; try globs:
903-
ret lookup_glob_in_mod(e, info, m, sp, id, ns, dr);
890+
ret lookup_glob_in_mod(e, info, sp, id, ns, dr);
904891
}
905892

906-
fn lookup_glob_in_mod(&env e, @indexed_mod info, list[def] m, &span sp,
907-
&ident id, namespace ns, dir dr) -> option::t[def] {
908-
fn l_i_m_r(&env e, list[def] prev_ms, &def m, &span sp, &ident id,
909-
namespace ns, dir dr) -> option::t[def] {
910-
be lookup_in_mod_recursively(e, cons[def](m, @prev_ms),
911-
sp, id, ns, dr);
893+
fn lookup_glob_in_mod(&env e, @indexed_mod info, &span sp,
894+
&ident id, namespace wanted_ns, dir dr)
895+
-> option::t[def] {
896+
fn per_ns(&env e, @indexed_mod info, &span sp, &ident id,
897+
namespace ns, dir dr) -> option::t[def] {
898+
fn l_i_m_r(&env e, &def m, &span sp, &ident id,
899+
namespace ns, dir dr) -> option::t[def] {
900+
be lookup_in_mod(e, m, sp, id, ns, dr);
901+
}
902+
903+
auto matches = vec::filter_map[def, def]
904+
(bind l_i_m_r(e, _, sp, id, ns, dr),
905+
info.glob_imports);
906+
if (vec::len(matches) == 0u) {
907+
ret none[def];
908+
} else if (vec::len(matches) == 1u){
909+
ret some[def](matches.(0));
910+
} else {
911+
for (def match in matches) {
912+
e.sess.span_note(e.ast_map.get
913+
(ast::def_id_of_def(match)).span,
914+
"'" + id + "' is defined here.");
915+
}
916+
e.sess.span_err(sp, "'" + id + "' is glob-imported from" +
917+
" multiple different modules.");
918+
fail;
919+
}
912920
}
913-
auto matches = vec::filter_map[def, def]
914-
(bind l_i_m_r(e, m, _, sp, id, ns, dr),
915-
info.glob_imports);
916-
if (vec::len(matches) == 0u) {
917-
ret none[def];
918-
} else if (vec::len(matches) == 1u){
919-
ret some[def](matches.(0));
920-
} else {
921-
for (def match in matches) {
922-
e.sess.span_note(e.ast_map.get(ast::def_id_of_def(match)).span,
923-
"'" + id + "' is defined here.");
921+
// since we don't know what names we have in advance,
922+
// absence takes the place of todo()
923+
if(!info.glob_imported_names.contains_key(id)) {
924+
info.glob_imported_names.insert(id, resolving(sp));
925+
auto val = per_ns(e, info, sp, id, ns_value, dr);
926+
auto typ = per_ns(e, info, sp, id, ns_type, dr);
927+
auto md = per_ns(e, info, sp, id, ns_module, dr);
928+
info.glob_imported_names.insert(id, resolved(val, typ, md));
929+
}
930+
alt (info.glob_imported_names.get(id)) {
931+
case (todo(_,_)) { e.sess.bug("Shouldn't've put a todo in."); }
932+
case (resolving(?sp)) {
933+
ret none[def]; //circularity is okay in import globs
934+
}
935+
case (resolved(?val, ?typ, ?md)) {
936+
ret alt (wanted_ns) { case (ns_value) { val }
937+
case (ns_type) { typ }
938+
case (ns_module) { md } };
924939
}
925-
e.sess.span_err(sp, "'" + id + "' is glob-imported from" +
926-
" multiple different modules.");
927-
fail;
928940
}
929941
}
930942

src/test/compile-fail/import-glob-multiple.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// error-pattern: common2
1+
// error-pattern:common2
22

33
import mod1::*;
44
import mod2::*;

src/test/run-pass/import-glob-circular.rs

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,47 @@ import test2::*;
33

44
mod circ1 {
55
import circ1::*;
6-
fn f1() {
7-
log "f1";
6+
fn f1() -> uint {
7+
ret 1u
88
}
99
fn common() -> uint {
10-
ret 0u;
10+
ret 1u;
1111
}
1212
}
1313

1414
mod circ2 {
1515
import circ2::*;
16-
fn f2() {
17-
log "f2";
16+
fn f2() -> uint {
17+
ret 2u;
1818
}
1919
fn common() -> uint {
20-
ret 1u;
20+
ret 2u;
2121
}
2222
}
2323

2424
mod test1 {
2525
import circ1::*;
2626
fn test1() {
27-
f1();
28-
f2();
29-
assert(common() == 0u);
27+
assert(f1() == 1u);
28+
//make sure that cached lookups work...
29+
assert(f1() == 1u);
30+
assert(f2() == 2u);
31+
assert(f2() == 2u);
32+
assert(common() == 1u);
33+
assert(common() == 1u);
3034
}
3135
}
3236

3337
mod test2 {
3438
import circ2::*;
3539
fn test2() {
36-
f1();
37-
f2();
38-
assert(common() == 1u);
40+
assert(f1() == 1u);
41+
//make sure that cached lookups work...
42+
assert(f1() == 1u);
43+
assert(f2() == 2u);
44+
assert(f2() == 2u);
45+
assert(common() == 2u);
46+
assert(common() == 2u);
3947
}
4048
}
4149

0 commit comments

Comments
 (0)