@@ -85,6 +85,7 @@ enum categorization {
85
85
cat_stack_upvar( cmt ) , // upvar in stack closure
86
86
cat_deref( cmt , uint , ptr_kind ) , // deref of a ptr
87
87
cat_comp( cmt , comp_kind ) , // adjust to locate an internal component
88
+ cat_discr( cmt , ast:: node_id ) , // alt discriminant (see preserve())
88
89
}
89
90
90
91
// different kinds of pointers:
@@ -205,7 +206,8 @@ fn req_loans_in_expr(ex: @ast::expr,
205
206
// If this expression is borrowed, have to ensure it remains valid:
206
207
for tcx. borrowings. find( ex. id) . each { |scope_id|
207
208
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) ;
209
211
}
210
212
211
213
// Special checks for various kinds of expressions:
@@ -224,20 +226,19 @@ fn req_loans_in_expr(ex: @ast::expr,
224
226
225
227
ast:: expr_call ( f, args, _) {
226
228
let arg_tys = ty:: ty_fn_args ( ty:: expr_ty ( self . tcx ( ) , f) ) ;
229
+ let scope_r = ty:: re_scope ( ex. id ) ;
227
230
vec:: iter2 ( args, arg_tys) { |arg, arg_ty|
228
231
alt ty:: resolved_mode ( self . tcx ( ) , arg_ty. mode ) {
229
232
ast:: by_mutbl_ref {
230
233
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 ) ;
232
235
}
233
236
ast:: by_ref {
234
237
let arg_cmt = self . bccx . cat_expr ( arg) ;
235
238
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) ;
238
240
} 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) ;
241
242
}
242
243
}
243
244
ast:: by_move | ast:: by_copy | ast:: by_val { }
@@ -249,7 +250,7 @@ fn req_loans_in_expr(ex: @ast::expr,
249
250
let cmt = self . bccx . cat_expr ( ex_v) ;
250
251
for arms. each { |arm|
251
252
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 ) ;
253
254
}
254
255
}
255
256
}
@@ -265,8 +266,10 @@ impl methods for gather_loan_ctxt {
265
266
fn tcx( ) -> ty:: ctxt { self. bccx. tcx }
266
267
267
268
// 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.
270
273
fn guarantee_valid( cmt: cmt,
271
274
mutbl: ast:: mutability,
272
275
scope_r: ty:: region) {
@@ -334,16 +337,24 @@ impl methods for gather_loan_ctxt {
334
337
}
335
338
}
336
339
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) {
338
342
339
343
// Here, `cmt` is the categorization for the value being
340
344
// matched and pat is the pattern it is being matched against.
341
345
//
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.
343
354
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" ,
345
356
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] ;
347
358
let _i = indenter( ) ;
348
359
349
360
let tcx = self . tcx( ) ;
@@ -359,7 +370,7 @@ impl methods for gather_loan_ctxt {
359
370
// variant(x, y, z)
360
371
for subpats. each { |subpat|
361
372
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) ;
363
374
}
364
375
}
365
376
@@ -370,32 +381,44 @@ impl methods for gather_loan_ctxt {
370
381
ast:: pat_ident( id, o_pat) {
371
382
// x or x @ p --- `x` must remain valid for the scope of the alt
372
383
#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
+ }
375
394
}
376
395
377
396
ast:: pat_rec( field_pats, _) {
378
397
// {f1: p1, ..., fN: pN}
379
398
for field_pats. each { |fp|
380
399
let cmt_field = self . bccx. cat_field( pat, cmt, fp. ident,
381
400
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) ;
383
402
}
384
403
}
385
404
386
405
ast:: pat_tup( subpats) {
387
406
// (p1, ..., pN)
388
407
for subpats. each { |subpat|
389
408
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) ;
391
410
}
392
411
}
393
412
394
413
ast:: pat_box( subpat) | ast:: pat_uniq( subpat) {
395
414
// @p1, ~p1
396
415
alt self. bccx. cat_deref( pat, cmt, 0 u, 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
+ }
399
422
}
400
423
}
401
424
@@ -1006,6 +1029,10 @@ impl categorize_methods for borrowck_ctxt {
1006
1029
}
1007
1030
}
1008
1031
1032
+ fn cat_discr( cmt: cmt, alt_id: ast:: node_id) -> cmt {
1033
+ ret @{ cat: cat_discr( cmt, alt_id) with * cmt} ;
1034
+ }
1035
+
1009
1036
fn cat_field < N : ast_node > ( node: N , base_cmt: cmt,
1010
1037
f_name: str , f_ty: ty:: t) -> cmt {
1011
1038
let f_mutbl = alt field_mutbl( self . tcx, base_cmt. ty, f_name) {
@@ -1239,6 +1266,7 @@ impl categorize_methods for borrowck_ctxt {
1239
1266
cat_comp( cmt, comp) {
1240
1267
#fmt[ "%s.%s" , self . cat_to_repr( cmt. cat) , self . comp_to_repr( comp) ]
1241
1268
}
1269
+ cat_discr( cmt, _) { self . cat_to_repr( cmt. cat) }
1242
1270
}
1243
1271
}
1244
1272
@@ -1326,6 +1354,9 @@ impl categorize_methods for borrowck_ctxt {
1326
1354
_ { mut_str + " indexed content" }
1327
1355
}
1328
1356
}
1357
+ cat_discr( cmt, _) {
1358
+ self . cmt_to_str( cmt)
1359
+ }
1329
1360
}
1330
1361
}
1331
1362
@@ -1420,34 +1451,23 @@ fn field_mutbl(tcx: ty::ctxt,
1420
1451
// Preserve(Ex, S) holds if ToAddr(Ex) will remain valid for the entirety of
1421
1452
// the scope S.
1422
1453
1423
- enum preserve_ctxt = @{
1424
- bccx: borrowck_ctxt, opt_scope_id: option < ast:: node_id >
1425
- } ;
1426
-
1427
1454
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) ] ;
1437
1457
let _i = indenter( ) ;
1438
1458
1439
1459
alt cmt. cat {
1440
1460
cat_rvalue | cat_special( _) {
1441
1461
ok( ( ) )
1442
1462
}
1443
1463
cat_stack_upvar( cmt) {
1444
- self . preserve( cmt)
1464
+ self . preserve( cmt, opt_scope_id )
1445
1465
}
1446
1466
cat_local( _) {
1447
1467
// This should never happen. Local variables are always lendable,
1448
1468
// so either `loan()` should be called or there must be some
1449
1469
// intermediate @ or &---they are not lendable but do not recurse.
1450
- self . bccx . tcx. sess. span_bug(
1470
+ self . tcx. sess. span_bug(
1451
1471
cmt. span,
1452
1472
"preserve() called with local" ) ;
1453
1473
}
@@ -1463,13 +1483,13 @@ impl preserve_methods for preserve_ctxt {
1463
1483
cat_comp( cmt_base, comp_res) {
1464
1484
// Most embedded components: if the base is stable, the
1465
1485
// type never changes.
1466
- self . preserve( cmt_base)
1486
+ self . preserve( cmt_base, opt_scope_id )
1467
1487
}
1468
1488
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)
1470
1490
}
1471
1491
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)
1473
1493
}
1474
1494
cat_deref( _, _, region_ptr) {
1475
1495
// References are always "stable" by induction (when the
@@ -1482,33 +1502,96 @@ impl preserve_methods for preserve_ctxt {
1482
1502
ok( ( ) )
1483
1503
}
1484
1504
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 {
1489
1509
some( scope_id) {
1490
1510
#debug[ "Inserting root map entry for %s: \
1491
1511
node %d:%u -> scope %d",
1492
- self . bccx . cmt_to_repr( cmt) , base. id,
1512
+ self . cmt_to_repr( cmt) , base. id,
1493
1513
derefs, scope_id] ;
1514
+
1494
1515
let rk = { id: base. id, derefs: derefs} ;
1495
- self . bccx . root_map. insert( rk, scope_id) ;
1516
+ self . root_map. insert( rk, scope_id) ;
1496
1517
ok( ( ) )
1497
1518
}
1498
1519
none {
1499
1520
err( { cmt : cmt, code : err_preserve_gc} )
1500
1521
}
1501
1522
}
1502
1523
}
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
+ }
1503
1583
}
1504
1584
}
1505
1585
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<( ) > {
1507
1590
// Variant contents and unique pointers: must be immutably
1508
1591
// rooted to a preserved address.
1509
1592
alt cmt1. mutbl {
1510
1593
m_mutbl | m_const { err( { cmt : cmt, code : code} ) }
1511
- m_imm { self . preserve( cmt1) }
1594
+ m_imm { self . preserve( cmt1, opt_scope_id ) }
1512
1595
}
1513
1596
}
1514
1597
}
@@ -1569,6 +1652,9 @@ impl loan_methods for loan_ctxt {
1569
1652
cat_local( _) | cat_arg( _) | cat_stack_upvar( _) {
1570
1653
self . ok_with_loan_of( cmt, req_mutbl)
1571
1654
}
1655
+ cat_discr( base, _) {
1656
+ self . loan( base, req_mutbl)
1657
+ }
1572
1658
cat_comp( cmt_base, comp_field( _) ) |
1573
1659
cat_comp( cmt_base, comp_index( _) ) |
1574
1660
cat_comp( cmt_base, comp_tuple) |
0 commit comments