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