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