closure.rs 16.1 KB
Newer Older
K
Kevin Butler 已提交
1
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 3 4 5 6 7 8 9 10
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

11
use arena::TypedArena;
12
use back::symbol_names;
13
use llvm::{ValueRef, get_param, get_params};
14
use rustc::hir::def_id::DefId;
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
use abi::{Abi, FnType};
use adt;
use attributes;
use base::*;
use build::*;
use callee::{self, ArgVals, Callee};
use cleanup::{CleanupMethods, CustomScope, ScopeId};
use common::*;
use datum::{ByRef, Datum, lvalue_scratch_datum};
use datum::{rvalue_scratch_datum, Rvalue};
use debuginfo::{self, DebugLoc};
use declare;
use expr;
use monomorphize::{Instance};
use value::Value;
use Disr;
31
use rustc::ty::{self, Ty, TyCtxt};
32
use session::config::FullDebugInfo;
33 34

use syntax::ast;
35

36
use rustc::hir;
37

38
use libc::c_uint;
39

40
fn load_closure_environment<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
41
                                        closure_def_id: DefId,
42
                                        arg_scope_id: ScopeId,
43
                                        id: ast::NodeId) {
44
    let _icx = push_ctxt("closure::load_closure_environment");
45 46 47 48
    let kind = kind_for_closure(bcx.ccx(), closure_def_id);

    let env_arg = &bcx.fcx.fn_ty.args[0];
    let mut env_idx = bcx.fcx.fn_ty.ret.is_indirect() as usize;
49

50
    // Special case for small by-value selfs.
51
    let llenv = if kind == ty::ClosureKind::FnOnce && !env_arg.is_indirect() {
52
        let closure_ty = node_id_type(bcx, id);
53
        let llenv = rvalue_scratch_datum(bcx, closure_ty, "closure_env").val;
54
        env_arg.store_fn_arg(&bcx.build(), &mut env_idx, llenv);
55
        llenv
56
    } else {
57
        get_param(bcx.fcx.llfn, env_idx as c_uint)
58 59
    };

60 61 62
    // Store the pointer to closure data in an alloca for debug info because that's what the
    // llvm.dbg.declare intrinsic expects
    let env_pointer_alloca = if bcx.sess().opts.debuginfo == FullDebugInfo {
63
        let alloc = alloca(bcx, val_ty(llenv), "__debuginfo_env_ptr");
64 65 66 67 68 69
        Store(bcx, llenv, alloc);
        Some(alloc)
    } else {
        None
    };

70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
    bcx.tcx().with_freevars(id, |fv| {
        for (i, freevar) in fv.iter().enumerate() {
            let upvar_id = ty::UpvarId { var_id: freevar.def.var_id(),
                                        closure_expr_id: id };
            let upvar_capture = bcx.tcx().upvar_capture(upvar_id).unwrap();
            let mut upvar_ptr = StructGEP(bcx, llenv, i);
            let captured_by_ref = match upvar_capture {
                ty::UpvarCapture::ByValue => false,
                ty::UpvarCapture::ByRef(..) => {
                    upvar_ptr = Load(bcx, upvar_ptr);
                    true
                }
            };
            let node_id = freevar.def.var_id();
            bcx.fcx.llupvars.borrow_mut().insert(node_id, upvar_ptr);

            if kind == ty::ClosureKind::FnOnce && !captured_by_ref {
                let hint = bcx.fcx.lldropflag_hints.borrow().hint_datum(upvar_id.var_id);
                bcx.fcx.schedule_drop_mem(arg_scope_id,
                                        upvar_ptr,
                                        node_id_type(bcx, node_id),
                                        hint)
92 93
            }

94 95 96 97 98 99 100 101 102
            if let Some(env_pointer_alloca) = env_pointer_alloca {
                debuginfo::create_captured_var_metadata(
                    bcx,
                    node_id,
                    env_pointer_alloca,
                    i,
                    captured_by_ref,
                    freevar.span);
            }
103
        }
104
    })
105 106
}

107
pub enum ClosureEnv {
108
    NotClosure,
109
    Closure(DefId, ast::NodeId),
110 111
}

112
impl ClosureEnv {
113
    pub fn load<'blk,'tcx>(self, bcx: Block<'blk, 'tcx>, arg_scope: ScopeId) {
114 115
        if let ClosureEnv::Closure(def_id, id) = self {
            load_closure_environment(bcx, def_id, arg_scope, id);
116 117 118 119
        }
    }
}

120
fn get_self_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
121 122 123
                           closure_id: DefId,
                           fn_ty: Ty<'tcx>)
                           -> Ty<'tcx> {
124 125 126 127 128 129 130 131 132 133 134
    match tcx.closure_kind(closure_id) {
        ty::ClosureKind::Fn => {
            tcx.mk_imm_ref(tcx.mk_region(ty::ReStatic), fn_ty)
        }
        ty::ClosureKind::FnMut => {
            tcx.mk_mut_ref(tcx.mk_region(ty::ReStatic), fn_ty)
        }
        ty::ClosureKind::FnOnce => fn_ty,
    }
}

135 136
/// Returns the LLVM function declaration for a closure, creating it if
/// necessary. If the ID does not correspond to a closure ID, returns None.
137 138
fn get_or_create_closure_declaration<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                               closure_id: DefId,
139
                                               substs: ty::ClosureSubsts<'tcx>)
140
                                               -> ValueRef {
141 142
    // Normalize type so differences in regions and typedefs don't cause
    // duplicate declarations
143
    let tcx = ccx.tcx();
144 145
    let substs = tcx.erase_regions(&substs);
    let instance = Instance::new(closure_id, substs.func_substs);
146

147
    if let Some(&llfn) = ccx.instances().borrow().get(&instance) {
N
nits  
Niko Matsakis 已提交
148
        debug!("get_or_create_closure_declaration(): found closure {:?}: {:?}",
149
               instance, Value(llfn));
150
        return llfn;
151 152
    }

153
    let symbol = instance.symbol_name(ccx.shared());
154

155
    // Compute the rust-call form of the closure call method.
156
    let sig = &tcx.closure_type(closure_id, substs).sig;
157
    let sig = tcx.erase_late_bound_regions(sig);
158
    let sig = tcx.normalize_associated_type(&sig);
159 160
    let closure_type = tcx.mk_closure_from_closure_substs(closure_id, substs);
    let function_type = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
161 162 163 164 165 166 167 168
        unsafety: hir::Unsafety::Normal,
        abi: Abi::RustCall,
        sig: ty::Binder(ty::FnSig {
            inputs: Some(get_self_type(tcx, closure_id, closure_type))
                        .into_iter().chain(sig.inputs).collect(),
            output: sig.output,
            variadic: false
        })
169
    }));
170
    let llfn = declare::define_internal_fn(ccx, &symbol, function_type);
171 172

    // set an inline hint for all closures
173
    attributes::inline(llfn, attributes::InlineAttr::Hint);
174

175
    debug!("get_or_create_declaration_if_closure(): inserting new \
176
            closure {:?}: {:?}",
177 178
           instance, Value(llfn));
    ccx.instances().borrow_mut().insert(instance, llfn);
179

180
    llfn
181 182
}

183 184 185 186 187 188
pub enum Dest<'a, 'tcx: 'a> {
    SaveIn(Block<'a, 'tcx>, ValueRef),
    Ignore(&'a CrateContext<'a, 'tcx>)
}

pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>,
189 190
                                    decl: &hir::FnDecl,
                                    body: &hir::Block,
191
                                    id: ast::NodeId,
192
                                    closure_def_id: DefId, // (*)
193
                                    closure_substs: ty::ClosureSubsts<'tcx>)
194
                                    -> Option<Block<'a, 'tcx>>
195
{
196 197 198 199
    // (*) Note that in the case of inlined functions, the `closure_def_id` will be the
    // defid of the closure in its original crate, whereas `id` will be the id of the local
    // inlined copy.

200 201
    let param_substs = closure_substs.func_substs;

202 203 204 205 206
    let ccx = match dest {
        Dest::SaveIn(bcx, _) => bcx.ccx(),
        Dest::Ignore(ccx) => ccx
    };
    let tcx = ccx.tcx();
207
    let _icx = push_ctxt("closure::trans_closure_expr");
208

209 210
    debug!("trans_closure_expr(id={:?}, closure_def_id={:?}, closure_substs={:?})",
           id, closure_def_id, closure_substs);
211

212
    let llfn = get_or_create_closure_declaration(ccx, closure_def_id, closure_substs);
213

214 215 216
    // Get the type of this closure. Use the current `param_substs` as
    // the closure substitutions. This makes sense because the closure
    // takes the same set of type arguments as the enclosing fn, and
217
    // this function (`trans_closure`) is invoked at the point
218
    // of the closure expression.
J
Jared Roesch 已提交
219

220
    let sig = &tcx.closure_type(closure_def_id, closure_substs).sig;
221 222
    let sig = tcx.erase_late_bound_regions(sig);
    let sig = tcx.normalize_associated_type(&sig);
223

224
    let closure_type = tcx.mk_closure_from_closure_substs(closure_def_id,
225
                                                          closure_substs);
226 227 228 229 230 231
    let sig = ty::FnSig {
        inputs: Some(get_self_type(tcx, closure_def_id, closure_type))
                    .into_iter().chain(sig.inputs).collect(),
        output: sig.output,
        variadic: false
    };
232

233
    trans_closure(ccx,
234 235
                  decl,
                  body,
236
                  llfn,
237
                  Instance::new(closure_def_id, param_substs),
238
                  id,
239
                  &sig,
240
                  Abi::RustCall,
241
                  ClosureEnv::Closure(closure_def_id, id));
242 243

    // Don't hoist this to the top of the function. It's perfectly legitimate
244 245
    // to have a zero-size closure (in which case dest will be `Ignore`) and
    // we must still generate the closure body.
246 247 248
    let (mut bcx, dest_addr) = match dest {
        Dest::SaveIn(bcx, p) => (bcx, p),
        Dest::Ignore(_) => {
249
            debug!("trans_closure_expr() ignoring result");
250
            return None;
251 252 253
        }
    };

254
    let repr = adt::represent_type(ccx, node_id_type(bcx, id));
255 256

    // Create the closure.
257 258 259 260 261 262 263 264 265 266 267 268 269 270
    tcx.with_freevars(id, |fv| {
        for (i, freevar) in fv.iter().enumerate() {
            let datum = expr::trans_var(bcx, freevar.def);
            let upvar_slot_dest = adt::trans_field_ptr(
                bcx, &repr, adt::MaybeSizedValue::sized(dest_addr), Disr(0), i);
            let upvar_id = ty::UpvarId { var_id: freevar.def.var_id(),
                                        closure_expr_id: id };
            match tcx.upvar_capture(upvar_id).unwrap() {
                ty::UpvarCapture::ByValue => {
                    bcx = datum.store_to(bcx, upvar_slot_dest);
                }
                ty::UpvarCapture::ByRef(..) => {
                    Store(bcx, datum.to_llref(), upvar_slot_dest);
                }
271 272
            }
        }
273
    });
J
Jonas Schievink 已提交
274
    adt::trans_set_discr(bcx, &repr, dest_addr, Disr(0));
275

276
    Some(bcx)
277
}
278 279

pub fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
N
Niko Matsakis 已提交
280
                                      closure_def_id: DefId,
281
                                      substs: ty::ClosureSubsts<'tcx>,
282 283 284
                                      trait_closure_kind: ty::ClosureKind)
                                      -> ValueRef
{
285
    // If this is a closure, redirect to it.
286
    let llfn = get_or_create_closure_declaration(ccx, closure_def_id, substs);
287 288 289

    // If the closure is a Fn closure, but a FnOnce is needed (etc),
    // then adapt the self type
290
    let llfn_closure_kind = ccx.tcx().closure_kind(closure_def_id);
291 292 293 294

    let _icx = push_ctxt("trans_closure_adapter_shim");

    debug!("trans_closure_adapter_shim(llfn_closure_kind={:?}, \
295 296
           trait_closure_kind={:?}, llfn={:?})",
           llfn_closure_kind, trait_closure_kind, Value(llfn));
297 298

    match (llfn_closure_kind, trait_closure_kind) {
299 300 301
        (ty::ClosureKind::Fn, ty::ClosureKind::Fn) |
        (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) |
        (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) => {
302 303 304
            // No adapter needed.
            llfn
        }
305
        (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => {
306 307 308 309 310
            // The closure fn `llfn` is a `fn(&self, ...)`.  We want a
            // `fn(&mut self, ...)`. In fact, at trans time, these are
            // basically the same thing, so we can just return llfn.
            llfn
        }
311 312
        (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) |
        (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
313 314 315 316 317 318 319 320
            // The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut
            // self, ...)`.  We want a `fn(self, ...)`. We can produce
            // this by doing something like:
            //
            //     fn call_once(self, ...) { call_mut(&self, ...) }
            //     fn call_once(mut self, ...) { call_mut(&mut self, ...) }
            //
            // These are both the same at trans time.
321
            trans_fn_once_adapter_shim(ccx, closure_def_id, substs, llfn)
322 323
        }
        _ => {
324 325 326
            bug!("trans_closure_adapter_shim: cannot convert {:?} to {:?}",
                 llfn_closure_kind,
                 trait_closure_kind);
327 328 329 330 331 332
        }
    }
}

fn trans_fn_once_adapter_shim<'a, 'tcx>(
    ccx: &'a CrateContext<'a, 'tcx>,
N
Niko Matsakis 已提交
333
    closure_def_id: DefId,
334
    substs: ty::ClosureSubsts<'tcx>,
335 336 337
    llreffn: ValueRef)
    -> ValueRef
{
338 339
    debug!("trans_fn_once_adapter_shim(closure_def_id={:?}, substs={:?}, llreffn={:?})",
           closure_def_id, substs, Value(llreffn));
340 341 342 343 344

    let tcx = ccx.tcx();

    // Find a version of the closure type. Substitute static for the
    // region since it doesn't really matter.
345
    let closure_ty = tcx.mk_closure_from_closure_substs(closure_def_id, substs);
346
    let ref_closure_ty = tcx.mk_imm_ref(tcx.mk_region(ty::ReStatic), closure_ty);
347 348

    // Make a version with the type of by-ref closure.
349
    let ty::ClosureTy { unsafety, abi, mut sig } =
350
        tcx.closure_type(closure_def_id, substs);
351
    sig.0.inputs.insert(0, ref_closure_ty); // sig has no self type as of yet
352
    let llref_fn_ty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
353 354 355
        unsafety: unsafety,
        abi: abi,
        sig: sig.clone()
356
    }));
357 358
    debug!("trans_fn_once_adapter_shim: llref_fn_ty={:?}",
           llref_fn_ty);
359

360

361 362
    // Make a version of the closure type with the same arguments, but
    // with argument #0 being by value.
363
    assert_eq!(abi, Abi::RustCall);
364
    sig.0.inputs[0] = closure_ty;
365 366

    let sig = tcx.erase_late_bound_regions(&sig);
367
    let sig = tcx.normalize_associated_type(&sig);
368 369
    let fn_ty = FnType::new(ccx, abi, &sig, &[]);

370
    let llonce_fn_ty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
371 372
        unsafety: unsafety,
        abi: abi,
373
        sig: ty::Binder(sig)
374
    }));
375 376

    // Create the by-value helper.
377 378
    let function_name =
        symbol_names::internal_name_from_type_and_suffix(ccx, llonce_fn_ty, "once_shim");
379
    let lloncefn = declare::define_internal_fn(ccx, &function_name, llonce_fn_ty);
380

381 382
    let (block_arena, fcx): (TypedArena<_>, FunctionContext);
    block_arena = TypedArena::new();
383
    fcx = FunctionContext::new(ccx, lloncefn, fn_ty, None, &block_arena);
384
    let mut bcx = fcx.init(false, None);
385

386

387 388 389
    // the first argument (`self`) will be the (by value) closure env.
    let self_scope = fcx.push_custom_cleanup_scope();
    let self_scope_id = CustomScope(self_scope);
390 391 392 393 394 395 396 397 398 399 400 401

    let mut llargs = get_params(fcx.llfn);
    let mut self_idx = fcx.fn_ty.ret.is_indirect() as usize;
    let env_arg = &fcx.fn_ty.args[0];
    let llenv = if env_arg.is_indirect() {
        Datum::new(llargs[self_idx], closure_ty, Rvalue::new(ByRef))
            .add_clean(&fcx, self_scope_id)
    } else {
        unpack_datum!(bcx, lvalue_scratch_datum(bcx, closure_ty, "self",
                                                InitAlloca::Dropped,
                                                self_scope_id, |bcx, llval| {
            let mut llarg_idx = self_idx;
402
            env_arg.store_fn_arg(&bcx.build(), &mut llarg_idx, llval);
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
            bcx.fcx.schedule_lifetime_end(self_scope_id, llval);
            bcx
        })).val
    };

    debug!("trans_fn_once_adapter_shim: env={:?}", Value(llenv));
    // Adjust llargs such that llargs[self_idx..] has the call arguments.
    // For zero-sized closures that means sneaking in a new argument.
    if env_arg.is_ignore() {
        if self_idx > 0 {
            self_idx -= 1;
            llargs[self_idx] = llenv;
        } else {
            llargs.insert(0, llenv);
        }
    } else {
        llargs[self_idx] = llenv;
    }
421 422 423

    let dest =
        fcx.llretslotptr.get().map(
424
            |_| expr::SaveIn(fcx.get_ret_slot(bcx, "ret_slot")));
425

426 427 428 429 430
    let callee = Callee {
        data: callee::Fn(llreffn),
        ty: llref_fn_ty
    };
    bcx = callee.call(bcx, DebugLoc::None, ArgVals(&llargs[self_idx..]), dest).bcx;
431

432
    fcx.pop_and_trans_custom_cleanup_scope(bcx, self_scope);
433

434
    fcx.finish(bcx, DebugLoc::None);
435 436 437

    lloncefn
}