Skip to content

Commit f2b09f8

Browse files
committed
---
yaml --- r: 149123 b: refs/heads/try2 c: b1962a2 h: refs/heads/master i: 149121: ea89fcb 149119: ae9fb3a v: v3
1 parent 1089ffd commit f2b09f8

File tree

4 files changed

+182
-2
lines changed

4 files changed

+182
-2
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ refs/heads/snap-stage3: 78a7676898d9f80ab540c6df5d4c9ce35bb50463
55
refs/heads/try: 519addf6277dbafccbb4159db4b710c37eaa2ec5
66
refs/tags/release-0.1: 1f5c5126e96c79d22cb7862f75304136e204f105
77
refs/heads/ndm: f3868061cd7988080c30d6d5bf352a5a5fe2460b
8-
refs/heads/try2: 807def022a6540dde6727b9a438ea3b0eaafd9b5
8+
refs/heads/try2: b1962a2b2e52f16ad817339e798f90fac973c2b1
99
refs/heads/dist-snap: ba4081a5a8573875fed17545846f6f6902c8ba8d
1010
refs/tags/release-0.2: c870d2dffb391e14efb05aa27898f1f6333a9596
1111
refs/tags/release-0.3: b5f0d0f648d9a6153664837026ba1be43d3e2503

branches/try2/src/librustc/middle/ty.rs

Lines changed: 143 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,9 @@ pub struct ctxt_ {
351351
// is used for lazy resolution of traits.
352352
populated_external_traits: RefCell<HashSet<ast::DefId>>,
353353

354+
// Borrows
355+
upvar_borrow_map: RefCell<UpvarBorrowMap>,
356+
354357
// These two caches are used by const_eval when decoding external statics
355358
// and variants that are found.
356359
extern_const_statics: RefCell<HashMap<ast::DefId, Option<@ast::Expr>>>,
@@ -494,6 +497,120 @@ pub enum Region {
494497
ReEmpty,
495498
}
496499

500+
/**
501+
* Upvars do not get their own node-id. Instead, we use the pair of
502+
* the original var id (that is, the root variable that is referenced
503+
* by the upvar) and the id of the closure expression.
504+
*/
505+
#[deriving(Clone, Eq, IterBytes)]
506+
pub struct UpvarId {
507+
var_id: ast::NodeId,
508+
closure_expr_id: ast::NodeId,
509+
}
510+
511+
#[deriving(Clone, Eq, IterBytes)]
512+
pub enum BorrowKind {
513+
/// Data must be immutable and is aliasable.
514+
ImmBorrow,
515+
516+
/// Data must be immutable but not aliasable. This kind of borrow
517+
/// cannot currently be expressed by the user and is used only in
518+
/// implicit closure bindings. It is needed when you the closure
519+
/// is borrowing or mutating a mutable referent, e.g.:
520+
///
521+
/// let x: &mut int = ...;
522+
/// let y = || *x += 5;
523+
///
524+
/// If we were to try to translate this closure into a more explicit
525+
/// form, we'd encounter an error with the code as written:
526+
///
527+
/// struct Env { x: & &mut int }
528+
/// let x: &mut int = ...;
529+
/// let y = (&mut Env { &x }, fn_ptr); // Closure is pair of env and fn
530+
/// fn fn_ptr(env: &mut Env) { **env.x += 5; }
531+
///
532+
/// This is then illegal because you cannot mutate a `&mut` found
533+
/// in an aliasable location. To solve, you'd have to translate with
534+
/// an `&mut` borrow:
535+
///
536+
/// struct Env { x: & &mut int }
537+
/// let x: &mut int = ...;
538+
/// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x
539+
/// fn fn_ptr(env: &mut Env) { **env.x += 5; }
540+
///
541+
/// Now the assignment to `**env.x` is legal, but creating a
542+
/// mutable pointer to `x` is not because `x` is not mutable. We
543+
/// could fix this by declaring `x` as `let mut x`. This is ok in
544+
/// user code, if awkward, but extra weird for closures, since the
545+
/// borrow is hidden.
546+
///
547+
/// So we introduce a "unique imm" borrow -- the referent is
548+
/// immutable, but not aliasable. This solves the problem. For
549+
/// simplicity, we don't give users the way to express this
550+
/// borrow, it's just used when translating closures.
551+
UniqueImmBorrow,
552+
553+
/// Data is mutable and not aliasable.
554+
MutBorrow
555+
}
556+
557+
/**
558+
* Information describing the borrowing of an upvar. This is computed
559+
* during `typeck`, specifically by `regionck`. The general idea is
560+
* that the compiler analyses treat closures like:
561+
*
562+
* let closure: &'e fn() = || {
563+
* x = 1; // upvar x is assigned to
564+
* use(y); // upvar y is read
565+
* foo(&z); // upvar z is borrowed immutably
566+
* };
567+
*
568+
* as if they were "desugared" to something loosely like:
569+
*
570+
* struct Vars<'x,'y,'z> { x: &'x mut int,
571+
* y: &'y const int,
572+
* z: &'z int }
573+
* let closure: &'e fn() = {
574+
* fn f(env: &Vars) {
575+
* *env.x = 1;
576+
* use(*env.y);
577+
* foo(env.z);
578+
* }
579+
* let env: &'e mut Vars<'x,'y,'z> = &mut Vars { x: &'x mut x,
580+
* y: &'y const y,
581+
* z: &'z z };
582+
* (env, f)
583+
* };
584+
*
585+
* This is basically what happens at runtime. The closure is basically
586+
* an existentially quantified version of the `(env, f)` pair.
587+
*
588+
* This data structure indicates the region and mutability of a single
589+
* one of the `x...z` borrows.
590+
*
591+
* It may not be obvious why each borrowed variable gets its own
592+
* lifetime (in the desugared version of the example, these are indicated
593+
* by the lifetime parameters `'x`, `'y`, and `'z` in the `Vars` definition).
594+
* Each such lifetime must encompass the lifetime `'e` of the closure itself,
595+
* but need not be identical to it. The reason that this makes sense:
596+
*
597+
* - Callers are only permitted to invoke the closure, and hence to
598+
* use the pointers, within the lifetime `'e`, so clearly `'e` must
599+
* be a sublifetime of `'x...'z`.
600+
* - The closure creator knows which upvars were borrowed by the closure
601+
* and thus `x...z` will be reserved for `'x...'z` respectively.
602+
* - Through mutation, the borrowed upvars can actually escape the
603+
* the closure, so sometimes it is necessary for them to be larger
604+
* than the closure lifetime itself.
605+
*/
606+
#[deriving(Eq, Clone)]
607+
pub struct UpvarBorrow {
608+
kind: BorrowKind,
609+
region: ty::Region,
610+
}
611+
612+
pub type UpvarBorrowMap = HashMap<UpvarId, UpvarBorrow>;
613+
497614
impl Region {
498615
pub fn is_bound(&self) -> bool {
499616
match self {
@@ -999,7 +1116,7 @@ pub fn mk_ctxt(s: session::Session,
9991116
impl_vtables: RefCell::new(HashMap::new()),
10001117
populated_external_types: RefCell::new(HashSet::new()),
10011118
populated_external_traits: RefCell::new(HashSet::new()),
1002-
1119+
upvar_borrow_map: RefCell::new(HashMap::new()),
10031120
extern_const_statics: RefCell::new(HashMap::new()),
10041121
extern_const_variants: RefCell::new(HashMap::new()),
10051122
}
@@ -5100,3 +5217,28 @@ impl substs {
51005217
}
51015218
}
51025219
}
5220+
5221+
impl BorrowKind {
5222+
pub fn from_mutbl(m: ast::Mutability) -> BorrowKind {
5223+
match m {
5224+
ast::MutMutable => MutBorrow,
5225+
ast::MutImmutable => ImmBorrow,
5226+
}
5227+
}
5228+
5229+
pub fn to_user_str(&self) -> &'static str {
5230+
match *self {
5231+
MutBorrow => "mutable",
5232+
ImmBorrow => "immutable",
5233+
UniqueImmBorrow => "uniquely immutable",
5234+
}
5235+
}
5236+
5237+
pub fn to_short_str(&self) -> &'static str {
5238+
match *self {
5239+
MutBorrow => "mut",
5240+
ImmBorrow => "imm",
5241+
UniqueImmBorrow => "own",
5242+
}
5243+
}
5244+
}

branches/try2/src/librustc/middle/typeck/check/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ pub struct Inherited {
164164
adjustments: RefCell<HashMap<ast::NodeId, @ty::AutoAdjustment>>,
165165
method_map: method_map,
166166
vtable_map: vtable_map,
167+
upvar_borrow_map: RefCell<ty::UpvarBorrowMap>,
167168
}
168169

169170
#[deriving(Clone)]
@@ -266,6 +267,7 @@ impl Inherited {
266267
adjustments: RefCell::new(HashMap::new()),
267268
method_map: @RefCell::new(HashMap::new()),
268269
vtable_map: @RefCell::new(HashMap::new()),
270+
upvar_borrow_map: RefCell::new(HashMap::new()),
269271
}
270272
}
271273
}

branches/try2/src/librustc/middle/typeck/check/writeback.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,10 +378,45 @@ impl Visitor<()> for WbCtxt {
378378
fn visit_ty(&mut self, _t: &ast::Ty, _: ()) {}
379379
}
380380

381+
fn resolve_upvar_borrow_map(wbcx: &mut WbCtxt) {
382+
if !wbcx.success {
383+
return;
384+
}
385+
386+
let fcx = wbcx.fcx;
387+
let tcx = fcx.tcx();
388+
let upvar_borrow_map = fcx.inh.upvar_borrow_map.borrow();
389+
for (upvar_id, upvar_borrow) in upvar_borrow_map.get().iter() {
390+
let r = upvar_borrow.region;
391+
match resolve_region(fcx.infcx(), r, resolve_all | force_all) {
392+
Ok(r) => {
393+
let new_upvar_borrow = ty::UpvarBorrow {
394+
kind: upvar_borrow.kind,
395+
region: r
396+
};
397+
debug!("Upvar borrow for {} resolved to {}",
398+
upvar_id.repr(tcx), new_upvar_borrow.repr(tcx));
399+
let mut tcx_upvar_borrow_map = tcx.upvar_borrow_map.borrow_mut();
400+
tcx_upvar_borrow_map.get().insert(*upvar_id, new_upvar_borrow);
401+
}
402+
Err(e) => {
403+
let span = ty::expr_span(tcx, upvar_id.closure_expr_id);
404+
fcx.ccx.tcx.sess.span_err(
405+
span, format!("cannot resolve lifetime for \
406+
captured variable `{}`: {}",
407+
ty::local_var_name_str(tcx, upvar_id.var_id).get().to_str(),
408+
infer::fixup_err_to_str(e)));
409+
wbcx.success = false;
410+
}
411+
};
412+
}
413+
}
414+
381415
pub fn resolve_type_vars_in_expr(fcx: @FnCtxt, e: &ast::Expr) -> bool {
382416
let mut wbcx = WbCtxt { fcx: fcx, success: true };
383417
let wbcx = &mut wbcx;
384418
wbcx.visit_expr(e, ());
419+
resolve_upvar_borrow_map(wbcx);
385420
return wbcx.success;
386421
}
387422

@@ -397,5 +432,6 @@ pub fn resolve_type_vars_in_fn(fcx: @FnCtxt, decl: &ast::FnDecl,
397432
resolve_type_vars_for_node(wbcx, arg.pat.span, arg.pat.id);
398433
}
399434
}
435+
resolve_upvar_borrow_map(wbcx);
400436
return wbcx.success;
401437
}

0 commit comments

Comments
 (0)