]> git.proxmox.com Git - rustc.git/blob - src/librustc_trans/trans/controlflow.rs
Imported Upstream version 1.6.0+dfsg1
[rustc.git] / src / librustc_trans / 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::ValueRef;
12 use middle::def;
13 use middle::lang_items::{PanicFnLangItem, PanicBoundsCheckFnLangItem};
14 use trans::base::*;
15 use trans::basic_block::BasicBlock;
16 use trans::build::*;
17 use trans::callee;
18 use trans::cleanup::CleanupMethods;
19 use trans::cleanup;
20 use trans::common::*;
21 use trans::consts;
22 use trans::debuginfo;
23 use trans::debuginfo::{DebugLoc, ToDebugLoc};
24 use trans::expr;
25 use trans::machine;
26 use trans;
27 use middle::ty;
28
29 use rustc_front::hir;
30 use rustc_front::util as ast_util;
31
32 use syntax::ast;
33 use syntax::parse::token::InternedString;
34 use syntax::parse::token;
35
36 pub fn trans_stmt<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
37 s: &hir::Stmt)
38 -> Block<'blk, 'tcx> {
39 let _icx = push_ctxt("trans_stmt");
40 let fcx = cx.fcx;
41 debug!("trans_stmt({:?})", s);
42
43 if cx.unreachable.get() {
44 return cx;
45 }
46
47 if cx.sess().asm_comments() {
48 add_span_comment(cx, s.span, &format!("{:?}", s));
49 }
50
51 let mut bcx = cx;
52
53 let id = ast_util::stmt_id(s);
54 let cleanup_debug_loc =
55 debuginfo::get_cleanup_debug_loc_for_ast_node(bcx.ccx(), id, s.span, false);
56 fcx.push_ast_cleanup_scope(cleanup_debug_loc);
57
58 match s.node {
59 hir::StmtExpr(ref e, _) | hir::StmtSemi(ref e, _) => {
60 bcx = trans_stmt_semi(bcx, &**e);
61 }
62 hir::StmtDecl(ref d, _) => {
63 match d.node {
64 hir::DeclLocal(ref local) => {
65 bcx = init_local(bcx, &**local);
66 debuginfo::create_local_var_metadata(bcx, &**local);
67 }
68 // Inner items are visited by `trans_item`/`trans_meth`.
69 hir::DeclItem(_) => {},
70 }
71 }
72 }
73
74 bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, ast_util::stmt_id(s));
75
76 return bcx;
77 }
78
79 pub fn trans_stmt_semi<'blk, 'tcx>(cx: Block<'blk, 'tcx>, e: &hir::Expr)
80 -> Block<'blk, 'tcx> {
81 let _icx = push_ctxt("trans_stmt_semi");
82
83 if cx.unreachable.get() {
84 return cx;
85 }
86
87 let ty = expr_ty(cx, e);
88 if cx.fcx.type_needs_drop(ty) {
89 expr::trans_to_lvalue(cx, e, "stmt").bcx
90 } else {
91 expr::trans_into(cx, e, expr::Ignore)
92 }
93 }
94
95 pub fn trans_block<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
96 b: &hir::Block,
97 mut dest: expr::Dest)
98 -> Block<'blk, 'tcx> {
99 let _icx = push_ctxt("trans_block");
100
101 if bcx.unreachable.get() {
102 return bcx;
103 }
104
105 let fcx = bcx.fcx;
106 let mut bcx = bcx;
107
108 let cleanup_debug_loc =
109 debuginfo::get_cleanup_debug_loc_for_ast_node(bcx.ccx(), b.id, b.span, true);
110 fcx.push_ast_cleanup_scope(cleanup_debug_loc);
111
112 for s in &b.stmts {
113 bcx = trans_stmt(bcx, s);
114 }
115
116 if dest != expr::Ignore {
117 let block_ty = node_id_type(bcx, b.id);
118
119 if b.expr.is_none() || type_is_zero_size(bcx.ccx(), block_ty) {
120 dest = expr::Ignore;
121 } else if b.expr.is_some() {
122 // If the block has an expression, but that expression isn't reachable,
123 // don't save into the destination given, ignore it.
124 if let Some(ref cfg) = bcx.fcx.cfg {
125 if !cfg.node_is_reachable(b.expr.as_ref().unwrap().id) {
126 dest = expr::Ignore;
127 }
128 }
129 }
130 }
131
132 match b.expr {
133 Some(ref e) => {
134 if !bcx.unreachable.get() {
135 bcx = expr::trans_into(bcx, &**e, dest);
136 }
137 }
138 None => {
139 assert!(dest == expr::Ignore || bcx.unreachable.get());
140 }
141 }
142
143 bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, b.id);
144
145 return bcx;
146 }
147
148 pub fn trans_if<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
149 if_id: ast::NodeId,
150 cond: &hir::Expr,
151 thn: &hir::Block,
152 els: Option<&hir::Expr>,
153 dest: expr::Dest)
154 -> Block<'blk, 'tcx> {
155 debug!("trans_if(bcx={}, if_id={}, cond={:?}, thn={}, dest={})",
156 bcx.to_str(), if_id, cond, thn.id,
157 dest.to_string(bcx.ccx()));
158 let _icx = push_ctxt("trans_if");
159
160 if bcx.unreachable.get() {
161 return bcx;
162 }
163
164 let mut bcx = bcx;
165
166 let cond_val = unpack_result!(bcx, expr::trans(bcx, cond).to_llbool());
167
168 // Drop branches that are known to be impossible
169 if let Some(cv) = const_to_opt_uint(cond_val) {
170 if cv == 1 {
171 // if true { .. } [else { .. }]
172 bcx = trans_block(bcx, &*thn, dest);
173 trans::debuginfo::clear_source_location(bcx.fcx);
174 } else {
175 if let Some(elexpr) = els {
176 bcx = expr::trans_into(bcx, &*elexpr, dest);
177 trans::debuginfo::clear_source_location(bcx.fcx);
178 }
179 }
180
181 return bcx;
182 }
183
184 let name = format!("then-block-{}-", thn.id);
185 let then_bcx_in = bcx.fcx.new_id_block(&name[..], thn.id);
186 let then_bcx_out = trans_block(then_bcx_in, &*thn, dest);
187 trans::debuginfo::clear_source_location(bcx.fcx);
188
189 let cond_source_loc = cond.debug_loc();
190
191 let next_bcx;
192 match els {
193 Some(elexpr) => {
194 let else_bcx_in = bcx.fcx.new_id_block("else-block", elexpr.id);
195 let else_bcx_out = expr::trans_into(else_bcx_in, &*elexpr, dest);
196 next_bcx = bcx.fcx.join_blocks(if_id,
197 &[then_bcx_out, else_bcx_out]);
198 CondBr(bcx, cond_val, then_bcx_in.llbb, else_bcx_in.llbb, cond_source_loc);
199 }
200
201 None => {
202 next_bcx = bcx.fcx.new_id_block("next-block", if_id);
203 Br(then_bcx_out, next_bcx.llbb, DebugLoc::None);
204 CondBr(bcx, cond_val, then_bcx_in.llbb, next_bcx.llbb, cond_source_loc);
205 }
206 }
207
208 // Clear the source location because it is still set to whatever has been translated
209 // right before.
210 trans::debuginfo::clear_source_location(next_bcx.fcx);
211
212 next_bcx
213 }
214
215 pub fn trans_while<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
216 loop_expr: &hir::Expr,
217 cond: &hir::Expr,
218 body: &hir::Block)
219 -> Block<'blk, 'tcx> {
220 let _icx = push_ctxt("trans_while");
221
222 if bcx.unreachable.get() {
223 return bcx;
224 }
225
226 let fcx = bcx.fcx;
227
228 // bcx
229 // |
230 // cond_bcx_in <--------+
231 // | |
232 // cond_bcx_out |
233 // | | |
234 // | body_bcx_in |
235 // cleanup_blk | |
236 // | body_bcx_out --+
237 // next_bcx_in
238
239 let next_bcx_in = fcx.new_id_block("while_exit", loop_expr.id);
240 let cond_bcx_in = fcx.new_id_block("while_cond", cond.id);
241 let body_bcx_in = fcx.new_id_block("while_body", body.id);
242
243 fcx.push_loop_cleanup_scope(loop_expr.id, [next_bcx_in, cond_bcx_in]);
244
245 Br(bcx, cond_bcx_in.llbb, loop_expr.debug_loc());
246
247 // compile the block where we will handle loop cleanups
248 let cleanup_llbb = fcx.normal_exit_block(loop_expr.id, cleanup::EXIT_BREAK);
249
250 // compile the condition
251 let Result {bcx: cond_bcx_out, val: cond_val} =
252 expr::trans(cond_bcx_in, cond).to_llbool();
253
254 CondBr(cond_bcx_out, cond_val, body_bcx_in.llbb, cleanup_llbb, cond.debug_loc());
255
256 // loop body:
257 let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
258 Br(body_bcx_out, cond_bcx_in.llbb, DebugLoc::None);
259
260 fcx.pop_loop_cleanup_scope(loop_expr.id);
261 return next_bcx_in;
262 }
263
264 pub fn trans_loop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
265 loop_expr: &hir::Expr,
266 body: &hir::Block)
267 -> Block<'blk, 'tcx> {
268 let _icx = push_ctxt("trans_loop");
269
270 if bcx.unreachable.get() {
271 return bcx;
272 }
273
274 let fcx = bcx.fcx;
275
276 // bcx
277 // |
278 // body_bcx_in
279 // |
280 // body_bcx_out
281 //
282 // next_bcx
283 //
284 // Links between body_bcx_in and next_bcx are created by
285 // break statements.
286
287 let next_bcx_in = bcx.fcx.new_id_block("loop_exit", loop_expr.id);
288 let body_bcx_in = bcx.fcx.new_id_block("loop_body", body.id);
289
290 fcx.push_loop_cleanup_scope(loop_expr.id, [next_bcx_in, body_bcx_in]);
291
292 Br(bcx, body_bcx_in.llbb, loop_expr.debug_loc());
293 let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
294 Br(body_bcx_out, body_bcx_in.llbb, DebugLoc::None);
295
296 fcx.pop_loop_cleanup_scope(loop_expr.id);
297
298 // If there are no predecessors for the next block, we just translated an endless loop and the
299 // next block is unreachable
300 if BasicBlock(next_bcx_in.llbb).pred_iter().next().is_none() {
301 Unreachable(next_bcx_in);
302 }
303
304 return next_bcx_in;
305 }
306
307 pub fn trans_break_cont<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
308 expr: &hir::Expr,
309 opt_label: Option<ast::Name>,
310 exit: usize)
311 -> Block<'blk, 'tcx> {
312 let _icx = push_ctxt("trans_break_cont");
313
314 if bcx.unreachable.get() {
315 return bcx;
316 }
317
318 let fcx = bcx.fcx;
319
320 // Locate loop that we will break to
321 let loop_id = match opt_label {
322 None => fcx.top_loop_scope(),
323 Some(_) => {
324 match bcx.tcx().def_map.borrow().get(&expr.id).map(|d| d.full_def()) {
325 Some(def::DefLabel(loop_id)) => loop_id,
326 r => {
327 bcx.tcx().sess.bug(&format!("{:?} in def-map for label", r))
328 }
329 }
330 }
331 };
332
333 // Generate appropriate cleanup code and branch
334 let cleanup_llbb = fcx.normal_exit_block(loop_id, exit);
335 Br(bcx, cleanup_llbb, expr.debug_loc());
336 Unreachable(bcx); // anything afterwards should be ignored
337 return bcx;
338 }
339
340 pub fn trans_break<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
341 expr: &hir::Expr,
342 label_opt: Option<ast::Name>)
343 -> Block<'blk, 'tcx> {
344 return trans_break_cont(bcx, expr, label_opt, cleanup::EXIT_BREAK);
345 }
346
347 pub fn trans_cont<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
348 expr: &hir::Expr,
349 label_opt: Option<ast::Name>)
350 -> Block<'blk, 'tcx> {
351 return trans_break_cont(bcx, expr, label_opt, cleanup::EXIT_LOOP);
352 }
353
354 pub fn trans_ret<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
355 return_expr: &hir::Expr,
356 retval_expr: Option<&hir::Expr>)
357 -> Block<'blk, 'tcx> {
358 let _icx = push_ctxt("trans_ret");
359
360 if bcx.unreachable.get() {
361 return bcx;
362 }
363
364 let fcx = bcx.fcx;
365 let mut bcx = bcx;
366 let dest = match (fcx.llretslotptr.get(), retval_expr) {
367 (Some(_), Some(retval_expr)) => {
368 let ret_ty = expr_ty_adjusted(bcx, &*retval_expr);
369 expr::SaveIn(fcx.get_ret_slot(bcx, ty::FnConverging(ret_ty), "ret_slot"))
370 }
371 _ => expr::Ignore,
372 };
373 if let Some(x) = retval_expr {
374 bcx = expr::trans_into(bcx, &*x, dest);
375 match dest {
376 expr::SaveIn(slot) if fcx.needs_ret_allocas => {
377 Store(bcx, slot, fcx.llretslotptr.get().unwrap());
378 }
379 _ => {}
380 }
381 }
382 let cleanup_llbb = fcx.return_exit_block();
383 Br(bcx, cleanup_llbb, return_expr.debug_loc());
384 Unreachable(bcx);
385 return bcx;
386 }
387
388 pub fn trans_fail<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
389 call_info: NodeIdAndSpan,
390 fail_str: InternedString)
391 -> Block<'blk, 'tcx> {
392 let ccx = bcx.ccx();
393 let _icx = push_ctxt("trans_fail_value");
394
395 if bcx.unreachable.get() {
396 return bcx;
397 }
398
399 let v_str = C_str_slice(ccx, fail_str);
400 let loc = bcx.sess().codemap().lookup_char_pos(call_info.span.lo);
401 let filename = token::intern_and_get_ident(&loc.file.name);
402 let filename = C_str_slice(ccx, filename);
403 let line = C_u32(ccx, loc.line as u32);
404 let expr_file_line_const = C_struct(ccx, &[v_str, filename, line], false);
405 let align = machine::llalign_of_min(ccx, val_ty(expr_file_line_const));
406 let expr_file_line = consts::addr_of(ccx, expr_file_line_const, align, "panic_loc");
407 let args = vec!(expr_file_line);
408 let did = langcall(bcx, Some(call_info.span), "", PanicFnLangItem);
409 let bcx = callee::trans_lang_call(bcx,
410 did,
411 &args[..],
412 Some(expr::Ignore),
413 call_info.debug_loc()).bcx;
414 Unreachable(bcx);
415 return bcx;
416 }
417
418 pub fn trans_fail_bounds_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
419 call_info: NodeIdAndSpan,
420 index: ValueRef,
421 len: ValueRef)
422 -> Block<'blk, 'tcx> {
423 let ccx = bcx.ccx();
424 let _icx = push_ctxt("trans_fail_bounds_check");
425
426 if bcx.unreachable.get() {
427 return bcx;
428 }
429
430 // Extract the file/line from the span
431 let loc = bcx.sess().codemap().lookup_char_pos(call_info.span.lo);
432 let filename = token::intern_and_get_ident(&loc.file.name);
433
434 // Invoke the lang item
435 let filename = C_str_slice(ccx, filename);
436 let line = C_u32(ccx, loc.line as u32);
437 let file_line_const = C_struct(ccx, &[filename, line], false);
438 let align = machine::llalign_of_min(ccx, val_ty(file_line_const));
439 let file_line = consts::addr_of(ccx, file_line_const, align, "panic_bounds_check_loc");
440 let args = vec!(file_line, index, len);
441 let did = langcall(bcx, Some(call_info.span), "", PanicBoundsCheckFnLangItem);
442 let bcx = callee::trans_lang_call(bcx,
443 did,
444 &args[..],
445 Some(expr::Ignore),
446 call_info.debug_loc()).bcx;
447 Unreachable(bcx);
448 return bcx;
449 }