Skip to content

Commit 47bfd4f

Browse files
committed
rustc: Refactor regions to handle nested functions properly and fix the subtyping relation
1 parent 664d71f commit 47bfd4f

File tree

3 files changed

+100
-105
lines changed

3 files changed

+100
-105
lines changed

src/rustc/middle/region.rs

Lines changed: 87 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,26 @@ import syntax::{ast, visit};
99
import std::map;
1010
import std::map::hashmap;
1111

12-
type region_map = {
13-
/* Mapping from a block to its parent block, if there is one. */
14-
parent_blocks: hashmap<ast::node_id,ast::node_id>,
15-
/* Mapping from a lambda to its parent function, if there is one. */
16-
parent_fns: hashmap<ast::node_id,ast::node_id>,
17-
/* Mapping from a region type in the AST to its resolved region. */
18-
ast_type_to_region: hashmap<ast::node_id,ty::region>,
19-
/* Mapping from a local variable to its containing block. */
20-
local_blocks: hashmap<ast::node_id,ast::node_id>
21-
};
22-
2312
/* Represents the type of the most immediate parent node. */
2413
enum parent {
2514
pa_item(ast::node_id),
2615
pa_block(ast::node_id),
27-
pa_alt,
16+
pa_nested_fn(ast::node_id),
2817
pa_crate
2918
}
3019

20+
type region_map = {
21+
/*
22+
* Mapping from blocks and function expression to their parent block or
23+
* function expression.
24+
*/
25+
parents: hashmap<ast::node_id,ast::node_id>,
26+
/* Mapping from a region type in the AST to its resolved region. */
27+
ast_type_to_region: hashmap<ast::node_id,ty::region>,
28+
/* Mapping from a local variable to its containing block. */
29+
local_blocks: hashmap<ast::node_id,ast::node_id>,
30+
};
31+
3132
type ctxt = {
3233
sess: session,
3334
def_map: resolve::def_map,
@@ -43,18 +44,29 @@ type ctxt = {
4344
mut queued_locals: [ast::node_id],
4445

4546
parent: parent,
46-
mut parent_fn: option<ast::node_id>
47+
48+
/* True if we're within the pattern part of an alt, false otherwise. */
49+
in_alt: bool
4750
};
4851

49-
// Returns true if `subblock` is equal to or is lexically nested inside
50-
// `superblock` and false otherwise.
51-
fn block_contains(region_map: @region_map, superblock: ast::node_id,
52-
subblock: ast::node_id) -> bool {
53-
let subblock = subblock;
54-
while superblock != subblock {
55-
alt region_map.parent_blocks.find(subblock) {
52+
fn region_to_scope(_region_map: @region_map, region: ty::region)
53+
-> ast::node_id {
54+
ret alt region {
55+
ty::re_caller(def_id) { def_id.node }
56+
ty::re_named(_) { fail "TODO: named regions" }
57+
ty::re_block(node_id) { node_id }
58+
};
59+
}
60+
61+
// Returns true if `subscope` is equal to or is lexically nested inside
62+
// `superscope` and false otherwise.
63+
fn scope_contains(region_map: @region_map, superscope: ast::node_id,
64+
subscope: ast::node_id) -> bool {
65+
let subscope = subscope;
66+
while superscope != subscope {
67+
alt region_map.parents.find(subscope) {
5668
none { ret false; }
57-
some(blk) { subblock = blk; }
69+
some(scope) { subscope = scope; }
5870
}
5971
}
6072
ret true;
@@ -68,24 +80,21 @@ fn resolve_ty(ty: @ast::ty, cx: ctxt, visitor: visit::vt<ctxt>) {
6880
ast::re_inferred {
6981
// We infer to the caller region if we're at item scope
7082
// and to the block region if we're at block scope.
83+
//
84+
// TODO: What do we do if we're in an alt?
85+
7186
alt cx.parent {
72-
pa_item(item_id) {
87+
pa_item(item_id) | pa_nested_fn(item_id) {
7388
let def_id = {crate: ast::local_crate,
7489
node: item_id};
7590
region = ty::re_caller(def_id);
7691
}
7792
pa_block(block_id) {
7893
region = ty::re_block(block_id);
7994
}
80-
pa_alt {
81-
// FIXME: Need a design decision here.
82-
cx.sess.span_bug(ty.span,
83-
"what does & in an alt " +
84-
"resolve to?");
85-
}
8695
pa_crate {
87-
cx.sess.span_bug(ty.span,
88-
"region type outside item");
96+
cx.sess.span_bug(ty.span, "inferred region at " +
97+
"crate level?!");
8998
}
9099
}
91100
}
@@ -97,16 +106,18 @@ fn resolve_ty(ty: @ast::ty, cx: ctxt, visitor: visit::vt<ctxt>) {
97106
some(def_id) { region = ty::re_named(def_id); }
98107
none {
99108
alt cx.parent {
100-
pa_item(_) { /* ok; fall through */ }
101-
pa_block(_) | pa_alt {
109+
pa_item(_) | pa_nested_fn(_) {
110+
/* ok; fall through */
111+
}
112+
pa_block(_) {
102113
cx.sess.span_err(ty.span,
103114
"unknown region `" +
104115
ident + "`");
105116
}
106117
pa_crate {
107-
cx.sess.span_bug(ty.span,
108-
"named region at " +
109-
"crate scope?!");
118+
cx.sess.span_bug(ty.span, "named " +
119+
"region at crate " +
120+
"level?!");
110121
}
111122
}
112123

@@ -120,23 +131,23 @@ fn resolve_ty(ty: @ast::ty, cx: ctxt, visitor: visit::vt<ctxt>) {
120131

121132
ast::re_self {
122133
// For blocks, "self" means "the current block".
134+
//
135+
// TODO: What do we do in an alt?
136+
//
137+
// FIXME: Doesn't work in type items.
138+
123139
alt cx.parent {
124-
pa_item(_) {
125-
cx.sess.span_unimpl(ty.span,
126-
"'self' region for items");
140+
pa_item(item_id) | pa_nested_fn(item_id) {
141+
let def_id = {crate: ast::local_crate,
142+
node: item_id};
143+
region = ty::re_caller(def_id);
127144
}
128145
pa_block(block_id) {
129146
region = ty::re_block(block_id);
130147
}
131-
pa_alt {
132-
// FIXME: Need a design decision here.
133-
cx.sess.span_bug(ty.span,
134-
"what does &self. in an alt " +
135-
"resolve to?");
136-
}
137148
pa_crate {
138149
cx.sess.span_bug(ty.span,
139-
"region type outside item");
150+
"region type outside item?!");
140151
}
141152
}
142153
}
@@ -151,28 +162,33 @@ fn resolve_ty(ty: @ast::ty, cx: ctxt, visitor: visit::vt<ctxt>) {
151162
visit::visit_ty(ty, cx, visitor);
152163
}
153164

154-
fn resolve_block(blk: ast::blk, cx: ctxt, visitor: visit::vt<ctxt>) {
165+
fn record_parent(cx: ctxt, child_id: ast::node_id) {
155166
alt cx.parent {
156-
pa_item(_) | pa_alt { /* no-op */ }
157-
pa_block(parent_block_id) {
158-
cx.region_map.parent_blocks.insert(blk.node.id, parent_block_id);
167+
pa_item(parent_id) | pa_block(parent_id) | pa_nested_fn(parent_id) {
168+
cx.region_map.parents.insert(child_id, parent_id);
159169
}
160-
pa_crate { cx.sess.span_bug(blk.span, "block outside item?!"); }
170+
pa_crate { /* no-op */ }
161171
}
172+
}
173+
174+
fn resolve_block(blk: ast::blk, cx: ctxt, visitor: visit::vt<ctxt>) {
175+
// Record the parent of this block.
176+
record_parent(cx, blk.node.id);
162177

163178
// Resolve queued locals to this block.
164179
for local_id in cx.queued_locals {
165180
cx.region_map.local_blocks.insert(local_id, blk.node.id);
166181
}
167182

183+
// Descend.
168184
let new_cx: ctxt = {parent: pa_block(blk.node.id),
169-
mut queued_locals: [] with cx};
185+
mut queued_locals: [],
186+
in_alt: false with cx};
170187
visit::visit_block(blk, new_cx, visitor);
171188
}
172189

173190
fn resolve_arm(arm: ast::arm, cx: ctxt, visitor: visit::vt<ctxt>) {
174-
let new_cx: ctxt = {parent: pa_alt,
175-
mut queued_locals: [] with cx};
191+
let new_cx: ctxt = {mut queued_locals: [], in_alt: true with cx};
176192
visit::visit_arm(arm, new_cx, visitor);
177193
}
178194

@@ -190,15 +206,19 @@ fn resolve_pat(pat: @ast::pat, cx: ctxt, visitor: visit::vt<ctxt>) {
190206
* containing block, depending on whether we're in an alt
191207
* or not.
192208
*/
193-
alt cx.parent {
194-
pa_block(block_id) {
195-
let local_blocks = cx.region_map.local_blocks;
196-
local_blocks.insert(pat.id, block_id);
197-
}
198-
pa_alt {
199-
vec::push(cx.queued_locals, pat.id);
209+
if cx.in_alt {
210+
vec::push(cx.queued_locals, pat.id);
211+
} else {
212+
alt cx.parent {
213+
pa_block(block_id) {
214+
let local_blocks = cx.region_map.local_blocks;
215+
local_blocks.insert(pat.id, block_id);
216+
}
217+
_ {
218+
cx.sess.span_bug(pat.span,
219+
"unexpected parent");
220+
}
200221
}
201-
_ { cx.sess.span_bug(pat.span, "unexpected parent"); }
202222
}
203223
}
204224
}
@@ -212,9 +232,9 @@ fn resolve_pat(pat: @ast::pat, cx: ctxt, visitor: visit::vt<ctxt>) {
212232
fn resolve_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) {
213233
alt expr.node {
214234
ast::expr_fn(_, _, _, _) | ast::expr_fn_block(_, _) {
215-
let parent_fns = cx.region_map.parent_fns;
216-
parent_fns.insert(expr.id, option::get(cx.parent_fn));
217-
let new_cx = {parent_fn: some(expr.id) with cx};
235+
record_parent(cx, expr.id);
236+
let new_cx = {parent: pa_nested_fn(expr.id),
237+
in_alt: false with cx};
218238
visit::visit_expr(expr, new_cx, visitor);
219239
}
220240
_ { visit::visit_expr(expr, cx, visitor); }
@@ -225,7 +245,7 @@ fn resolve_item(item: @ast::item, cx: ctxt, visitor: visit::vt<ctxt>) {
225245
// Items create a new outer block scope as far as we're concerned.
226246
let new_cx: ctxt = {names_in_scope: map::new_str_hash(),
227247
parent: pa_item(item.id),
228-
parent_fn: some(item.id)
248+
in_alt: false
229249
with cx};
230250
visit::visit_item(item, new_cx, visitor);
231251
}
@@ -234,14 +254,13 @@ fn resolve_crate(sess: session, def_map: resolve::def_map, crate: @ast::crate)
234254
-> @region_map {
235255
let cx: ctxt = {sess: sess,
236256
def_map: def_map,
237-
region_map: @{parent_blocks: map::new_int_hash(),
238-
parent_fns: map::new_int_hash(),
257+
region_map: @{parents: map::new_int_hash(),
239258
ast_type_to_region: map::new_int_hash(),
240259
local_blocks: map::new_int_hash()},
241260
names_in_scope: map::new_str_hash(),
242261
mut queued_locals: [],
243262
parent: pa_crate,
244-
mut parent_fn: none};
263+
in_alt: false};
245264
let visitor = visit::mk_vt(@{
246265
visit_block: resolve_block,
247266
visit_item: resolve_item,

src/rustc/middle/regionck.rs

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,12 @@ fn check_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) {
3838
some(eb) { eb }
3939
};
4040

41-
let parent_blocks = cx.tcx.region_map.parent_blocks;
42-
while enclosing_block_id != referent_block_id {
43-
if parent_blocks.contains_key(referent_block_id) {
44-
referent_block_id =
45-
parent_blocks.get(referent_block_id);
46-
} else {
47-
cx.tcx.sess.span_err(expr.span,
48-
"reference escapes " +
49-
"its block");
50-
break;
51-
}
41+
if !region::scope_contains(cx.tcx.region_map,
42+
referent_block_id,
43+
enclosing_block_id) {
44+
45+
cx.tcx.sess.span_err(expr.span, "reference " +
46+
"escapes its block");
5247
}
5348
}
5449
}

src/rustc/middle/ty.rs

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1897,33 +1897,14 @@ mod unify {
18971897
}
18981898
}
18991899

1900-
alt (super, sub) {
1901-
(re_caller(_), re_caller(_)) {
1902-
// FIXME: This is wrong w/r/t nested functions.
1903-
ret some(super);
1904-
}
1905-
(re_caller(_), re_named(_)) | (re_named(_), re_caller(_)) {
1906-
ret none;
1907-
}
1908-
(re_named(a), re_named(b)) {
1909-
ret if a == b { some(super) } else { none }
1910-
}
1911-
(re_caller(_), re_block(_)) | (re_named(_), re_block(_)) {
1912-
// FIXME: This is wrong w/r/t nested functions.
1913-
ret some(super);
1914-
}
1915-
(re_block(_), re_caller(_)) | (re_block(_), re_named(_)) {
1916-
ret none;
1917-
}
1918-
(re_block(superblock), re_block(subblock)) {
1919-
if region::block_contains(cx.tcx.region_map, superblock,
1920-
subblock) {
1921-
ret some(super);
1922-
} else {
1923-
ret none;
1924-
}
1925-
}
1900+
// Outer regions are subtypes of inner regions. (This is somewhat
1901+
// surprising!)
1902+
let superscope = region::region_to_scope(cx.tcx.region_map, super);
1903+
let subscope = region::region_to_scope(cx.tcx.region_map, sub);
1904+
if region::scope_contains(cx.tcx.region_map, subscope, superscope) {
1905+
ret some(super);
19261906
}
1907+
ret none;
19271908
}
19281909

19291910
fn unify_step(cx: @uctxt, expected: t, actual: t,

0 commit comments

Comments
 (0)