]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/trans/controlflow.rs
auto merge of #13049 : alexcrichton/rust/io-fill, r=huonw
[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::session::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::common::*;
18 use middle::trans::debuginfo;
19 use middle::trans::cleanup;
20 use middle::trans::cleanup::CleanupMethods;
21 use middle::trans::expr;
22 use util::ppaux::Repr;
23
24 use middle::trans::type_::Type;
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()));
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 = expr::trans_into(cx, e, expr::Ignore);
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_block<'a>(bcx: &'a Block<'a>,
75                        b: &ast::Block,
76                        mut dest: expr::Dest)
77                        -> &'a Block<'a> {
78     let _icx = push_ctxt("trans_block");
79     let fcx = bcx.fcx;
80     let mut bcx = bcx;
81
82     fcx.push_ast_cleanup_scope(b.id);
83
84     for s in b.stmts.iter() {
85         bcx = trans_stmt(bcx, *s);
86     }
87
88     if dest != expr::Ignore {
89         let block_ty = node_id_type(bcx, b.id);
90         if b.expr.is_none() || type_is_zero_size(bcx.ccx(), block_ty) {
91             dest = expr::Ignore;
92         }
93     }
94
95     match b.expr {
96         Some(e) => {
97             bcx = expr::trans_into(bcx, e, dest);
98         }
99         None => {
100             assert!(dest == expr::Ignore || bcx.unreachable.get());
101         }
102     }
103
104     bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, b.id);
105
106     return bcx;
107 }
108
109 pub fn trans_if<'a>(bcx: &'a Block<'a>,
110                     if_id: ast::NodeId,
111                     cond: &ast::Expr,
112                     thn: ast::P<ast::Block>,
113                     els: Option<@ast::Expr>,
114                     dest: expr::Dest)
115                     -> &'a Block<'a> {
116     debug!("trans_if(bcx={}, if_id={}, cond={}, thn={:?}, dest={})",
117            bcx.to_str(), if_id, bcx.expr_to_str(cond), thn.id,
118            dest.to_str(bcx.ccx()));
119     let _icx = push_ctxt("trans_if");
120     let mut bcx = bcx;
121
122     let cond_val = unpack_result!(bcx, expr::trans(bcx, cond).to_llbool());
123
124     // Drop branches that are known to be impossible
125     if is_const(cond_val) && !is_undef(cond_val) {
126         if const_to_uint(cond_val) == 1 {
127             match els {
128                 Some(elexpr) => {
129                     let mut trans = TransItemVisitor { ccx: bcx.fcx.ccx };
130                     trans.visit_expr(elexpr, ());
131                 }
132                 None => {}
133             }
134             // if true { .. } [else { .. }]
135             bcx = trans_block(bcx, thn, dest);
136             debuginfo::clear_source_location(bcx.fcx);
137         } else {
138             let mut trans = TransItemVisitor { ccx: bcx.fcx.ccx } ;
139             trans.visit_block(thn, ());
140
141             match els {
142                 // if false { .. } else { .. }
143                 Some(elexpr) => {
144                     bcx = expr::trans_into(bcx, elexpr, dest);
145                     debuginfo::clear_source_location(bcx.fcx);
146                 }
147
148                 // if false { .. }
149                 None => { }
150             }
151         }
152
153         return bcx;
154     }
155
156     let name = format!("then-block-{}-", thn.id);
157     let then_bcx_in = bcx.fcx.new_id_block(name, thn.id);
158     let then_bcx_out = trans_block(then_bcx_in, thn, dest);
159     debuginfo::clear_source_location(bcx.fcx);
160
161     let next_bcx;
162     match els {
163         Some(elexpr) => {
164             let else_bcx_in = bcx.fcx.new_id_block("else-block", elexpr.id);
165             let else_bcx_out = expr::trans_into(else_bcx_in, elexpr, dest);
166             next_bcx = bcx.fcx.join_blocks(if_id,
167                                            [then_bcx_out, else_bcx_out]);
168             CondBr(bcx, cond_val, then_bcx_in.llbb, else_bcx_in.llbb);
169         }
170
171         None => {
172             next_bcx = bcx.fcx.new_id_block("next-block", if_id);
173             Br(then_bcx_out, next_bcx.llbb);
174             CondBr(bcx, cond_val, then_bcx_in.llbb, next_bcx.llbb);
175         }
176     }
177
178     // Clear the source location because it is still set to whatever has been translated
179     // right before.
180     debuginfo::clear_source_location(next_bcx.fcx);
181
182     next_bcx
183 }
184
185 pub fn trans_while<'a>(bcx: &'a Block<'a>,
186                        loop_id: ast::NodeId,
187                        cond: &ast::Expr,
188                        body: &ast::Block)
189                        -> &'a Block<'a> {
190     let _icx = push_ctxt("trans_while");
191     let fcx = bcx.fcx;
192
193     //            bcx
194     //             |
195     //         cond_bcx_in  <--------+
196     //             |                 |
197     //         cond_bcx_out          |
198     //           |      |            |
199     //           |    body_bcx_in    |
200     // cleanup_blk      |            |
201     //    |           body_bcx_out --+
202     // next_bcx_in
203
204     let next_bcx_in = fcx.new_id_block("while_exit", loop_id);
205     let cond_bcx_in = fcx.new_id_block("while_cond", cond.id);
206     let body_bcx_in = fcx.new_id_block("while_body", body.id);
207
208     fcx.push_loop_cleanup_scope(loop_id, [next_bcx_in, cond_bcx_in]);
209
210     Br(bcx, cond_bcx_in.llbb);
211
212     // compile the block where we will handle loop cleanups
213     let cleanup_llbb = fcx.normal_exit_block(loop_id, cleanup::EXIT_BREAK);
214
215     // compile the condition
216     let Result {bcx: cond_bcx_out, val: cond_val} =
217         expr::trans(cond_bcx_in, cond).to_llbool();
218     CondBr(cond_bcx_out, cond_val, body_bcx_in.llbb, cleanup_llbb);
219
220     // loop body:
221     let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
222     Br(body_bcx_out, cond_bcx_in.llbb);
223
224     fcx.pop_loop_cleanup_scope(loop_id);
225     return next_bcx_in;
226 }
227
228 pub fn trans_loop<'a>(bcx:&'a Block<'a>,
229                       loop_id: ast::NodeId,
230                       body: &ast::Block)
231                       -> &'a Block<'a> {
232     let _icx = push_ctxt("trans_loop");
233     let fcx = bcx.fcx;
234
235     //            bcx
236     //             |
237     //         body_bcx_in
238     //             |
239     //         body_bcx_out
240     //
241     // next_bcx
242     //
243     // Links between body_bcx_in and next_bcx are created by
244     // break statements.
245
246     let next_bcx_in = bcx.fcx.new_id_block("loop_exit", loop_id);
247     let body_bcx_in = bcx.fcx.new_id_block("loop_body", body.id);
248
249     fcx.push_loop_cleanup_scope(loop_id, [next_bcx_in, body_bcx_in]);
250
251     Br(bcx, body_bcx_in.llbb);
252     let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
253     Br(body_bcx_out, body_bcx_in.llbb);
254
255     fcx.pop_loop_cleanup_scope(loop_id);
256
257     return next_bcx_in;
258 }
259
260 pub fn trans_break_cont<'a>(bcx: &'a Block<'a>,
261                             expr_id: ast::NodeId,
262                             opt_label: Option<Ident>,
263                             exit: uint)
264                             -> &'a Block<'a> {
265     let _icx = push_ctxt("trans_break_cont");
266     let fcx = bcx.fcx;
267
268     if bcx.unreachable.get() {
269         return bcx;
270     }
271
272     // Locate loop that we will break to
273     let loop_id = match opt_label {
274         None => fcx.top_loop_scope(),
275         Some(_) => {
276             match bcx.tcx().def_map.borrow().find(&expr_id) {
277                 Some(&ast::DefLabel(loop_id)) => loop_id,
278                 ref r => {
279                     bcx.tcx().sess.bug(format!("{:?} in def-map for label", r))
280                 }
281             }
282         }
283     };
284
285     // Generate appropriate cleanup code and branch
286     let cleanup_llbb = fcx.normal_exit_block(loop_id, exit);
287     Br(bcx, cleanup_llbb);
288     Unreachable(bcx); // anything afterwards should be ignored
289     return bcx;
290 }
291
292 pub fn trans_break<'a>(bcx: &'a Block<'a>,
293                        expr_id: ast::NodeId,
294                        label_opt: Option<Ident>)
295                        -> &'a Block<'a> {
296     return trans_break_cont(bcx, expr_id, label_opt, cleanup::EXIT_BREAK);
297 }
298
299 pub fn trans_cont<'a>(bcx: &'a Block<'a>,
300                       expr_id: ast::NodeId,
301                       label_opt: Option<Ident>)
302                       -> &'a Block<'a> {
303     return trans_break_cont(bcx, expr_id, label_opt, cleanup::EXIT_LOOP);
304 }
305
306 pub fn trans_ret<'a>(bcx: &'a Block<'a>,
307                      e: Option<@ast::Expr>)
308                      -> &'a Block<'a> {
309     let _icx = push_ctxt("trans_ret");
310     let fcx = bcx.fcx;
311     let mut bcx = bcx;
312     let dest = match bcx.fcx.llretptr.get() {
313         None => expr::Ignore,
314         Some(retptr) => expr::SaveIn(retptr),
315     };
316     match e {
317         Some(x) => {
318             bcx = expr::trans_into(bcx, x, dest);
319         }
320         _ => {}
321     }
322     let cleanup_llbb = fcx.return_exit_block();
323     Br(bcx, cleanup_llbb);
324     Unreachable(bcx);
325     return bcx;
326 }
327
328 pub fn trans_fail<'a>(
329                   bcx: &'a Block<'a>,
330                   sp: Span,
331                   fail_str: InternedString)
332                   -> &'a Block<'a> {
333     let ccx = bcx.ccx();
334     let v_fail_str = C_cstr(ccx, fail_str);
335     let _icx = push_ctxt("trans_fail_value");
336     let loc = bcx.sess().codemap().lookup_char_pos(sp.lo);
337     let v_filename = C_cstr(ccx, token::intern_and_get_ident(loc.file.name));
338     let v_line = loc.line as int;
339     let v_str = PointerCast(bcx, v_fail_str, Type::i8p(ccx));
340     let v_filename = PointerCast(bcx, v_filename, Type::i8p(ccx));
341     let args = vec!(v_str, v_filename, C_int(ccx, v_line));
342     let did = langcall(bcx, Some(sp), "", FailFnLangItem);
343     let bcx = callee::trans_lang_call(bcx,
344                                       did,
345                                       args.as_slice(),
346                                       Some(expr::Ignore)).bcx;
347     Unreachable(bcx);
348     return bcx;
349 }
350
351 pub fn trans_fail_bounds_check<'a>(
352                                bcx: &'a Block<'a>,
353                                sp: Span,
354                                index: ValueRef,
355                                len: ValueRef)
356                                -> &'a Block<'a> {
357     let _icx = push_ctxt("trans_fail_bounds_check");
358     let (filename, line) = filename_and_line_num_from_span(bcx, sp);
359     let args = vec!(filename, line, index, len);
360     let did = langcall(bcx, Some(sp), "", FailBoundsCheckFnLangItem);
361     let bcx = callee::trans_lang_call(bcx,
362                                       did,
363                                       args.as_slice(),
364                                       Some(expr::Ignore)).bcx;
365     Unreachable(bcx);
366     return bcx;
367 }