]> git.lizzy.rs Git - rust.git/blob - src/librustc_trans/trans/controlflow.rs
Auto merge of #23011 - nagisa:the-war-of-symbol-and-symbol, r=pnkfelix
[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             bcx = expr::trans_into(bcx, &**e, dest);
135         }
136         None => {
137             assert!(dest == expr::Ignore || bcx.unreachable.get());
138         }
139     }
140
141     bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, b.id);
142
143     return bcx;
144 }
145
146 pub fn trans_if<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
147                             if_id: ast::NodeId,
148                             cond: &ast::Expr,
149                             thn: &ast::Block,
150                             els: Option<&ast::Expr>,
151                             dest: expr::Dest)
152                             -> Block<'blk, 'tcx> {
153     debug!("trans_if(bcx={}, if_id={}, cond={}, thn={}, dest={})",
154            bcx.to_str(), if_id, bcx.expr_to_string(cond), thn.id,
155            dest.to_string(bcx.ccx()));
156     let _icx = push_ctxt("trans_if");
157
158     if bcx.unreachable.get() {
159         return bcx;
160     }
161
162     let mut bcx = bcx;
163
164     let cond_val = unpack_result!(bcx, expr::trans(bcx, cond).to_llbool());
165
166     // Drop branches that are known to be impossible
167     if is_const(cond_val) && !is_undef(cond_val) {
168         if const_to_uint(cond_val) == 1 {
169             match els {
170                 Some(elexpr) => {
171                     let mut trans = TransItemVisitor { ccx: bcx.fcx.ccx };
172                     trans.visit_expr(&*elexpr);
173                 }
174                 None => {}
175             }
176             // if true { .. } [else { .. }]
177             bcx = trans_block(bcx, &*thn, dest);
178             trans::debuginfo::clear_source_location(bcx.fcx);
179         } else {
180             let mut trans = TransItemVisitor { ccx: bcx.fcx.ccx } ;
181             trans.visit_block(&*thn);
182
183             match els {
184                 // if false { .. } else { .. }
185                 Some(elexpr) => {
186                     bcx = expr::trans_into(bcx, &*elexpr, dest);
187                     trans::debuginfo::clear_source_location(bcx.fcx);
188                 }
189
190                 // if false { .. }
191                 None => { }
192             }
193         }
194
195         return bcx;
196     }
197
198     let name = format!("then-block-{}-", thn.id);
199     let then_bcx_in = bcx.fcx.new_id_block(&name[..], thn.id);
200     let then_bcx_out = trans_block(then_bcx_in, &*thn, dest);
201     trans::debuginfo::clear_source_location(bcx.fcx);
202
203     let cond_source_loc = cond.debug_loc();
204
205     let next_bcx;
206     match els {
207         Some(elexpr) => {
208             let else_bcx_in = bcx.fcx.new_id_block("else-block", elexpr.id);
209             let else_bcx_out = expr::trans_into(else_bcx_in, &*elexpr, dest);
210             next_bcx = bcx.fcx.join_blocks(if_id,
211                                            &[then_bcx_out, else_bcx_out]);
212             CondBr(bcx, cond_val, then_bcx_in.llbb, else_bcx_in.llbb, cond_source_loc);
213         }
214
215         None => {
216             next_bcx = bcx.fcx.new_id_block("next-block", if_id);
217             Br(then_bcx_out, next_bcx.llbb, DebugLoc::None);
218             CondBr(bcx, cond_val, then_bcx_in.llbb, next_bcx.llbb, cond_source_loc);
219         }
220     }
221
222     // Clear the source location because it is still set to whatever has been translated
223     // right before.
224     trans::debuginfo::clear_source_location(next_bcx.fcx);
225
226     next_bcx
227 }
228
229 pub fn trans_while<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
230                                loop_expr: &ast::Expr,
231                                cond: &ast::Expr,
232                                body: &ast::Block)
233                                -> Block<'blk, 'tcx> {
234     let _icx = push_ctxt("trans_while");
235
236     if bcx.unreachable.get() {
237         return bcx;
238     }
239
240     let fcx = bcx.fcx;
241
242     //            bcx
243     //             |
244     //         cond_bcx_in  <--------+
245     //             |                 |
246     //         cond_bcx_out          |
247     //           |      |            |
248     //           |    body_bcx_in    |
249     // cleanup_blk      |            |
250     //    |           body_bcx_out --+
251     // next_bcx_in
252
253     let next_bcx_in = fcx.new_id_block("while_exit", loop_expr.id);
254     let cond_bcx_in = fcx.new_id_block("while_cond", cond.id);
255     let body_bcx_in = fcx.new_id_block("while_body", body.id);
256
257     fcx.push_loop_cleanup_scope(loop_expr.id, [next_bcx_in, cond_bcx_in]);
258
259     Br(bcx, cond_bcx_in.llbb, loop_expr.debug_loc());
260
261     // compile the block where we will handle loop cleanups
262     let cleanup_llbb = fcx.normal_exit_block(loop_expr.id, cleanup::EXIT_BREAK);
263
264     // compile the condition
265     let Result {bcx: cond_bcx_out, val: cond_val} =
266         expr::trans(cond_bcx_in, cond).to_llbool();
267
268     CondBr(cond_bcx_out, cond_val, body_bcx_in.llbb, cleanup_llbb, cond.debug_loc());
269
270     // loop body:
271     let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
272     Br(body_bcx_out, cond_bcx_in.llbb, DebugLoc::None);
273
274     fcx.pop_loop_cleanup_scope(loop_expr.id);
275     return next_bcx_in;
276 }
277
278 pub fn trans_loop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
279                               loop_expr: &ast::Expr,
280                               body: &ast::Block)
281                               -> Block<'blk, 'tcx> {
282     let _icx = push_ctxt("trans_loop");
283
284     if bcx.unreachable.get() {
285         return bcx;
286     }
287
288     let fcx = bcx.fcx;
289
290     //            bcx
291     //             |
292     //         body_bcx_in
293     //             |
294     //         body_bcx_out
295     //
296     // next_bcx
297     //
298     // Links between body_bcx_in and next_bcx are created by
299     // break statements.
300
301     let next_bcx_in = bcx.fcx.new_id_block("loop_exit", loop_expr.id);
302     let body_bcx_in = bcx.fcx.new_id_block("loop_body", body.id);
303
304     fcx.push_loop_cleanup_scope(loop_expr.id, [next_bcx_in, body_bcx_in]);
305
306     Br(bcx, body_bcx_in.llbb, loop_expr.debug_loc());
307     let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
308     Br(body_bcx_out, body_bcx_in.llbb, DebugLoc::None);
309
310     fcx.pop_loop_cleanup_scope(loop_expr.id);
311
312     // If there are no predecessors for the next block, we just translated an endless loop and the
313     // next block is unreachable
314     if BasicBlock(next_bcx_in.llbb).pred_iter().next().is_none() {
315         Unreachable(next_bcx_in);
316     }
317
318     return next_bcx_in;
319 }
320
321 pub fn trans_break_cont<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
322                                     expr: &ast::Expr,
323                                     opt_label: Option<ast::Ident>,
324                                     exit: usize)
325                                     -> Block<'blk, 'tcx> {
326     let _icx = push_ctxt("trans_break_cont");
327
328     if bcx.unreachable.get() {
329         return bcx;
330     }
331
332     let fcx = bcx.fcx;
333
334     // Locate loop that we will break to
335     let loop_id = match opt_label {
336         None => fcx.top_loop_scope(),
337         Some(_) => {
338             match bcx.tcx().def_map.borrow().get(&expr.id).map(|d| d.full_def())  {
339                 Some(def::DefLabel(loop_id)) => loop_id,
340                 r => {
341                     bcx.tcx().sess.bug(&format!("{:?} in def-map for label", r))
342                 }
343             }
344         }
345     };
346
347     // Generate appropriate cleanup code and branch
348     let cleanup_llbb = fcx.normal_exit_block(loop_id, exit);
349     Br(bcx, cleanup_llbb, expr.debug_loc());
350     Unreachable(bcx); // anything afterwards should be ignored
351     return bcx;
352 }
353
354 pub fn trans_break<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
355                                expr: &ast::Expr,
356                                label_opt: Option<ast::Ident>)
357                                -> Block<'blk, 'tcx> {
358     return trans_break_cont(bcx, expr, label_opt, cleanup::EXIT_BREAK);
359 }
360
361 pub fn trans_cont<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
362                               expr: &ast::Expr,
363                               label_opt: Option<ast::Ident>)
364                               -> Block<'blk, 'tcx> {
365     return trans_break_cont(bcx, expr, label_opt, cleanup::EXIT_LOOP);
366 }
367
368 pub fn trans_ret<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
369                              return_expr: &ast::Expr,
370                              retval_expr: Option<&ast::Expr>)
371                              -> Block<'blk, 'tcx> {
372     let _icx = push_ctxt("trans_ret");
373
374     if bcx.unreachable.get() {
375         return bcx;
376     }
377
378     let fcx = bcx.fcx;
379     let mut bcx = bcx;
380     let dest = match (fcx.llretslotptr.get(), retval_expr) {
381         (Some(_), Some(retval_expr)) => {
382             let ret_ty = expr_ty_adjusted(bcx, &*retval_expr);
383             expr::SaveIn(fcx.get_ret_slot(bcx, ty::FnConverging(ret_ty), "ret_slot"))
384         }
385         _ => expr::Ignore,
386     };
387     if let Some(x) = retval_expr {
388         bcx = expr::trans_into(bcx, &*x, dest);
389         match dest {
390             expr::SaveIn(slot) if fcx.needs_ret_allocas => {
391                 Store(bcx, slot, fcx.llretslotptr.get().unwrap());
392             }
393             _ => {}
394         }
395     }
396     let cleanup_llbb = fcx.return_exit_block();
397     Br(bcx, cleanup_llbb, return_expr.debug_loc());
398     Unreachable(bcx);
399     return bcx;
400 }
401
402 pub fn trans_fail<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
403                               call_info: NodeIdAndSpan,
404                               fail_str: InternedString)
405                               -> Block<'blk, 'tcx> {
406     let ccx = bcx.ccx();
407     let _icx = push_ctxt("trans_fail_value");
408
409     if bcx.unreachable.get() {
410         return bcx;
411     }
412
413     let v_str = C_str_slice(ccx, fail_str);
414     let loc = bcx.sess().codemap().lookup_char_pos(call_info.span.lo);
415     let filename = token::intern_and_get_ident(&loc.file.name);
416     let filename = C_str_slice(ccx, filename);
417     let line = C_u32(ccx, loc.line as u32);
418     let expr_file_line_const = C_struct(ccx, &[v_str, filename, line], false);
419     let expr_file_line = consts::addr_of(ccx, expr_file_line_const, "panic_loc");
420     let args = vec!(expr_file_line);
421     let did = langcall(bcx, Some(call_info.span), "", PanicFnLangItem);
422     let bcx = callee::trans_lang_call(bcx,
423                                       did,
424                                       &args[..],
425                                       Some(expr::Ignore),
426                                       call_info.debug_loc()).bcx;
427     Unreachable(bcx);
428     return bcx;
429 }
430
431 pub fn trans_fail_bounds_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
432                                            call_info: NodeIdAndSpan,
433                                            index: ValueRef,
434                                            len: ValueRef)
435                                            -> Block<'blk, 'tcx> {
436     let ccx = bcx.ccx();
437     let _icx = push_ctxt("trans_fail_bounds_check");
438
439     if bcx.unreachable.get() {
440         return bcx;
441     }
442
443     // Extract the file/line from the span
444     let loc = bcx.sess().codemap().lookup_char_pos(call_info.span.lo);
445     let filename = token::intern_and_get_ident(&loc.file.name);
446
447     // Invoke the lang item
448     let filename = C_str_slice(ccx,  filename);
449     let line = C_u32(ccx, loc.line as u32);
450     let file_line_const = C_struct(ccx, &[filename, line], false);
451     let file_line = consts::addr_of(ccx, file_line_const, "panic_bounds_check_loc");
452     let args = vec!(file_line, index, len);
453     let did = langcall(bcx, Some(call_info.span), "", PanicBoundsCheckFnLangItem);
454     let bcx = callee::trans_lang_call(bcx,
455                                       did,
456                                       &args[..],
457                                       Some(expr::Ignore),
458                                       call_info.debug_loc()).bcx;
459     Unreachable(bcx);
460     return bcx;
461 }