]> git.lizzy.rs Git - rust.git/blob - src/librustc_trans/trans/closure.rs
Auto merge of #22541 - Manishearth:rollup, r=Gankro
[rust.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 back::link::mangle_internal_name_by_path_and_seq;
12 use llvm::ValueRef;
13 use middle::mem_categorization::Typer;
14 use trans::adt;
15 use trans::base::*;
16 use trans::build::*;
17 use trans::cleanup::{CleanupMethods, ScopeId};
18 use trans::common::*;
19 use trans::datum::{Datum, rvalue_scratch_datum};
20 use trans::datum::{Rvalue, ByValue};
21 use trans::debuginfo;
22 use trans::expr;
23 use trans::monomorphize::{self, MonoId};
24 use trans::type_of::*;
25 use middle::ty::{self, ClosureTyper};
26 use middle::subst::{Substs};
27 use session::config::FullDebugInfo;
28
29 use syntax::ast;
30 use syntax::ast_util;
31
32
33 fn load_closure_environment<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
34                                         arg_scope_id: ScopeId,
35                                         freevars: &[ty::Freevar])
36                                         -> Block<'blk, 'tcx>
37 {
38     let _icx = push_ctxt("closure::load_closure_environment");
39
40     // Special case for small by-value selfs.
41     let closure_id = ast_util::local_def(bcx.fcx.id);
42     let self_type = self_type_for_closure(bcx.ccx(), closure_id,
43                                                   node_id_type(bcx, closure_id.node));
44     let kind = kind_for_closure(bcx.ccx(), closure_id);
45     let llenv = if kind == ty::FnOnceClosureKind &&
46             !arg_is_indirect(bcx.ccx(), self_type) {
47         let datum = rvalue_scratch_datum(bcx,
48                                          self_type,
49                                          "closure_env");
50         store_ty(bcx, bcx.fcx.llenv.unwrap(), datum.val, self_type);
51         datum.val
52     } else {
53         bcx.fcx.llenv.unwrap()
54     };
55
56     // Store the pointer to closure data in an alloca for debug info because that's what the
57     // llvm.dbg.declare intrinsic expects
58     let env_pointer_alloca = if bcx.sess().opts.debuginfo == FullDebugInfo {
59         let alloc = alloca(bcx, val_ty(llenv), "__debuginfo_env_ptr");
60         Store(bcx, llenv, alloc);
61         Some(alloc)
62     } else {
63         None
64     };
65
66     for (i, freevar) in freevars.iter().enumerate() {
67         let upvar_id = ty::UpvarId { var_id: freevar.def.local_node_id(),
68                                      closure_expr_id: closure_id.node };
69         let upvar_capture = bcx.tcx().upvar_capture(upvar_id).unwrap();
70         let mut upvar_ptr = GEPi(bcx, llenv, &[0, i]);
71         let captured_by_ref = match upvar_capture {
72             ty::UpvarCapture::ByValue => false,
73             ty::UpvarCapture::ByRef(..) => {
74                 upvar_ptr = Load(bcx, upvar_ptr);
75                 true
76             }
77         };
78         let def_id = freevar.def.def_id();
79         bcx.fcx.llupvars.borrow_mut().insert(def_id.node, upvar_ptr);
80
81         if kind == ty::FnOnceClosureKind && !captured_by_ref {
82             bcx.fcx.schedule_drop_mem(arg_scope_id,
83                                       upvar_ptr,
84                                       node_id_type(bcx, def_id.node))
85         }
86
87         if let Some(env_pointer_alloca) = env_pointer_alloca {
88             debuginfo::create_captured_var_metadata(
89                 bcx,
90                 def_id.node,
91                 env_pointer_alloca,
92                 i,
93                 captured_by_ref,
94                 freevar.span);
95         }
96     }
97
98     bcx
99 }
100
101 pub enum ClosureEnv<'a> {
102     NotClosure,
103     Closure(&'a [ty::Freevar]),
104 }
105
106 impl<'a> ClosureEnv<'a> {
107     pub fn load<'blk,'tcx>(self, bcx: Block<'blk, 'tcx>, arg_scope: ScopeId)
108                            -> Block<'blk, 'tcx>
109     {
110         match self {
111             ClosureEnv::NotClosure => bcx,
112             ClosureEnv::Closure(freevars) => {
113                 if freevars.is_empty() {
114                     bcx
115                 } else {
116                     load_closure_environment(bcx, arg_scope, freevars)
117                 }
118             }
119         }
120     }
121 }
122
123 /// Returns the LLVM function declaration for a closure, creating it if
124 /// necessary. If the ID does not correspond to a closure ID, returns None.
125 pub fn get_or_create_declaration_if_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
126                                                       closure_id: ast::DefId,
127                                                       substs: &Substs<'tcx>)
128                                                       -> Option<Datum<'tcx, Rvalue>> {
129     if !ccx.tcx().closure_kinds.borrow().contains_key(&closure_id) {
130         // Not a closure.
131         return None
132     }
133
134     let function_type = ty::node_id_to_type(ccx.tcx(), closure_id.node);
135     let function_type = monomorphize::apply_param_substs(ccx.tcx(), substs, &function_type);
136
137     // Normalize type so differences in regions and typedefs don't cause
138     // duplicate declarations
139     let function_type = erase_regions(ccx.tcx(), &function_type);
140     let params = match function_type.sty {
141         ty::ty_closure(_, _, substs) => &substs.types,
142         _ => unreachable!()
143     };
144     let mono_id = MonoId {
145         def: closure_id,
146         params: params
147     };
148
149     match ccx.closure_vals().borrow().get(&mono_id) {
150         Some(&llfn) => {
151             debug!("get_or_create_declaration_if_closure(): found closure");
152             return Some(Datum::new(llfn, function_type, Rvalue::new(ByValue)))
153         }
154         None => {}
155     }
156
157     let symbol = ccx.tcx().map.with_path(closure_id.node, |path| {
158         mangle_internal_name_by_path_and_seq(path, "closure")
159     });
160
161     let llfn = decl_internal_rust_fn(ccx, function_type, &symbol[..]);
162
163     // set an inline hint for all closures
164     set_inline_hint(llfn);
165
166     debug!("get_or_create_declaration_if_closure(): inserting new \
167             closure {:?} (type {})",
168            mono_id,
169            ccx.tn().type_to_string(val_ty(llfn)));
170     ccx.closure_vals().borrow_mut().insert(mono_id, llfn);
171
172     Some(Datum::new(llfn, function_type, Rvalue::new(ByValue)))
173 }
174
175 pub enum Dest<'a, 'tcx: 'a> {
176     SaveIn(Block<'a, 'tcx>, ValueRef),
177     Ignore(&'a CrateContext<'a, 'tcx>)
178 }
179
180 pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>,
181                                     decl: &ast::FnDecl,
182                                     body: &ast::Block,
183                                     id: ast::NodeId,
184                                     param_substs: &'tcx Substs<'tcx>)
185                                     -> Option<Block<'a, 'tcx>>
186 {
187     let ccx = match dest {
188         Dest::SaveIn(bcx, _) => bcx.ccx(),
189         Dest::Ignore(ccx) => ccx
190     };
191     let tcx = ccx.tcx();
192     let _icx = push_ctxt("closure::trans_closure");
193
194     debug!("trans_closure()");
195
196     let closure_id = ast_util::local_def(id);
197     let llfn = get_or_create_declaration_if_closure(
198         ccx,
199         closure_id,
200         param_substs).unwrap();
201
202     // Get the type of this closure. Use the current `param_substs` as
203     // the closure substitutions. This makes sense because the closure
204     // takes the same set of type arguments as the enclosing fn, and
205     // this function (`trans_closure`) is invoked at the point
206     // of the closure expression.
207     let typer = NormalizingClosureTyper::new(tcx);
208     let function_type = typer.closure_type(closure_id, param_substs);
209
210     let freevars: Vec<ty::Freevar> =
211         ty::with_freevars(tcx, id, |fv| fv.iter().cloned().collect());
212
213     let sig = ty::erase_late_bound_regions(tcx, &function_type.sig);
214
215     trans_closure(ccx,
216                   decl,
217                   body,
218                   llfn.val,
219                   param_substs,
220                   id,
221                   &[],
222                   sig.output,
223                   function_type.abi,
224                   ClosureEnv::Closure(&freevars[..]));
225
226     // Don't hoist this to the top of the function. It's perfectly legitimate
227     // to have a zero-size closure (in which case dest will be `Ignore`) and
228     // we must still generate the closure body.
229     let (mut bcx, dest_addr) = match dest {
230         Dest::SaveIn(bcx, p) => (bcx, p),
231         Dest::Ignore(_) => {
232             debug!("trans_closure() ignoring result");
233             return None;
234         }
235     };
236
237     let repr = adt::represent_type(ccx, node_id_type(bcx, id));
238
239     // Create the closure.
240     for (i, freevar) in freevars.iter().enumerate() {
241         let datum = expr::trans_local_var(bcx, freevar.def);
242         let upvar_slot_dest = adt::trans_field_ptr(bcx,
243                                                    &*repr,
244                                                    dest_addr,
245                                                    0,
246                                                    i);
247         let upvar_id = ty::UpvarId { var_id: freevar.def.local_node_id(),
248                                      closure_expr_id: id };
249         match tcx.upvar_capture(upvar_id).unwrap() {
250             ty::UpvarCapture::ByValue => {
251                 bcx = datum.store_to(bcx, upvar_slot_dest);
252             }
253             ty::UpvarCapture::ByRef(..) => {
254                 Store(bcx, datum.to_llref(), upvar_slot_dest);
255             }
256         }
257     }
258     adt::trans_set_discr(bcx, &*repr, dest_addr, 0);
259
260     Some(bcx)
261 }
262