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