2012-03-09 18:39:54 -06:00
|
|
|
/*
|
|
|
|
* Region resolution. This pass runs before typechecking and resolves region
|
|
|
|
* names to the appropriate block.
|
|
|
|
*/
|
|
|
|
|
|
|
|
import driver::session::session;
|
|
|
|
import middle::ty;
|
|
|
|
import syntax::{ast, visit};
|
2012-03-12 15:24:37 -05:00
|
|
|
import util::common::new_def_hash;
|
|
|
|
|
2012-03-09 18:39:54 -06:00
|
|
|
import std::map;
|
|
|
|
import std::map::hashmap;
|
|
|
|
|
2012-03-11 14:05:17 -05:00
|
|
|
/* Represents the type of the most immediate parent node. */
|
2012-03-09 18:39:54 -06:00
|
|
|
enum parent {
|
|
|
|
pa_item(ast::node_id),
|
|
|
|
pa_block(ast::node_id),
|
2012-03-12 14:43:02 -05:00
|
|
|
pa_nested_fn(ast::node_id),
|
2012-03-09 18:39:54 -06:00
|
|
|
pa_crate
|
|
|
|
}
|
|
|
|
|
2012-03-12 14:43:02 -05:00
|
|
|
type region_map = {
|
|
|
|
/*
|
|
|
|
* Mapping from blocks and function expression to their parent block or
|
|
|
|
* function expression.
|
|
|
|
*/
|
|
|
|
parents: hashmap<ast::node_id,ast::node_id>,
|
|
|
|
/* Mapping from a region type in the AST to its resolved region. */
|
|
|
|
ast_type_to_region: hashmap<ast::node_id,ty::region>,
|
|
|
|
/* Mapping from a local variable to its containing block. */
|
|
|
|
local_blocks: hashmap<ast::node_id,ast::node_id>,
|
2012-03-12 15:24:37 -05:00
|
|
|
/* Mapping from a region name to its function. */
|
|
|
|
region_name_to_fn: hashmap<ast::def_id,ast::node_id>,
|
2012-03-12 14:43:02 -05:00
|
|
|
};
|
|
|
|
|
2012-03-09 18:39:54 -06:00
|
|
|
type ctxt = {
|
|
|
|
sess: session,
|
2012-03-11 14:05:17 -05:00
|
|
|
def_map: resolve::def_map,
|
2012-03-09 18:39:54 -06:00
|
|
|
region_map: @region_map,
|
|
|
|
names_in_scope: hashmap<str,ast::def_id>,
|
2012-03-11 14:05:17 -05:00
|
|
|
|
|
|
|
/*
|
2012-03-11 18:18:52 -05:00
|
|
|
* A list of local IDs that will be parented to the next block we
|
|
|
|
* traverse. This is used when resolving `alt` statements. Since we see
|
|
|
|
* the pattern before the associated block, upon seeing a pattern we must
|
|
|
|
* parent all the bindings in that pattern to the next block we see.
|
2012-03-11 14:05:17 -05:00
|
|
|
*/
|
|
|
|
mut queued_locals: [ast::node_id],
|
|
|
|
|
2012-03-11 19:01:28 -05:00
|
|
|
parent: parent,
|
2012-03-12 14:43:02 -05:00
|
|
|
|
|
|
|
/* True if we're within the pattern part of an alt, false otherwise. */
|
|
|
|
in_alt: bool
|
2012-03-09 18:39:54 -06:00
|
|
|
};
|
|
|
|
|
2012-03-12 15:57:01 -05:00
|
|
|
fn region_to_scope(region_map: @region_map, region: ty::region)
|
2012-03-12 14:43:02 -05:00
|
|
|
-> ast::node_id {
|
|
|
|
ret alt region {
|
|
|
|
ty::re_caller(def_id) { def_id.node }
|
2012-03-12 15:57:01 -05:00
|
|
|
ty::re_named(def_id) { region_map.region_name_to_fn.get(def_id) }
|
2012-03-12 14:43:02 -05:00
|
|
|
ty::re_block(node_id) { node_id }
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns true if `subscope` is equal to or is lexically nested inside
|
|
|
|
// `superscope` and false otherwise.
|
|
|
|
fn scope_contains(region_map: @region_map, superscope: ast::node_id,
|
|
|
|
subscope: ast::node_id) -> bool {
|
|
|
|
let subscope = subscope;
|
|
|
|
while superscope != subscope {
|
|
|
|
alt region_map.parents.find(subscope) {
|
2012-03-11 15:28:43 -05:00
|
|
|
none { ret false; }
|
2012-03-12 14:43:02 -05:00
|
|
|
some(scope) { subscope = scope; }
|
2012-03-11 15:28:43 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ret true;
|
|
|
|
}
|
|
|
|
|
2012-03-09 18:39:54 -06:00
|
|
|
fn resolve_ty(ty: @ast::ty, cx: ctxt, visitor: visit::vt<ctxt>) {
|
|
|
|
alt ty.node {
|
|
|
|
ast::ty_rptr({id: region_id, node: node}, _) {
|
|
|
|
let region;
|
|
|
|
alt node {
|
|
|
|
ast::re_inferred {
|
|
|
|
// We infer to the caller region if we're at item scope
|
|
|
|
// and to the block region if we're at block scope.
|
2012-03-12 14:43:02 -05:00
|
|
|
//
|
|
|
|
// TODO: What do we do if we're in an alt?
|
|
|
|
|
2012-03-09 18:39:54 -06:00
|
|
|
alt cx.parent {
|
2012-03-12 14:43:02 -05:00
|
|
|
pa_item(item_id) | pa_nested_fn(item_id) {
|
2012-03-09 18:39:54 -06:00
|
|
|
let def_id = {crate: ast::local_crate,
|
|
|
|
node: item_id};
|
|
|
|
region = ty::re_caller(def_id);
|
|
|
|
}
|
|
|
|
pa_block(block_id) {
|
|
|
|
region = ty::re_block(block_id);
|
|
|
|
}
|
|
|
|
pa_crate {
|
2012-03-12 14:43:02 -05:00
|
|
|
cx.sess.span_bug(ty.span, "inferred region at " +
|
|
|
|
"crate level?!");
|
2012-03-09 18:39:54 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ast::re_named(ident) {
|
|
|
|
// If at item scope, introduce or reuse a binding. If at
|
|
|
|
// block scope, require that the binding be introduced.
|
|
|
|
alt cx.names_in_scope.find(ident) {
|
|
|
|
some(def_id) { region = ty::re_named(def_id); }
|
|
|
|
none {
|
2012-03-12 15:24:37 -05:00
|
|
|
let def_id = {crate: ast::local_crate,
|
|
|
|
node: region_id};
|
|
|
|
cx.names_in_scope.insert(ident, def_id);
|
|
|
|
region = ty::re_named(def_id);
|
|
|
|
|
2012-03-09 18:39:54 -06:00
|
|
|
alt cx.parent {
|
2012-03-12 15:24:37 -05:00
|
|
|
pa_item(fn_id) | pa_nested_fn(fn_id) {
|
|
|
|
let rf = cx.region_map.region_name_to_fn;
|
|
|
|
rf.insert(def_id, fn_id);
|
2012-03-12 14:43:02 -05:00
|
|
|
}
|
|
|
|
pa_block(_) {
|
2012-03-09 18:39:54 -06:00
|
|
|
cx.sess.span_err(ty.span,
|
|
|
|
"unknown region `" +
|
|
|
|
ident + "`");
|
|
|
|
}
|
|
|
|
pa_crate {
|
2012-03-12 14:43:02 -05:00
|
|
|
cx.sess.span_bug(ty.span, "named " +
|
|
|
|
"region at crate " +
|
|
|
|
"level?!");
|
2012-03-09 18:39:54 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ast::re_self {
|
|
|
|
// For blocks, "self" means "the current block".
|
2012-03-12 14:43:02 -05:00
|
|
|
//
|
|
|
|
// TODO: What do we do in an alt?
|
|
|
|
//
|
|
|
|
// FIXME: Doesn't work in type items.
|
|
|
|
|
2012-03-09 18:39:54 -06:00
|
|
|
alt cx.parent {
|
2012-03-12 14:43:02 -05:00
|
|
|
pa_item(item_id) | pa_nested_fn(item_id) {
|
|
|
|
let def_id = {crate: ast::local_crate,
|
|
|
|
node: item_id};
|
|
|
|
region = ty::re_caller(def_id);
|
2012-03-09 18:39:54 -06:00
|
|
|
}
|
|
|
|
pa_block(block_id) {
|
|
|
|
region = ty::re_block(block_id);
|
|
|
|
}
|
|
|
|
pa_crate {
|
|
|
|
cx.sess.span_bug(ty.span,
|
2012-03-12 14:43:02 -05:00
|
|
|
"region type outside item?!");
|
2012-03-09 18:39:54 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
cx.region_map.ast_type_to_region.insert(region_id, region);
|
|
|
|
}
|
|
|
|
_ { /* nothing to do */ }
|
|
|
|
}
|
|
|
|
|
|
|
|
visit::visit_ty(ty, cx, visitor);
|
|
|
|
}
|
|
|
|
|
2012-03-12 14:43:02 -05:00
|
|
|
fn record_parent(cx: ctxt, child_id: ast::node_id) {
|
2012-03-09 18:39:54 -06:00
|
|
|
alt cx.parent {
|
2012-03-12 14:43:02 -05:00
|
|
|
pa_item(parent_id) | pa_block(parent_id) | pa_nested_fn(parent_id) {
|
|
|
|
cx.region_map.parents.insert(child_id, parent_id);
|
2012-03-09 18:39:54 -06:00
|
|
|
}
|
2012-03-12 14:43:02 -05:00
|
|
|
pa_crate { /* no-op */ }
|
2012-03-09 18:39:54 -06:00
|
|
|
}
|
2012-03-12 14:43:02 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn resolve_block(blk: ast::blk, cx: ctxt, visitor: visit::vt<ctxt>) {
|
|
|
|
// Record the parent of this block.
|
|
|
|
record_parent(cx, blk.node.id);
|
2012-03-09 18:39:54 -06:00
|
|
|
|
2012-03-11 14:05:17 -05:00
|
|
|
// Resolve queued locals to this block.
|
|
|
|
for local_id in cx.queued_locals {
|
|
|
|
cx.region_map.local_blocks.insert(local_id, blk.node.id);
|
|
|
|
}
|
|
|
|
|
2012-03-12 14:43:02 -05:00
|
|
|
// Descend.
|
2012-03-11 14:05:17 -05:00
|
|
|
let new_cx: ctxt = {parent: pa_block(blk.node.id),
|
2012-03-12 14:43:02 -05:00
|
|
|
mut queued_locals: [],
|
|
|
|
in_alt: false with cx};
|
2012-03-09 18:39:54 -06:00
|
|
|
visit::visit_block(blk, new_cx, visitor);
|
|
|
|
}
|
|
|
|
|
2012-03-11 14:05:17 -05:00
|
|
|
fn resolve_arm(arm: ast::arm, cx: ctxt, visitor: visit::vt<ctxt>) {
|
2012-03-12 14:43:02 -05:00
|
|
|
let new_cx: ctxt = {mut queued_locals: [], in_alt: true with cx};
|
2012-03-11 14:05:17 -05:00
|
|
|
visit::visit_arm(arm, new_cx, visitor);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn resolve_pat(pat: @ast::pat, cx: ctxt, visitor: visit::vt<ctxt>) {
|
|
|
|
alt pat.node {
|
|
|
|
ast::pat_ident(path, _) {
|
|
|
|
let defn_opt = cx.def_map.find(pat.id);
|
|
|
|
alt defn_opt {
|
|
|
|
some(ast::def_variant(_,_)) {
|
|
|
|
/* Nothing to do; this names a variant. */
|
|
|
|
}
|
|
|
|
_ {
|
|
|
|
/*
|
2012-03-11 18:18:52 -05:00
|
|
|
* This names a local. Enqueue it or bind it to the
|
|
|
|
* containing block, depending on whether we're in an alt
|
|
|
|
* or not.
|
2012-03-11 14:05:17 -05:00
|
|
|
*/
|
2012-03-12 14:43:02 -05:00
|
|
|
if cx.in_alt {
|
|
|
|
vec::push(cx.queued_locals, pat.id);
|
|
|
|
} else {
|
|
|
|
alt cx.parent {
|
|
|
|
pa_block(block_id) {
|
|
|
|
let local_blocks = cx.region_map.local_blocks;
|
|
|
|
local_blocks.insert(pat.id, block_id);
|
|
|
|
}
|
|
|
|
_ {
|
|
|
|
cx.sess.span_bug(pat.span,
|
|
|
|
"unexpected parent");
|
|
|
|
}
|
2012-03-11 14:05:17 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ { /* no-op */ }
|
|
|
|
}
|
|
|
|
|
|
|
|
visit::visit_pat(pat, cx, visitor);
|
|
|
|
}
|
|
|
|
|
2012-03-11 19:01:28 -05:00
|
|
|
fn resolve_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) {
|
|
|
|
alt expr.node {
|
|
|
|
ast::expr_fn(_, _, _, _) | ast::expr_fn_block(_, _) {
|
2012-03-12 14:43:02 -05:00
|
|
|
record_parent(cx, expr.id);
|
|
|
|
let new_cx = {parent: pa_nested_fn(expr.id),
|
|
|
|
in_alt: false with cx};
|
2012-03-11 19:01:28 -05:00
|
|
|
visit::visit_expr(expr, new_cx, visitor);
|
|
|
|
}
|
|
|
|
_ { visit::visit_expr(expr, cx, visitor); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-09 18:39:54 -06:00
|
|
|
fn resolve_item(item: @ast::item, cx: ctxt, visitor: visit::vt<ctxt>) {
|
|
|
|
// Items create a new outer block scope as far as we're concerned.
|
|
|
|
let new_cx: ctxt = {names_in_scope: map::new_str_hash(),
|
2012-03-11 19:01:28 -05:00
|
|
|
parent: pa_item(item.id),
|
2012-03-12 14:43:02 -05:00
|
|
|
in_alt: false
|
2012-03-09 18:39:54 -06:00
|
|
|
with cx};
|
|
|
|
visit::visit_item(item, new_cx, visitor);
|
|
|
|
}
|
|
|
|
|
2012-03-11 14:05:17 -05:00
|
|
|
fn resolve_crate(sess: session, def_map: resolve::def_map, crate: @ast::crate)
|
|
|
|
-> @region_map {
|
2012-03-09 18:39:54 -06:00
|
|
|
let cx: ctxt = {sess: sess,
|
2012-03-11 14:05:17 -05:00
|
|
|
def_map: def_map,
|
2012-03-12 14:43:02 -05:00
|
|
|
region_map: @{parents: map::new_int_hash(),
|
2012-03-11 14:05:17 -05:00
|
|
|
ast_type_to_region: map::new_int_hash(),
|
2012-03-12 15:24:37 -05:00
|
|
|
local_blocks: map::new_int_hash(),
|
|
|
|
region_name_to_fn: new_def_hash()},
|
2012-03-09 18:39:54 -06:00
|
|
|
names_in_scope: map::new_str_hash(),
|
2012-03-11 14:05:17 -05:00
|
|
|
mut queued_locals: [],
|
2012-03-11 19:01:28 -05:00
|
|
|
parent: pa_crate,
|
2012-03-12 14:43:02 -05:00
|
|
|
in_alt: false};
|
2012-03-09 18:39:54 -06:00
|
|
|
let visitor = visit::mk_vt(@{
|
|
|
|
visit_block: resolve_block,
|
|
|
|
visit_item: resolve_item,
|
2012-03-11 14:05:17 -05:00
|
|
|
visit_ty: resolve_ty,
|
|
|
|
visit_arm: resolve_arm,
|
2012-03-11 19:01:28 -05:00
|
|
|
visit_pat: resolve_pat,
|
|
|
|
visit_expr: resolve_expr
|
2012-03-09 18:39:54 -06:00
|
|
|
with *visit::default_visitor()
|
|
|
|
});
|
|
|
|
visit::visit_crate(*crate, cx, visitor);
|
|
|
|
ret cx.region_map;
|
|
|
|
}
|
|
|
|
|