2012-03-09 18:39:54 -06:00
|
|
|
/*
|
2012-04-01 16:28:30 -05:00
|
|
|
|
|
|
|
Region resolution. This pass runs before typechecking and resolves region
|
|
|
|
names to the appropriate block.
|
|
|
|
|
|
|
|
This seems to be as good a place as any to explain in detail how
|
|
|
|
region naming, representation, and type check works.
|
|
|
|
|
|
|
|
### Naming and so forth
|
|
|
|
|
|
|
|
We really want regions to be very lightweight to use. Therefore,
|
|
|
|
unlike other named things, the scopes for regions are not explicitly
|
|
|
|
declared: instead, they are implicitly defined. Functions declare new
|
|
|
|
scopes: if the function is not a bare function, then as always it
|
|
|
|
inherits the names in scope from the outer scope. Within a function
|
|
|
|
declaration, new names implicitly declare new region variables. Outside
|
|
|
|
of function declarations, new names are illegal. To make this more
|
|
|
|
concrete, here is an example:
|
|
|
|
|
|
|
|
fn foo(s: &a.S, t: &b.T) {
|
|
|
|
let s1: &a.S = s; // a refers to the same a as in the decl
|
|
|
|
let t1: &c.T = t; // illegal: cannot introduce new name here
|
|
|
|
}
|
|
|
|
|
|
|
|
The code in this file is what actually handles resolving these names.
|
|
|
|
It creates a couple of maps that map from the AST node representing a
|
|
|
|
region ptr type to the resolved form of its region parameter. If new
|
|
|
|
names are introduced where they shouldn't be, then an error is
|
|
|
|
reported.
|
|
|
|
|
|
|
|
If regions are not given an explicit name, then the behavior depends
|
|
|
|
a bit on the context. Within a function declaration, all unnamed regions
|
|
|
|
are mapped to a single, anonymous parameter. That is, a function like:
|
|
|
|
|
|
|
|
fn foo(s: &S) -> &S { s }
|
|
|
|
|
|
|
|
is equivalent to a declaration like:
|
|
|
|
|
|
|
|
fn foo(s: &a.S) -> &a.S { s }
|
|
|
|
|
|
|
|
Within a function body or other non-binding context, an unnamed region
|
|
|
|
reference is mapped to a fresh region variable whose value can be
|
|
|
|
inferred as normal.
|
|
|
|
|
|
|
|
The resolved form of regions is `ty::region`. Before I can explain
|
|
|
|
why this type is setup the way it is, I have to digress a little bit
|
|
|
|
into some ill-explained type theory.
|
|
|
|
|
|
|
|
### Universal Quantification
|
|
|
|
|
|
|
|
Regions are more complex than type parameters because, unlike type
|
|
|
|
parameters, they can be universally quantified within a type. To put
|
|
|
|
it another way, you cannot (at least at the time of this writing) have
|
|
|
|
a variable `x` of type `fn<T>(T) -> T`. You can have an *item* of
|
2012-05-22 12:37:04 -05:00
|
|
|
type `fn<T>(T) -> T`, but whenever it is referenced within a method,
|
2012-04-01 16:28:30 -05:00
|
|
|
that type parameter `T` is replaced with a concrete type *variable*
|
|
|
|
`$T`. To make this more concrete, imagine this code:
|
|
|
|
|
|
|
|
fn identity<T>(x: T) -> T { x }
|
|
|
|
let f = identity; // f has type fn($T) -> $T
|
|
|
|
f(3u); // $T is bound to uint
|
|
|
|
f(3); // Type error
|
|
|
|
|
|
|
|
You can see here that a type error will result because the type of `f`
|
|
|
|
(as opposed to the type of `identity`) is not universally quantified
|
|
|
|
over `$T`. That's fancy math speak for saying that the type variable
|
|
|
|
`$T` refers to a specific type that may not yet be known, unlike the
|
|
|
|
type parameter `T` which refers to some type which will never be
|
|
|
|
known.
|
|
|
|
|
|
|
|
Anyway, regions work differently. If you have an item of type
|
|
|
|
`fn(&a.T) -> &a.T` and you reference it, its type remains the same:
|
|
|
|
only when the function *is called* is `&a` instantiated with a
|
|
|
|
concrete region variable. This means you could call it twice and give
|
|
|
|
different values for `&a` each time.
|
|
|
|
|
|
|
|
This more general form is possible for regions because they do not
|
|
|
|
impact code generation. We do not need to monomorphize functions
|
|
|
|
differently just because they contain region pointers. In fact, we
|
|
|
|
don't really do *anything* differently.
|
|
|
|
|
|
|
|
### Representing regions; or, why do I care about all that?
|
|
|
|
|
|
|
|
The point of this discussion is that the representation of regions
|
|
|
|
must distinguish between a *bound* reference to a region and a *free*
|
|
|
|
reference. A bound reference is one which will be replaced with a
|
|
|
|
fresh type variable when the function is called, like the type
|
|
|
|
parameter `T` in `identity`. They can only appear within function
|
|
|
|
types. A free reference is a region that may not yet be concretely
|
|
|
|
known, like the variable `$T`.
|
|
|
|
|
|
|
|
To see why we must distinguish them carefully, consider this program:
|
|
|
|
|
|
|
|
fn item1(s: &a.S) {
|
|
|
|
let choose = fn@(s1: &a.S) -> &a.S {
|
|
|
|
if some_cond { s } else { s1 }
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
Here, the variable `s1: &a.S` that appears within the `fn@` is a free
|
|
|
|
reference to `a`. That is, when you call `choose()`, you don't
|
|
|
|
replace `&a` with a fresh region variable, but rather you expect `s1`
|
|
|
|
to be in the same region as the parameter `s`.
|
|
|
|
|
|
|
|
But in this program, this is not the case at all:
|
|
|
|
|
|
|
|
fn item2() {
|
|
|
|
let identity = fn@(s1: &a.S) -> &a.S { s1 };
|
|
|
|
}
|
|
|
|
|
|
|
|
To distinguish between these two cases, `ty::region` contains two
|
|
|
|
variants: `re_bound` and `re_free`. In `item1()`, the outer reference
|
|
|
|
to `&a` would be `re_bound(rid_param("a", 0u))`, and the inner reference
|
|
|
|
would be `re_free(rid_param("a", 0u))`. In `item2()`, the inner reference
|
|
|
|
would be `re_bound(rid_param("a", 0u))`.
|
|
|
|
|
2012-05-22 12:37:04 -05:00
|
|
|
#### Implications for typeck
|
2012-04-01 16:28:30 -05:00
|
|
|
|
|
|
|
In typeck, whenever we call a function, we must go over and replace
|
|
|
|
all references to `re_bound()` regions within its parameters with
|
|
|
|
fresh type variables (we do not, however, replace bound regions within
|
|
|
|
nested function types, as those nested functions have not yet been
|
|
|
|
called).
|
|
|
|
|
|
|
|
Also, when we typecheck the *body* of an item, we must replace all
|
|
|
|
`re_bound` references with `re_free` references. This means that the
|
|
|
|
region in the type of the argument `s` in `item1()` *within `item1()`*
|
|
|
|
is not `re_bound(re_param("a", 0u))` but rather `re_free(re_param("a",
|
|
|
|
0u))`. This is because, for any particular *invocation of `item1()`*,
|
|
|
|
`&a` will be bound to some specific region, and hence it is no longer
|
|
|
|
bound.
|
|
|
|
|
|
|
|
*/
|
2012-03-09 18:39:54 -06:00
|
|
|
|
|
|
|
import driver::session::session;
|
|
|
|
import middle::ty;
|
|
|
|
import syntax::{ast, visit};
|
2012-04-01 16:28:30 -05:00
|
|
|
import syntax::codemap::span;
|
2012-04-26 18:02:01 -05:00
|
|
|
import syntax::print::pprust;
|
2012-05-23 02:38:39 -05:00
|
|
|
import syntax::ast_util::new_def_hash;
|
2012-07-11 12:28:30 -05:00
|
|
|
import syntax::ast_map;
|
|
|
|
import dvec::{dvec, extensions};
|
|
|
|
import metadata::csearch;
|
2012-03-12 15:24:37 -05:00
|
|
|
|
2012-03-12 20:47:18 -05:00
|
|
|
import std::list;
|
|
|
|
import std::list::list;
|
2012-07-11 12:28:30 -05:00
|
|
|
import std::map::{hashmap, int_hash};
|
2012-03-09 18:39:54 -06:00
|
|
|
|
2012-04-18 23:26:25 -05:00
|
|
|
type parent = option<ast::node_id>;
|
2012-03-09 18:39:54 -06:00
|
|
|
|
2012-03-23 13:37:10 -05:00
|
|
|
/* Records the parameter ID of a region name. */
|
2012-04-01 16:28:30 -05:00
|
|
|
type binding = {node_id: ast::node_id,
|
2012-07-14 00:57:48 -05:00
|
|
|
name: ~str,
|
2012-04-01 16:28:30 -05:00
|
|
|
br: ty::bound_region};
|
2012-03-12 20:47:18 -05:00
|
|
|
|
2012-05-15 23:19:35 -05:00
|
|
|
// Mapping from a block/expr/binding to the innermost scope that
|
|
|
|
// bounds its lifetime. For a block/expression, this is the lifetime
|
|
|
|
// in which it will be evaluated. For a binding, this is the lifetime
|
|
|
|
// in which is in scope.
|
|
|
|
type region_map = hashmap<ast::node_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-05-15 23:19:35 -05:00
|
|
|
region_map: region_map,
|
2012-04-01 16:28:30 -05:00
|
|
|
|
2012-05-23 23:47:11 -05:00
|
|
|
// The parent scope is the innermost block, call, or alt
|
|
|
|
// expression during the execution of which the current expression
|
|
|
|
// will be evaluated. Generally speaking, the innermost parent
|
|
|
|
// scope is also the closest suitable ancestor in the AST tree.
|
2012-04-26 18:02:01 -05:00
|
|
|
//
|
2012-05-23 23:47:11 -05:00
|
|
|
// There is a subtle point concerning call arguments. Imagine
|
|
|
|
// you have a call:
|
2012-04-26 18:02:01 -05:00
|
|
|
//
|
|
|
|
// { // block a
|
2012-05-23 23:47:11 -05:00
|
|
|
// foo( // call b
|
2012-04-26 18:02:01 -05:00
|
|
|
// x,
|
2012-05-23 23:47:11 -05:00
|
|
|
// y);
|
2012-04-26 18:02:01 -05:00
|
|
|
// }
|
|
|
|
//
|
2012-05-23 23:47:11 -05:00
|
|
|
// In what lifetime are the expressions `x` and `y` evaluated? At
|
|
|
|
// first, I imagine the answer was the block `a`, as the arguments
|
|
|
|
// are evaluated before the call takes place. But this turns out
|
|
|
|
// to be wrong. The lifetime of the call must encompass the
|
|
|
|
// argument evaluation as well.
|
2012-04-26 18:02:01 -05:00
|
|
|
//
|
2012-05-23 23:47:11 -05:00
|
|
|
// The reason is that evaluation of an earlier argument could
|
|
|
|
// create a borrow which exists during the evaluation of later
|
|
|
|
// arguments. Consider this torture test, for example,
|
|
|
|
//
|
|
|
|
// fn test1(x: @mut ~int) {
|
|
|
|
// foo(&**x, *x = ~5);
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// Here, the first argument `&**x` will be a borrow of the `~int`,
|
|
|
|
// but the second argument overwrites that very value! Bad.
|
|
|
|
// (This test is borrowck-pure-scope-in-call.rs, btw)
|
|
|
|
parent: parent
|
2012-03-23 13:37:10 -05:00
|
|
|
};
|
2012-03-12 14:43:02 -05:00
|
|
|
|
|
|
|
// Returns true if `subscope` is equal to or is lexically nested inside
|
|
|
|
// `superscope` and false otherwise.
|
2012-05-15 23:19:35 -05:00
|
|
|
fn scope_contains(region_map: region_map, superscope: ast::node_id,
|
2012-03-12 14:43:02 -05:00
|
|
|
subscope: ast::node_id) -> bool {
|
2012-03-15 08:47:03 -05:00
|
|
|
let mut subscope = subscope;
|
2012-03-12 14:43:02 -05:00
|
|
|
while superscope != subscope {
|
2012-05-15 23:19:35 -05:00
|
|
|
alt region_map.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-05-15 23:19:35 -05:00
|
|
|
fn nearest_common_ancestor(region_map: region_map, scope_a: ast::node_id,
|
2012-03-26 17:07:15 -05:00
|
|
|
scope_b: ast::node_id) -> option<ast::node_id> {
|
|
|
|
|
2012-05-15 23:19:35 -05:00
|
|
|
fn ancestors_of(region_map: region_map, scope: ast::node_id)
|
2012-06-29 18:26:56 -05:00
|
|
|
-> ~[ast::node_id] {
|
|
|
|
let mut result = ~[scope];
|
2012-03-26 17:07:15 -05:00
|
|
|
let mut scope = scope;
|
|
|
|
loop {
|
2012-05-15 23:19:35 -05:00
|
|
|
alt region_map.find(scope) {
|
2012-03-26 17:07:15 -05:00
|
|
|
none { ret result; }
|
|
|
|
some(superscope) {
|
2012-06-28 17:00:03 -05:00
|
|
|
vec::push(result, superscope);
|
2012-03-26 17:07:15 -05:00
|
|
|
scope = superscope;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if scope_a == scope_b { ret some(scope_a); }
|
|
|
|
|
|
|
|
let a_ancestors = ancestors_of(region_map, scope_a);
|
|
|
|
let b_ancestors = ancestors_of(region_map, scope_b);
|
|
|
|
let mut a_index = vec::len(a_ancestors) - 1u;
|
|
|
|
let mut b_index = vec::len(b_ancestors) - 1u;
|
2012-04-01 16:28:30 -05:00
|
|
|
|
2012-06-29 18:26:56 -05:00
|
|
|
// Here, ~[ab]_ancestors is a vector going from narrow to broad.
|
2012-04-01 16:28:30 -05:00
|
|
|
// The end of each vector will be the item where the scope is
|
|
|
|
// defined; if there are any common ancestors, then the tails of
|
|
|
|
// the vector will be the same. So basically we want to walk
|
|
|
|
// backwards from the tail of each vector and find the first point
|
|
|
|
// where they diverge. If one vector is a suffix of the other,
|
|
|
|
// then the corresponding scope is a superscope of the other.
|
|
|
|
|
2012-04-26 18:02:01 -05:00
|
|
|
if a_ancestors[a_index] != b_ancestors[b_index] {
|
|
|
|
ret none;
|
|
|
|
}
|
|
|
|
|
2012-04-01 16:28:30 -05:00
|
|
|
loop {
|
2012-04-26 18:02:01 -05:00
|
|
|
// Loop invariant: a_ancestors[a_index] == b_ancestors[b_index]
|
|
|
|
// for all indices between a_index and the end of the array
|
2012-04-01 16:28:30 -05:00
|
|
|
if a_index == 0u { ret some(scope_a); }
|
|
|
|
if b_index == 0u { ret some(scope_b); }
|
2012-03-26 17:07:15 -05:00
|
|
|
a_index -= 1u;
|
|
|
|
b_index -= 1u;
|
2012-04-26 18:02:01 -05:00
|
|
|
if a_ancestors[a_index] != b_ancestors[b_index] {
|
|
|
|
ret some(a_ancestors[a_index + 1u]);
|
|
|
|
}
|
2012-03-26 17:07:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-12 23:59:33 -05:00
|
|
|
fn parent_id(cx: ctxt, span: span) -> ast::node_id {
|
2012-04-18 23:26:25 -05:00
|
|
|
alt cx.parent {
|
2012-04-12 23:59:33 -05:00
|
|
|
none {
|
2012-07-14 00:57:48 -05:00
|
|
|
cx.sess.span_bug(span, ~"crate should not be parent here");
|
2012-04-12 23:59:33 -05:00
|
|
|
}
|
|
|
|
some(parent_id) {
|
|
|
|
parent_id
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn record_parent(cx: ctxt, child_id: ast::node_id) {
|
2012-04-18 23:26:25 -05:00
|
|
|
alt cx.parent {
|
2012-04-12 23:59:33 -05:00
|
|
|
none { /* no-op */ }
|
|
|
|
some(parent_id) {
|
2012-04-26 18:02:01 -05:00
|
|
|
#debug["parent of node %d is node %d", child_id, parent_id];
|
2012-05-15 23:19:35 -05:00
|
|
|
cx.region_map.insert(child_id, parent_id);
|
2012-04-12 23:59:33 -05:00
|
|
|
}
|
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-12 14:43:02 -05:00
|
|
|
// Descend.
|
2012-05-23 23:47:11 -05:00
|
|
|
let new_cx: ctxt = {parent: some(blk.node.id) 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-04-12 23:59:33 -05:00
|
|
|
visit::visit_arm(arm, cx, visitor);
|
2012-03-11 14:05:17 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn resolve_pat(pat: @ast::pat, cx: ctxt, visitor: visit::vt<ctxt>) {
|
|
|
|
alt pat.node {
|
2012-04-12 23:59:33 -05:00
|
|
|
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. */
|
|
|
|
}
|
|
|
|
_ {
|
|
|
|
/* This names a local. Bind it to the containing scope. */
|
2012-05-15 23:19:35 -05:00
|
|
|
record_parent(cx, pat.id);
|
2012-04-12 23:59:33 -05:00
|
|
|
}
|
2012-03-11 14:05:17 -05:00
|
|
|
}
|
2012-04-12 23:59:33 -05:00
|
|
|
}
|
|
|
|
_ { /* no-op */ }
|
2012-03-11 14:05:17 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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>) {
|
2012-04-12 23:59:33 -05:00
|
|
|
record_parent(cx, expr.id);
|
2012-03-11 19:01:28 -05:00
|
|
|
alt expr.node {
|
2012-04-26 18:02:01 -05:00
|
|
|
ast::expr_call(*) {
|
|
|
|
#debug["node %d: %s", expr.id, pprust::expr_to_str(expr)];
|
2012-05-23 23:47:11 -05:00
|
|
|
let new_cx = {parent: some(expr.id) with cx};
|
2012-04-12 23:59:33 -05:00
|
|
|
visit::visit_expr(expr, new_cx, visitor);
|
|
|
|
}
|
|
|
|
ast::expr_alt(subexpr, _, _) {
|
2012-04-26 18:02:01 -05:00
|
|
|
#debug["node %d: %s", expr.id, pprust::expr_to_str(expr)];
|
2012-05-23 23:47:11 -05:00
|
|
|
let new_cx = {parent: some(expr.id) with cx};
|
2012-04-12 23:59:33 -05:00
|
|
|
visit::visit_expr(expr, new_cx, visitor);
|
|
|
|
}
|
2012-05-10 21:58:23 -05:00
|
|
|
ast::expr_fn(_, _, _, cap_clause) |
|
|
|
|
ast::expr_fn_block(_, _, cap_clause) {
|
|
|
|
// although the capture items are not expressions per se, they
|
|
|
|
// do get "evaluated" in some sense as copies or moves of the
|
|
|
|
// relevant variables so we parent them like an expression
|
2012-06-30 18:19:07 -05:00
|
|
|
for (*cap_clause).each |cap_item| {
|
2012-05-10 21:58:23 -05:00
|
|
|
record_parent(cx, cap_item.id);
|
|
|
|
}
|
|
|
|
visit::visit_expr(expr, cx, visitor);
|
|
|
|
}
|
2012-04-12 23:59:33 -05:00
|
|
|
_ {
|
|
|
|
visit::visit_expr(expr, cx, visitor);
|
|
|
|
}
|
2012-03-11 19:01:28 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-14 13:41:20 -05:00
|
|
|
fn resolve_local(local: @ast::local, cx: ctxt, visitor: visit::vt<ctxt>) {
|
2012-05-15 23:19:35 -05:00
|
|
|
record_parent(cx, local.node.id);
|
2012-03-14 13:41:20 -05:00
|
|
|
visit::visit_local(local, 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.
|
2012-05-23 23:47:11 -05:00
|
|
|
let new_cx: ctxt = {parent: none with cx};
|
2012-03-09 18:39:54 -06:00
|
|
|
visit::visit_item(item, new_cx, visitor);
|
|
|
|
}
|
|
|
|
|
2012-04-26 18:02:01 -05:00
|
|
|
fn resolve_fn(fk: visit::fn_kind, decl: ast::fn_decl, body: ast::blk,
|
|
|
|
sp: span, id: ast::node_id, cx: ctxt,
|
|
|
|
visitor: visit::vt<ctxt>) {
|
|
|
|
|
|
|
|
let fn_cx = alt fk {
|
2012-06-24 17:09:57 -05:00
|
|
|
visit::fk_item_fn(*) | visit::fk_method(*) |
|
2012-05-14 16:13:32 -05:00
|
|
|
visit::fk_ctor(*) | visit::fk_dtor(*) {
|
2012-04-26 18:02:01 -05:00
|
|
|
// Top-level functions are a root scope.
|
2012-05-23 23:47:11 -05:00
|
|
|
{parent: some(id) with cx}
|
2012-04-26 18:02:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
visit::fk_anon(*) | visit::fk_fn_block(*) {
|
2012-05-23 23:47:11 -05:00
|
|
|
// Closures continue with the inherited scope.
|
|
|
|
cx
|
2012-04-26 18:02:01 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
#debug["visiting fn with body %d. cx.parent: %? \
|
2012-05-23 23:47:11 -05:00
|
|
|
fn_cx.parent: %?",
|
|
|
|
body.node.id, cx.parent, fn_cx.parent];
|
2012-04-26 18:02:01 -05:00
|
|
|
|
2012-06-30 18:19:07 -05:00
|
|
|
for decl.inputs.each |input| {
|
2012-05-15 23:19:35 -05:00
|
|
|
cx.region_map.insert(input.id, body.node.id);
|
2012-04-26 18:02:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
visit::visit_fn(fk, decl, body, sp, id, fn_cx, visitor);
|
|
|
|
}
|
|
|
|
|
2012-03-11 14:05:17 -05:00
|
|
|
fn resolve_crate(sess: session, def_map: resolve::def_map, crate: @ast::crate)
|
2012-05-15 23:19:35 -05:00
|
|
|
-> 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-07-11 12:28:30 -05:00
|
|
|
region_map: int_hash(),
|
2012-05-23 23:47:11 -05:00
|
|
|
parent: none};
|
2012-03-09 18:39:54 -06:00
|
|
|
let visitor = visit::mk_vt(@{
|
|
|
|
visit_block: resolve_block,
|
|
|
|
visit_item: resolve_item,
|
2012-04-26 18:02:01 -05:00
|
|
|
visit_fn: resolve_fn,
|
2012-03-11 14:05:17 -05:00
|
|
|
visit_arm: resolve_arm,
|
2012-03-11 19:01:28 -05:00
|
|
|
visit_pat: resolve_pat,
|
2012-03-14 13:41:20 -05:00
|
|
|
visit_expr: resolve_expr,
|
|
|
|
visit_local: resolve_local
|
2012-03-09 18:39:54 -06:00
|
|
|
with *visit::default_visitor()
|
|
|
|
});
|
|
|
|
visit::visit_crate(*crate, cx, visitor);
|
|
|
|
ret cx.region_map;
|
|
|
|
}
|
|
|
|
|
2012-07-11 12:28:30 -05:00
|
|
|
// ___________________________________________________________________________
|
|
|
|
// Determining region parameterization
|
|
|
|
//
|
|
|
|
// Infers which type defns must be region parameterized---this is done
|
|
|
|
// by scanning their contents to see whether they reference a region
|
|
|
|
// type, directly or indirectly. This is a fixed-point computation.
|
|
|
|
//
|
|
|
|
// We do it in two passes. First we walk the AST and construct a map
|
|
|
|
// from each type defn T1 to other defns which make use of it. For example,
|
|
|
|
// if we have a type like:
|
|
|
|
//
|
|
|
|
// type S = *int;
|
|
|
|
// type T = S;
|
|
|
|
//
|
|
|
|
// Then there would be a map entry from S to T. During the same walk,
|
|
|
|
// we also construct add any types that reference regions to a set and
|
|
|
|
// a worklist. We can then process the worklist, propagating indirect
|
|
|
|
// dependencies until a fixed point is reached.
|
|
|
|
|
|
|
|
type region_paramd_items = hashmap<ast::node_id, ()>;
|
|
|
|
type dep_map = hashmap<ast::node_id, @dvec<ast::node_id>>;
|
|
|
|
|
2012-07-11 17:00:40 -05:00
|
|
|
type determine_rp_ctxt_ = {
|
2012-07-11 12:28:30 -05:00
|
|
|
sess: session,
|
|
|
|
ast_map: ast_map::map,
|
|
|
|
def_map: resolve::def_map,
|
|
|
|
region_paramd_items: region_paramd_items,
|
|
|
|
dep_map: dep_map,
|
|
|
|
worklist: dvec<ast::node_id>,
|
|
|
|
|
2012-07-12 16:01:23 -05:00
|
|
|
// the innermost enclosing item id
|
2012-07-11 12:28:30 -05:00
|
|
|
mut item_id: ast::node_id,
|
2012-07-12 16:01:23 -05:00
|
|
|
|
|
|
|
// true when we are within an item but not within a method.
|
|
|
|
// see long discussion on region_is_relevant()
|
2012-07-11 12:28:30 -05:00
|
|
|
mut anon_implies_rp: bool
|
|
|
|
};
|
|
|
|
|
2012-07-11 17:00:40 -05:00
|
|
|
enum determine_rp_ctxt {
|
|
|
|
determine_rp_ctxt_(@determine_rp_ctxt_)
|
|
|
|
}
|
|
|
|
|
2012-07-11 12:28:30 -05:00
|
|
|
impl methods for determine_rp_ctxt {
|
|
|
|
fn add_rp(id: ast::node_id) {
|
|
|
|
assert id != 0;
|
|
|
|
if self.region_paramd_items.insert(id, ()) {
|
|
|
|
#debug["add region-parameterized item: %d (%s)",
|
|
|
|
id, ast_map::node_id_to_str(self.ast_map, id)];
|
|
|
|
self.worklist.push(id);
|
|
|
|
} else {
|
|
|
|
#debug["item %d already region-parameterized", id];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn add_dep(from: ast::node_id, to: ast::node_id) {
|
|
|
|
#debug["add dependency from %d -> %d (%s -> %s)",
|
|
|
|
from, to,
|
|
|
|
ast_map::node_id_to_str(self.ast_map, from),
|
|
|
|
ast_map::node_id_to_str(self.ast_map, to)];
|
|
|
|
let vec = alt self.dep_map.find(from) {
|
|
|
|
some(vec) => {vec}
|
|
|
|
none => {
|
|
|
|
let vec = @dvec();
|
|
|
|
self.dep_map.insert(from, vec);
|
|
|
|
vec
|
|
|
|
}
|
|
|
|
};
|
|
|
|
if !vec.contains(to) { vec.push(to); }
|
|
|
|
}
|
|
|
|
|
2012-07-12 16:01:23 -05:00
|
|
|
// Determines whether a reference to a region that appears in the
|
|
|
|
// AST implies that the enclosing type is region-parameterized.
|
|
|
|
//
|
|
|
|
// This point is subtle. Here are four examples to make it more
|
|
|
|
// concrete.
|
|
|
|
//
|
|
|
|
// 1. impl foo for &int { ... }
|
|
|
|
// 2. impl foo for &self/int { ... }
|
|
|
|
// 3. impl foo for bar { fn m() -> &self/int { ... } }
|
|
|
|
// 4. impl foo for bar { fn m() -> &int { ... } }
|
|
|
|
//
|
|
|
|
// In case 1, the anonymous region is being referenced,
|
|
|
|
// but it appears in a context where the anonymous region
|
|
|
|
// resolves to self, so the impl foo is region-parameterized.
|
|
|
|
//
|
|
|
|
// In case 2, the self parameter is written explicitly.
|
|
|
|
//
|
|
|
|
// In case 3, the method refers to self, so that implies that the
|
|
|
|
// impl must be region parameterized. (If the type bar is not
|
|
|
|
// region parameterized, that is an error, because the self region
|
|
|
|
// is effectively unconstrained, but that is detected elsewhere).
|
|
|
|
//
|
|
|
|
// In case 4, the anonymous region is referenced, but it
|
|
|
|
// bound by the method, so it does not refer to self. This impl
|
|
|
|
// need not be region parameterized.
|
|
|
|
//
|
|
|
|
// So the rules basically are: the `self` region always implies
|
|
|
|
// that the enclosing type is region parameterized. The anonymous
|
|
|
|
// region also does, unless it appears within a method, in which
|
|
|
|
// case it is bound. We handle this by setting a flag
|
|
|
|
// (anon_implies_rp) to true when we enter an item and setting
|
|
|
|
// that flag to false when we enter a method.
|
2012-07-11 12:28:30 -05:00
|
|
|
fn region_is_relevant(r: @ast::region) -> bool {
|
|
|
|
alt r.node {
|
|
|
|
ast::re_anon {self.anon_implies_rp}
|
2012-07-14 00:57:48 -05:00
|
|
|
ast::re_named(@~"self") {true}
|
2012-07-11 12:28:30 -05:00
|
|
|
ast::re_named(_) {false}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn with(item_id: ast::node_id, anon_implies_rp: bool, f: fn()) {
|
|
|
|
let old_item_id = self.item_id;
|
|
|
|
let old_anon_implies_rp = self.anon_implies_rp;
|
|
|
|
self.item_id = item_id;
|
|
|
|
self.anon_implies_rp = anon_implies_rp;
|
|
|
|
#debug["with_item_id(%d, %b)", item_id, anon_implies_rp];
|
|
|
|
let _i = util::common::indenter();
|
|
|
|
f();
|
|
|
|
self.item_id = old_item_id;
|
|
|
|
self.anon_implies_rp = old_anon_implies_rp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn determine_rp_in_item(item: @ast::item,
|
|
|
|
&&cx: determine_rp_ctxt,
|
|
|
|
visitor: visit::vt<determine_rp_ctxt>) {
|
|
|
|
do cx.with(item.id, true) {
|
|
|
|
visit::visit_item(item, cx, visitor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn determine_rp_in_fn(fk: visit::fn_kind,
|
|
|
|
decl: ast::fn_decl,
|
|
|
|
body: ast::blk,
|
|
|
|
sp: span,
|
|
|
|
id: ast::node_id,
|
|
|
|
&&cx: determine_rp_ctxt,
|
|
|
|
visitor: visit::vt<determine_rp_ctxt>) {
|
|
|
|
do cx.with(cx.item_id, false) {
|
|
|
|
visit::visit_fn(fk, decl, body, sp, id, cx, visitor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn determine_rp_in_ty_method(ty_m: ast::ty_method,
|
|
|
|
&&cx: determine_rp_ctxt,
|
|
|
|
visitor: visit::vt<determine_rp_ctxt>) {
|
|
|
|
do cx.with(cx.item_id, false) {
|
|
|
|
visit::visit_ty_method(ty_m, cx, visitor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn determine_rp_in_ty(ty: @ast::ty,
|
|
|
|
&&cx: determine_rp_ctxt,
|
|
|
|
visitor: visit::vt<determine_rp_ctxt>) {
|
|
|
|
|
|
|
|
// we are only interesting in types that will require an item to
|
|
|
|
// be region-parameterized. if cx.item_id is zero, then this type
|
|
|
|
// is not a member of a type defn nor is it a constitutent of an
|
|
|
|
// impl etc. So we can ignore it and its components.
|
|
|
|
if cx.item_id == 0 { ret; }
|
|
|
|
|
|
|
|
// if this type directly references a region, either via a
|
|
|
|
// region pointer like &r.ty or a region-parameterized path
|
|
|
|
// like path/r, add to the worklist/set
|
|
|
|
alt ty.node {
|
|
|
|
ast::ty_rptr(r, _) |
|
2012-07-14 14:45:45 -05:00
|
|
|
ast::ty_path(@{rp: some(r), _}, _) => {
|
2012-07-11 12:28:30 -05:00
|
|
|
#debug["referenced type with regions %s", pprust::ty_to_str(ty)];
|
|
|
|
if cx.region_is_relevant(r) {
|
|
|
|
cx.add_rp(cx.item_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if this references another named type, add the dependency
|
|
|
|
// to the dep_map. If the type is not defined in this crate,
|
|
|
|
// then check whether it is region-parameterized and consider
|
|
|
|
// that as a direct dependency.
|
|
|
|
alt ty.node {
|
|
|
|
ast::ty_path(_, id) {
|
|
|
|
alt cx.def_map.get(id) {
|
|
|
|
ast::def_ty(did) | ast::def_class(did) {
|
|
|
|
if did.crate == ast::local_crate {
|
|
|
|
cx.add_dep(did.node, cx.item_id);
|
|
|
|
} else {
|
|
|
|
let cstore = cx.sess.cstore;
|
|
|
|
if csearch::get_region_param(cstore, did) {
|
|
|
|
#debug["reference to external, rp'd type %s",
|
|
|
|
pprust::ty_to_str(ty)];
|
|
|
|
cx.add_rp(cx.item_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ {}
|
|
|
|
}
|
|
|
|
|
|
|
|
visit::visit_ty(ty, cx, visitor);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn determine_rp_in_crate(sess: session,
|
|
|
|
ast_map: ast_map::map,
|
|
|
|
def_map: resolve::def_map,
|
|
|
|
crate: @ast::crate) -> region_paramd_items {
|
2012-07-11 17:00:40 -05:00
|
|
|
let cx = determine_rp_ctxt_(@{sess: sess,
|
|
|
|
ast_map: ast_map,
|
|
|
|
def_map: def_map,
|
|
|
|
region_paramd_items: int_hash(),
|
|
|
|
dep_map: int_hash(),
|
|
|
|
worklist: dvec(),
|
|
|
|
mut item_id: 0,
|
|
|
|
mut anon_implies_rp: false});
|
2012-07-11 12:28:30 -05:00
|
|
|
|
|
|
|
// gather up the base set, worklist and dep_map:
|
|
|
|
let visitor = visit::mk_vt(@{
|
|
|
|
visit_fn: determine_rp_in_fn,
|
|
|
|
visit_item: determine_rp_in_item,
|
|
|
|
visit_ty: determine_rp_in_ty,
|
|
|
|
visit_ty_method: determine_rp_in_ty_method,
|
|
|
|
with *visit::default_visitor()
|
|
|
|
});
|
|
|
|
visit::visit_crate(*crate, cx, visitor);
|
|
|
|
|
|
|
|
// propagate indirect dependencies
|
|
|
|
while cx.worklist.len() != 0 {
|
|
|
|
let id = cx.worklist.pop();
|
|
|
|
#debug["popped %d from worklist", id];
|
|
|
|
alt cx.dep_map.find(id) {
|
|
|
|
none {}
|
|
|
|
some(vec) {
|
|
|
|
for vec.each |to_id| {
|
|
|
|
cx.add_rp(to_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// return final set
|
|
|
|
ret cx.region_paramd_items;
|
2012-07-14 14:45:45 -05:00
|
|
|
}
|