rust/src/rustc/middle/resolve.rs
Niko Matsakis 34b42eeb65 change list so that it must be used in a purely boxed fashion
The old way was inconsistent---the head was unboxed but the
tail was boxed.  This resulted in numerous needless copies and
also made the borrow check unhappy, because the head tended to be
stored in mutable memory.
2012-05-21 09:37:34 -07:00

2372 lines
79 KiB
Rust

import syntax::{ast, ast_util, codemap};
import syntax::ast::*;
import ast::{ident, fn_ident, def, def_id, node_id};
import syntax::ast_util::{local_def, def_id_of_def, class_item_ident};
import pat_util::*;
import syntax::attr;
import metadata::{csearch, cstore};
import driver::session::session;
import util::common::*;
import std::map::{int_hash, str_hash, hashmap};
import vec::each;
import syntax::codemap::span;
import syntax::visit;
import visit::vt;
import std::{list};
import std::list::{list, nil, cons};
import option::{is_none, is_some};
import syntax::print::pprust::*;
import dvec::{dvec, extensions};
export resolve_crate;
export def_map, ext_map, exp_map, impl_map;
export _impl, iscopes, method_info;
// Resolving happens in two passes. The first pass collects defids of all
// (internal) imports and modules, so that they can be looked up when needed,
// and then uses this information to resolve the imports. The second pass
// locates all names (in expressions, types, and alt patterns) and resolves
// them, storing the resulting def in the AST nodes.
enum scope {
scope_toplevel,
scope_crate,
scope_item(@ast::item),
scope_bare_fn(ast::fn_decl, node_id, [ast::ty_param]),
scope_fn_expr(ast::fn_decl, node_id, [ast::ty_param]),
scope_native_item(@ast::native_item),
scope_loop(@ast::local), // there's only 1 decl per loop.
scope_block(ast::blk, @mut uint, @mut uint),
scope_arm(ast::arm),
scope_method(node_id, [ast::ty_param]),
}
type scopes = @list<scope>;
fn top_scope() -> scopes {
@cons(scope_crate, @cons(scope_toplevel, @nil))
}
enum import_state {
todo(ast::ident, @[ast::ident], span, scopes),
is_glob(@[ast::ident], scopes, span),
resolving(span),
resolved(option<def>, /* value */
option<def>, /* type */
option<def>, /* module */
@[@_impl], /* impls */
/* used for reporting unused import warning */
ast::ident, span),
}
enum glob_import_state {
glob_resolving(span),
glob_resolved(option<def>, /* value */
option<def>, /* type */
option<def>), /* module */
}
type ext_hash = hashmap<{did: def_id, ident: str, ns: namespace}, def>;
fn new_ext_hash() -> ext_hash {
type key = {did: def_id, ident: str, ns: namespace};
fn hash(v: key) -> uint {
str::hash(v.ident) + util::common::hash_def(v.did) + v.ns as uint
}
fn eq(v1: key, v2: key) -> bool {
ret util::common::def_eq(v1.did, v2.did) &&
str::eq(v1.ident, v2.ident) && v1.ns == v2.ns;
}
std::map::hashmap(hash, {|a, b| a == b})
}
enum mod_index_entry {
mie_view_item(ident, node_id, span),
mie_import_ident(node_id, span),
mie_item(@ast::item),
mie_native_item(@ast::native_item),
mie_enum_variant(/* variant index */uint,
/*parts of enum item*/ [variant],
node_id, span),
}
type mod_index = hashmap<ident, @list<mod_index_entry>>;
// A tuple of an imported def and the view_path from its originating import
type glob_imp_def = {def: def, path: @ast::view_path};
type indexed_mod = {
m: option<ast::_mod>,
index: mod_index,
mut glob_imports: [glob_imp_def],
mut globbed_exports: [ident],
glob_imported_names: hashmap<str, glob_import_state>,
path: str
};
/* native modules can't contain enums, and we don't store their ASTs because
we only need to look at them to determine exports, which they can't
control.*/
type def_map = hashmap<node_id, def>;
type ext_map = hashmap<def_id, [ident]>;
type impl_map = hashmap<node_id, iscopes>;
type impl_cache = hashmap<def_id, option<@[@_impl]>>;
type exp = {reexp: bool, id: def_id};
type exp_map = hashmap<node_id, [exp]>;
type env =
{cstore: cstore::cstore,
def_map: def_map,
ast_map: ast_map::map,
imports: hashmap<node_id, import_state>,
mut exp_map: exp_map,
mod_map: hashmap<node_id, @indexed_mod>,
block_map: hashmap<node_id, [glob_imp_def]>,
ext_map: ext_map,
impl_map: impl_map,
impl_cache: impl_cache,
ext_cache: ext_hash,
used_imports: {mut track: bool,
mut data: [node_id]},
mut reported: [{ident: str, sc: scope}],
mut ignored_imports: [node_id],
mut current_tp: option<uint>,
mut resolve_unexported: bool,
sess: session};
// Used to distinguish between lookups from outside and from inside modules,
// since export restrictions should only be applied for the former.
enum dir { inside, outside, }
enum namespace { ns_val, ns_type, ns_module, }
fn resolve_crate(sess: session, amap: ast_map::map, crate: @ast::crate) ->
{def_map: def_map, exp_map: exp_map, impl_map: impl_map} {
let e = create_env(sess, amap);
map_crate(e, crate);
resolve_imports(*e);
check_exports(e);
resolve_names(e, crate);
resolve_impls(e, crate);
// check_for_collisions must happen after resolve_names so we
// don't complain if a pattern uses the same nullary enum twice
check_for_collisions(e, *crate);
// FIXME: move this to the lint pass when rewriting resolve.
for sess.opts.lint_opts.each {|pair|
let (lint,level) = pair;
if lint == lint::unused_imports && level != lint::ignore {
check_unused_imports(e, level);
break;
}
}
ret {def_map: e.def_map, exp_map: e.exp_map, impl_map: e.impl_map};
}
fn create_env(sess: session, amap: ast_map::map) -> @env {
@{cstore: sess.cstore,
def_map: int_hash(),
ast_map: amap,
imports: int_hash(),
mut exp_map: int_hash(),
mod_map: int_hash(),
block_map: int_hash(),
ext_map: new_def_hash(),
impl_map: int_hash(),
impl_cache: new_def_hash(),
ext_cache: new_ext_hash(),
used_imports: {mut track: false, mut data: []},
mut reported: [],
mut ignored_imports: [],
mut current_tp: none,
mut resolve_unexported: false,
sess: sess}
}
fn iter_export_paths(vi: ast::view_item, f: fn(vp: @ast::view_path)) {
alt vi.node {
ast::view_item_export(vps) {
for vps.each {|vp|
f(vp);
}
}
_ {}
}
}
fn iter_import_paths(vi: ast::view_item, f: fn(vp: @ast::view_path)) {
alt vi.node {
ast::view_item_import(vps) {
for vps.each {|vp| 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) {|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
// resolve through them.
fn map_crate(e: @env, c: @ast::crate) {
fn index_vi(e: @env, i: @ast::view_item, &&sc: scopes, _v: vt<scopes>) {
iter_effective_import_paths(*i) { |vp|
alt vp.node {
ast::view_path_simple(name, path, id) {
e.imports.insert(id, todo(name, @path.idents, vp.span,
sc));
}
ast::view_path_glob(path, id) {
e.imports.insert(id, is_glob(@path.idents, sc, vp.span));
}
ast::view_path_list(mod_path, idents, _) {
for idents.each {|ident|
let t = todo(ident.node.name,
@(mod_path.idents + [ident.node.name]),
ident.span, sc);
e.imports.insert(ident.node.id, t);
}
}
}
}
}
fn path_from_scope(sc: scopes, n: str) -> str {
let mut path = n + "::";
list::iter(sc) {|s|
alt s {
scope_item(i) { path = i.ident + "::" + path; }
_ {}
}
}
path
}
fn index_i(e: @env, i: @ast::item, &&sc: scopes, v: vt<scopes>) {
visit_item_with_scope(e, i, sc, v);
alt i.node {
ast::item_mod(md) {
e.mod_map.insert(i.id,
@{m: some(md),
index: index_mod(md),
mut glob_imports: [],
mut globbed_exports: [],
glob_imported_names: str_hash(),
path: path_from_scope(sc, i.ident)});
}
ast::item_native_mod(nmd) {
e.mod_map.insert(i.id,
@{m: none::<ast::_mod>,
index: index_nmod(nmd),
mut glob_imports: [],
mut globbed_exports: [],
glob_imported_names: str_hash(),
path: path_from_scope(sc, i.ident)});
}
_ { }
}
}
// Note: a glob export works as an implicit import, along with a
// re-export of anything that was exported at the glob-target location.
// 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<scopes>) {
iter_effective_import_paths(*vi) { |vp|
alt vp.node {
ast::view_path_glob(path, _) {
alt follow_import(*e, sc, path.idents, vp.span) {
some(imp) {
let glob = {def: imp, path: vp};
alt list::head(sc) {
scope_item(i) {
e.mod_map.get(i.id).glob_imports += [glob];
}
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);
}
scope_crate {
e.mod_map.get(ast::crate_node_id).glob_imports
+= [glob];
}
_ { e.sess.span_bug(vi.span, "unexpected scope in a \
glob import"); }
}
}
_ { }
}
}
_ { }
}
}
}
// 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, _, _, _),
visit_block: visit_block_with_scope
with *visit::default_visitor::<scopes>()};
visit::visit_crate(*c, top_scope(), visit::mk_vt(v_map_mod));
// Register the top-level mod
e.mod_map.insert(ast::crate_node_id,
@{m: some(c.node.module),
index: index_mod(c.node.module),
mut glob_imports: [],
mut globbed_exports: [],
glob_imported_names: str_hash(),
path: ""});
// Next, assemble the links for globbed imports and exports.
let v_link_glob =
@{visit_view_item: bind link_glob(e, _, _, _),
visit_block: visit_block_with_scope,
visit_item: bind visit_item_with_scope(e, _, _, _)
with *visit::default_visitor::<scopes>()};
visit::visit_crate(*c, top_scope(), visit::mk_vt(v_link_glob));
}
fn resolve_imports(e: env) {
e.used_imports.track = true;
for e.imports.each {|id, v|
alt check v {
todo(name, path, span, scopes) {
resolve_import(e, id, name, *path, span, scopes);
}
resolved(_, _, _, _, _, _) | is_glob(_, _, _) { }
}
}
e.used_imports.track = false;
e.sess.abort_if_errors();
}
// FIXME (#1634): move this to the lint pass when rewriting resolve. It's
// using lint-specific control flags presently but resolve-specific data
// structures. Should use the general lint framework (with scopes, attrs).
fn check_unused_imports(e: @env, level: lint::level) {
for e.imports.each {|k, v|
alt v {
resolved(_, _, _, _, name, sp) {
if !vec::contains(e.used_imports.data, k) {
alt level {
lint::warn {
e.sess.span_warn(sp, "unused import " + name);
}
lint::error {
e.sess.span_err(sp, "unused import " + name);
}
lint::ignore {
}
}
}
}
_ { }
}
};
}
fn resolve_capture_item(e: @env, sc: scopes, cap_item: ast::capture_item) {
let dcur = lookup_in_scope_strict(
*e, sc, cap_item.span, cap_item.name, ns_val);
maybe_insert(e, cap_item.id, dcur);
}
fn maybe_insert(e: @env, id: node_id, def: option<def>) {
alt def {
some(df) { e.def_map.insert(id, df); }
_ {}
}
}
fn resolve_iface_ref(p: @iface_ref, sc: scopes, e: @env) {
maybe_insert(e, p.id,
lookup_path_strict(*e, sc, p.path.span, p.path, ns_type));
}
fn resolve_names(e: @env, c: @ast::crate) {
e.used_imports.track = true;
let v =
@{visit_native_item: visit_native_item_with_scope,
visit_item: bind walk_item(e, _, _, _),
visit_block: visit_block_with_scope,
visit_decl: visit_decl_with_scope,
visit_arm: visit_arm_with_scope,
visit_local: bind visit_local_with_scope(e, _, _, _),
visit_pat: bind walk_pat(e, _, _, _),
visit_expr: bind walk_expr(e, _, _, _),
visit_ty: bind walk_ty(e, _, _, _),
visit_ty_params: bind walk_tps(e, _, _, _),
visit_constr: bind walk_constr(e, _, _, _, _, _),
visit_fn: bind visit_fn_with_scope(e, _, _, _, _, _, _, _)
with *visit::default_visitor()};
visit::visit_crate(*c, top_scope(), visit::mk_vt(v));
e.used_imports.track = false;
e.sess.abort_if_errors();
fn walk_item(e: @env, i: @ast::item, &&sc: scopes, v: vt<scopes>) {
visit_item_with_scope(e, i, sc, v);
alt i.node {
/* At this point, the code knows what ifaces the iface refs
refer to, so it's possible to resolve them.
*/
ast::item_impl(_, _, ifce, _, _) {
ifce.iter {|p| resolve_iface_ref(p, sc, e);}
}
ast::item_class(_, ifaces, _, _, _, _) {
for ifaces.each {|p|
resolve_iface_ref(p, sc, e);
}
}
_ {}
}
}
fn walk_expr(e: @env, exp: @ast::expr, &&sc: scopes, v: vt<scopes>) {
visit::visit_expr(exp, sc, v);
alt exp.node {
ast::expr_path(p) {
maybe_insert(e, exp.id,
lookup_path_strict(*e, sc, exp.span, p, ns_val));
}
ast::expr_fn(_, _, _, cap_clause) |
ast::expr_fn_block(_, _, cap_clause) {
for (*cap_clause).each { |ci|
resolve_capture_item(e, sc, ci);
}
}
_ { }
}
}
fn walk_ty(e: @env, t: @ast::ty, &&sc: scopes, v: vt<scopes>) {
visit::visit_ty(t, sc, v);
alt t.node {
ast::ty_path(p, id) {
maybe_insert(e, id,
lookup_path_strict(*e, sc, t.span, p, ns_type));
}
_ { }
}
}
fn walk_tps(e: @env, tps: [ast::ty_param], &&sc: scopes, v: vt<scopes>) {
let outer_current_tp = e.current_tp;
let mut current = 0u;
for tps.each {|tp|
e.current_tp = some(current);
for vec::each(*tp.bounds) {|bound|
alt bound {
bound_iface(t) { v.visit_ty(t, sc, v); }
_ {}
}
}
current += 1u;
}
e.current_tp = outer_current_tp;
}
fn walk_constr(e: @env, p: @ast::path, sp: span, id: node_id,
&&sc: scopes, _v: vt<scopes>) {
maybe_insert(e, id, lookup_path_strict(*e, sc, sp, p, ns_val));
}
fn walk_pat(e: @env, pat: @ast::pat, &&sc: scopes, v: vt<scopes>) {
visit::visit_pat(pat, sc, v);
alt pat.node {
ast::pat_enum(p, _) {
alt lookup_path_strict(*e, sc, p.span, p, ns_val) {
some(fnd@ast::def_variant(_,_)) {
e.def_map.insert(pat.id, fnd);
}
_ {
e.sess.span_err(p.span,
"not an enum variant: " +
ast_util::path_name(p));
}
}
}
/* Here we determine whether a given pat_ident binds a new
variable or refers to a nullary enum. */
ast::pat_ident(p, none) {
alt lookup_in_scope(*e, sc, p.span, path_to_ident(p),
ns_val, false) {
some(fnd@ast::def_variant(_,_)) {
e.def_map.insert(pat.id, fnd);
}
some(fnd@ast::def_const(_)) {
e.sess.span_err(p.span, "pattern variable conflicts \
with constant '" + path_to_ident(p) + "'");
}
// Binds a var -- nothing needs to be done
_ {}
}
}
_ { }
}
}
}
// Visit helper functions
/*
This is used in more than one context, thus should only call generic
visit methods. Called both from map_crate and resolve_names.
*/
fn visit_item_with_scope(e: @env, i: @ast::item,
&&sc: scopes, v: vt<scopes>) {
// Some magic here. Items with the !resolve_unexported attribute
// cause us to consider every name to be exported when resolving their
// contents. This is used to allow the test runner to run unexported
// tests.
let old_resolve_unexported = e.resolve_unexported;
e.resolve_unexported |=
attr::contains_name(attr::attr_metas(i.attrs),
"!resolve_unexported");
let sc = @cons(scope_item(i), sc);
alt i.node {
ast::item_impl(tps, _, ifce, sty, methods) {
visit::visit_ty_params(tps, sc, v);
option::iter(ifce) {|p| visit::visit_path(p.path, sc, v)};
v.visit_ty(sty, sc, v);
for methods.each {|m|
v.visit_ty_params(m.tps, sc, v);
let msc = @cons(scope_method(m.self_id, tps + m.tps), sc);
v.visit_fn(visit::fk_method(m.ident, [], m),
m.decl, m.body, m.span, m.id, msc, v);
}
}
ast::item_iface(tps, _, methods) {
visit::visit_ty_params(tps, sc, v);
for methods.each {|m|
let msc = @cons(scope_method(i.id, tps + m.tps), sc);
for m.decl.inputs.each {|a| v.visit_ty(a.ty, msc, v); }
v.visit_ty(m.decl.output, msc, v);
}
}
ast::item_class(tps, ifaces, members, ctor, m_dtor, _) {
visit::visit_ty_params(tps, sc, v);
// Can maybe skip this now that we require self on class fields
let class_scope = @cons(scope_item(i), sc);
/* visit the constructor... */
let ctor_scope = @cons(scope_method(ctor.node.self_id, tps),
class_scope);
/* visit the iface refs in the class scope */
for ifaces.each {|p|
visit::visit_path(p.path, class_scope, v);
}
// FIXME: should be fk_ctor?
visit_fn_with_scope(e, visit::fk_item_fn(i.ident, tps), ctor.node.dec,
ctor.node.body, ctor.span, ctor.node.id,
ctor_scope, v);
option::iter(m_dtor) {|dtor|
let dtor_scope = @cons(scope_method(dtor.node.self_id, tps),
class_scope);
visit_fn_with_scope(e, visit::fk_dtor(tps, dtor.node.self_id,
local_def(i.id)),
ast_util::dtor_dec(),
dtor.node.body, dtor.span, dtor.node.id,
dtor_scope, v);
};
/* visit the items */
for members.each {|cm|
alt cm.node {
class_method(m) {
let msc = @cons(scope_method(m.self_id, tps + m.tps),
class_scope);
visit_fn_with_scope(e,
visit::fk_item_fn(m.ident, tps), m.decl, m.body,
m.span, m.id, msc, v); }
instance_var(_,t,_,_,_) { v.visit_ty(t, class_scope, v); }
}
}
}
_ { visit::visit_item(i, sc, v); }
}
e.resolve_unexported = old_resolve_unexported;
}
fn visit_native_item_with_scope(ni: @ast::native_item, &&sc: scopes,
v: vt<scopes>) {
visit::visit_native_item(ni, @cons(scope_native_item(ni), sc), v);
}
fn visit_fn_with_scope(e: @env, fk: visit::fn_kind, decl: ast::fn_decl,
body: ast::blk, sp: span,
id: node_id, &&sc: scopes, v: vt<scopes>) {
// is this a main fn declaration?
alt fk {
visit::fk_item_fn(nm, _) {
if is_main_name([ast_map::path_name(nm)]) &&
!e.sess.building_library {
// This is a main function -- set it in the session
// as the main ID
e.sess.main_fn = some((id, sp));
}
}
_ { /* fallthrough */ }
}
// here's where we need to set up the mapping
// for f's constrs in the table.
for decl.constraints.each {|c| resolve_constr(e, c, sc, v); }
let scope = alt fk {
visit::fk_item_fn(_, tps) | visit::fk_res(_, tps, _) |
visit::fk_method(_, tps, _) | visit::fk_ctor(_, tps, _, _) |
visit::fk_dtor(tps, _, _) {
scope_bare_fn(decl, id, tps) }
visit::fk_anon(ast::proto_bare, _) {
scope_bare_fn(decl, id, []) }
visit::fk_anon(_, _) | visit::fk_fn_block(_) {
scope_fn_expr(decl, id, []) }
};
visit::visit_fn(fk, decl, body, sp, id, @cons(scope, sc), v);
}
fn visit_block_with_scope(b: ast::blk, &&sc: scopes, v: vt<scopes>) {
let pos = @mut 0u, loc = @mut 0u;
let block_sc = @cons(scope_block(b, pos, loc), sc);
for b.node.view_items.each {|vi| v.visit_view_item(vi, block_sc, v); }
for b.node.stmts.each {|stmt|
v.visit_stmt(stmt, block_sc, v);;
*pos += 1u;;
*loc = 0u;
}
visit::visit_expr_opt(b.node.expr, block_sc, v);
}
fn visit_decl_with_scope(d: @decl, &&sc: scopes, v: vt<scopes>) {
let loc_pos = alt list::head(sc) {
scope_block(_, _, pos) { pos }
_ { @mut 0u }
};
alt d.node {
decl_local(locs) {
for locs.each {|loc| v.visit_local(loc, sc, v);; *loc_pos += 1u; }
}
decl_item(it) { v.visit_item(it, sc, v); }
}
}
fn visit_arm_with_scope(a: ast::arm, &&sc: scopes, v: vt<scopes>) {
for a.pats.each {|p| v.visit_pat(p, sc, v); }
let sc_inner = @cons(scope_arm(a), sc);
visit::visit_expr_opt(a.guard, sc_inner, v);
v.visit_block(a.body, sc_inner, v);
}
// This is only for irrefutable patterns (e.g. ones that appear in a let)
// So if x occurs, and x is already known to be a enum, that's always an error
fn visit_local_with_scope(e: @env, loc: @local, &&sc: scopes, v:vt<scopes>) {
// Check whether the given local has the same name as a enum that's in
// scope. We disallow this, in order to make alt patterns consisting of a
// single identifier unambiguous (does the pattern "foo" refer to enum
// foo, or is it binding a new name foo?)
pat_util::walk_pat(loc.node.pat) { |p|
alt p.node {
pat_ident(path, _) {
alt lookup_in_scope(*e, sc, loc.span, path_to_ident(path),
ns_val, false) {
some(ast::def_variant(enum_id, variant_id)) {
// Declaration shadows an enum that's in scope.
// That's an error.
e.sess.span_err(loc.span,
#fmt("declaration of `%s` shadows an \
enum that's in scope",
path_to_ident(path)));
}
_ {}
}
}
_ {}
}
}
visit::visit_local(loc, sc, v);
}
fn follow_import(e: env, &&sc: scopes, path: [ident], sp: span) ->
option<def> {
let path_len = vec::len(path);
let mut dcur = lookup_in_scope_strict(e, sc, sp, path[0], ns_module);
let mut i = 1u;
loop {
alt dcur {
some(dcur_def) {
if i == path_len { break; }
dcur =
lookup_in_mod_strict(e, dcur_def, sp, path[i],
ns_module, outside);
i += 1u;
}
_ { break; }
}
}
if i == path_len {
alt dcur {
some(ast::def_mod(_)) | some(ast::def_native_mod(_)) { ret dcur; }
_ {
e.sess.span_err(sp, str::connect(path, "::") +
" does not name a module.");
ret none;
}
}
} else { ret none; }
}
fn resolve_constr(e: @env, c: @ast::constr, &&sc: scopes, _v: vt<scopes>) {
alt lookup_path_strict(*e, sc, c.span, c.node.path, ns_val) {
some(d@ast::def_fn(_,ast::pure_fn)) {
e.def_map.insert(c.node.id, d);
}
_ {
let s = path_to_str(c.node.path);
e.sess.span_err(c.span, #fmt("%s is not declared pure. Try \
`pure fn %s` instead of `fn %s`.", s, s, s));
}
}
}
// Import resolution
fn resolve_import(e: env, n_id: node_id, name: ast::ident,
ids: [ast::ident], sp: codemap::span, &&sc: scopes) {
fn register(e: env, id: node_id, cx: ctxt, sp: codemap::span,
name: ast::ident, lookup: fn(namespace) -> option<def>,
impls: [@_impl]) {
let val = lookup(ns_val), typ = lookup(ns_type),
md = lookup(ns_module);
if is_none(val) && is_none(typ) && is_none(md) &&
vec::len(impls) == 0u {
unresolved_err(e, cx, sp, name, "import");
} else {
e.imports.insert(id, resolved(val, typ, md, @impls, name, sp));
}
}
// Temporarily disable this import and the imports coming after during
// resolution of this import.
fn find_imports_after(e: env, id: node_id, &&sc: scopes) -> [node_id] {
fn lst(my_id: node_id, vis: [@view_item]) -> [node_id] {
let mut imports = [], found = false;
for vis.each {|vi|
iter_effective_import_paths(*vi) {|vp|
alt vp.node {
view_path_simple(_, _, id)
| view_path_glob(_, id) {
if id == my_id { found = true; }
if found { imports += [id]; }
}
view_path_list(_, ids, _) {
for ids.each {|id|
if id.node.id == my_id { found = true; }
if found { imports += [id.node.id]; }
}
}
}
}
}
imports
}
alt *sc {
cons(scope_item(@{node: item_mod(m), _}), _) {
lst(id, m.view_items)
}
cons(scope_item(@{node: item_native_mod(m), _}), _) {
lst(id, m.view_items)
}
cons(scope_block(b, _, _), _) {
lst(id, b.node.view_items)
}
cons(scope_crate, _) {
lst(id,
option::get(e.mod_map.get(ast::crate_node_id).m).view_items)
}
_ {
e.sess.bug("find_imports_after: nil or unexpected scope");
}
}
}
// This function has cleanup code at the end. Do not return without going
// through that.
e.imports.insert(n_id, resolving(sp));
let mut ignored = find_imports_after(e, n_id, sc);
e.ignored_imports <-> ignored;
let n_idents = vec::len(ids);
let end_id = ids[n_idents - 1u];
if n_idents == 1u {
register(e, n_id, in_scope(sc), sp, name,
{|ns| lookup_in_scope(e, sc, sp, end_id, ns, true) }, []);
} else {
alt lookup_in_scope(e, sc, sp, ids[0], ns_module, true) {
none {
unresolved_err(e, in_scope(sc), sp, ids[0], ns_name(ns_module));
}
some(dcur_) {
let mut dcur = dcur_, i = 1u;
loop {
if i == n_idents - 1u {
let mut impls = [];
find_impls_in_mod(e, dcur, impls, some(end_id));
register(e, n_id, in_mod(dcur), sp, name, {|ns|
lookup_in_mod(e, dcur, sp, end_id, ns, outside)
}, impls);
break;
} else {
dcur = alt lookup_in_mod(e, dcur, sp, ids[i], ns_module,
outside) {
some(dcur) { dcur }
none {
unresolved_err(e, in_mod(dcur), sp, ids[i],
ns_name(ns_module));
break;
}
};
i += 1u;
}
}
}
}
}
e.ignored_imports <-> ignored;
// If we couldn't resolve the import, don't leave it in a partially
// resolved state, to avoid having it reported later as a cyclic
// import
alt e.imports.find(n_id) {
some(resolving(sp)) {
e.imports.insert(n_id, resolved(none, none, none, @[], "", sp));
}
_ { }
}
}
// Utilities
fn ns_name(ns: namespace) -> str {
alt ns {
ns_type { "typename" }
ns_val { "name" }
ns_module { "modulename" }
}
}
enum ctxt { in_mod(def), in_scope(scopes), }
fn unresolved_err(e: env, cx: ctxt, sp: span, name: ident, kind: str) {
fn find_fn_or_mod_scope(sc: scopes) -> option<scope> {
for list::each(sc) {|cur|
alt cur {
scope_crate | scope_bare_fn(_, _, _) | scope_fn_expr(_, _, _) |
scope_item(@{node: ast::item_mod(_), _}) {
ret some(cur);
}
_ {}
}
}
ret none;
}
let mut path = name;
alt cx {
in_scope(sc) {
alt find_fn_or_mod_scope(sc) {
some(err_scope) {
for e.reported.each {|rs|
if str::eq(rs.ident, name) && err_scope == rs.sc { ret; }
}
e.reported += [{ident: name, sc: err_scope}];
}
_ {}
}
}
in_mod(def) {
let did = def_id_of_def(def);
if did.crate == ast::local_crate {
path = e.mod_map.get(did.node).path + path;
} else if did.node != ast::crate_node_id {
let paths = e.ext_map.get(did);
path = str::connect(paths + [path], "::");
}
}
}
e.sess.span_err(sp, mk_unresolved_msg(path, kind));
}
fn unresolved_fatal(e: env, sp: span, id: ident, kind: str) -> ! {
e.sess.span_fatal(sp, mk_unresolved_msg(id, kind));
}
fn mk_unresolved_msg(id: ident, kind: str) -> str {
ret #fmt["unresolved %s: %s", kind, id];
}
// Lookup helpers
fn lookup_path_strict(e: env, &&sc: scopes, sp: span, pth: @ast::path,
ns: namespace) -> option<def> {
let n_idents = vec::len(pth.idents);
let headns = if n_idents == 1u { ns } else { ns_module };
let first_scope = if pth.global { top_scope() } else { sc };
let dcur_ =
lookup_in_scope_strict(e, first_scope, sp, pth.idents[0], headns);
alt dcur_ {
none { ret none; }
some(dcur__) {
let mut i = 1u;
let mut dcur = dcur__;
while i < n_idents {
let curns = if n_idents == i + 1u { ns } else { ns_module };
alt lookup_in_mod_strict(e, dcur, sp, pth.idents[i],
curns, outside) {
none { break; }
some(thing) { dcur = thing; }
}
i += 1u;
}
ret some(dcur);
}
}
}
fn lookup_in_scope_strict(e: env, &&sc: scopes, sp: span, name: ident,
ns: namespace) -> option<def> {
alt lookup_in_scope(e, sc, sp, name, ns, true) {
none {
unresolved_err(e, in_scope(sc), sp, name, ns_name(ns));
ret none;
}
some(d) { ret some(d); }
}
}
fn scope_is_fn(sc: scope) -> bool {
ret alt sc {
scope_bare_fn(_, _, _) | scope_native_item(_) { true }
_ { false }
};
}
// Returns:
// none - does not close
// some(node_id) - closes via the expr w/ node_id
fn scope_closes(sc: scope) -> option<node_id> {
alt sc {
scope_fn_expr(_, node_id, _) { some(node_id) }
_ { none }
}
}
fn def_is_local(d: def) -> bool {
alt d {
ast::def_arg(_, _) | ast::def_local(_, _) | ast::def_binding(_) |
ast::def_upvar(_, _, _) { true }
_ { false }
}
}
fn def_is_self(d: def) -> bool {
alt d {
ast::def_self(_) { true }
_ { false }
}
}
fn def_is_ty_arg(d: def) -> bool {
ret alt d { ast::def_ty_param(_, _) { true } _ { false } };
}
fn lookup_in_scope(e: env, &&sc: scopes, sp: span, name: ident, ns: namespace,
check_capture: bool) -> option<def> {
fn in_scope(e: env, sp: span, name: ident, s: scope, ns: namespace) ->
option<def> {
alt s {
scope_toplevel {
if ns == ns_type {
ret some(ast::def_prim_ty(alt name {
"bool" { ast::ty_bool }
"int" { ast::ty_int(ast::ty_i) }
"uint" { ast::ty_uint(ast::ty_u) }
"float" { ast::ty_float(ast::ty_f) }
"str" { ast::ty_str }
"char" { ast::ty_int(ast::ty_char) }
"i8" { ast::ty_int(ast::ty_i8) }
"i16" { ast::ty_int(ast::ty_i16) }
"i32" { ast::ty_int(ast::ty_i32) }
"i64" { ast::ty_int(ast::ty_i64) }
"u8" { ast::ty_uint(ast::ty_u8) }
"u16" { ast::ty_uint(ast::ty_u16) }
"u32" { ast::ty_uint(ast::ty_u32) }
"u64" { ast::ty_uint(ast::ty_u64) }
"f32" { ast::ty_float(ast::ty_f32) }
"f64" { ast::ty_float(ast::ty_f64) }
_ { ret none; }
}));
}
}
scope_crate {
ret lookup_in_local_mod(e, ast::crate_node_id, sp,
name, ns, inside);
}
scope_item(it) {
alt it.node {
ast::item_impl(tps, _, _, _, _) {
if ns == ns_type { ret lookup_in_ty_params(e, name, tps); }
}
ast::item_enum(_, tps, _) | ast::item_ty(_, tps, _) {
if ns == ns_type { ret lookup_in_ty_params(e, name, tps); }
}
ast::item_iface(tps, _, _) {
if ns == ns_type {
if name == "self" {
ret some(def_self(it.id));
}
ret lookup_in_ty_params(e, name, tps);
}
}
ast::item_mod(_) {
ret lookup_in_local_mod(e, it.id, sp, name, ns, inside);
}
ast::item_native_mod(m) {
ret lookup_in_local_native_mod(e, it.id, sp, name, ns);
}
ast::item_class(tps, _, members, ctor, _, _) {
if ns == ns_type {
ret lookup_in_ty_params(e, name, tps);
}
if ns == ns_val && name == it.ident {
ret some(ast::def_fn(local_def(ctor.node.id),
ast::impure_fn));
}
// FIXME: AST allows other items to appear in a class,
// but that might not be wise
}
_ { }
}
}
scope_method(id, tps) {
if (name == "self" && ns == ns_val) {
ret some(ast::def_self(id));
} else if ns == ns_type {
ret lookup_in_ty_params(e, name, tps);
}
}
scope_native_item(it) {
alt check it.node {
ast::native_item_fn(decl, ty_params) {
ret lookup_in_fn(e, name, decl, ty_params, ns);
}
}
}
scope_bare_fn(decl, _, ty_params) |
scope_fn_expr(decl, _, ty_params) {
ret lookup_in_fn(e, name, decl, ty_params, ns);
}
scope_loop(local) {
if ns == ns_val {
alt lookup_in_pat(e, name, local.node.pat) {
some(nid) { ret some(ast::def_binding(nid)); }
_ { }
}
}
}
scope_block(b, pos, loc) {
ret lookup_in_block(e, name, sp, b.node, *pos, *loc, ns);
}
scope_arm(a) {
if ns == ns_val {
alt lookup_in_pat(e, name, a.pats[0]) {
some(nid) { ret some(ast::def_binding(nid)); }
_ { ret none; }
}
}
}
}
ret none;
}
let mut left_fn = false;
let mut closing = [];
// Used to determine whether self is in scope
let mut left_fn_level2 = false;
let mut sc = sc;
loop {
alt *sc {
nil { ret none; }
cons(hd, tl) {
alt in_scope(e, sp, name, hd, ns) {
some(df_) {
let mut df = df_;
let local = def_is_local(df), self_scope = def_is_self(df);
if check_capture &&
(left_fn && local || left_fn_level2 && self_scope
|| scope_is_fn(hd) && left_fn && def_is_ty_arg(df)) {
let msg = if ns == ns_type {
"attempt to use a type argument out of scope"
} else {
"attempted dynamic environment-capture"
};
e.sess.span_fatal(sp, msg);
} else if local || self_scope {
let mut i = vec::len(closing);
while i > 0u {
i -= 1u;
#debug["name=%s df=%?", name, df];
assert def_is_local(df) || def_is_self(df);
let df_id = def_id_of_def(df).node;
df = ast::def_upvar(df_id, @df, closing[i]);
}
}
ret some(df);
}
_ {}
}
if left_fn {
left_fn_level2 = true;
} else if ns != ns_module {
left_fn = scope_is_fn(hd);
alt scope_closes(hd) {
some(node_id) { closing += [node_id]; }
_ { }
}
}
sc = tl;
}
}
};
}
fn lookup_in_ty_params(e: env, name: ident, ty_params: [ast::ty_param])
-> option<def> {
let mut n = 0u;
for ty_params.each {|tp|
if str::eq(tp.ident, name) && alt e.current_tp {
some(cur) { n < cur } none { true }
} { ret some(ast::def_ty_param(local_def(tp.id), n)); }
n += 1u;
}
ret none;
}
fn lookup_in_pat(e: env, name: ident, pat: @ast::pat) -> option<node_id> {
let mut found = none;
pat_util::pat_bindings(e.def_map, pat) {|p_id, _sp, n|
if str::eq(path_to_ident(n), name)
{ found = some(p_id); }
};
ret found;
}
fn lookup_in_fn(e: env, name: ident, decl: ast::fn_decl,
ty_params: [ast::ty_param],
ns: namespace) -> option<def> {
alt ns {
ns_val {
for decl.inputs.each {|a|
if str::eq(a.ident, name) {
ret some(ast::def_arg(a.id, a.mode));
}
}
ret none;
}
ns_type { ret lookup_in_ty_params(e, name, ty_params); }
_ { ret none; }
}
}
fn lookup_in_block(e: env, name: ident, sp: span, b: ast::blk_, pos: uint,
loc_pos: uint, ns: namespace) -> option<def> {
let mut i = vec::len(b.stmts);
while i > 0u {
i -= 1u;
let st = b.stmts[i];
alt st.node {
ast::stmt_decl(d, _) {
alt d.node {
ast::decl_local(locs) {
if i <= pos {
let mut j = vec::len(locs);
while j > 0u {
j -= 1u;
let loc = locs[j];
if ns == ns_val && (i < pos || j < loc_pos) {
alt lookup_in_pat(e, name, loc.node.pat) {
some(nid) {
ret some(ast::def_local(nid,
loc.node.is_mutbl));
}
_ { }
}
}
}
}
}
ast::decl_item(it) {
alt it.node {
ast::item_enum(variants, _, _) {
if ns == ns_type {
if str::eq(it.ident, name) {
ret some(ast::def_ty(local_def(it.id)));
}
} else {
alt ns {
ns_val {
for variants.each {|v|
if str::eq(v.node.name, name) {
let i = v.node.id;
ret some(ast::def_variant
(local_def(it.id), local_def(i)));
}
}
}
_ {}
}
}
}
_ {
if str::eq(it.ident, name) {
let found = found_def_item(it, ns);
if !is_none(found) {
ret found;
}
}
}
}
}
}
}
_ { }
}
}
for b.view_items.each {|vi|
let mut is_import = false;
alt vi.node {
ast::view_item_import(_) { is_import = true; }
_ {}
}
alt vi.node {
ast::view_item_import(vps) | ast::view_item_export(vps) {
for vps.each {|vp|
alt vp.node {
ast::view_path_simple(ident, _, id) {
if is_import && name == ident {
ret lookup_import(e, id, ns);
}
}
ast::view_path_list(path, idents, _) {
for idents.each {|ident|
if name == ident.node.name {
ret lookup_import(e, ident.node.id, ns);
}
}
}
ast::view_path_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;
}
}
_ {}
}
}
}
}
}
_ { e.sess.span_bug(vi.span, "unexpected view_item in block"); }
}
}
ret none;
}
fn found_def_item(i: @ast::item, ns: namespace) -> option<def> {
alt i.node {
ast::item_const(*) {
if ns == ns_val {
ret some(ast::def_const(local_def(i.id))); }
}
ast::item_fn(decl, _, _) {
if ns == ns_val {
ret some(ast::def_fn(local_def(i.id), decl.purity));
}
}
ast::item_mod(_) {
if ns == ns_module { ret some(ast::def_mod(local_def(i.id))); }
}
ast::item_native_mod(_) {
if ns == ns_module { ret some(ast::def_native_mod(local_def(i.id))); }
}
ast::item_ty(*) | item_iface(*) | item_enum(*) {
if ns == ns_type { ret some(ast::def_ty(local_def(i.id))); }
}
ast::item_res(_, _, _, _, ctor_id, _) {
alt ns {
ns_val {
ret some(ast::def_fn(local_def(ctor_id), ast::impure_fn));
}
ns_type { ret some(ast::def_ty(local_def(i.id))); }
_ { }
}
}
ast::item_class(*) {
if ns == ns_type {
ret some(ast::def_class(local_def(i.id)));
}
}
ast::item_impl(*) { /* ??? */ }
}
ret none;
}
fn lookup_in_mod_strict(e: env, m: def, sp: span, name: ident,
ns: namespace, dr: dir) -> option<def> {
alt lookup_in_mod(e, m, sp, name, ns, dr) {
none {
unresolved_err(e, in_mod(m), sp, name, ns_name(ns));
ret none;
}
some(d) { ret some(d); }
}
}
fn lookup_in_mod(e: env, m: def, sp: span, name: ident, ns: namespace,
dr: dir) -> option<def> {
let defid = def_id_of_def(m);
if defid.crate != ast::local_crate {
// examining a module in an external crate
let cached = e.ext_cache.find({did: defid, ident: name, ns: ns});
if !is_none(cached) { ret cached; }
let mut path = [name];
if defid.node != ast::crate_node_id {
path = cstore::get_path(e.cstore, defid) + path;
}
alt lookup_external(e, defid.crate, path, ns) {
some(df) {
e.ext_cache.insert({did: defid, ident: name, ns: ns}, df);
ret some(df);
}
_ { ret none; }
}
}
alt m {
ast::def_mod(defid) {
ret lookup_in_local_mod(e, defid.node, sp, name, ns, dr);
}
ast::def_native_mod(defid) {
ret lookup_in_local_native_mod(e, defid.node, sp, name, ns);
}
_ {
// Precondition
e.sess.span_bug(sp, "lookup_in_mod was passed a non-mod def");
}
}
}
fn found_view_item(e: env, id: node_id) -> option<def> {
alt cstore::find_use_stmt_cnum(e.cstore, id) {
some(cnum) {
some(ast::def_mod({crate: cnum, node: ast::crate_node_id}))
}
none {
// This can happen if we didn't load external crate info.
// Rustdoc depends on this.
none
}
}
}
fn lookup_import(e: env, n_id: node_id, ns: namespace) -> option<def> {
// Imports are simply ignored when resolving themselves.
if vec::contains(e.ignored_imports, n_id) { ret none; }
alt e.imports.get(n_id) {
todo(name, path, span, scopes) {
resolve_import(e, n_id, name, *path, span, scopes);
ret lookup_import(e, n_id, ns);
}
resolving(sp) {
e.sess.span_err(sp, "cyclic import");
ret none;
}
resolved(val, typ, md, _, _, _) {
if e.used_imports.track {
e.used_imports.data += [n_id];
}
ret alt ns { ns_val { val } ns_type { typ } ns_module { md } };
}
is_glob(_,_,_) {
e.sess.bug("lookup_import: can't handle is_glob");
}
}
}
fn lookup_in_local_native_mod(e: env, node_id: node_id, sp: span, id: ident,
ns: namespace) -> option<def> {
ret lookup_in_local_mod(e, node_id, sp, id, ns, inside);
}
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;
}
// A list search function. Applies `f` to each element of `v`, starting from
// the first. When `f` returns `some(x)`, `list_search` returns `some(x)`. If
// `f` returns `none` for every element, `list_search` returns `none`.
fn list_search<T: copy, U: copy>(ls: @list<T>, f: fn(T) -> option<U>)
-> option<U> {
let mut ls = ls;
loop {
ls = alt *ls {
cons(hd, tl) {
let result = f(hd);
if !is_none(result) { ret result; }
tl
}
nil { ret none; }
};
}
}
fn lookup_in_local_mod(e: env, node_id: node_id, sp: span, id: ident,
ns: namespace, dr: dir) -> option<def> {
let inf = alt e.mod_map.find(node_id) {
some(x) { x }
none { e.sess.span_bug(sp, #fmt("lookup_in_local_mod: \
module %d not in mod_map", node_id)); }
};
if dr == outside && !is_exported(e, id, inf) {
// if we're in a native mod, then dr==inside, so inf.m is some _mod
ret none; // name is not visible
}
alt inf.index.find(id) {
none { }
some(lst) {
let found = list_search(lst, bind lookup_in_mie(e, _, ns));
if !is_none(found) {
ret found;
}
}
}
// not local or explicitly imported; try globs:
ret lookup_glob_in_mod(e, inf, sp, id, ns, outside);
}
fn lookup_in_globs(e: env, globs: [glob_imp_def], sp: span, id: ident,
ns: namespace, dr: dir) -> option<def> {
fn lookup_in_mod_(e: env, def: glob_imp_def, sp: span, name: ident,
ns: namespace, dr: dir) -> option<glob_imp_def> {
alt def.path.node {
ast::view_path_glob(_, id) {
if vec::contains(e.ignored_imports, id) { ret none; }
}
_ {
e.sess.span_bug(sp, "lookup_in_globs: not a glob");
}
}
alt lookup_in_mod(e, def.def, sp, name, ns, dr) {
some(d) { option::some({def: d, path: def.path}) }
none { none }
}
}
let matches = vec::filter_map(copy globs,
bind lookup_in_mod_(e, _, sp, id, ns, dr));
if vec::len(matches) == 0u {
ret none;
}
else if vec::len(matches) == 1u {
ret some(matches[0].def);
} else {
for matches.each {|match|
let sp = match.path.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<def> {
// 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, glob_resolving(sp));
let globs = info.glob_imports;
let val = lookup_in_globs(e, globs, sp, id, ns_val, dr);
let typ = lookup_in_globs(e, globs, sp, id, ns_type, dr);
let md = lookup_in_globs(e, globs, sp, id, ns_module, dr);
info.glob_imported_names.insert(id, glob_resolved(val, typ, md));
}
alt info.glob_imported_names.get(id) {
glob_resolving(sp) {
ret none;
}
glob_resolved(val, typ, md) {
ret alt wanted_ns {
ns_val { val }
ns_type { typ }
ns_module { md }
};
}
}
}
fn lookup_in_mie(e: env, mie: mod_index_entry, ns: namespace) ->
option<def> {
alt mie {
mie_view_item(_, id, _) {
if ns == ns_module { ret found_view_item(e, id); }
}
mie_import_ident(id, _) { ret lookup_import(e, id, ns); }
mie_item(item) { ret found_def_item(item, ns); }
mie_enum_variant(variant_idx, variants, parent_id, parent_span) {
alt ns {
ns_val {
let vid = variants[variant_idx].node.id;
ret some(ast::def_variant(local_def(parent_id),
local_def(vid)));
}
_ { ret none; }
}
}
mie_native_item(native_item) {
alt native_item.node {
ast::native_item_fn(decl, _) {
if ns == ns_val {
ret some(ast::def_fn(local_def(native_item.id),
decl.purity));
}
}
}
}
}
ret none;
}
// Module indexing
fn add_to_index(index: hashmap<ident, @list<mod_index_entry>>, id: ident,
ent: mod_index_entry) {
alt index.find(id) {
none { index.insert(id, @cons(ent, @nil)); }
some(prev) { index.insert(id, @cons(ent, prev)); }
}
}
fn index_view_items(view_items: [@ast::view_item],
index: hashmap<ident, @list<mod_index_entry>>) {
for view_items.each {|vi|
alt vi.node {
ast::view_item_use(ident, _, id) {
add_to_index(index, ident, mie_view_item(ident, id, vi.span));
}
_ {}
}
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));
}
ast::view_path_list(_, idents, _) {
for idents.each {|ident|
add_to_index(index, ident.node.name,
mie_import_ident(ident.node.id,
ident.span));
}
}
// globbed imports have to be resolved lazily.
ast::view_path_glob(_, _) {}
}
}
}
}
fn index_mod(md: ast::_mod) -> mod_index {
let index = str_hash::<@list<mod_index_entry>>();
index_view_items(md.view_items, index);
for md.items.each {|it|
alt it.node {
ast::item_const(_, _) | ast::item_fn(_, _, _) | ast::item_mod(_) |
ast::item_native_mod(_) | ast::item_ty(_, _, _) |
ast::item_res(*) | ast::item_impl(*) | ast::item_iface(*) {
add_to_index(index, it.ident, mie_item(it));
}
ast::item_enum(variants, _, _) {
add_to_index(index, it.ident, mie_item(it));
let mut variant_idx: uint = 0u;
for variants.each {|v|
add_to_index(index, v.node.name,
mie_enum_variant(variant_idx, variants,
it.id, it.span));
variant_idx += 1u;
}
}
ast::item_class(tps, _, items, ctor, _, _) {
// add the class name itself
add_to_index(index, it.ident, mie_item(it));
// add the constructor decl
add_to_index(index, it.ident,
mie_item(@{ident: it.ident, attrs: [],
id: ctor.node.id,
node:
item_fn(ctor.node.dec, tps, ctor.node.body),
vis: ast::public,
span: ctor.node.body.span}));
}
}
}
ret index;
}
fn index_nmod(md: ast::native_mod) -> mod_index {
let index = str_hash::<@list<mod_index_entry>>();
index_view_items(md.view_items, index);
for md.items.each {|it|
add_to_index(index, it.ident, mie_native_item(it));
}
ret index;
}
// External lookups
fn ns_for_def(d: def) -> namespace {
alt d {
ast::def_variant(_, _) { ns_val }
ast::def_fn(_, _) | ast::def_self(_) |
ast::def_const(_) | ast::def_arg(_, _) | ast::def_local(_, _) |
ast::def_upvar(_, _, _) { ns_val }
ast::def_mod(_) | ast::def_native_mod(_) { ns_module }
ast::def_ty(_) | ast::def_binding(_) | ast::def_use(_) |
ast::def_ty_param(_, _) | ast::def_prim_ty(_) | ast::def_class(_)
{ ns_type }
ast::def_region(_) { fail "regions are not handled by this pass" }
}
}
fn lookup_external(e: env, cnum: int, ids: [ident], ns: namespace) ->
option<def> {
let mut result = none;
for csearch::lookup_defs(e.sess.cstore, cnum, ids).each {|d|
e.ext_map.insert(def_id_of_def(d), ids);
if ns == ns_for_def(d) { result = some(d); }
}
ret result;
}
// Collision detection
fn check_for_collisions(e: @env, c: ast::crate) {
// Module indices make checking those relatively simple -- just check each
// name for multiple entities in the same namespace.
for e.mod_map.each_value {|val|
for val.index.each {|k, v| check_mod_name(*e, k, v); };
};
// Other scopes have to be checked the hard way.
let v =
@{visit_item: bind check_item(e, _, _, _),
visit_block: bind check_block(e, _, _, _),
visit_arm: bind check_arm(e, _, _, _),
visit_expr: bind check_expr(e, _, _, _),
visit_ty: bind check_ty(e, _, _, _) with *visit::default_visitor()};
visit::visit_crate(c, (), visit::mk_vt(v));
}
fn check_mod_name(e: env, name: ident, entries: @list<mod_index_entry>) {
let mut saw_mod = false;
let mut saw_type = false;
let mut saw_value = false;
let mut entries = entries;
fn dup(e: env, sp: span, word: str, name: ident) {
e.sess.span_fatal(sp, "duplicate definition of " + word + name);
}
loop {
alt *entries {
cons(entry, rest) {
if !is_none(lookup_in_mie(e, entry, ns_val)) {
if saw_value {
dup(e, mie_span(entry), "", name);
} else { saw_value = true; }
}
if !is_none(lookup_in_mie(e, entry, ns_type)) {
if saw_type {
dup(e, mie_span(entry), "type ", name);
} else { saw_type = true; }
}
if !is_none(lookup_in_mie(e, entry, ns_module)) {
if saw_mod {
dup(e, mie_span(entry), "module ", name);
} else { saw_mod = true; }
}
entries = rest;
}
nil { break; }
}
}
}
fn mie_span(mie: mod_index_entry) -> span {
ret alt mie {
mie_view_item(_, _, span) { span }
mie_import_ident(_, span) { span }
mie_item(item) { item.span }
mie_enum_variant(_, _, _, span) { span }
mie_native_item(item) { item.span }
};
}
fn check_item(e: @env, i: @ast::item, &&x: (), v: vt<()>) {
fn typaram_names(tps: [ast::ty_param]) -> [ident] {
let mut x: [ast::ident] = [];
for tps.each {|tp| x += [tp.ident]; }
ret x;
}
visit::visit_item(i, x, v);
alt i.node {
ast::item_fn(decl, ty_params, _) {
check_fn(*e, i.span, decl);
ensure_unique(*e, i.span, ty_params, {|tp| tp.ident},
"type parameter");
}
ast::item_enum(_, ty_params, _) {
ensure_unique(*e, i.span, ty_params, {|tp| tp.ident},
"type parameter");
}
ast::item_iface(_, _, methods) {
ensure_unique(*e, i.span, methods, {|m| m.ident},
"method");
}
ast::item_impl(_, _, _, _, methods) {
ensure_unique(*e, i.span, methods, {|m| m.ident},
"method");
}
_ { }
}
}
fn check_pat(e: @env, ch: checker, p: @ast::pat) {
pat_util::pat_bindings(e.def_map, p) {|_i, p_sp, n|
add_name(ch, p_sp, path_to_ident(n));
};
}
fn check_arm(e: @env, a: ast::arm, &&x: (), v: vt<()>) {
visit::visit_arm(a, x, v);
let ch0 = checker(*e, "binding");
check_pat(e, ch0, a.pats[0]);
let seen0 = ch0.seen.get();
let mut i = vec::len(a.pats);
while i > 1u {
i -= 1u;
let ch = checker(*e, "binding");
check_pat(e, ch, a.pats[i]);
// Ensure the bindings introduced in this pattern are the same as in
// the first pattern.
if ch.seen.len() != seen0.len() {
e.sess.span_err(a.pats[i].span,
"inconsistent number of bindings");
} else {
for ch.seen.each {|name|
if is_none(vec::find(seen0, bind str::eq(name, _))) {
// Fight the alias checker
let name_ = name;
e.sess.span_err(a.pats[i].span,
"binding " + name_ +
" does not occur in first pattern");
}
}
}
}
}
fn check_block(e: @env, b: ast::blk, &&x: (), v: vt<()>) {
visit::visit_block(b, x, v);
let values = checker(*e, "value");
let types = checker(*e, "type");
let mods = checker(*e, "module");
for b.node.stmts.each {|st|
alt st.node {
ast::stmt_decl(d, _) {
alt d.node {
ast::decl_local(locs) {
let local_values = checker(*e, "value");
for locs.each {|loc|
pat_util::pat_bindings(e.def_map, loc.node.pat)
{|_i, p_sp, n|
let ident = path_to_ident(n);
add_name(local_values, p_sp, ident);
check_name(values, p_sp, ident);
};
}
}
ast::decl_item(it) {
alt it.node {
ast::item_enum(variants, _, _) {
add_name(types, it.span, it.ident);
for variants.each {|v|
add_name(values, v.span, v.node.name);
}
}
ast::item_mod(_) | ast::item_native_mod(_) {
add_name(mods, it.span, it.ident);
}
ast::item_const(_, _) | ast::item_fn(*) {
add_name(values, it.span, it.ident);
}
ast::item_ty(*) | ast::item_iface(*) {
add_name(types, it.span, it.ident);
}
ast::item_res(*) {
add_name(types, it.span, it.ident);
add_name(values, it.span, it.ident);
}
_ { }
}
}
}
}
_ { }
}
}
}
fn check_fn(e: env, sp: span, decl: ast::fn_decl) {
fn arg_name(a: ast::arg) -> ident { ret a.ident; }
ensure_unique(e, sp, decl.inputs, arg_name, "argument");
}
fn check_expr(e: @env, ex: @ast::expr, &&x: (), v: vt<()>) {
alt ex.node {
ast::expr_rec(fields, _) {
fn field_name(f: ast::field) -> ident { ret f.node.ident; }
ensure_unique(*e, ex.span, fields, field_name, "field");
}
_ { }
}
visit::visit_expr(ex, x, v);
}
fn check_ty(e: @env, ty: @ast::ty, &&x: (), v: vt<()>) {
alt ty.node {
ast::ty_rec(fields) {
fn field_name(f: ast::ty_field) -> ident { ret f.node.ident; }
ensure_unique(*e, ty.span, fields, field_name, "field");
}
_ { }
}
visit::visit_ty(ty, x, v);
}
type checker = @{seen: dvec<ident>, kind: str, sess: session};
fn checker(e: env, kind: str) -> checker {
ret @{seen: dvec(), kind: kind, sess: e.sess};
}
fn check_name(ch: checker, sp: span, name: ident) {
for ch.seen.each {|s|
if str::eq(s, name) {
ch.sess.span_fatal(sp, "duplicate " + ch.kind + " name: " + name);
}
}
}
fn add_name(ch: checker, sp: span, name: ident) {
check_name(ch, sp, name);
ch.seen.push(name);
}
fn ensure_unique<T>(e: env, sp: span, elts: [T], id: fn(T) -> ident,
kind: str) {
let ch = checker(e, kind);
for elts.each {|elt| add_name(ch, sp, id(elt)); }
}
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);
for ixm.index.each {|ident, mies|
list::iter(mies) {|mie|
alt mie {
mie_item(item) {
let defs =
[ found_def_item(item, ns_val),
found_def_item(item, ns_type),
found_def_item(item, ns_module) ];
for defs.each {|d|
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,
ident: ident, export_id: node_id) -> bool {
let m = lookup_glob_in_mod(*e, info, sp, ident, ns_module, inside);
let v = lookup_glob_in_mod(*e, info, sp, ident, ns_val, inside);
let t = lookup_glob_in_mod(*e, info, sp, ident, ns_type, inside);
maybe_add_reexport(e, export_id, m);
maybe_add_reexport(e, export_id, v);
maybe_add_reexport(e, export_id, t);
is_some(m) || is_some(v) || is_some(t)
}
fn maybe_add_reexport(e: @env, export_id: node_id, def: option<def>) {
option::iter(def) {|def|
add_export(e, export_id, def_id_of_def(def), true);
}
}
fn add_export(e: @env, export_id: node_id, target_id: def_id,
reexp: bool) {
let found = alt e.exp_map.find(export_id) {
some(f) { f } none { [] }
};
e.exp_map.insert(export_id, found + [{reexp: reexp, id: target_id}]);
}
fn check_export(e: @env, ident: str, _mod: @indexed_mod,
export_id: node_id, vi: @view_item) {
let mut found_something = false;
if _mod.index.contains_key(ident) {
found_something = true;
let xs = _mod.index.get(ident);
list::iter(xs) {|x|
alt x {
mie_import_ident(id, _) {
alt check e.imports.get(id) {
resolved(v, t, m, _, rid, _) {
maybe_add_reexport(e, export_id, v);
maybe_add_reexport(e, export_id, t);
maybe_add_reexport(e, export_id, m);
}
_ { e.sess.span_bug(vi.span, "unresolved export"); }
}
}
mie_item(@{id, _}) | mie_native_item(@{id, _}) |
mie_enum_variant(_, _, id, _) {
add_export(e, export_id, local_def(id), false);
}
_ { }
}
}
}
/*
This code previously used bitwise or (|=) but that was wrong,
because we need or to be lazy here. If something was already
found, we don't want to call lookup_glob_any (see #2316 for
what happens if we do)
*/
found_something = found_something ||
lookup_glob_any(e, _mod, vi.span, ident, export_id);
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, _mod: @indexed_mod)
-> node_id {
alt _mod.index.find(id) {
none {
e.sess.span_fatal(sp, #fmt("undefined id %s in an export", id));
}
some(ms) {
let maybe_id = list_search(ms) {|m|
alt m {
mie_item(@{node: item_enum(_, _, _), id, _}) { some(id) }
_ { none }
}
};
alt maybe_id {
some(an_id) { an_id }
_ { e.sess.span_fatal(sp, #fmt("%s does not refer \
to an enumeration", id)); }
}
}
}
}
fn check_export_enum_list(e: @env, export_id: node_id, _mod: @indexed_mod,
span: codemap::span, id: ast::ident,
ids: [ast::path_list_ident]) {
let parent_id = check_enum_ok(e, span, id, _mod);
add_export(e, export_id, local_def(parent_id), false);
for ids.each {|variant_id|
let mut found = false;
alt _mod.index.find(variant_id.node.name) {
some(ms) {
list::iter(ms) {|m|
alt m {
mie_enum_variant(_, _, actual_parent_id, _) {
found = true;
if actual_parent_id != parent_id {
e.sess.span_err(
span, #fmt("variant %s doesn't belong to \
enum %s",
variant_id.node.name, id));
}
}
_ {}
}
}
}
_ {}
}
if !found {
e.sess.span_err(span, #fmt("%s is not a variant",
variant_id.node.name));
}
}
}
for e.mod_map.each_value {|_mod|
alt _mod.m {
some(m) {
let glob_is_re_exported = int_hash();
for m.view_items.each {|vi|
iter_export_paths(*vi) { |vp|
alt vp.node {
ast::view_path_simple(ident, _, id) {
check_export(e, ident, _mod, id, vi);
}
ast::view_path_list(path, ids, node_id) {
let id = if vec::len(path.idents) == 1u {
path.idents[0]
} else {
e.sess.span_fatal(vp.span, "bad export name-list")
};
check_export_enum_list(e, node_id, _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 _mod.glob_imports.each {|glob|
let id = alt check glob.path.node {
ast::view_path_glob(_, node_id) { node_id }
};
if ! glob_is_re_exported.contains_key(id) { cont; }
iter_mod(*e, glob.def,
glob.path.span, outside) {|ident, def|
_mod.globbed_exports += [ident];
maybe_add_reexport(e, id, some(def));
}
}
}
none { }
}
}
}
// Impl resolution
type method_info = {did: def_id, n_tps: uint, ident: ast::ident};
/* An _impl represents an implementation that's currently in scope.
Its fields:
* did: the def id of the class or impl item
* ident: the name of the impl, unless it has no name (as in
"impl of X") in which case the ident
is the ident of the iface that's being implemented
* methods: the item's methods
*/
type _impl = {did: def_id, ident: ast::ident, methods: [@method_info]};
type iscopes = @list<@[@_impl]>;
fn resolve_impls(e: @env, c: @ast::crate) {
visit::visit_crate(*c, @nil, visit::mk_vt(@{
visit_block: bind visit_block_with_impl_scope(e, _, _, _),
visit_mod: bind visit_mod_with_impl_scope(e, _, _, _, _, _),
visit_expr: bind resolve_impl_in_expr(e, _, _, _)
with *visit::default_visitor()
}));
}
fn find_impls_in_view_item(e: env, vi: @ast::view_item,
&impls: [@_impl], sc: option<iscopes>) {
fn lookup_imported_impls(e: env, id: node_id,
act: fn(@[@_impl])) {
alt e.imports.get(id) {
resolved(_, _, _, is, _, _) { act(is); }
todo(name, path, span, scopes) {
resolve_import(e, id, name, *path, span, scopes);
alt check e.imports.get(id) {
resolved(_, _, _, is, _, _) { act(is); }
}
}
_ {}
}
}
iter_effective_import_paths(*vi) { |vp|
alt vp.node {
ast::view_path_simple(name, pt, id) {
let mut found = [];
if vec::len(pt.idents) == 1u {
option::iter(sc) {|sc|
list::iter(sc) {|level|
if vec::len(found) == 0u {
for vec::each(*level) {|imp|
if imp.ident == pt.idents[0] {
found += [@{ident: name with *imp}];
}
}
if vec::len(found) > 0u { impls += found; }
}
}
}
} else {
lookup_imported_impls(e, id) {|is|
for vec::each(*is) {|i|
impls += [@{ident: name with *i}];
}
}
}
}
ast::view_path_list(base, names, _) {
for names.each {|nm|
lookup_imported_impls(e, nm.node.id) {|is| impls += *is; }
}
}
ast::view_path_glob(ids, id) {
alt check e.imports.get(id) {
is_glob(path, sc, sp) {
alt follow_import(e, sc, *path, sp) {
some(def) { find_impls_in_mod(e, def, impls, none); }
_ {}
}
}
}
}
}
}
}
/*
Given an item <i>, adds one record to the mutable vec
<impls> if the item is an impl; zero or more records if the
item is a class; and none otherwise. Each record describes
one interface implemented by i.
*/
fn find_impls_in_item(e: env, i: @ast::item, &impls: [@_impl],
name: option<ident>,
ck_exports: option<@indexed_mod>) {
alt i.node {
ast::item_impl(_, _, ifce, _, mthds) {
if alt name { some(n) { n == i.ident } _ { true } } &&
alt ck_exports {
some(m) { is_exported(e, i.ident, m) }
_ { true }
} {
impls += [@{did: local_def(i.id),
ident: i.ident,
methods: vec::map(mthds, {|m|
@{did: local_def(m.id),
n_tps: vec::len(m.tps),
ident: m.ident}
})}];
}
}
ast::item_class(tps, ifces, items, _, _, _) {
let (_, mthds) = ast_util::split_class_items(items);
let n_tps = tps.len();
vec::iter(ifces) {|p|
// The def_id, in this case, identifies the combination of
// class and iface
impls += [@{did: local_def(p.id),
ident: i.ident,
methods: vec::map(mthds, {|m|
@{did: local_def(m.id),
n_tps: n_tps + m.tps.len(),
ident: m.ident}})}];
}
}
_ {}
}
}
fn find_impls_in_mod_by_id(e: env, defid: def_id, &impls: [@_impl],
name: option<ident>) {
let mut cached;
alt e.impl_cache.find(defid) {
some(some(v)) { cached = v; }
some(none) { ret; }
none {
e.impl_cache.insert(defid, none);
cached = if defid.crate == ast::local_crate {
let mut tmp = [];
let mi = e.mod_map.get(defid.node);
let md = option::get(mi.m);
for md.view_items.each {|vi|
find_impls_in_view_item(e, vi, tmp, none);
}
for md.items.each {|i|
find_impls_in_item(e, i, tmp, none, none);
}
@vec::filter(tmp) {|i| is_exported(e, i.ident, mi)}
} else {
csearch::get_impls_for_mod(e.sess.cstore, defid, none)
};
e.impl_cache.insert(defid, some(cached));
}
}
alt name {
some(n) {
for vec::each(*cached) {|im|
if n == im.ident { impls += [im]; }
}
}
_ { impls += *cached; }
}
}
fn find_impls_in_mod(e: env, m: def, &impls: [@_impl],
name: option<ident>) {
alt m {
ast::def_mod(defid) {
find_impls_in_mod_by_id(e, defid, impls, name);
}
_ {}
}
}
fn visit_block_with_impl_scope(e: @env, b: ast::blk, &&sc: iscopes,
v: vt<iscopes>) {
let mut impls = [];
for b.node.view_items.each {|vi|
find_impls_in_view_item(*e, vi, impls, some(sc));
}
for b.node.stmts.each {|st|
alt st.node {
ast::stmt_decl(@{node: ast::decl_item(i), _}, _) {
find_impls_in_item(*e, i, impls, none, none);
}
_ {}
}
}
let sc = if vec::len(impls) > 0u { @cons(@impls, sc) } else { sc };
visit::visit_block(b, sc, v);
}
fn visit_mod_with_impl_scope(e: @env, m: ast::_mod, s: span, id: node_id,
&&sc: iscopes, v: vt<iscopes>) {
let mut impls = [];
for m.view_items.each {|vi|
find_impls_in_view_item(*e, vi, impls, some(sc));
}
for m.items.each {|i| find_impls_in_item(*e, i, impls, none, none); }
let impls = @impls;
visit::visit_mod(m, s, id, if vec::len(*impls) > 0u {
@cons(impls, sc)
} else {
sc
}, v);
e.impl_map.insert(id, @cons(impls, @nil));
}
fn resolve_impl_in_expr(e: @env, x: @ast::expr,
&&sc: iscopes, v: vt<iscopes>) {
alt x.node {
// Store the visible impls in all exprs that might need them
ast::expr_field(_, _, _) | ast::expr_path(_) | ast::expr_cast(_, _) |
ast::expr_binary(_, _, _) | ast::expr_unary(_, _) |
ast::expr_assign_op(_, _, _) | ast::expr_index(_, _) {
e.impl_map.insert(x.id, sc);
}
ast::expr_new(p, _, _) {
e.impl_map.insert(p.id, sc);
}
_ {}
}
visit::visit_expr(x, sc, v);
}
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End: