Skip to content

Commit a559329

Browse files
committed
test that we preserve boxes in patterns---still one bug
1 parent 3f283bb commit a559329

File tree

7 files changed

+255
-61
lines changed

7 files changed

+255
-61
lines changed

src/rustc/middle/borrowck.rs

Lines changed: 132 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ enum categorization {
8585
cat_stack_upvar(cmt), // upvar in stack closure
8686
cat_deref(cmt, uint, ptr_kind), // deref of a ptr
8787
cat_comp(cmt, comp_kind), // adjust to locate an internal component
88+
cat_discr(cmt, ast::node_id), // alt discriminant (see preserve())
8889
}
8990

9091
// different kinds of pointers:
@@ -205,7 +206,8 @@ fn req_loans_in_expr(ex: @ast::expr,
205206
// If this expression is borrowed, have to ensure it remains valid:
206207
for tcx.borrowings.find(ex.id).each { |scope_id|
207208
let cmt = self.bccx.cat_borrow_of_expr(ex);
208-
self.guarantee_valid(cmt, m_const, ty::re_scope(scope_id));
209+
let scope_r = ty::re_scope(scope_id);
210+
self.guarantee_valid(cmt, m_const, scope_r);
209211
}
210212

211213
// Special checks for various kinds of expressions:
@@ -224,20 +226,19 @@ fn req_loans_in_expr(ex: @ast::expr,
224226

225227
ast::expr_call(f, args, _) {
226228
let arg_tys = ty::ty_fn_args(ty::expr_ty(self.tcx(), f));
229+
let scope_r = ty::re_scope(ex.id);
227230
vec::iter2(args, arg_tys) { |arg, arg_ty|
228231
alt ty::resolved_mode(self.tcx(), arg_ty.mode) {
229232
ast::by_mutbl_ref {
230233
let arg_cmt = self.bccx.cat_expr(arg);
231-
self.guarantee_valid(arg_cmt, m_mutbl, ty::re_scope(ex.id));
234+
self.guarantee_valid(arg_cmt, m_mutbl, scope_r);
232235
}
233236
ast::by_ref {
234237
let arg_cmt = self.bccx.cat_expr(arg);
235238
if TREAT_CONST_AS_IMM {
236-
self.guarantee_valid(arg_cmt, m_imm,
237-
ty::re_scope(ex.id));
239+
self.guarantee_valid(arg_cmt, m_imm, scope_r);
238240
} else {
239-
self.guarantee_valid(arg_cmt, m_const,
240-
ty::re_scope(ex.id));
241+
self.guarantee_valid(arg_cmt, m_const, scope_r);
241242
}
242243
}
243244
ast::by_move | ast::by_copy | ast::by_val {}
@@ -249,7 +250,7 @@ fn req_loans_in_expr(ex: @ast::expr,
249250
let cmt = self.bccx.cat_expr(ex_v);
250251
for arms.each { |arm|
251252
for arm.pats.each { |pat|
252-
self.gather_pat(cmt, pat, arm.body.node.id);
253+
self.gather_pat(cmt, pat, arm.body.node.id, ex.id);
253254
}
254255
}
255256
}
@@ -265,8 +266,10 @@ impl methods for gather_loan_ctxt {
265266
fn tcx() -> ty::ctxt { self.bccx.tcx }
266267

267268
// guarantees that addr_of(cmt) will be valid for the duration of
268-
// `scope_r`, or reports an error. This may entail taking out loans,
269-
// which will be added to the `req_loan_map`.
269+
// `static_scope_r`, or reports an error. This may entail taking
270+
// out loans, which will be added to the `req_loan_map`. This can
271+
// also entail "rooting" GC'd pointers, which means ensuring
272+
// dynamically that they are not freed.
270273
fn guarantee_valid(cmt: cmt,
271274
mutbl: ast::mutability,
272275
scope_r: ty::region) {
@@ -334,16 +337,24 @@ impl methods for gather_loan_ctxt {
334337
}
335338
}
336339

337-
fn gather_pat(cmt: cmt, pat: @ast::pat, alt_id: ast::node_id) {
340+
fn gather_pat(cmt: cmt, pat: @ast::pat,
341+
arm_id: ast::node_id, alt_id: ast::node_id) {
338342

339343
// Here, `cmt` is the categorization for the value being
340344
// matched and pat is the pattern it is being matched against.
341345
//
342-
// In general, the way that this works is that we
346+
// In general, the way that this works is that we walk down
347+
// the pattern, constructing a cmt that represents the path
348+
// that will be taken to reach the value being matched.
349+
//
350+
// When we encounter named bindings, we take the cmt that has
351+
// been built up and pass it off to guarantee_valid() so that
352+
// we can be sure that the binding will remain valid for the
353+
// duration of the arm.
343354

344-
#debug["gather_pat: id=%d pat=%s cmt=%s alt_id=%d",
355+
#debug["gather_pat: id=%d pat=%s cmt=%s arm_id=%d alt_id=%d",
345356
pat.id, pprust::pat_to_str(pat),
346-
self.bccx.cmt_to_repr(cmt), alt_id];
357+
self.bccx.cmt_to_repr(cmt), arm_id, alt_id];
347358
let _i = indenter();
348359

349360
let tcx = self.tcx();
@@ -359,7 +370,7 @@ impl methods for gather_loan_ctxt {
359370
// variant(x, y, z)
360371
for subpats.each { |subpat|
361372
let subcmt = self.bccx.cat_variant(pat, cmt, subpat);
362-
self.gather_pat(subcmt, subpat, alt_id);
373+
self.gather_pat(subcmt, subpat, arm_id, alt_id);
363374
}
364375
}
365376

@@ -370,32 +381,44 @@ impl methods for gather_loan_ctxt {
370381
ast::pat_ident(id, o_pat) {
371382
// x or x @ p --- `x` must remain valid for the scope of the alt
372383
#debug["defines identifier %s", pprust::path_to_str(id)];
373-
self.guarantee_valid(cmt, m_const, ty::re_scope(alt_id));
374-
for o_pat.each { |p| self.gather_pat(cmt, p, alt_id); }
384+
385+
// Note: there is a discussion of the function of
386+
// cat_discr in the method preserve():
387+
let cmt1 = self.bccx.cat_discr(cmt, alt_id);
388+
let arm_scope = ty::re_scope(arm_id);
389+
self.guarantee_valid(cmt1, m_const, arm_scope);
390+
391+
for o_pat.each { |p|
392+
self.gather_pat(cmt, p, arm_id, alt_id);
393+
}
375394
}
376395

377396
ast::pat_rec(field_pats, _) {
378397
// {f1: p1, ..., fN: pN}
379398
for field_pats.each { |fp|
380399
let cmt_field = self.bccx.cat_field(pat, cmt, fp.ident,
381400
tcx.ty(fp.pat));
382-
self.gather_pat(cmt_field, fp.pat, alt_id);
401+
self.gather_pat(cmt_field, fp.pat, arm_id, alt_id);
383402
}
384403
}
385404

386405
ast::pat_tup(subpats) {
387406
// (p1, ..., pN)
388407
for subpats.each { |subpat|
389408
let subcmt = self.bccx.cat_tuple_elt(pat, cmt, subpat);
390-
self.gather_pat(subcmt, subpat, alt_id);
409+
self.gather_pat(subcmt, subpat, arm_id, alt_id);
391410
}
392411
}
393412

394413
ast::pat_box(subpat) | ast::pat_uniq(subpat) {
395414
// @p1, ~p1
396415
alt self.bccx.cat_deref(pat, cmt, 0u, true) {
397-
some(subcmt) { self.gather_pat(subcmt, subpat, alt_id); }
398-
none { tcx.sess.span_bug(pat.span, "Non derefable type"); }
416+
some(subcmt) {
417+
self.gather_pat(subcmt, subpat, arm_id, alt_id);
418+
}
419+
none {
420+
tcx.sess.span_bug(pat.span, "Non derefable type");
421+
}
399422
}
400423
}
401424

@@ -1006,6 +1029,10 @@ impl categorize_methods for borrowck_ctxt {
10061029
}
10071030
}
10081031

1032+
fn cat_discr(cmt: cmt, alt_id: ast::node_id) -> cmt {
1033+
ret @{cat:cat_discr(cmt, alt_id) with *cmt};
1034+
}
1035+
10091036
fn cat_field<N:ast_node>(node: N, base_cmt: cmt,
10101037
f_name: str, f_ty: ty::t) -> cmt {
10111038
let f_mutbl = alt field_mutbl(self.tcx, base_cmt.ty, f_name) {
@@ -1239,6 +1266,7 @@ impl categorize_methods for borrowck_ctxt {
12391266
cat_comp(cmt, comp) {
12401267
#fmt["%s.%s", self.cat_to_repr(cmt.cat), self.comp_to_repr(comp)]
12411268
}
1269+
cat_discr(cmt, _) { self.cat_to_repr(cmt.cat) }
12421270
}
12431271
}
12441272

@@ -1326,6 +1354,9 @@ impl categorize_methods for borrowck_ctxt {
13261354
_ { mut_str + " indexed content" }
13271355
}
13281356
}
1357+
cat_discr(cmt, _) {
1358+
self.cmt_to_str(cmt)
1359+
}
13291360
}
13301361
}
13311362

@@ -1420,34 +1451,23 @@ fn field_mutbl(tcx: ty::ctxt,
14201451
// Preserve(Ex, S) holds if ToAddr(Ex) will remain valid for the entirety of
14211452
// the scope S.
14221453

1423-
enum preserve_ctxt = @{
1424-
bccx: borrowck_ctxt, opt_scope_id: option<ast::node_id>
1425-
};
1426-
14271454
impl preserve_methods for borrowck_ctxt {
1428-
fn preserve(cmt: cmt,
1429-
opt_scope_id: option<ast::node_id>) -> bckres<()> {
1430-
preserve_ctxt(@{bccx:self, opt_scope_id:opt_scope_id}).preserve(cmt)
1431-
}
1432-
}
1433-
1434-
impl preserve_methods for preserve_ctxt {
1435-
fn preserve(cmt: cmt) -> bckres<()> {
1436-
#debug["preserve(%s)", self.bccx.cmt_to_repr(cmt)];
1455+
fn preserve(cmt: cmt, opt_scope_id: option<ast::node_id>) -> bckres<()> {
1456+
#debug["preserve(%s)", self.cmt_to_repr(cmt)];
14371457
let _i = indenter();
14381458

14391459
alt cmt.cat {
14401460
cat_rvalue | cat_special(_) {
14411461
ok(())
14421462
}
14431463
cat_stack_upvar(cmt) {
1444-
self.preserve(cmt)
1464+
self.preserve(cmt, opt_scope_id)
14451465
}
14461466
cat_local(_) {
14471467
// This should never happen. Local variables are always lendable,
14481468
// so either `loan()` should be called or there must be some
14491469
// intermediate @ or &---they are not lendable but do not recurse.
1450-
self.bccx.tcx.sess.span_bug(
1470+
self.tcx.sess.span_bug(
14511471
cmt.span,
14521472
"preserve() called with local");
14531473
}
@@ -1463,13 +1483,13 @@ impl preserve_methods for preserve_ctxt {
14631483
cat_comp(cmt_base, comp_res) {
14641484
// Most embedded components: if the base is stable, the
14651485
// type never changes.
1466-
self.preserve(cmt_base)
1486+
self.preserve(cmt_base, opt_scope_id)
14671487
}
14681488
cat_comp(cmt1, comp_variant) {
1469-
self.require_imm(cmt, cmt1, err_mut_variant)
1489+
self.require_imm(cmt, cmt1, opt_scope_id, err_mut_variant)
14701490
}
14711491
cat_deref(cmt1, _, uniq_ptr) {
1472-
self.require_imm(cmt, cmt1, err_mut_uniq)
1492+
self.require_imm(cmt, cmt1, opt_scope_id, err_mut_uniq)
14731493
}
14741494
cat_deref(_, _, region_ptr) {
14751495
// References are always "stable" by induction (when the
@@ -1482,33 +1502,96 @@ impl preserve_methods for preserve_ctxt {
14821502
ok(())
14831503
}
14841504
cat_deref(base, derefs, gc_ptr) {
1485-
// GC'd pointers of type @MT: always stable because we can inc
1486-
// the ref count or keep a GC root as necessary. We need to
1487-
// insert this id into the root_map, however.
1488-
alt self.opt_scope_id {
1505+
// GC'd pointers of type @MT: always stable because we can
1506+
// inc the ref count or keep a GC root as necessary. We
1507+
// need to insert this id into the root_map, however.
1508+
alt opt_scope_id {
14891509
some(scope_id) {
14901510
#debug["Inserting root map entry for %s: \
14911511
node %d:%u -> scope %d",
1492-
self.bccx.cmt_to_repr(cmt), base.id,
1512+
self.cmt_to_repr(cmt), base.id,
14931513
derefs, scope_id];
1514+
14941515
let rk = {id: base.id, derefs: derefs};
1495-
self.bccx.root_map.insert(rk, scope_id);
1516+
self.root_map.insert(rk, scope_id);
14961517
ok(())
14971518
}
14981519
none {
14991520
err({cmt:cmt, code:err_preserve_gc})
15001521
}
15011522
}
15021523
}
1524+
cat_discr(base, alt_id) {
1525+
// Subtle: in an alt, we must ensure that each binding
1526+
// variable remains valid for the duration of the arm in
1527+
// which it appears, presuming that this arm is taken.
1528+
// But it is inconvenient in trans to root something just
1529+
// for one arm. Therefore, we insert a cat_discr(),
1530+
// basically a special kind of category that says "if this
1531+
// value must be dynamically rooted, root it for the scope
1532+
// `alt_id`.
1533+
//
1534+
// As an example, consider this scenario:
1535+
//
1536+
// let mut x = @some(3);
1537+
// alt *x { some(y) {...} none {...} }
1538+
//
1539+
// Technically, the value `x` need only be rooted
1540+
// in the `some` arm. However, we evaluate `x` in trans
1541+
// before we know what arm will be taken, so we just
1542+
// always root it for the duration of the alt.
1543+
//
1544+
// As a second example, consider *this* scenario:
1545+
//
1546+
// let x = @mut @some(3);
1547+
// alt x { @@some(y) {...} @@none {...} }
1548+
//
1549+
// Here again, `x` need only be rooted in the `some` arm.
1550+
// In this case, the value which needs to be rooted is
1551+
// found only when checking which pattern matches: but
1552+
// this check is done before entering the arm. Therefore,
1553+
// even in this case we just choose to keep the value
1554+
// rooted for the entire alt. This means the value will be
1555+
// rooted even if the none arm is taken. Oh well.
1556+
//
1557+
// At first, I tried to optimize the second case to only
1558+
// root in one arm, but the result was suboptimal: first,
1559+
// it interfered with the construction of phi nodes in the
1560+
// arm, as we were adding code to root values before the
1561+
// phi nodes were added. This could have been addressed
1562+
// with a second basic block. However, the naive approach
1563+
// also yielded suboptimal results for patterns like:
1564+
//
1565+
// let x = @mut @...;
1566+
// alt x { @@some_variant(y) | @@some_other_variant(y) {...} }
1567+
//
1568+
// The reason is that we would root the value once for
1569+
// each pattern and not once per arm. This is also easily
1570+
// fixed, but it's yet more code for what is really quite
1571+
// the corner case.
1572+
//
1573+
// Nonetheless, if you decide to optimize this case in the
1574+
// future, you need only adjust where the cat_discr()
1575+
// node appears to draw the line between what will be rooted
1576+
// in the *arm* vs the *alt*.
1577+
1578+
// current scope must be the arm, which is always a child of alt:
1579+
assert self.tcx.region_map.get(opt_scope_id.get()) == alt_id;
1580+
1581+
self.preserve(base, some(alt_id))
1582+
}
15031583
}
15041584
}
15051585

1506-
fn require_imm(cmt: cmt, cmt1: cmt, code: bckerr_code) -> bckres<()> {
1586+
fn require_imm(cmt: cmt,
1587+
cmt1: cmt,
1588+
opt_scope_id: option<ast::node_id>,
1589+
code: bckerr_code) -> bckres<()> {
15071590
// Variant contents and unique pointers: must be immutably
15081591
// rooted to a preserved address.
15091592
alt cmt1.mutbl {
15101593
m_mutbl | m_const { err({cmt:cmt, code:code}) }
1511-
m_imm { self.preserve(cmt1) }
1594+
m_imm { self.preserve(cmt1, opt_scope_id) }
15121595
}
15131596
}
15141597
}
@@ -1569,6 +1652,9 @@ impl loan_methods for loan_ctxt {
15691652
cat_local(_) | cat_arg(_) | cat_stack_upvar(_) {
15701653
self.ok_with_loan_of(cmt, req_mutbl)
15711654
}
1655+
cat_discr(base, _) {
1656+
self.loan(base, req_mutbl)
1657+
}
15721658
cat_comp(cmt_base, comp_field(_)) |
15731659
cat_comp(cmt_base, comp_index(_)) |
15741660
cat_comp(cmt_base, comp_tuple) |

0 commit comments

Comments
 (0)