]> git.proxmox.com Git - rustc.git/blob - src/librustc_trans/closure.rs
New upstream version 1.13.0+dfsg1
[rustc.git] / src / librustc_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 llvm::{self, ValueRef, get_params};
13 use rustc::hir::def_id::DefId;
14 use abi::{Abi, FnType};
15 use attributes;
16 use base::*;
17 use callee::{self, Callee};
18 use common::*;
19 use debuginfo::{DebugLoc};
20 use declare;
21 use monomorphize::{Instance};
22 use value::Value;
23 use rustc::ty::{self, Ty, TyCtxt};
24
25 use rustc::hir;
26
27 fn get_self_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
28 closure_id: DefId,
29 fn_ty: Ty<'tcx>)
30 -> Ty<'tcx> {
31 match tcx.closure_kind(closure_id) {
32 ty::ClosureKind::Fn => {
33 tcx.mk_imm_ref(tcx.mk_region(ty::ReErased), fn_ty)
34 }
35 ty::ClosureKind::FnMut => {
36 tcx.mk_mut_ref(tcx.mk_region(ty::ReErased), fn_ty)
37 }
38 ty::ClosureKind::FnOnce => fn_ty,
39 }
40 }
41
42 /// Returns the LLVM function declaration for a closure, creating it if
43 /// necessary. If the ID does not correspond to a closure ID, returns None.
44 fn get_or_create_closure_declaration<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
45 closure_id: DefId,
46 substs: ty::ClosureSubsts<'tcx>)
47 -> ValueRef {
48 // Normalize type so differences in regions and typedefs don't cause
49 // duplicate declarations
50 let tcx = ccx.tcx();
51 let substs = tcx.erase_regions(&substs);
52 let instance = Instance::new(closure_id, substs.func_substs);
53
54 if let Some(&llfn) = ccx.instances().borrow().get(&instance) {
55 debug!("get_or_create_closure_declaration(): found closure {:?}: {:?}",
56 instance, Value(llfn));
57 return llfn;
58 }
59
60 let symbol = instance.symbol_name(ccx.shared());
61
62 // Compute the rust-call form of the closure call method.
63 let sig = &tcx.closure_type(closure_id, substs).sig;
64 let sig = tcx.erase_late_bound_regions_and_normalize(sig);
65 let closure_type = tcx.mk_closure_from_closure_substs(closure_id, substs);
66 let function_type = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
67 unsafety: hir::Unsafety::Normal,
68 abi: Abi::RustCall,
69 sig: ty::Binder(ty::FnSig {
70 inputs: Some(get_self_type(tcx, closure_id, closure_type))
71 .into_iter().chain(sig.inputs).collect(),
72 output: sig.output,
73 variadic: false
74 })
75 }));
76 let llfn = declare::declare_fn(ccx, &symbol, function_type);
77
78 attributes::set_frame_pointer_elimination(ccx, llfn);
79
80 debug!("get_or_create_declaration_if_closure(): inserting new \
81 closure {:?}: {:?}",
82 instance, Value(llfn));
83
84 // NOTE: We do *not* store llfn in the ccx.instances() map here,
85 // that is only done, when the closures body is translated.
86
87 llfn
88 }
89
90 pub fn trans_closure_body_via_mir<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
91 closure_def_id: DefId,
92 closure_substs: ty::ClosureSubsts<'tcx>) {
93 // (*) Note that in the case of inlined functions, the `closure_def_id` will be the
94 // defid of the closure in its original crate, whereas `id` will be the id of the local
95 // inlined copy.
96 debug!("trans_closure_body_via_mir(closure_def_id={:?}, closure_substs={:?})",
97 closure_def_id, closure_substs);
98
99 let tcx = ccx.tcx();
100 let _icx = push_ctxt("closure::trans_closure_expr");
101
102 let param_substs = closure_substs.func_substs;
103 let instance = Instance::new(closure_def_id, param_substs);
104
105 // If we have not done so yet, translate this closure's body
106 if !ccx.instances().borrow().contains_key(&instance) {
107 let llfn = get_or_create_closure_declaration(ccx, closure_def_id, closure_substs);
108
109 unsafe {
110 if ccx.sess().target.target.options.allows_weak_linkage {
111 llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::WeakODRLinkage);
112 llvm::SetUniqueComdat(ccx.llmod(), llfn);
113 } else {
114 llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::InternalLinkage);
115 }
116 }
117
118 // set an inline hint for all closures
119 attributes::inline(llfn, attributes::InlineAttr::Hint);
120
121 // Get the type of this closure. Use the current `param_substs` as
122 // the closure substitutions. This makes sense because the closure
123 // takes the same set of type arguments as the enclosing fn, and
124 // this function (`trans_closure`) is invoked at the point
125 // of the closure expression.
126
127 let sig = &tcx.closure_type(closure_def_id, closure_substs).sig;
128 let sig = tcx.erase_late_bound_regions_and_normalize(sig);
129
130 let closure_type = tcx.mk_closure_from_closure_substs(closure_def_id,
131 closure_substs);
132 let sig = ty::FnSig {
133 inputs: Some(get_self_type(tcx, closure_def_id, closure_type))
134 .into_iter().chain(sig.inputs).collect(),
135 output: sig.output,
136 variadic: false
137 };
138
139 trans_closure(ccx,
140 llfn,
141 Instance::new(closure_def_id, param_substs),
142 &sig,
143 Abi::RustCall);
144
145 ccx.instances().borrow_mut().insert(instance, llfn);
146 }
147 }
148
149 pub fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
150 closure_def_id: DefId,
151 substs: ty::ClosureSubsts<'tcx>,
152 method_instance: Instance<'tcx>,
153 trait_closure_kind: ty::ClosureKind)
154 -> ValueRef
155 {
156 // If this is a closure, redirect to it.
157 let llfn = get_or_create_closure_declaration(ccx, closure_def_id, substs);
158
159 // If weak linkage is not allowed, we have to make sure that a local,
160 // private copy of the closure is available in this codegen unit
161 if !ccx.sess().target.target.options.allows_weak_linkage &&
162 !ccx.sess().opts.single_codegen_unit() {
163
164 trans_closure_body_via_mir(ccx, closure_def_id, substs);
165 }
166
167 // If the closure is a Fn closure, but a FnOnce is needed (etc),
168 // then adapt the self type
169 let llfn_closure_kind = ccx.tcx().closure_kind(closure_def_id);
170
171 let _icx = push_ctxt("trans_closure_adapter_shim");
172
173 debug!("trans_closure_adapter_shim(llfn_closure_kind={:?}, \
174 trait_closure_kind={:?}, llfn={:?})",
175 llfn_closure_kind, trait_closure_kind, Value(llfn));
176
177 match (llfn_closure_kind, trait_closure_kind) {
178 (ty::ClosureKind::Fn, ty::ClosureKind::Fn) |
179 (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) |
180 (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) => {
181 // No adapter needed.
182 llfn
183 }
184 (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => {
185 // The closure fn `llfn` is a `fn(&self, ...)`. We want a
186 // `fn(&mut self, ...)`. In fact, at trans time, these are
187 // basically the same thing, so we can just return llfn.
188 llfn
189 }
190 (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) |
191 (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
192 // The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut
193 // self, ...)`. We want a `fn(self, ...)`. We can produce
194 // this by doing something like:
195 //
196 // fn call_once(self, ...) { call_mut(&self, ...) }
197 // fn call_once(mut self, ...) { call_mut(&mut self, ...) }
198 //
199 // These are both the same at trans time.
200 trans_fn_once_adapter_shim(ccx, closure_def_id, substs, method_instance, llfn)
201 }
202 _ => {
203 bug!("trans_closure_adapter_shim: cannot convert {:?} to {:?}",
204 llfn_closure_kind,
205 trait_closure_kind);
206 }
207 }
208 }
209
210 fn trans_fn_once_adapter_shim<'a, 'tcx>(
211 ccx: &'a CrateContext<'a, 'tcx>,
212 closure_def_id: DefId,
213 substs: ty::ClosureSubsts<'tcx>,
214 method_instance: Instance<'tcx>,
215 llreffn: ValueRef)
216 -> ValueRef
217 {
218 if let Some(&llfn) = ccx.instances().borrow().get(&method_instance) {
219 return llfn;
220 }
221
222 debug!("trans_fn_once_adapter_shim(closure_def_id={:?}, substs={:?}, llreffn={:?})",
223 closure_def_id, substs, Value(llreffn));
224
225 let tcx = ccx.tcx();
226
227 // Find a version of the closure type. Substitute static for the
228 // region since it doesn't really matter.
229 let closure_ty = tcx.mk_closure_from_closure_substs(closure_def_id, substs);
230 let ref_closure_ty = tcx.mk_imm_ref(tcx.mk_region(ty::ReErased), closure_ty);
231
232 // Make a version with the type of by-ref closure.
233 let ty::ClosureTy { unsafety, abi, mut sig } =
234 tcx.closure_type(closure_def_id, substs);
235 sig.0.inputs.insert(0, ref_closure_ty); // sig has no self type as of yet
236 let llref_fn_ty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
237 unsafety: unsafety,
238 abi: abi,
239 sig: sig.clone()
240 }));
241 debug!("trans_fn_once_adapter_shim: llref_fn_ty={:?}",
242 llref_fn_ty);
243
244
245 // Make a version of the closure type with the same arguments, but
246 // with argument #0 being by value.
247 assert_eq!(abi, Abi::RustCall);
248 sig.0.inputs[0] = closure_ty;
249
250 let sig = tcx.erase_late_bound_regions_and_normalize(&sig);
251 let fn_ty = FnType::new(ccx, abi, &sig, &[]);
252
253 let llonce_fn_ty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
254 unsafety: unsafety,
255 abi: abi,
256 sig: ty::Binder(sig)
257 }));
258
259 // Create the by-value helper.
260 let function_name = method_instance.symbol_name(ccx.shared());
261 let lloncefn = declare::define_internal_fn(ccx, &function_name, llonce_fn_ty);
262 attributes::set_frame_pointer_elimination(ccx, lloncefn);
263
264 let (block_arena, fcx): (TypedArena<_>, FunctionContext);
265 block_arena = TypedArena::new();
266 fcx = FunctionContext::new(ccx, lloncefn, fn_ty, None, &block_arena);
267 let mut bcx = fcx.init(false);
268
269
270 // the first argument (`self`) will be the (by value) closure env.
271
272 let mut llargs = get_params(fcx.llfn);
273 let mut self_idx = fcx.fn_ty.ret.is_indirect() as usize;
274 let env_arg = &fcx.fn_ty.args[0];
275 let llenv = if env_arg.is_indirect() {
276 llargs[self_idx]
277 } else {
278 let scratch = alloc_ty(bcx, closure_ty, "self");
279 let mut llarg_idx = self_idx;
280 env_arg.store_fn_arg(&bcx.build(), &mut llarg_idx, scratch);
281 scratch
282 };
283
284 debug!("trans_fn_once_adapter_shim: env={:?}", Value(llenv));
285 // Adjust llargs such that llargs[self_idx..] has the call arguments.
286 // For zero-sized closures that means sneaking in a new argument.
287 if env_arg.is_ignore() {
288 if self_idx > 0 {
289 self_idx -= 1;
290 llargs[self_idx] = llenv;
291 } else {
292 llargs.insert(0, llenv);
293 }
294 } else {
295 llargs[self_idx] = llenv;
296 }
297
298 let dest = fcx.llretslotptr.get();
299
300 let callee = Callee {
301 data: callee::Fn(llreffn),
302 ty: llref_fn_ty
303 };
304
305 // Call the by-ref closure body with `self` in a cleanup scope,
306 // to drop `self` when the body returns, or in case it unwinds.
307 let self_scope = fcx.push_custom_cleanup_scope();
308 fcx.schedule_drop_mem(self_scope, llenv, closure_ty);
309
310 bcx = callee.call(bcx, DebugLoc::None, &llargs[self_idx..], dest).bcx;
311
312 fcx.pop_and_trans_custom_cleanup_scope(bcx, self_scope);
313
314 fcx.finish(bcx, DebugLoc::None);
315
316 ccx.instances().borrow_mut().insert(method_instance, lloncefn);
317
318 lloncefn
319 }