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