add upvar_borrow_map to tcx and fcx in typeck
This commit is contained in:
parent
807def022a
commit
b1962a2b2e
@ -351,6 +351,9 @@ pub struct ctxt_ {
|
||||
// is used for lazy resolution of traits.
|
||||
populated_external_traits: RefCell<HashSet<ast::DefId>>,
|
||||
|
||||
// Borrows
|
||||
upvar_borrow_map: RefCell<UpvarBorrowMap>,
|
||||
|
||||
// These two caches are used by const_eval when decoding external statics
|
||||
// and variants that are found.
|
||||
extern_const_statics: RefCell<HashMap<ast::DefId, Option<@ast::Expr>>>,
|
||||
@ -494,6 +497,120 @@ pub enum Region {
|
||||
ReEmpty,
|
||||
}
|
||||
|
||||
/**
|
||||
* Upvars do not get their own node-id. Instead, we use the pair of
|
||||
* the original var id (that is, the root variable that is referenced
|
||||
* by the upvar) and the id of the closure expression.
|
||||
*/
|
||||
#[deriving(Clone, Eq, IterBytes)]
|
||||
pub struct UpvarId {
|
||||
var_id: ast::NodeId,
|
||||
closure_expr_id: ast::NodeId,
|
||||
}
|
||||
|
||||
#[deriving(Clone, Eq, IterBytes)]
|
||||
pub enum BorrowKind {
|
||||
/// Data must be immutable and is aliasable.
|
||||
ImmBorrow,
|
||||
|
||||
/// Data must be immutable but not aliasable. This kind of borrow
|
||||
/// cannot currently be expressed by the user and is used only in
|
||||
/// implicit closure bindings. It is needed when you the closure
|
||||
/// is borrowing or mutating a mutable referent, e.g.:
|
||||
///
|
||||
/// let x: &mut int = ...;
|
||||
/// let y = || *x += 5;
|
||||
///
|
||||
/// If we were to try to translate this closure into a more explicit
|
||||
/// form, we'd encounter an error with the code as written:
|
||||
///
|
||||
/// struct Env { x: & &mut int }
|
||||
/// let x: &mut int = ...;
|
||||
/// let y = (&mut Env { &x }, fn_ptr); // Closure is pair of env and fn
|
||||
/// fn fn_ptr(env: &mut Env) { **env.x += 5; }
|
||||
///
|
||||
/// This is then illegal because you cannot mutate a `&mut` found
|
||||
/// in an aliasable location. To solve, you'd have to translate with
|
||||
/// an `&mut` borrow:
|
||||
///
|
||||
/// struct Env { x: & &mut int }
|
||||
/// let x: &mut int = ...;
|
||||
/// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x
|
||||
/// fn fn_ptr(env: &mut Env) { **env.x += 5; }
|
||||
///
|
||||
/// Now the assignment to `**env.x` is legal, but creating a
|
||||
/// mutable pointer to `x` is not because `x` is not mutable. We
|
||||
/// could fix this by declaring `x` as `let mut x`. This is ok in
|
||||
/// user code, if awkward, but extra weird for closures, since the
|
||||
/// borrow is hidden.
|
||||
///
|
||||
/// So we introduce a "unique imm" borrow -- the referent is
|
||||
/// immutable, but not aliasable. This solves the problem. For
|
||||
/// simplicity, we don't give users the way to express this
|
||||
/// borrow, it's just used when translating closures.
|
||||
UniqueImmBorrow,
|
||||
|
||||
/// Data is mutable and not aliasable.
|
||||
MutBorrow
|
||||
}
|
||||
|
||||
/**
|
||||
* Information describing the borrowing of an upvar. This is computed
|
||||
* during `typeck`, specifically by `regionck`. The general idea is
|
||||
* that the compiler analyses treat closures like:
|
||||
*
|
||||
* let closure: &'e fn() = || {
|
||||
* x = 1; // upvar x is assigned to
|
||||
* use(y); // upvar y is read
|
||||
* foo(&z); // upvar z is borrowed immutably
|
||||
* };
|
||||
*
|
||||
* as if they were "desugared" to something loosely like:
|
||||
*
|
||||
* struct Vars<'x,'y,'z> { x: &'x mut int,
|
||||
* y: &'y const int,
|
||||
* z: &'z int }
|
||||
* let closure: &'e fn() = {
|
||||
* fn f(env: &Vars) {
|
||||
* *env.x = 1;
|
||||
* use(*env.y);
|
||||
* foo(env.z);
|
||||
* }
|
||||
* let env: &'e mut Vars<'x,'y,'z> = &mut Vars { x: &'x mut x,
|
||||
* y: &'y const y,
|
||||
* z: &'z z };
|
||||
* (env, f)
|
||||
* };
|
||||
*
|
||||
* This is basically what happens at runtime. The closure is basically
|
||||
* an existentially quantified version of the `(env, f)` pair.
|
||||
*
|
||||
* This data structure indicates the region and mutability of a single
|
||||
* one of the `x...z` borrows.
|
||||
*
|
||||
* It may not be obvious why each borrowed variable gets its own
|
||||
* lifetime (in the desugared version of the example, these are indicated
|
||||
* by the lifetime parameters `'x`, `'y`, and `'z` in the `Vars` definition).
|
||||
* Each such lifetime must encompass the lifetime `'e` of the closure itself,
|
||||
* but need not be identical to it. The reason that this makes sense:
|
||||
*
|
||||
* - Callers are only permitted to invoke the closure, and hence to
|
||||
* use the pointers, within the lifetime `'e`, so clearly `'e` must
|
||||
* be a sublifetime of `'x...'z`.
|
||||
* - The closure creator knows which upvars were borrowed by the closure
|
||||
* and thus `x...z` will be reserved for `'x...'z` respectively.
|
||||
* - Through mutation, the borrowed upvars can actually escape the
|
||||
* the closure, so sometimes it is necessary for them to be larger
|
||||
* than the closure lifetime itself.
|
||||
*/
|
||||
#[deriving(Eq, Clone)]
|
||||
pub struct UpvarBorrow {
|
||||
kind: BorrowKind,
|
||||
region: ty::Region,
|
||||
}
|
||||
|
||||
pub type UpvarBorrowMap = HashMap<UpvarId, UpvarBorrow>;
|
||||
|
||||
impl Region {
|
||||
pub fn is_bound(&self) -> bool {
|
||||
match self {
|
||||
@ -999,7 +1116,7 @@ pub fn mk_ctxt(s: session::Session,
|
||||
impl_vtables: RefCell::new(HashMap::new()),
|
||||
populated_external_types: RefCell::new(HashSet::new()),
|
||||
populated_external_traits: RefCell::new(HashSet::new()),
|
||||
|
||||
upvar_borrow_map: RefCell::new(HashMap::new()),
|
||||
extern_const_statics: RefCell::new(HashMap::new()),
|
||||
extern_const_variants: RefCell::new(HashMap::new()),
|
||||
}
|
||||
@ -5100,3 +5217,28 @@ impl substs {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BorrowKind {
|
||||
pub fn from_mutbl(m: ast::Mutability) -> BorrowKind {
|
||||
match m {
|
||||
ast::MutMutable => MutBorrow,
|
||||
ast::MutImmutable => ImmBorrow,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_user_str(&self) -> &'static str {
|
||||
match *self {
|
||||
MutBorrow => "mutable",
|
||||
ImmBorrow => "immutable",
|
||||
UniqueImmBorrow => "uniquely immutable",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_short_str(&self) -> &'static str {
|
||||
match *self {
|
||||
MutBorrow => "mut",
|
||||
ImmBorrow => "imm",
|
||||
UniqueImmBorrow => "own",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -164,6 +164,7 @@ pub struct Inherited {
|
||||
adjustments: RefCell<HashMap<ast::NodeId, @ty::AutoAdjustment>>,
|
||||
method_map: method_map,
|
||||
vtable_map: vtable_map,
|
||||
upvar_borrow_map: RefCell<ty::UpvarBorrowMap>,
|
||||
}
|
||||
|
||||
#[deriving(Clone)]
|
||||
@ -266,6 +267,7 @@ impl Inherited {
|
||||
adjustments: RefCell::new(HashMap::new()),
|
||||
method_map: @RefCell::new(HashMap::new()),
|
||||
vtable_map: @RefCell::new(HashMap::new()),
|
||||
upvar_borrow_map: RefCell::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -378,10 +378,45 @@ impl Visitor<()> for WbCtxt {
|
||||
fn visit_ty(&mut self, _t: &ast::Ty, _: ()) {}
|
||||
}
|
||||
|
||||
fn resolve_upvar_borrow_map(wbcx: &mut WbCtxt) {
|
||||
if !wbcx.success {
|
||||
return;
|
||||
}
|
||||
|
||||
let fcx = wbcx.fcx;
|
||||
let tcx = fcx.tcx();
|
||||
let upvar_borrow_map = fcx.inh.upvar_borrow_map.borrow();
|
||||
for (upvar_id, upvar_borrow) in upvar_borrow_map.get().iter() {
|
||||
let r = upvar_borrow.region;
|
||||
match resolve_region(fcx.infcx(), r, resolve_all | force_all) {
|
||||
Ok(r) => {
|
||||
let new_upvar_borrow = ty::UpvarBorrow {
|
||||
kind: upvar_borrow.kind,
|
||||
region: r
|
||||
};
|
||||
debug!("Upvar borrow for {} resolved to {}",
|
||||
upvar_id.repr(tcx), new_upvar_borrow.repr(tcx));
|
||||
let mut tcx_upvar_borrow_map = tcx.upvar_borrow_map.borrow_mut();
|
||||
tcx_upvar_borrow_map.get().insert(*upvar_id, new_upvar_borrow);
|
||||
}
|
||||
Err(e) => {
|
||||
let span = ty::expr_span(tcx, upvar_id.closure_expr_id);
|
||||
fcx.ccx.tcx.sess.span_err(
|
||||
span, format!("cannot resolve lifetime for \
|
||||
captured variable `{}`: {}",
|
||||
ty::local_var_name_str(tcx, upvar_id.var_id).get().to_str(),
|
||||
infer::fixup_err_to_str(e)));
|
||||
wbcx.success = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_type_vars_in_expr(fcx: @FnCtxt, e: &ast::Expr) -> bool {
|
||||
let mut wbcx = WbCtxt { fcx: fcx, success: true };
|
||||
let wbcx = &mut wbcx;
|
||||
wbcx.visit_expr(e, ());
|
||||
resolve_upvar_borrow_map(wbcx);
|
||||
return wbcx.success;
|
||||
}
|
||||
|
||||
@ -397,5 +432,6 @@ pub fn resolve_type_vars_in_fn(fcx: @FnCtxt, decl: &ast::FnDecl,
|
||||
resolve_type_vars_for_node(wbcx, arg.pat.span, arg.pat.id);
|
||||
}
|
||||
}
|
||||
resolve_upvar_borrow_map(wbcx);
|
||||
return wbcx.success;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user