]> git.lizzy.rs Git - rust.git/blob - src/librustc_trans/trans/controlflow.rs
Auto merge of #22541 - Manishearth:rollup, r=Gankro
[rust.git] / src / librustc_trans / trans / controlflow.rs
1 // Copyright 2012 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 llvm::ValueRef;
12 use middle::def;
13 use middle::lang_items::{PanicFnLangItem, PanicBoundsCheckFnLangItem};
14 use trans::base::*;
15 use trans::build::*;
16 use trans::callee;
17 use trans::cleanup::CleanupMethods;
18 use trans::cleanup;
19 use trans::common::*;
20 use trans::consts;
21 use trans::debuginfo;
22 use trans::debuginfo::{DebugLoc, ToDebugLoc};
23 use trans::expr;
24 use trans;
25 use middle::ty;
26 use util::ppaux::Repr;
27
28 use syntax::ast;
29 use syntax::ast::Ident;
30 use syntax::ast_util;
31 use syntax::parse::token::InternedString;
32 use syntax::parse::token;
33 use syntax::visit::Visitor;
34
35 pub fn trans_stmt<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
36                               s: &ast::Stmt)
37                               -> Block<'blk, 'tcx> {
38     let _icx = push_ctxt("trans_stmt");
39     let fcx = cx.fcx;
40     debug!("trans_stmt({})", s.repr(cx.tcx()));
41
42     if cx.sess().asm_comments() {
43         add_span_comment(cx, s.span, &s.repr(cx.tcx())[]);
44     }
45
46     let mut bcx = cx;
47
48     let id = ast_util::stmt_id(s);
49     let cleanup_debug_loc =
50         debuginfo::get_cleanup_debug_loc_for_ast_node(bcx.ccx(), id, s.span, false);
51     fcx.push_ast_cleanup_scope(cleanup_debug_loc);
52
53     match s.node {
54         ast::StmtExpr(ref e, _) | ast::StmtSemi(ref e, _) => {
55             bcx = trans_stmt_semi(bcx, &**e);
56         }
57         ast::StmtDecl(ref d, _) => {
58             match d.node {
59                 ast::DeclLocal(ref local) => {
60                     bcx = init_local(bcx, &**local);
61                     debuginfo::create_local_var_metadata(bcx, &**local);
62                 }
63                 // Inner items are visited by `trans_item`/`trans_meth`.
64                 ast::DeclItem(_) => {},
65             }
66         }
67         ast::StmtMac(..) => cx.tcx().sess.bug("unexpanded macro")
68     }
69
70     bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, ast_util::stmt_id(s));
71
72     return bcx;
73 }
74
75 pub fn trans_stmt_semi<'blk, 'tcx>(cx: Block<'blk, 'tcx>, e: &ast::Expr)
76                                    -> Block<'blk, 'tcx> {
77     let _icx = push_ctxt("trans_stmt_semi");
78     let ty = expr_ty(cx, e);
79     if type_needs_drop(cx.tcx(), ty) {
80         expr::trans_to_lvalue(cx, e, "stmt").bcx
81     } else {
82         expr::trans_into(cx, e, expr::Ignore)
83     }
84 }
85
86 pub fn trans_block<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
87                                b: &ast::Block,
88                                mut dest: expr::Dest)
89                                -> Block<'blk, 'tcx> {
90     let _icx = push_ctxt("trans_block");
91     let fcx = bcx.fcx;
92     let mut bcx = bcx;
93
94     let cleanup_debug_loc =
95         debuginfo::get_cleanup_debug_loc_for_ast_node(bcx.ccx(), b.id, b.span, true);
96     fcx.push_ast_cleanup_scope(cleanup_debug_loc);
97
98     for s in &b.stmts {
99         bcx = trans_stmt(bcx, &**s);
100     }
101
102     if dest != expr::Ignore {
103         let block_ty = node_id_type(bcx, b.id);
104
105         if b.expr.is_none() || type_is_zero_size(bcx.ccx(), block_ty) {
106             dest = expr::Ignore;
107         } else if b.expr.is_some() {
108             // If the block has an expression, but that expression isn't reachable,
109             // don't save into the destination given, ignore it.
110             if let Some(ref cfg) = bcx.fcx.cfg {
111                 if !cfg.node_is_reachable(b.expr.as_ref().unwrap().id) {
112                     dest = expr::Ignore;
113                 }
114             }
115         }
116     }
117
118     match b.expr {
119         Some(ref e) => {
120             bcx = expr::trans_into(bcx, &**e, dest);
121         }
122         None => {
123             assert!(dest == expr::Ignore || bcx.unreachable.get());
124         }
125     }
126
127     bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, b.id);
128
129     return bcx;
130 }
131
132 pub fn trans_if<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
133                             if_id: ast::NodeId,
134                             cond: &ast::Expr,
135                             thn: &ast::Block,
136                             els: Option<&ast::Expr>,
137                             dest: expr::Dest)
138                             -> Block<'blk, 'tcx> {
139     debug!("trans_if(bcx={}, if_id={}, cond={}, thn={}, dest={})",
140            bcx.to_str(), if_id, bcx.expr_to_string(cond), thn.id,
141            dest.to_string(bcx.ccx()));
142     let _icx = push_ctxt("trans_if");
143     let mut bcx = bcx;
144
145     let cond_val = unpack_result!(bcx, expr::trans(bcx, cond).to_llbool());
146
147     // Drop branches that are known to be impossible
148     if is_const(cond_val) && !is_undef(cond_val) {
149         if const_to_uint(cond_val) == 1 {
150             match els {
151                 Some(elexpr) => {
152                     let mut trans = TransItemVisitor { ccx: bcx.fcx.ccx };
153                     trans.visit_expr(&*elexpr);
154                 }
155                 None => {}
156             }
157             // if true { .. } [else { .. }]
158             bcx = trans_block(bcx, &*thn, dest);
159             trans::debuginfo::clear_source_location(bcx.fcx);
160         } else {
161             let mut trans = TransItemVisitor { ccx: bcx.fcx.ccx } ;
162             trans.visit_block(&*thn);
163
164             match els {
165                 // if false { .. } else { .. }
166                 Some(elexpr) => {
167                     bcx = expr::trans_into(bcx, &*elexpr, dest);
168                     trans::debuginfo::clear_source_location(bcx.fcx);
169                 }
170
171                 // if false { .. }
172                 None => { }
173             }
174         }
175
176         return bcx;
177     }
178
179     let name = format!("then-block-{}-", thn.id);
180     let then_bcx_in = bcx.fcx.new_id_block(&name[..], thn.id);
181     let then_bcx_out = trans_block(then_bcx_in, &*thn, dest);
182     trans::debuginfo::clear_source_location(bcx.fcx);
183
184     let cond_source_loc = cond.debug_loc();
185
186     let next_bcx;
187     match els {
188         Some(elexpr) => {
189             let else_bcx_in = bcx.fcx.new_id_block("else-block", elexpr.id);
190             let else_bcx_out = expr::trans_into(else_bcx_in, &*elexpr, dest);
191             next_bcx = bcx.fcx.join_blocks(if_id,
192                                            &[then_bcx_out, else_bcx_out]);
193             CondBr(bcx, cond_val, then_bcx_in.llbb, else_bcx_in.llbb, cond_source_loc);
194         }
195
196         None => {
197             next_bcx = bcx.fcx.new_id_block("next-block", if_id);
198             Br(then_bcx_out, next_bcx.llbb, DebugLoc::None);
199             CondBr(bcx, cond_val, then_bcx_in.llbb, next_bcx.llbb, cond_source_loc);
200         }
201     }
202
203     // Clear the source location because it is still set to whatever has been translated
204     // right before.
205     trans::debuginfo::clear_source_location(next_bcx.fcx);
206
207     next_bcx
208 }
209
210 pub fn trans_while<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
211                                loop_expr: &ast::Expr,
212                                cond: &ast::Expr,
213                                body: &ast::Block)
214                                -> Block<'blk, 'tcx> {
215     let _icx = push_ctxt("trans_while");
216     let fcx = bcx.fcx;
217
218     //            bcx
219     //             |
220     //         cond_bcx_in  <--------+
221     //             |                 |
222     //         cond_bcx_out          |
223     //           |      |            |
224     //           |    body_bcx_in    |
225     // cleanup_blk      |            |
226     //    |           body_bcx_out --+
227     // next_bcx_in
228
229     let next_bcx_in = fcx.new_id_block("while_exit", loop_expr.id);
230     let cond_bcx_in = fcx.new_id_block("while_cond", cond.id);
231     let body_bcx_in = fcx.new_id_block("while_body", body.id);
232
233     fcx.push_loop_cleanup_scope(loop_expr.id, [next_bcx_in, cond_bcx_in]);
234
235     Br(bcx, cond_bcx_in.llbb, loop_expr.debug_loc());
236
237     // compile the block where we will handle loop cleanups
238     let cleanup_llbb = fcx.normal_exit_block(loop_expr.id, cleanup::EXIT_BREAK);
239
240     // compile the condition
241     let Result {bcx: cond_bcx_out, val: cond_val} =
242         expr::trans(cond_bcx_in, cond).to_llbool();
243
244     CondBr(cond_bcx_out, cond_val, body_bcx_in.llbb, cleanup_llbb, cond.debug_loc());
245
246     // loop body:
247     let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
248     Br(body_bcx_out, cond_bcx_in.llbb, DebugLoc::None);
249
250     fcx.pop_loop_cleanup_scope(loop_expr.id);
251     return next_bcx_in;
252 }
253
254 pub fn trans_loop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
255                               loop_expr: &ast::Expr,
256                               body: &ast::Block)
257                               -> Block<'blk, 'tcx> {
258     let _icx = push_ctxt("trans_loop");
259     let fcx = bcx.fcx;
260
261     //            bcx
262     //             |
263     //         body_bcx_in
264     //             |
265     //         body_bcx_out
266     //
267     // next_bcx
268     //
269     // Links between body_bcx_in and next_bcx are created by
270     // break statements.
271
272     let next_bcx_in = bcx.fcx.new_id_block("loop_exit", loop_expr.id);
273     let body_bcx_in = bcx.fcx.new_id_block("loop_body", body.id);
274
275     fcx.push_loop_cleanup_scope(loop_expr.id, [next_bcx_in, body_bcx_in]);
276
277     Br(bcx, body_bcx_in.llbb, loop_expr.debug_loc());
278     let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
279     Br(body_bcx_out, body_bcx_in.llbb, DebugLoc::None);
280
281     fcx.pop_loop_cleanup_scope(loop_expr.id);
282
283     return next_bcx_in;
284 }
285
286 pub fn trans_break_cont<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
287                                     expr: &ast::Expr,
288                                     opt_label: Option<Ident>,
289                                     exit: uint)
290                                     -> Block<'blk, 'tcx> {
291     let _icx = push_ctxt("trans_break_cont");
292     let fcx = bcx.fcx;
293
294     if bcx.unreachable.get() {
295         return bcx;
296     }
297
298     // Locate loop that we will break to
299     let loop_id = match opt_label {
300         None => fcx.top_loop_scope(),
301         Some(_) => {
302             match bcx.tcx().def_map.borrow().get(&expr.id) {
303                 Some(&def::DefLabel(loop_id)) => loop_id,
304                 ref r => {
305                     bcx.tcx().sess.bug(&format!("{:?} in def-map for label",
306                                                r)[])
307                 }
308             }
309         }
310     };
311
312     // Generate appropriate cleanup code and branch
313     let cleanup_llbb = fcx.normal_exit_block(loop_id, exit);
314     Br(bcx, cleanup_llbb, expr.debug_loc());
315     Unreachable(bcx); // anything afterwards should be ignored
316     return bcx;
317 }
318
319 pub fn trans_break<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
320                                expr: &ast::Expr,
321                                label_opt: Option<Ident>)
322                                -> Block<'blk, 'tcx> {
323     return trans_break_cont(bcx, expr, label_opt, cleanup::EXIT_BREAK);
324 }
325
326 pub fn trans_cont<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
327                               expr: &ast::Expr,
328                               label_opt: Option<Ident>)
329                               -> Block<'blk, 'tcx> {
330     return trans_break_cont(bcx, expr, label_opt, cleanup::EXIT_LOOP);
331 }
332
333 pub fn trans_ret<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
334                              return_expr: &ast::Expr,
335                              retval_expr: Option<&ast::Expr>)
336                              -> Block<'blk, 'tcx> {
337     let _icx = push_ctxt("trans_ret");
338     let fcx = bcx.fcx;
339     let mut bcx = bcx;
340     let dest = match (fcx.llretslotptr.get(), retval_expr) {
341         (Some(_), Some(retval_expr)) => {
342             let ret_ty = expr_ty_adjusted(bcx, &*retval_expr);
343             expr::SaveIn(fcx.get_ret_slot(bcx, ty::FnConverging(ret_ty), "ret_slot"))
344         }
345         _ => expr::Ignore,
346     };
347     if let Some(x) = retval_expr {
348         bcx = expr::trans_into(bcx, &*x, dest);
349         match dest {
350             expr::SaveIn(slot) if fcx.needs_ret_allocas => {
351                 Store(bcx, slot, fcx.llretslotptr.get().unwrap());
352             }
353             _ => {}
354         }
355     }
356     let cleanup_llbb = fcx.return_exit_block();
357     Br(bcx, cleanup_llbb, return_expr.debug_loc());
358     Unreachable(bcx);
359     return bcx;
360 }
361
362 pub fn trans_fail<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
363                               call_info: NodeIdAndSpan,
364                               fail_str: InternedString)
365                               -> Block<'blk, 'tcx> {
366     let ccx = bcx.ccx();
367     let _icx = push_ctxt("trans_fail_value");
368
369     let v_str = C_str_slice(ccx, fail_str);
370     let loc = bcx.sess().codemap().lookup_char_pos(call_info.span.lo);
371     let filename = token::intern_and_get_ident(&loc.file.name[]);
372     let filename = C_str_slice(ccx, filename);
373     let line = C_uint(ccx, loc.line);
374     let expr_file_line_const = C_struct(ccx, &[v_str, filename, line], false);
375     let expr_file_line = consts::addr_of(ccx, expr_file_line_const,
376                                          "panic_loc", call_info.id);
377     let args = vec!(expr_file_line);
378     let did = langcall(bcx, Some(call_info.span), "", PanicFnLangItem);
379     let bcx = callee::trans_lang_call(bcx,
380                                       did,
381                                       &args[..],
382                                       Some(expr::Ignore),
383                                       call_info.debug_loc()).bcx;
384     Unreachable(bcx);
385     return bcx;
386 }
387
388 pub fn trans_fail_bounds_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
389                                            call_info: NodeIdAndSpan,
390                                            index: ValueRef,
391                                            len: ValueRef)
392                                            -> Block<'blk, 'tcx> {
393     let ccx = bcx.ccx();
394     let _icx = push_ctxt("trans_fail_bounds_check");
395
396     // Extract the file/line from the span
397     let loc = bcx.sess().codemap().lookup_char_pos(call_info.span.lo);
398     let filename = token::intern_and_get_ident(&loc.file.name[]);
399
400     // Invoke the lang item
401     let filename = C_str_slice(ccx,  filename);
402     let line = C_uint(ccx, loc.line);
403     let file_line_const = C_struct(ccx, &[filename, line], false);
404     let file_line = consts::addr_of(ccx, file_line_const,
405                                     "panic_bounds_check_loc", call_info.id);
406     let args = vec!(file_line, index, len);
407     let did = langcall(bcx, Some(call_info.span), "", PanicBoundsCheckFnLangItem);
408     let bcx = callee::trans_lang_call(bcx,
409                                       did,
410                                       &args[..],
411                                       Some(expr::Ignore),
412                                       call_info.debug_loc()).bcx;
413     Unreachable(bcx);
414     return bcx;
415 }