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