]> git.proxmox.com Git - rustc.git/blob - src/librustc_trans/trans/closure.rs
Imported Upstream version 1.0.0~beta
[rustc.git] / src / librustc_trans / trans / closure.rs
1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use arena::TypedArena;
12 use back::link::{self, mangle_internal_name_by_path_and_seq};
13 use llvm::{ValueRef, get_param};
14 use middle::mem_categorization::Typer;
15 use trans::adt;
16 use trans::base::*;
17 use trans::build::*;
18 use trans::callee::{self, ArgVals, Callee, TraitItem, MethodData};
19 use trans::cleanup::{CleanupMethods, CustomScope, ScopeId};
20 use trans::common::*;
21 use trans::datum::{self, Datum, rvalue_scratch_datum, Rvalue, ByValue};
22 use trans::debuginfo::{self, DebugLoc};
23 use trans::expr;
24 use trans::monomorphize::{self, MonoId};
25 use trans::type_of::*;
26 use middle::ty::{self, ClosureTyper};
27 use middle::subst::Substs;
28 use session::config::FullDebugInfo;
29 use util::ppaux::Repr;
30
31 use syntax::abi::RustCall;
32 use syntax::ast;
33 use syntax::ast_util;
34
35
36 fn load_closure_environment<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
37 arg_scope_id: ScopeId,
38 freevars: &[ty::Freevar])
39 -> Block<'blk, 'tcx>
40 {
41 let _icx = push_ctxt("closure::load_closure_environment");
42
43 // Special case for small by-value selfs.
44 let closure_id = ast_util::local_def(bcx.fcx.id);
45 let self_type = self_type_for_closure(bcx.ccx(), closure_id,
46 node_id_type(bcx, closure_id.node));
47 let kind = kind_for_closure(bcx.ccx(), closure_id);
48 let llenv = if kind == ty::FnOnceClosureKind &&
49 !arg_is_indirect(bcx.ccx(), self_type) {
50 let datum = rvalue_scratch_datum(bcx,
51 self_type,
52 "closure_env");
53 store_ty(bcx, bcx.fcx.llenv.unwrap(), datum.val, self_type);
54 datum.val
55 } else {
56 bcx.fcx.llenv.unwrap()
57 };
58
59 // Store the pointer to closure data in an alloca for debug info because that's what the
60 // llvm.dbg.declare intrinsic expects
61 let env_pointer_alloca = if bcx.sess().opts.debuginfo == FullDebugInfo {
62 let alloc = alloca(bcx, val_ty(llenv), "__debuginfo_env_ptr");
63 Store(bcx, llenv, alloc);
64 Some(alloc)
65 } else {
66 None
67 };
68
69 for (i, freevar) in freevars.iter().enumerate() {
70 let upvar_id = ty::UpvarId { var_id: freevar.def.local_node_id(),
71 closure_expr_id: closure_id.node };
72 let upvar_capture = bcx.tcx().upvar_capture(upvar_id).unwrap();
73 let mut upvar_ptr = GEPi(bcx, llenv, &[0, i]);
74 let captured_by_ref = match upvar_capture {
75 ty::UpvarCapture::ByValue => false,
76 ty::UpvarCapture::ByRef(..) => {
77 upvar_ptr = Load(bcx, upvar_ptr);
78 true
79 }
80 };
81 let def_id = freevar.def.def_id();
82 bcx.fcx.llupvars.borrow_mut().insert(def_id.node, upvar_ptr);
83
84 if kind == ty::FnOnceClosureKind && !captured_by_ref {
85 bcx.fcx.schedule_drop_mem(arg_scope_id,
86 upvar_ptr,
87 node_id_type(bcx, def_id.node))
88 }
89
90 if let Some(env_pointer_alloca) = env_pointer_alloca {
91 debuginfo::create_captured_var_metadata(
92 bcx,
93 def_id.node,
94 env_pointer_alloca,
95 i,
96 captured_by_ref,
97 freevar.span);
98 }
99 }
100
101 bcx
102 }
103
104 pub enum ClosureEnv<'a> {
105 NotClosure,
106 Closure(&'a [ty::Freevar]),
107 }
108
109 impl<'a> ClosureEnv<'a> {
110 pub fn load<'blk,'tcx>(self, bcx: Block<'blk, 'tcx>, arg_scope: ScopeId)
111 -> Block<'blk, 'tcx>
112 {
113 match self {
114 ClosureEnv::NotClosure => bcx,
115 ClosureEnv::Closure(freevars) => {
116 if freevars.is_empty() {
117 bcx
118 } else {
119 load_closure_environment(bcx, arg_scope, freevars)
120 }
121 }
122 }
123 }
124 }
125
126 /// Returns the LLVM function declaration for a closure, creating it if
127 /// necessary. If the ID does not correspond to a closure ID, returns None.
128 pub fn get_or_create_declaration_if_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
129 closure_id: ast::DefId,
130 substs: &Substs<'tcx>)
131 -> Option<Datum<'tcx, Rvalue>> {
132 if !ccx.tcx().closure_kinds.borrow().contains_key(&closure_id) {
133 // Not a closure.
134 return None
135 }
136
137 let function_type = ty::node_id_to_type(ccx.tcx(), closure_id.node);
138 let function_type = monomorphize::apply_param_substs(ccx.tcx(), substs, &function_type);
139
140 // Normalize type so differences in regions and typedefs don't cause
141 // duplicate declarations
142 let function_type = erase_regions(ccx.tcx(), &function_type);
143 let params = match function_type.sty {
144 ty::ty_closure(_, substs) => &substs.types,
145 _ => unreachable!()
146 };
147 let mono_id = MonoId {
148 def: closure_id,
149 params: params
150 };
151
152 match ccx.closure_vals().borrow().get(&mono_id) {
153 Some(&llfn) => {
154 debug!("get_or_create_declaration_if_closure(): found closure");
155 return Some(Datum::new(llfn, function_type, Rvalue::new(ByValue)))
156 }
157 None => {}
158 }
159
160 let symbol = ccx.tcx().map.with_path(closure_id.node, |path| {
161 mangle_internal_name_by_path_and_seq(path, "closure")
162 });
163
164 let llfn = decl_internal_rust_fn(ccx, function_type, &symbol[..]);
165
166 // set an inline hint for all closures
167 set_inline_hint(llfn);
168
169 debug!("get_or_create_declaration_if_closure(): inserting new \
170 closure {:?} (type {})",
171 mono_id,
172 ccx.tn().type_to_string(val_ty(llfn)));
173 ccx.closure_vals().borrow_mut().insert(mono_id, llfn);
174
175 Some(Datum::new(llfn, function_type, Rvalue::new(ByValue)))
176 }
177
178 pub enum Dest<'a, 'tcx: 'a> {
179 SaveIn(Block<'a, 'tcx>, ValueRef),
180 Ignore(&'a CrateContext<'a, 'tcx>)
181 }
182
183 pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>,
184 decl: &ast::FnDecl,
185 body: &ast::Block,
186 id: ast::NodeId,
187 param_substs: &'tcx Substs<'tcx>)
188 -> Option<Block<'a, 'tcx>>
189 {
190 let ccx = match dest {
191 Dest::SaveIn(bcx, _) => bcx.ccx(),
192 Dest::Ignore(ccx) => ccx
193 };
194 let tcx = ccx.tcx();
195 let _icx = push_ctxt("closure::trans_closure");
196
197 debug!("trans_closure()");
198
199 let closure_id = ast_util::local_def(id);
200 let llfn = get_or_create_declaration_if_closure(
201 ccx,
202 closure_id,
203 param_substs).unwrap();
204
205 // Get the type of this closure. Use the current `param_substs` as
206 // the closure substitutions. This makes sense because the closure
207 // takes the same set of type arguments as the enclosing fn, and
208 // this function (`trans_closure`) is invoked at the point
209 // of the closure expression.
210 let typer = NormalizingClosureTyper::new(tcx);
211 let function_type = typer.closure_type(closure_id, param_substs);
212
213 let freevars: Vec<ty::Freevar> =
214 ty::with_freevars(tcx, id, |fv| fv.iter().cloned().collect());
215
216 let sig = ty::erase_late_bound_regions(tcx, &function_type.sig);
217
218 trans_closure(ccx,
219 decl,
220 body,
221 llfn.val,
222 param_substs,
223 id,
224 &[],
225 sig.output,
226 function_type.abi,
227 ClosureEnv::Closure(&freevars[..]));
228
229 // Don't hoist this to the top of the function. It's perfectly legitimate
230 // to have a zero-size closure (in which case dest will be `Ignore`) and
231 // we must still generate the closure body.
232 let (mut bcx, dest_addr) = match dest {
233 Dest::SaveIn(bcx, p) => (bcx, p),
234 Dest::Ignore(_) => {
235 debug!("trans_closure() ignoring result");
236 return None;
237 }
238 };
239
240 let repr = adt::represent_type(ccx, node_id_type(bcx, id));
241
242 // Create the closure.
243 for (i, freevar) in freevars.iter().enumerate() {
244 let datum = expr::trans_local_var(bcx, freevar.def);
245 let upvar_slot_dest = adt::trans_field_ptr(bcx, &*repr, dest_addr, 0, i);
246 let upvar_id = ty::UpvarId { var_id: freevar.def.local_node_id(),
247 closure_expr_id: id };
248 match tcx.upvar_capture(upvar_id).unwrap() {
249 ty::UpvarCapture::ByValue => {
250 bcx = datum.store_to(bcx, upvar_slot_dest);
251 }
252 ty::UpvarCapture::ByRef(..) => {
253 Store(bcx, datum.to_llref(), upvar_slot_dest);
254 }
255 }
256 }
257 adt::trans_set_discr(bcx, &*repr, dest_addr, 0);
258
259 Some(bcx)
260 }
261
262 pub fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
263 closure_def_id: ast::DefId,
264 substs: Substs<'tcx>,
265 node: ExprOrMethodCall,
266 param_substs: &'tcx Substs<'tcx>,
267 trait_closure_kind: ty::ClosureKind)
268 -> ValueRef
269 {
270 // The substitutions should have no type parameters remaining
271 // after passing through fulfill_obligation
272 let llfn = callee::trans_fn_ref_with_substs(ccx,
273 closure_def_id,
274 node,
275 param_substs,
276 substs.clone()).val;
277
278 // If the closure is a Fn closure, but a FnOnce is needed (etc),
279 // then adapt the self type
280 let closure_kind = ccx.tcx().closure_kind(closure_def_id);
281 trans_closure_adapter_shim(ccx,
282 closure_def_id,
283 substs,
284 closure_kind,
285 trait_closure_kind,
286 llfn)
287 }
288
289 fn trans_closure_adapter_shim<'a, 'tcx>(
290 ccx: &'a CrateContext<'a, 'tcx>,
291 closure_def_id: ast::DefId,
292 substs: Substs<'tcx>,
293 llfn_closure_kind: ty::ClosureKind,
294 trait_closure_kind: ty::ClosureKind,
295 llfn: ValueRef)
296 -> ValueRef
297 {
298 let _icx = push_ctxt("trans_closure_adapter_shim");
299 let tcx = ccx.tcx();
300
301 debug!("trans_closure_adapter_shim(llfn_closure_kind={:?}, \
302 trait_closure_kind={:?}, \
303 llfn={})",
304 llfn_closure_kind,
305 trait_closure_kind,
306 ccx.tn().val_to_string(llfn));
307
308 match (llfn_closure_kind, trait_closure_kind) {
309 (ty::FnClosureKind, ty::FnClosureKind) |
310 (ty::FnMutClosureKind, ty::FnMutClosureKind) |
311 (ty::FnOnceClosureKind, ty::FnOnceClosureKind) => {
312 // No adapter needed.
313 llfn
314 }
315 (ty::FnClosureKind, ty::FnMutClosureKind) => {
316 // The closure fn `llfn` is a `fn(&self, ...)`. We want a
317 // `fn(&mut self, ...)`. In fact, at trans time, these are
318 // basically the same thing, so we can just return llfn.
319 llfn
320 }
321 (ty::FnClosureKind, ty::FnOnceClosureKind) |
322 (ty::FnMutClosureKind, ty::FnOnceClosureKind) => {
323 // The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut
324 // self, ...)`. We want a `fn(self, ...)`. We can produce
325 // this by doing something like:
326 //
327 // fn call_once(self, ...) { call_mut(&self, ...) }
328 // fn call_once(mut self, ...) { call_mut(&mut self, ...) }
329 //
330 // These are both the same at trans time.
331 trans_fn_once_adapter_shim(ccx, closure_def_id, substs, llfn)
332 }
333 _ => {
334 tcx.sess.bug(&format!("trans_closure_adapter_shim: cannot convert {:?} to {:?}",
335 llfn_closure_kind,
336 trait_closure_kind));
337 }
338 }
339 }
340
341 fn trans_fn_once_adapter_shim<'a, 'tcx>(
342 ccx: &'a CrateContext<'a, 'tcx>,
343 closure_def_id: ast::DefId,
344 substs: Substs<'tcx>,
345 llreffn: ValueRef)
346 -> ValueRef
347 {
348 debug!("trans_fn_once_adapter_shim(closure_def_id={}, substs={}, llreffn={})",
349 closure_def_id.repr(ccx.tcx()),
350 substs.repr(ccx.tcx()),
351 ccx.tn().val_to_string(llreffn));
352
353 let tcx = ccx.tcx();
354 let typer = NormalizingClosureTyper::new(tcx);
355
356 // Find a version of the closure type. Substitute static for the
357 // region since it doesn't really matter.
358 let substs = tcx.mk_substs(substs);
359 let closure_ty = ty::mk_closure(tcx, closure_def_id, substs);
360 let ref_closure_ty = ty::mk_imm_rptr(tcx, tcx.mk_region(ty::ReStatic), closure_ty);
361
362 // Make a version with the type of by-ref closure.
363 let ty::ClosureTy { unsafety, abi, mut sig } = typer.closure_type(closure_def_id, substs);
364 sig.0.inputs.insert(0, ref_closure_ty); // sig has no self type as of yet
365 let llref_bare_fn_ty = tcx.mk_bare_fn(ty::BareFnTy { unsafety: unsafety,
366 abi: abi,
367 sig: sig.clone() });
368 let llref_fn_ty = ty::mk_bare_fn(tcx, None, llref_bare_fn_ty);
369 debug!("trans_fn_once_adapter_shim: llref_fn_ty={}",
370 llref_fn_ty.repr(tcx));
371
372 // Make a version of the closure type with the same arguments, but
373 // with argument #0 being by value.
374 assert_eq!(abi, RustCall);
375 sig.0.inputs[0] = closure_ty;
376 let llonce_bare_fn_ty = tcx.mk_bare_fn(ty::BareFnTy { unsafety: unsafety,
377 abi: abi,
378 sig: sig });
379 let llonce_fn_ty = ty::mk_bare_fn(tcx, None, llonce_bare_fn_ty);
380
381 // Create the by-value helper.
382 let function_name = link::mangle_internal_name_by_type_and_seq(ccx, llonce_fn_ty, "once_shim");
383 let lloncefn = decl_internal_rust_fn(ccx, llonce_fn_ty, &function_name);
384
385 let sig = ty::erase_late_bound_regions(tcx, &llonce_bare_fn_ty.sig);
386 let (block_arena, fcx): (TypedArena<_>, FunctionContext);
387 block_arena = TypedArena::new();
388 fcx = new_fn_ctxt(ccx,
389 lloncefn,
390 ast::DUMMY_NODE_ID,
391 false,
392 sig.output,
393 substs,
394 None,
395 &block_arena);
396 let mut bcx = init_function(&fcx, false, sig.output);
397
398 // the first argument (`self`) will be the (by value) closure env.
399 let self_scope = fcx.push_custom_cleanup_scope();
400 let self_scope_id = CustomScope(self_scope);
401 let rvalue_mode = datum::appropriate_rvalue_mode(ccx, closure_ty);
402 let llself = get_param(lloncefn, fcx.arg_pos(0) as u32);
403 let env_datum = Datum::new(llself, closure_ty, Rvalue::new(rvalue_mode));
404 let env_datum = unpack_datum!(bcx,
405 env_datum.to_lvalue_datum_in_scope(bcx, "self",
406 self_scope_id));
407
408 debug!("trans_fn_once_adapter_shim: env_datum={}",
409 bcx.val_to_string(env_datum.val));
410
411 // the remaining arguments will be packed up in a tuple.
412 let input_tys = match sig.inputs[1].sty {
413 ty::ty_tup(ref tys) => &**tys,
414 _ => bcx.sess().bug(&format!("trans_fn_once_adapter_shim: not rust-call! \
415 closure_def_id={}",
416 closure_def_id.repr(tcx)))
417 };
418 let llargs: Vec<_> =
419 input_tys.iter()
420 .enumerate()
421 .map(|(i, _)| get_param(lloncefn, fcx.arg_pos(i+1) as u32))
422 .collect();
423
424 let dest =
425 fcx.llretslotptr.get().map(
426 |_| expr::SaveIn(fcx.get_ret_slot(bcx, sig.output, "ret_slot")));
427
428 let callee_data = TraitItem(MethodData { llfn: llreffn,
429 llself: env_datum.val });
430
431 bcx = callee::trans_call_inner(bcx,
432 DebugLoc::None,
433 llref_fn_ty,
434 |bcx, _| Callee { bcx: bcx, data: callee_data },
435 ArgVals(&llargs),
436 dest).bcx;
437
438 fcx.pop_custom_cleanup_scope(self_scope);
439
440 finish_fn(&fcx, bcx, sig.output, DebugLoc::None);
441
442 lloncefn
443 }