]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/trans/controlflow.rs
2a3003812fe07383533862c4d40ac36d8c9ddff4
[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 std::c_str::ToCStr;
12
13 use back::link;
14 use lib;
15 use lib::llvm::*;
16 use middle::lang_items::{FailFnLangItem, FailBoundsCheckFnLangItem};
17 use middle::lang_items::LogTypeFnLangItem;
18 use middle::trans::base::*;
19 use middle::trans::build::*;
20 use middle::trans::callee;
21 use middle::trans::common::*;
22 use middle::trans::expr;
23 use middle::trans::type_of::*;
24 use middle::ty;
25 use util::common::indenter;
26 use util::ppaux;
27
28 use middle::trans::type_::Type;
29
30 use syntax::ast;
31 use syntax::ast::ident;
32 use syntax::ast_map::path_mod;
33 use syntax::ast_util;
34 use syntax::codemap::span;
35
36 pub fn trans_block(bcx: @mut Block, b: &ast::Block, dest: expr::Dest) -> @mut Block {
37     let _icx = push_ctxt("trans_block");
38     let mut bcx = bcx;
39     for s in b.stmts.iter() {
40         bcx = trans_stmt(bcx, *s);
41     }
42     match b.expr {
43         Some(e) => {
44             bcx = expr::trans_into(bcx, e, dest);
45         }
46         None => {
47             assert!(dest == expr::Ignore || bcx.unreachable);
48         }
49     }
50     return bcx;
51 }
52
53 pub fn trans_if(bcx: @mut Block,
54             cond: @ast::expr,
55             thn: &ast::Block,
56             els: Option<@ast::expr>,
57             dest: expr::Dest)
58          -> @mut Block {
59     debug!("trans_if(bcx=%s, cond=%s, thn=%?, dest=%s)",
60            bcx.to_str(), bcx.expr_to_str(cond), thn.id,
61            dest.to_str(bcx.ccx()));
62     let _indenter = indenter();
63
64     let _icx = push_ctxt("trans_if");
65
66     let Result {bcx, val: cond_val} =
67         expr::trans_to_datum(bcx, cond).to_result();
68
69     let cond_val = bool_to_i1(bcx, cond_val);
70
71     // Drop branches that are known to be impossible
72     if is_const(cond_val) && !is_undef(cond_val) {
73         if const_to_uint(cond_val) == 1 {
74             // if true { .. } [else { .. }]
75             return do with_scope(bcx, thn.info(), "if_true_then") |bcx| {
76                 let bcx_out = trans_block(bcx, thn, dest);
77                 trans_block_cleanups(bcx_out, block_cleanups(bcx))
78             }
79         } else {
80             match els {
81                 // if false { .. } else { .. }
82                 Some(elexpr) => {
83                     return do with_scope(bcx, elexpr.info(), "if_false_then") |bcx| {
84                         let bcx_out = trans_if_else(bcx, elexpr, dest);
85                         trans_block_cleanups(bcx_out, block_cleanups(bcx))
86                     }
87                 }
88                 // if false { .. }
89                 None => return bcx,
90             }
91         }
92     }
93
94     let then_bcx_in = scope_block(bcx, thn.info(), "then");
95
96     let then_bcx_out = trans_block(then_bcx_in, thn, dest);
97     let then_bcx_out = trans_block_cleanups(then_bcx_out,
98                                             block_cleanups(then_bcx_in));
99
100     // Calling trans_block directly instead of trans_expr
101     // because trans_expr will create another scope block
102     // context for the block, but we've already got the
103     // 'else' context
104     let (else_bcx_in, next_bcx) = match els {
105       Some(elexpr) => {
106           let else_bcx_in = scope_block(bcx, elexpr.info(), "else");
107           let else_bcx_out = trans_if_else(else_bcx_in, elexpr, dest);
108           (else_bcx_in, join_blocks(bcx, [then_bcx_out, else_bcx_out]))
109       }
110       _ => {
111           let next_bcx = sub_block(bcx, "next");
112           Br(then_bcx_out, next_bcx.llbb);
113
114           (next_bcx, next_bcx)
115       }
116     };
117
118     debug!("then_bcx_in=%s, else_bcx_in=%s",
119            then_bcx_in.to_str(), else_bcx_in.to_str());
120
121     CondBr(bcx, cond_val, then_bcx_in.llbb, else_bcx_in.llbb);
122     return next_bcx;
123
124     // trans `else [ if { .. } ... | { .. } ]`
125     fn trans_if_else(else_bcx_in: @mut Block, elexpr: @ast::expr,
126                      dest: expr::Dest) -> @mut Block {
127         let else_bcx_out = match elexpr.node {
128             ast::expr_if(_, _, _) => {
129                 let elseif_blk = ast_util::block_from_expr(elexpr);
130                 trans_block(else_bcx_in, &elseif_blk, dest)
131             }
132             ast::expr_block(ref blk) => {
133                 trans_block(else_bcx_in, blk, dest)
134             }
135             // would be nice to have a constraint on ifs
136             _ => else_bcx_in.tcx().sess.bug("strange alternative in if")
137         };
138         trans_block_cleanups(else_bcx_out, block_cleanups(else_bcx_in))
139     }
140 }
141
142 pub fn join_blocks(parent_bcx: @mut Block, in_cxs: &[@mut Block]) -> @mut Block {
143     let out = sub_block(parent_bcx, "join");
144     let mut reachable = false;
145     for bcx in in_cxs.iter() {
146         if !bcx.unreachable {
147             Br(*bcx, out.llbb);
148             reachable = true;
149         }
150     }
151     if !reachable {
152         Unreachable(out);
153     }
154     return out;
155 }
156
157 pub fn trans_while(bcx: @mut Block, cond: @ast::expr, body: &ast::Block) -> @mut Block {
158     let _icx = push_ctxt("trans_while");
159     let next_bcx = sub_block(bcx, "while next");
160
161     //            bcx
162     //             |
163     //          loop_bcx
164     //             |
165     //         cond_bcx_in  <--------+
166     //             |                 |
167     //         cond_bcx_out          |
168     //           |      |            |
169     //           |    body_bcx_in    |
170     //    +------+      |            |
171     //    |           body_bcx_out --+
172     // next_bcx
173
174     let loop_bcx = loop_scope_block(bcx, next_bcx, None, "`while`",
175                                     body.info());
176     let cond_bcx_in = scope_block(loop_bcx, cond.info(), "while loop cond");
177     let body_bcx_in = scope_block(loop_bcx, body.info(), "while loop body");
178     Br(bcx, loop_bcx.llbb);
179     Br(loop_bcx, cond_bcx_in.llbb);
180
181     // compile the condition
182     let Result {bcx: cond_bcx_out, val: cond_val} =
183         expr::trans_to_datum(cond_bcx_in, cond).to_result();
184     let cond_val = bool_to_i1(cond_bcx_out, cond_val);
185     let cond_bcx_out =
186         trans_block_cleanups(cond_bcx_out, block_cleanups(cond_bcx_in));
187     CondBr(cond_bcx_out, cond_val, body_bcx_in.llbb, next_bcx.llbb);
188
189     // loop body:
190     let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
191     cleanup_and_Br(body_bcx_out, body_bcx_in, cond_bcx_in.llbb);
192
193     return next_bcx;
194 }
195
196 pub fn trans_loop(bcx:@mut Block,
197                   body: &ast::Block,
198                   opt_label: Option<ident>)
199                -> @mut Block {
200     let _icx = push_ctxt("trans_loop");
201     let next_bcx = sub_block(bcx, "next");
202     let body_bcx_in = loop_scope_block(bcx, next_bcx, opt_label, "`loop`",
203                                        body.info());
204     Br(bcx, body_bcx_in.llbb);
205     let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
206     cleanup_and_Br(body_bcx_out, body_bcx_in, body_bcx_in.llbb);
207     return next_bcx;
208 }
209
210 pub fn trans_log(log_ex: &ast::expr,
211                  lvl: @ast::expr,
212                  bcx: @mut Block,
213                  e: @ast::expr) -> @mut Block {
214     let _icx = push_ctxt("trans_log");
215     let ccx = bcx.ccx();
216     let mut bcx = bcx;
217     if ty::type_is_bot(expr_ty(bcx, lvl)) {
218        return expr::trans_into(bcx, lvl, expr::Ignore);
219     }
220
221     let (modpath, modname) = {
222         let path = &mut bcx.fcx.path;
223         let mut modpath = ~[path_mod(ccx.sess.ident_of(ccx.link_meta.name))];
224         for e in path.iter() {
225             match *e {
226                 path_mod(_) => { modpath.push(*e) }
227                 _ => {}
228             }
229         }
230         let modname = path_str(ccx.sess, modpath);
231         (modpath, modname)
232     };
233
234     let global = if ccx.module_data.contains_key(&modname) {
235         ccx.module_data.get_copy(&modname)
236     } else {
237         let s = link::mangle_internal_name_by_path_and_seq(
238             ccx, modpath, "loglevel");
239         let global;
240         unsafe {
241             global = do s.to_c_str().with_ref |buf| {
242                 llvm::LLVMAddGlobal(ccx.llmod, Type::i32().to_ref(), buf)
243             };
244             llvm::LLVMSetGlobalConstant(global, False);
245             llvm::LLVMSetInitializer(global, C_null(Type::i32()));
246             lib::llvm::SetLinkage(global, lib::llvm::InternalLinkage);
247         }
248         ccx.module_data.insert(modname, global);
249         global
250     };
251     let current_level = Load(bcx, global);
252     let level = unpack_result!(bcx, {
253         do with_scope_result(bcx, lvl.info(), "level") |bcx| {
254             expr::trans_to_datum(bcx, lvl).to_result()
255         }
256     });
257
258     let llenabled = ICmp(bcx, lib::llvm::IntUGE, current_level, level);
259     do with_cond(bcx, llenabled) |bcx| {
260         do with_scope(bcx, log_ex.info(), "log") |bcx| {
261             let mut bcx = bcx;
262
263             // Translate the value to be logged
264             let val_datum = unpack_datum!(bcx, expr::trans_to_datum(bcx, e));
265
266             // Call the polymorphic log function
267             let val = val_datum.to_ref_llval(bcx);
268             let did = langcall(bcx, Some(e.span), "", LogTypeFnLangItem);
269             let bcx = callee::trans_lang_call_with_type_params(
270                 bcx, did, [level, val], [val_datum.ty], expr::Ignore);
271             bcx
272         }
273     }
274 }
275
276 pub fn trans_break_cont(bcx: @mut Block,
277                         opt_label: Option<ident>,
278                         to_end: bool)
279                      -> @mut Block {
280     let _icx = push_ctxt("trans_break_cont");
281     // Locate closest loop block, outputting cleanup as we go.
282     let mut unwind = bcx;
283     let mut cur_scope = unwind.scope;
284     let mut target;
285     loop {
286         cur_scope = match cur_scope {
287             Some(@ScopeInfo {
288                 loop_break: Some(brk),
289                 loop_label: l,
290                 parent,
291                 _
292             }) => {
293                 // If we're looking for a labeled loop, check the label...
294                 target = if to_end {
295                     brk
296                 } else {
297                     unwind
298                 };
299                 match opt_label {
300                     Some(desired) => match l {
301                         Some(actual) if actual == desired => break,
302                         // If it doesn't match the one we want,
303                         // don't break
304                         _ => parent,
305                     },
306                     None => break,
307                 }
308             }
309             Some(inf) => inf.parent,
310             None => {
311                 unwind = match unwind.parent {
312                     Some(bcx) => bcx,
313                         // This is a return from a loop body block
314                         None => {
315                             Store(bcx, C_bool(!to_end), bcx.fcx.llretptr.unwrap());
316                             cleanup_and_leave(bcx, None, Some(bcx.fcx.get_llreturn()));
317                             Unreachable(bcx);
318                             return bcx;
319                         }
320                 };
321                 unwind.scope
322             }
323         }
324     }
325     cleanup_and_Br(bcx, unwind, target.llbb);
326     Unreachable(bcx);
327     return bcx;
328 }
329
330 pub fn trans_break(bcx: @mut Block, label_opt: Option<ident>) -> @mut Block {
331     return trans_break_cont(bcx, label_opt, true);
332 }
333
334 pub fn trans_cont(bcx: @mut Block, label_opt: Option<ident>) -> @mut Block {
335     return trans_break_cont(bcx, label_opt, false);
336 }
337
338 pub fn trans_ret(bcx: @mut Block, e: Option<@ast::expr>) -> @mut Block {
339     let _icx = push_ctxt("trans_ret");
340     let mut bcx = bcx;
341     let dest = match bcx.fcx.loop_ret {
342       Some((flagptr, retptr)) => {
343         // This is a loop body return. Must set continue flag (our retptr)
344         // to false, return flag to true, and then store the value in the
345         // parent's retptr.
346         Store(bcx, C_bool(true), flagptr);
347         Store(bcx, C_bool(false), bcx.fcx.llretptr.unwrap());
348         expr::SaveIn(match e {
349           Some(x) => PointerCast(bcx, retptr,
350                                  type_of(bcx.ccx(), expr_ty(bcx, x)).ptr_to()),
351           None => retptr
352         })
353       }
354       None => match bcx.fcx.llretptr {
355         None => expr::Ignore,
356         Some(retptr) => expr::SaveIn(retptr),
357       }
358     };
359     match e {
360       Some(x) => {
361         bcx = expr::trans_into(bcx, x, dest);
362       }
363       _ => ()
364     }
365     cleanup_and_leave(bcx, None, Some(bcx.fcx.get_llreturn()));
366     Unreachable(bcx);
367     return bcx;
368 }
369
370 pub fn trans_fail_expr(bcx: @mut Block,
371                        sp_opt: Option<span>,
372                        fail_expr: Option<@ast::expr>)
373                     -> @mut Block {
374     let _icx = push_ctxt("trans_fail_expr");
375     let mut bcx = bcx;
376     match fail_expr {
377         Some(arg_expr) => {
378             let ccx = bcx.ccx();
379             let tcx = ccx.tcx;
380             let arg_datum = unpack_datum!(
381                 bcx, expr::trans_to_datum(bcx, arg_expr));
382
383             if ty::type_is_str(arg_datum.ty) {
384                 let (lldata, _) = arg_datum.get_vec_base_and_len_no_root(bcx);
385                 return trans_fail_value(bcx, sp_opt, lldata);
386             } else if bcx.unreachable || ty::type_is_bot(arg_datum.ty) {
387                 return bcx;
388             } else {
389                 bcx.sess().span_bug(
390                     arg_expr.span, ~"fail called with unsupported type " +
391                     ppaux::ty_to_str(tcx, arg_datum.ty));
392             }
393         }
394         _ => trans_fail(bcx, sp_opt, @"explicit failure")
395     }
396 }
397
398 pub fn trans_fail(bcx: @mut Block,
399                   sp_opt: Option<span>,
400                   fail_str: @str)
401                -> @mut Block {
402     let _icx = push_ctxt("trans_fail");
403     let V_fail_str = C_cstr(bcx.ccx(), fail_str);
404     return trans_fail_value(bcx, sp_opt, V_fail_str);
405 }
406
407 fn trans_fail_value(bcx: @mut Block,
408                     sp_opt: Option<span>,
409                     V_fail_str: ValueRef)
410                  -> @mut Block {
411     let _icx = push_ctxt("trans_fail_value");
412     let ccx = bcx.ccx();
413     let (V_filename, V_line) = match sp_opt {
414       Some(sp) => {
415         let sess = bcx.sess();
416         let loc = sess.parse_sess.cm.lookup_char_pos(sp.lo);
417         (C_cstr(bcx.ccx(), loc.file.name),
418          loc.line as int)
419       }
420       None => {
421         (C_cstr(bcx.ccx(), @"<runtime>"), 0)
422       }
423     };
424     let V_str = PointerCast(bcx, V_fail_str, Type::i8p());
425     let V_filename = PointerCast(bcx, V_filename, Type::i8p());
426     let args = ~[V_str, V_filename, C_int(ccx, V_line)];
427     let did = langcall(bcx, sp_opt, "", FailFnLangItem);
428     let bcx = callee::trans_lang_call(bcx, did, args, Some(expr::Ignore)).bcx;
429     Unreachable(bcx);
430     return bcx;
431 }
432
433 pub fn trans_fail_bounds_check(bcx: @mut Block, sp: span,
434                                index: ValueRef, len: ValueRef) -> @mut Block {
435     let _icx = push_ctxt("trans_fail_bounds_check");
436     let (filename, line) = filename_and_line_num_from_span(bcx, sp);
437     let args = ~[filename, line, index, len];
438     let did = langcall(bcx, Some(sp), "", FailBoundsCheckFnLangItem);
439     let bcx = callee::trans_lang_call(bcx, did, args, Some(expr::Ignore)).bcx;
440     Unreachable(bcx);
441     return bcx;
442 }