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