From b44be95e4543d63aa9174069a881abf51c9dd923 Mon Sep 17 00:00:00 2001 From: Graydon Hoare Date: Thu, 23 Feb 2012 18:32:37 -0800 Subject: [PATCH] Support basic, unsophisticated, somewhat wrong export-globs. --- src/comp/middle/resolve.rs | 209 ++++++++++++++++++++++--------- src/test/run-pass/export-glob.rs | 2 - 2 files changed, 152 insertions(+), 59 deletions(-) diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs index d325e8fcf2e..a35de9997f9 100644 --- a/src/comp/middle/resolve.rs +++ b/src/comp/middle/resolve.rs @@ -13,7 +13,7 @@ import std::map::{new_int_hash, new_str_hash}; import syntax::codemap::span; import syntax::visit; import visit::vt; -import std::list; +import std::{list, deque}; import std::map::hashmap; import std::list::{list, nil, cons}; import option::{is_none, is_some}; @@ -107,6 +107,7 @@ type indexed_mod = { m: option, index: mod_index, mutable glob_imports: [glob_imp_def], + mutable globbed_exports: [ident], glob_imported_names: hashmap, path: str }; @@ -203,17 +204,6 @@ fn create_env(sess: session, amap: ast_map::map) -> @env { sess: sess} } -fn iter_import_paths(vi: ast::view_item, f: fn(vp: @ast::view_path)) { - alt vi.node { - ast::view_item_import(vps) { - for vp in vps { - f(vp); - } - } - _ {} - } -} - fn iter_export_paths(vi: ast::view_item, f: fn(vp: @ast::view_path)) { alt vi.node { ast::view_item_export(vps) { @@ -225,10 +215,32 @@ fn iter_export_paths(vi: ast::view_item, f: fn(vp: @ast::view_path)) { } } -fn iter_all_view_paths(vi: ast::view_item, - f: fn(vp: @ast::view_path)) { +fn iter_import_paths(vi: ast::view_item, f: fn(vp: @ast::view_path)) { + alt vi.node { + ast::view_item_import(vps) { + for vp in vps { + f(vp); + } + } + _ {} + } +} + +fn iter_effective_import_paths(vi: ast::view_item, + f: fn(vp: @ast::view_path)) { iter_import_paths(vi, f); - iter_export_paths(vi, f); + iter_export_paths(vi) {|vp| + alt vp.node { + ast::view_path_simple(_, _, _) { } + // FIXME: support uniform ident-list exports eventually; + // at the moment they have half a meaning as reaching into + // tags. + ast::view_path_list(_, _, _) {} + ast::view_path_glob(_,_) { + f(vp); + } + } + } } // Locate all modules and imports and index them, so that the next passes can @@ -236,7 +248,7 @@ fn iter_all_view_paths(vi: ast::view_item, fn map_crate(e: @env, c: @ast::crate) { fn index_vi(e: @env, i: @ast::view_item, sc: scopes, _v: vt) { - iter_import_paths(*i) { |vp| + iter_effective_import_paths(*i) { |vp| alt vp.node { ast::view_path_simple(name, path, id) { e.imports.insert(id, todo(id, name, path, vp.span, sc)); @@ -275,6 +287,7 @@ fn map_crate(e: @env, c: @ast::crate) { @{m: some(md), index: index_mod(md), mutable glob_imports: [], + mutable globbed_exports: [], glob_imported_names: new_str_hash(), path: path_from_scope(sc, i.ident)}); } @@ -283,6 +296,7 @@ fn map_crate(e: @env, c: @ast::crate) { @{m: none::, index: index_nmod(nmd), mutable glob_imports: [], + mutable globbed_exports: [], glob_imported_names: new_str_hash(), path: path_from_scope(sc, i.ident)}); } @@ -295,7 +309,7 @@ fn map_crate(e: @env, c: @ast::crate) { // So we wind up reusing the glob-import machinery when looking at // glob exports. They just do re-exporting in a later step. fn link_glob(e: @env, vi: @ast::view_item, sc: scopes, _v: vt) { - iter_all_view_paths(*vi) { |vp| + iter_effective_import_paths(*vi) { |vp| alt vp.node { ast::view_path_glob(path, _) { alt follow_import(*e, sc, *path, vp.span) { @@ -341,6 +355,7 @@ fn map_crate(e: @env, c: @ast::crate) { @{m: some(c.node.module), index: index_mod(c.node.module), mutable glob_imports: [], + mutable globbed_exports: [], glob_imported_names: new_str_hash(), path: ""}); @@ -721,7 +736,7 @@ fn resolve_import(e: env, defid: ast::def_id, name: ast::ident, fn lst(my_id: node_id, vis: [@view_item]) -> [node_id] { let imports = [], found = false; for vi in vis { - iter_import_paths(*vi) {|vp| + iter_effective_import_paths(*vi) {|vp| alt vp.node { view_path_simple(_, _, id) | view_path_glob(_, id) { @@ -1201,6 +1216,7 @@ fn lookup_in_class(parent_id: def_id, fn lookup_in_block(e: env, name: ident, sp: span, b: ast::blk_, pos: uint, loc_pos: uint, ns: namespace) -> option { + let i = vec::len(b.stmts); while i > 0u { i -= 1u; @@ -1251,7 +1267,9 @@ fn lookup_in_block(e: env, name: ident, sp: span, b: ast::blk_, pos: uint, _ { if str::eq(it.ident, name) { let found = found_def_item(it, ns); - if !is_none(found) { ret found; } + if !is_none(found) { + ret found; + } } } } @@ -1262,13 +1280,20 @@ fn lookup_in_block(e: env, name: ident, sp: span, b: ast::blk_, pos: uint, } } for vi in b.view_items { + + let is_import = false; + alt vi.node { + ast::view_item_import(_) { is_import = true; } + _ {} + } + alt vi.node { - ast::view_item_import(vps) { + ast::view_item_import(vps) | ast::view_item_export(vps) { for vp in vps { alt vp.node { ast::view_path_simple(ident, _, id) { - if name == ident { + if is_import && name == ident { ret lookup_import(e, local_def(id), ns); } } @@ -1287,7 +1312,9 @@ fn lookup_in_block(e: env, name: ident, sp: span, b: ast::blk_, pos: uint, some(globs) { let found = lookup_in_globs(e, globs, sp, name, ns, inside); - if found != none { ret found; } + if found != none { + ret found; + } } _ {} } @@ -1427,30 +1454,32 @@ fn lookup_in_local_native_mod(e: env, node_id: node_id, sp: span, id: ident, ret lookup_in_local_mod(e, node_id, sp, id, ns, inside); } -fn is_exported(e: env, i: ident, m: _mod) -> bool { - ast_util::is_exported(i, m) || e.resolve_unexported +fn is_exported(e: env, i: ident, m: @indexed_mod) -> bool { + + alt m.m { + some(_m) { + if ast_util::is_exported(i, _m) { ret true; } + } + _ {} + } + + ret vec::contains(m.globbed_exports, i) + || e.resolve_unexported; } fn lookup_in_local_mod(e: env, node_id: node_id, sp: span, id: ident, ns: namespace, dr: dir) -> option { let info = e.mod_map.get(node_id); - if dr == outside && !is_exported(e, id, option::get(info.m)) { + if dr == outside && !is_exported(e, id, info) { // if we're in a native mod, then dr==inside, so info.m is some _mod ret none; // name is not visible } alt info.index.find(id) { none { } - some(lst_) { - let lst = lst_; - while true { - alt lst { - nil { break; } - cons(hd, tl) { - let found = lookup_in_mie(e, hd, ns); - if !is_none(found) { ret found; } - lst = *tl; - } - } + some(lst) { + let found = list::find(lst, bind lookup_in_mie(e, _, ns)); + if !is_none(found) { + ret found; } } } @@ -1590,7 +1619,7 @@ fn index_view_items(view_items: [@ast::view_item], _ {} } - iter_import_paths(*vi) {|vp| + iter_effective_import_paths(*vi) {|vp| alt vp.node { ast::view_path_simple(ident, _, id) { add_to_index(index, ident, mie_import_ident(id, vp.span)); @@ -1931,6 +1960,49 @@ fn ensure_unique(e: env, sp: span, elts: [T], id: fn(T) -> ident, } fn check_exports(e: @env) { + + fn iter_mod(e: env, m: def, sp: span, _dr: dir, + f: fn(ident: ident, def: def)) { + let defid = def_id_of_def(m); + + if defid.crate != ast::local_crate { + // FIXME: ought to support external export-globs eventually. + e.sess.span_unimpl(sp, "glob-export of items in external crate"); + } else { + + let mid = def_id_of_def(m); + assert mid.crate == ast::local_crate; + let ixm = e.mod_map.get(mid.node); + + ixm.index.items() {|ident, mies| + list::iter(mies) {|mie| + alt mie { + mie_item(item) { + let defs = + [ found_def_item(item, ns_val(value_or_enum)), + found_def_item(item, ns_type), + found_def_item(item, ns_module) ]; + for d in defs { + alt d { + some(def) { + f(ident, def); + } + _ {} + } + } + } + _ { + let s = "glob-export from mod with non-items"; + e.sess.span_unimpl(sp, s); + } + } + } + } + } + } + + + fn lookup_glob_any(e: @env, info: @indexed_mod, sp: span, path: str, ident: ident) -> bool { let lookup = @@ -1957,13 +2029,13 @@ fn check_exports(e: @env) { } } - fn check_export(e: @env, ident: str, val: @indexed_mod, + fn check_export(e: @env, ident: str, _mod: @indexed_mod, vi: @view_item) { let found_something = false; - let full_path = val.path + ident; - if val.index.contains_key(ident) { + let full_path = _mod.path + ident; + if _mod.index.contains_key(ident) { found_something = true; - let xs = val.index.get(ident); + let xs = _mod.index.get(ident); list::iter(xs) {|x| alt x { mie_import_ident(id, _) { @@ -1980,16 +2052,17 @@ fn check_exports(e: @env) { } } } - found_something |= lookup_glob_any(e, val, vi.span, val.path, ident); + found_something |= lookup_glob_any(e, _mod, vi.span, + _mod.path, ident); if !found_something { e.sess.span_warn(vi.span, #fmt("exported item %s is not defined", ident)); } } - fn check_enum_ok(e: @env, sp:span, id: ident, val: @indexed_mod) + fn check_enum_ok(e: @env, sp:span, id: ident, _mod: @indexed_mod) -> node_id { - alt val.index.find(id) { + alt _mod.index.find(id) { none { e.sess.span_fatal(sp, #fmt("error: undefined id %s \ in an export", id)); } some(ms) { @@ -2013,15 +2086,15 @@ fn check_exports(e: @env) { } } - fn check_export_enum_list(e: @env, val: @indexed_mod, + fn check_export_enum_list(e: @env, _mod: @indexed_mod, span: codemap::span, id: ast::ident, ids: [ast::path_list_ident]) { if vec::len(ids) == 0u { - let _ = check_enum_ok(e, span, id, val); + let _ = check_enum_ok(e, span, id, _mod); } else { - let parent_id = check_enum_ok(e, span, id, val); + let parent_id = check_enum_ok(e, span, id, _mod); for variant_id in ids { - alt val.index.find(variant_id.node.name) { + alt _mod.index.find(variant_id.node.name) { some(ms) { list::iter(ms) {|m| alt m { @@ -2052,14 +2125,16 @@ fn check_exports(e: @env) { } } - e.mod_map.values {|val| - alt val.m { + e.mod_map.values {|_mod| + alt _mod.m { some(m) { + let glob_is_re_exported = new_int_hash(); + for vi in m.view_items { iter_export_paths(*vi) { |vp| alt vp.node { ast::view_path_simple(ident, _, _) { - check_export(e, ident, val, vi); + check_export(e, ident, _mod, vi); } ast::view_path_list(path, ids, _) { let id = if vec::len(*path) == 1u { @@ -2068,12 +2143,31 @@ fn check_exports(e: @env) { e.sess.span_fatal(vp.span, #fmt("bad export name-list")) }; - check_export_enum_list(e, val, vp.span, id, ids); + check_export_enum_list(e, _mod, vp.span, id, ids); + } + ast::view_path_glob(_, node_id) { + glob_is_re_exported.insert(node_id, ()); } - _ {} } } } + // Now follow the export-glob links and fill in the + // globbed_exports and exp_map lists. + for glob in _mod.glob_imports { + alt check glob.path.node { + ast::view_path_glob(path, node_id) { + if ! glob_is_re_exported.contains_key(node_id) { + cont; + } + } + } + iter_mod(*e, glob.def, + glob.path.span, outside) {|ident, def| + let full_path = _mod.path + ident; + _mod.globbed_exports += [ident]; + maybe_add_reexport(e, full_path, some(def)); + } + } } none { } } @@ -2112,7 +2206,7 @@ fn find_impls_in_view_item(e: env, vi: @ast::view_item, } } - iter_import_paths(*vi) { |vp| + iter_effective_import_paths(*vi) { |vp| alt vp.node { ast::view_path_simple(name, pt, id) { let found = []; @@ -2157,7 +2251,7 @@ fn find_impls_in_view_item(e: env, vi: @ast::view_item, fn find_impls_in_item(e: env, i: @ast::item, &impls: [@_impl], name: option, - ck_exports: option) { + ck_exports: option<@indexed_mod>) { alt i.node { ast::item_impl(_, ifce, _, mthds) { if alt name { some(n) { n == i.ident } _ { true } } && @@ -2188,14 +2282,15 @@ fn find_impls_in_mod_by_id(e: env, defid: def_id, &impls: [@_impl], e.impl_cache.insert(defid, none); cached = if defid.crate == ast::local_crate { let tmp = []; - let md = option::get(e.mod_map.get(defid.node).m); + let mi = e.mod_map.get(defid.node); + let md = option::get(mi.m); for vi in md.view_items { find_impls_in_view_item(e, vi, tmp, none); } for i in md.items { find_impls_in_item(e, i, tmp, none, none); } - @vec::filter(tmp) {|i| is_exported(e, i.ident, md)} + @vec::filter(tmp) {|i| is_exported(e, i.ident, mi)} } else { csearch::get_impls_for_mod(e.sess.cstore, defid, none) }; diff --git a/src/test/run-pass/export-glob.rs b/src/test/run-pass/export-glob.rs index 889805911c1..20c117f5914 100644 --- a/src/test/run-pass/export-glob.rs +++ b/src/test/run-pass/export-glob.rs @@ -1,5 +1,3 @@ -// xfail-test - // Test that a glob-export functions as an explicit // named export when referenced from outside its scope.