From e98286b5944c27cde4a5a268ff4ca926b40b8e76 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 23 Nov 2011 20:57:34 +0100 Subject: [PATCH] Allow import directives in any block Closes #49 --- src/comp/front/config.rs | 3 +- src/comp/front/test.rs | 1 + src/comp/middle/resolve.rs | 162 +++++++++++++++------------ src/comp/syntax/ast.rs | 5 +- src/comp/syntax/ast_util.rs | 2 +- src/comp/syntax/fold.rs | 3 +- src/comp/syntax/parse/parser.rs | 7 +- src/comp/syntax/visit.rs | 3 +- src/test/run-pass/import-in-block.rs | 11 ++ 9 files changed, 114 insertions(+), 83 deletions(-) create mode 100644 src/test/run-pass/import-in-block.rs diff --git a/src/comp/front/config.rs b/src/comp/front/config.rs index c227ca2c50f4..955510864ea9 100644 --- a/src/comp/front/config.rs +++ b/src/comp/front/config.rs @@ -70,7 +70,8 @@ fn fold_block(cfg: ast::crate_cfg, b: ast::blk_, fld: fold::ast_fold) -> ast::blk_ { let filter = bind filter_stmt(cfg, _); let filtered_stmts = vec::filter_map(filter, b.stmts); - ret {stmts: vec::map(fld.fold_stmt, filtered_stmts), + ret {view_items: b.view_items, + stmts: vec::map(fld.fold_stmt, filtered_stmts), expr: option::map(fld.fold_expr, b.expr), id: b.id, rules: b.rules}; diff --git a/src/comp/front/test.rs b/src/comp/front/test.rs index 95c592eeada3..d704045c990e 100644 --- a/src/comp/front/test.rs +++ b/src/comp/front/test.rs @@ -348,6 +348,7 @@ fn mk_test_wrapper(cx: test_ctxt, }; let wrapper_body: ast::blk = nospan({ + view_items: [], stmts: [@call_stmt], expr: option::none, id: cx.next_node_id(), diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs index 5b1dca2a2f6e..d5c70b35cef6 100644 --- a/src/comp/middle/resolve.rs +++ b/src/comp/middle/resolve.rs @@ -37,7 +37,6 @@ tag scope { scope_fn(ast::fn_decl, ast::proto, [ast::ty_param]); scope_native_item(@ast::native_item); scope_loop(@ast::local); // there's only 1 decl per loop. - scope_block(ast::blk, @mutable uint, @mutable uint); scope_arm(ast::arm); } @@ -105,6 +104,7 @@ type env = ast_map: ast_map::map, imports: hashmap, mod_map: hashmap, + block_map: hashmap, ext_map: hashmap, ext_cache: ext_hash, used_imports: {mutable track: bool, @@ -124,11 +124,12 @@ fn resolve_crate(sess: session, amap: ast_map::map, crate: @ast::crate) -> {def_map: def_map, ext_map: ext_map} { let e = @{cstore: sess.get_cstore(), - def_map: new_int_hash::(), + def_map: new_int_hash(), ast_map: amap, - imports: new_int_hash::(), - mod_map: new_int_hash::<@indexed_mod>(), - ext_map: new_def_hash::<[ident]>(), + imports: new_int_hash(), + mod_map: new_int_hash(), + block_map: new_int_hash(), + ext_map: new_def_hash(), ext_cache: new_ext_hash(), used_imports: {mutable track: false, mutable data: []}, mutable reported: [], @@ -149,14 +150,14 @@ fn resolve_crate(sess: session, amap: ast_map::map, crate: @ast::crate) -> // resolve through them. fn map_crate(e: @env, c: @ast::crate) { // First, find all the modules, and index the names that they contain - let v_map_mod = @{visit_view_item: bind index_vi(e, _, _, _), - visit_item: bind index_i(e, _, _, _) - with *visit::default_visitor::()}; + visit_item: bind index_i(e, _, _, _), + visit_block: visit_block_with_scope + with *visit::default_visitor::()}; visit::visit_crate(*c, cons(scope_crate, @nil), visit::mk_vt(v_map_mod)); - // Register the top-level mod + // Register the top-level mod e.mod_map.insert(-1, @{m: some(c.node.module), index: index_mod(c.node.module), @@ -200,38 +201,36 @@ fn map_crate(e: @env, c: @ast::crate) { _ { } } } - // Next, assemble the links for globbed imports. + // Next, assemble the links for globbed imports. let v_link_glob = @{visit_view_item: bind link_glob(e, _, _, _), + visit_block: visit_block_with_scope, visit_item: visit_item_with_scope - with *visit::default_visitor::()}; + with *visit::default_visitor::()}; visit::visit_crate(*c, cons(scope_crate, @nil), visit::mk_vt(v_link_glob)); fn link_glob(e: @env, vi: @ast::view_item, sc: scopes, _v: vt) { - fn find_mod(e: @env, sc: scopes) -> @indexed_mod { - alt sc { - cons(scope_item(i), tl) { - alt i.node { - ast::item_mod(_) | ast::item_native_mod(_) { - ret e.mod_map.get(i.id); - } - _ { be find_mod(e, *tl); } - } - } - _ { - ret e.mod_map.get(-1); //top-level - - } - } - } alt vi.node { //if it really is a glob import, that is ast::view_item_import_glob(path, _) { let imp = follow_import(*e, sc, path, vi.span); if option::is_some(imp) { - find_mod(e, sc).glob_imports += - [{def: option::get(imp), item: vi}]; + let glob = {def: option::get(imp), item: vi};; + alt sc { + cons(scope_item(i), _) { + e.mod_map.get(i.id).glob_imports += [glob]; + } + cons(scope_block(b, _, _), _) { + let globs = alt e.block_map.find(b.node.id) { + some(globs) { globs + [glob] } none. { [glob] } + }; + e.block_map.insert(b.node.id, globs); + } + nil. { + e.mod_map.get(-1).glob_imports += [glob]; + } + } } } _ { } @@ -370,6 +369,7 @@ fn visit_fn_with_scope(e: @env, f: ast::_fn, tp: [ast::ty_param], sp: span, fn visit_block_with_scope(b: ast::blk, sc: scopes, v: vt) { let pos = @mutable 0u, loc = @mutable 0u; let block_sc = cons(scope_block(b, pos, loc), @sc); + for vi in b.node.view_items { v.visit_view_item(vi, block_sc, v); } for stmt in b.node.stmts { v.visit_stmt(stmt, block_sc, v);; *pos += 1u;; @@ -426,9 +426,8 @@ fn follow_import(e: env, sc: scopes, path: [ident], sp: span) -> alt option::get(dcur) { ast::def_mod(_) | ast::def_native_mod(_) { ret dcur; } _ { - e.sess.span_err(sp, - str::connect(path, "::") + - " does not name a module."); + e.sess.span_err(sp, str::connect(path, "::") + + " does not name a module."); ret none; } } @@ -678,7 +677,7 @@ fn lookup_in_scope(e: env, sc: scopes, sp: span, name: ident, ns: namespace) } } scope_block(b, pos, loc) { - ret lookup_in_block(name, b.node, *pos, *loc, ns); + ret lookup_in_block(e, name, sp, b.node, *pos, *loc, ns); } scope_arm(a) { if ns == ns_value { @@ -793,8 +792,8 @@ fn lookup_in_obj(name: ident, ob: ast::_obj, ty_params: [ast::ty_param], } } -fn lookup_in_block(name: ident, b: ast::blk_, pos: uint, loc_pos: uint, - ns: namespace) -> option::t { +fn lookup_in_block(e: env, name: ident, sp: span, b: ast::blk_, pos: uint, + loc_pos: uint, ns: namespace) -> option::t { let i = vec::len(b.stmts); while i > 0u { i -= 1u; @@ -849,7 +848,30 @@ fn lookup_in_block(name: ident, b: ast::blk_, pos: uint, loc_pos: uint, _ { } } } - ret none::; + for vi in b.view_items { + alt vi.node { + ast::view_item_import(ident, _, id) { + if name == ident { ret lookup_import(e, local_def(id), ns); } + } + ast::view_item_import_from(mod_path, idents, id) { + for ident in idents { + if name == ident.node.name { + ret lookup_import(e, local_def(ident.node.id), ns); + } + } + } + ast::view_item_import_glob(_, _) { + alt e.block_map.find(b.id) { + some(globs) { + let found = lookup_in_globs(e, globs, sp, name, ns, inside); + if found != none { ret found; } + } + _ {} + } + } + } + } + ret none; } fn found_def_item(i: @ast::item, ns: namespace) -> option::t { @@ -991,57 +1013,51 @@ fn lookup_in_local_mod(e: env, node_id: node_id, sp: span, id: ident, } } } - // not local or explicitly imported; try globs: ret lookup_glob_in_mod(e, info, sp, id, ns, outside); } -fn lookup_glob_in_mod(e: env, info: @indexed_mod, sp: span, id: ident, - wanted_ns: namespace, dr: dir) -> option::t { - fn per_ns(e: env, info: @indexed_mod, sp: span, id: ident, ns: namespace, - dr: dir) -> option::t { - - fn lookup_in_mod_(e: env, def: glob_imp_def, sp: span, name: ident, - ns: namespace, dr: dir) -> option::t { - alt lookup_in_mod(e, def.def, sp, name, ns, dr) { - option::some(d) { option::some({def: d, item: def.item}) } - option::none. { option::none } - } - } - - let matches = - vec::filter_map(bind lookup_in_mod_(e, _, sp, id, ns, dr), - { info.glob_imports }); - if vec::len(matches) == 0u { - ret none; - } else if vec::len(matches) == 1u { - ret some(matches[0].def); - } else { - for match: glob_imp_def in matches { - let sp = match.item.span; - e.sess.span_note(sp, #fmt["'%s' is imported here", id]); - } - e.sess.span_fatal(sp, - "'" + id + "' is glob-imported from" + - " multiple different modules."); +fn lookup_in_globs(e: env, globs: [glob_imp_def], sp: span, id: ident, + ns: namespace, dr: dir) -> option::t { + fn lookup_in_mod_(e: env, def: glob_imp_def, sp: span, name: ident, + ns: namespace, dr: dir) -> option::t { + alt lookup_in_mod(e, def.def, sp, name, ns, dr) { + option::some(d) { option::some({def: d, item: def.item}) } + option::none. { option::none } } } + let matches = + vec::filter_map(bind lookup_in_mod_(e, _, sp, id, ns, dr), + { globs }); + if vec::len(matches) == 0u { + ret none; + } else if vec::len(matches) == 1u { + ret some(matches[0].def); + } else { + for match: glob_imp_def in matches { + let sp = match.item.span; + e.sess.span_note(sp, #fmt["'%s' is imported here", id]); + } + e.sess.span_fatal(sp, "'" + id + "' is glob-imported from" + + " multiple different modules."); + } +} + +fn lookup_glob_in_mod(e: env, info: @indexed_mod, sp: span, id: ident, + wanted_ns: namespace, dr: dir) -> option::t { // 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)); - let val = per_ns(e, info, sp, id, ns_value, dr); - let typ = per_ns(e, info, sp, id, ns_type, dr); - let md = per_ns(e, info, sp, id, ns_module, dr); + let val = lookup_in_globs(e, info.glob_imports, sp, id, ns_value, dr); + let typ = lookup_in_globs(e, info.glob_imports, sp, id, ns_type, dr); + let md = lookup_in_globs(e, info.glob_imports, sp, id, ns_module, dr); info.glob_imported_names.insert(id, resolved(val, typ, md, id, sp)); } alt info.glob_imported_names.get(id) { todo(_, _, _, _, _) { e.sess.bug("Shouldn't've put a todo in."); } - resolving(sp) { - ret none::; //circularity is okay in import globs - - } + //circularity is okay in import globs + resolving(sp) { ret none::; } resolved(val, typ, md, _, _) { ret alt wanted_ns { ns_value. { val } diff --git a/src/comp/syntax/ast.rs b/src/comp/syntax/ast.rs index b1e1a16c6af4..ecba4efceca9 100644 --- a/src/comp/syntax/ast.rs +++ b/src/comp/syntax/ast.rs @@ -76,9 +76,8 @@ tag meta_item_ { type blk = spanned; -type blk_ = - {stmts: [@stmt], expr: option::t<@expr>, id: node_id, - rules: blk_check_mode}; +type blk_ = {view_items: [@view_item], stmts: [@stmt], expr: option::t<@expr>, + id: node_id, rules: blk_check_mode}; type pat = {id: node_id, node: pat_, span: span}; diff --git a/src/comp/syntax/ast_util.rs b/src/comp/syntax/ast_util.rs index 862d182b9fd3..2b574ae1cbe4 100644 --- a/src/comp/syntax/ast_util.rs +++ b/src/comp/syntax/ast_util.rs @@ -199,7 +199,7 @@ fn block_from_expr(e: @expr) -> blk { fn default_block(stmts1: [@stmt], expr1: option::t<@expr>, id1: node_id) -> blk_ { - ret {stmts: stmts1, expr: expr1, id: id1, rules: default_blk}; + {view_items: [], stmts: stmts1, expr: expr1, id: id1, rules: default_blk} } fn obj_field_from_anon_obj_field(f: anon_obj_field) -> obj_field { diff --git a/src/comp/syntax/fold.rs b/src/comp/syntax/fold.rs index 956f9e538f06..f9c23b4de250 100644 --- a/src/comp/syntax/fold.rs +++ b/src/comp/syntax/fold.rs @@ -247,7 +247,8 @@ fn noop_fold_method(m: method_, fld: ast_fold) -> method_ { fn noop_fold_block(b: blk_, fld: ast_fold) -> blk_ { - ret {stmts: vec::map(fld.fold_stmt, b.stmts), + ret {view_items: vec::map(fld.fold_view_item, b.view_items), + stmts: vec::map(fld.fold_stmt, b.stmts), expr: option::map(fld.fold_expr, b.expr), id: b.id, rules: b.rules}; diff --git a/src/comp/syntax/parse/parser.rs b/src/comp/syntax/parse/parser.rs index 391d5eb88195..df7690330286 100644 --- a/src/comp/syntax/parse/parser.rs +++ b/src/comp/syntax/parse/parser.rs @@ -1697,8 +1697,8 @@ fn parse_block_no_value(p: parser) -> ast::blk { // necessary, and this should take a qualifier. // some blocks start with "#{"... fn parse_block_tail(p: parser, lo: uint, s: ast::blk_check_mode) -> ast::blk { - let stmts: [@ast::stmt] = []; - let expr: option::t<@ast::expr> = none; + let view_items = [], stmts = [], expr = none; + while is_word(p, "import") { view_items += [parse_view_item(p)]; } while p.peek() != token::RBRACE { alt p.peek() { token::SEMI. { @@ -1736,7 +1736,8 @@ fn parse_block_tail(p: parser, lo: uint, s: ast::blk_check_mode) -> ast::blk { } let hi = p.get_hi_pos(); p.bump(); - let bloc = {stmts: stmts, expr: expr, id: p.get_id(), rules: s}; + let bloc = {view_items: view_items, stmts: stmts, expr: expr, + id: p.get_id(), rules: s}; ret spanned(lo, hi, bloc); } diff --git a/src/comp/syntax/visit.rs b/src/comp/syntax/visit.rs index 7430c28b749f..5273d16e544c 100644 --- a/src/comp/syntax/visit.rs +++ b/src/comp/syntax/visit.rs @@ -200,7 +200,8 @@ fn visit_fn(f: _fn, _tp: [ty_param], _sp: span, _i: fn_ident, _id: node_id, } fn visit_block(b: ast::blk, e: E, v: vt) { - for s: @stmt in b.node.stmts { v.visit_stmt(s, e, v); } + for vi in b.node.view_items { v.visit_view_item(vi, e, v); } + for s in b.node.stmts { v.visit_stmt(s, e, v); } visit_expr_opt(b.node.expr, e, v); } diff --git a/src/test/run-pass/import-in-block.rs b/src/test/run-pass/import-in-block.rs new file mode 100644 index 000000000000..fae30afa4ad8 --- /dev/null +++ b/src/test/run-pass/import-in-block.rs @@ -0,0 +1,11 @@ +use std; + +fn main() { + import std::vec; + import vec::to_mut; + log vec::len(to_mut([1, 2])); + { + import vec::*; + log len([2]); + } +}