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