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.
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.
13 use middle::lang_items::{PanicFnLangItem, PanicBoundsCheckFnLangItem};
17 use trans::cleanup::CleanupMethods;
22 use trans::debuginfo::{DebugLoc, ToDebugLoc};
26 use util::ppaux::Repr;
29 use syntax::ast::Ident;
31 use syntax::parse::token::InternedString;
32 use syntax::parse::token;
33 use syntax::visit::Visitor;
35 pub fn trans_stmt<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
37 -> Block<'blk, 'tcx> {
38 let _icx = push_ctxt("trans_stmt");
40 debug!("trans_stmt({})", s.repr(cx.tcx()));
42 if cx.sess().asm_comments() {
43 add_span_comment(cx, s.span, &s.repr(cx.tcx())[]);
48 let id = ast_util::stmt_id(s);
49 let cleanup_debug_loc =
50 debuginfo::get_cleanup_debug_loc_for_ast_node(bcx.ccx(), id, s.span, false);
51 fcx.push_ast_cleanup_scope(cleanup_debug_loc);
54 ast::StmtExpr(ref e, _) | ast::StmtSemi(ref e, _) => {
55 bcx = trans_stmt_semi(bcx, &**e);
57 ast::StmtDecl(ref d, _) => {
59 ast::DeclLocal(ref local) => {
60 bcx = init_local(bcx, &**local);
61 debuginfo::create_local_var_metadata(bcx, &**local);
63 // Inner items are visited by `trans_item`/`trans_meth`.
64 ast::DeclItem(_) => {},
67 ast::StmtMac(..) => cx.tcx().sess.bug("unexpanded macro")
70 bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, ast_util::stmt_id(s));
75 pub fn trans_stmt_semi<'blk, 'tcx>(cx: Block<'blk, 'tcx>, e: &ast::Expr)
76 -> Block<'blk, 'tcx> {
77 let _icx = push_ctxt("trans_stmt_semi");
78 let ty = expr_ty(cx, e);
79 if type_needs_drop(cx.tcx(), ty) {
80 expr::trans_to_lvalue(cx, e, "stmt").bcx
82 expr::trans_into(cx, e, expr::Ignore)
86 pub fn trans_block<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
89 -> Block<'blk, 'tcx> {
90 let _icx = push_ctxt("trans_block");
94 let cleanup_debug_loc =
95 debuginfo::get_cleanup_debug_loc_for_ast_node(bcx.ccx(), b.id, b.span, true);
96 fcx.push_ast_cleanup_scope(cleanup_debug_loc);
99 bcx = trans_stmt(bcx, &**s);
102 if dest != expr::Ignore {
103 let block_ty = node_id_type(bcx, b.id);
105 if b.expr.is_none() || type_is_zero_size(bcx.ccx(), block_ty) {
107 } else if b.expr.is_some() {
108 // If the block has an expression, but that expression isn't reachable,
109 // don't save into the destination given, ignore it.
110 if let Some(ref cfg) = bcx.fcx.cfg {
111 if !cfg.node_is_reachable(b.expr.as_ref().unwrap().id) {
120 bcx = expr::trans_into(bcx, &**e, dest);
123 assert!(dest == expr::Ignore || bcx.unreachable.get());
127 bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, b.id);
132 pub fn trans_if<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
136 els: Option<&ast::Expr>,
138 -> Block<'blk, 'tcx> {
139 debug!("trans_if(bcx={}, if_id={}, cond={}, thn={}, dest={})",
140 bcx.to_str(), if_id, bcx.expr_to_string(cond), thn.id,
141 dest.to_string(bcx.ccx()));
142 let _icx = push_ctxt("trans_if");
145 let cond_val = unpack_result!(bcx, expr::trans(bcx, cond).to_llbool());
147 // Drop branches that are known to be impossible
148 if is_const(cond_val) && !is_undef(cond_val) {
149 if const_to_uint(cond_val) == 1 {
152 let mut trans = TransItemVisitor { ccx: bcx.fcx.ccx };
153 trans.visit_expr(&*elexpr);
157 // if true { .. } [else { .. }]
158 bcx = trans_block(bcx, &*thn, dest);
159 trans::debuginfo::clear_source_location(bcx.fcx);
161 let mut trans = TransItemVisitor { ccx: bcx.fcx.ccx } ;
162 trans.visit_block(&*thn);
165 // if false { .. } else { .. }
167 bcx = expr::trans_into(bcx, &*elexpr, dest);
168 trans::debuginfo::clear_source_location(bcx.fcx);
179 let name = format!("then-block-{}-", thn.id);
180 let then_bcx_in = bcx.fcx.new_id_block(&name[..], thn.id);
181 let then_bcx_out = trans_block(then_bcx_in, &*thn, dest);
182 trans::debuginfo::clear_source_location(bcx.fcx);
184 let cond_source_loc = cond.debug_loc();
189 let else_bcx_in = bcx.fcx.new_id_block("else-block", elexpr.id);
190 let else_bcx_out = expr::trans_into(else_bcx_in, &*elexpr, dest);
191 next_bcx = bcx.fcx.join_blocks(if_id,
192 &[then_bcx_out, else_bcx_out]);
193 CondBr(bcx, cond_val, then_bcx_in.llbb, else_bcx_in.llbb, cond_source_loc);
197 next_bcx = bcx.fcx.new_id_block("next-block", if_id);
198 Br(then_bcx_out, next_bcx.llbb, DebugLoc::None);
199 CondBr(bcx, cond_val, then_bcx_in.llbb, next_bcx.llbb, cond_source_loc);
203 // Clear the source location because it is still set to whatever has been translated
205 trans::debuginfo::clear_source_location(next_bcx.fcx);
210 pub fn trans_while<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
211 loop_expr: &ast::Expr,
214 -> Block<'blk, 'tcx> {
215 let _icx = push_ctxt("trans_while");
220 // cond_bcx_in <--------+
226 // | body_bcx_out --+
229 let next_bcx_in = fcx.new_id_block("while_exit", loop_expr.id);
230 let cond_bcx_in = fcx.new_id_block("while_cond", cond.id);
231 let body_bcx_in = fcx.new_id_block("while_body", body.id);
233 fcx.push_loop_cleanup_scope(loop_expr.id, [next_bcx_in, cond_bcx_in]);
235 Br(bcx, cond_bcx_in.llbb, loop_expr.debug_loc());
237 // compile the block where we will handle loop cleanups
238 let cleanup_llbb = fcx.normal_exit_block(loop_expr.id, cleanup::EXIT_BREAK);
240 // compile the condition
241 let Result {bcx: cond_bcx_out, val: cond_val} =
242 expr::trans(cond_bcx_in, cond).to_llbool();
244 CondBr(cond_bcx_out, cond_val, body_bcx_in.llbb, cleanup_llbb, cond.debug_loc());
247 let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
248 Br(body_bcx_out, cond_bcx_in.llbb, DebugLoc::None);
250 fcx.pop_loop_cleanup_scope(loop_expr.id);
254 pub fn trans_loop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
255 loop_expr: &ast::Expr,
257 -> Block<'blk, 'tcx> {
258 let _icx = push_ctxt("trans_loop");
269 // Links between body_bcx_in and next_bcx are created by
272 let next_bcx_in = bcx.fcx.new_id_block("loop_exit", loop_expr.id);
273 let body_bcx_in = bcx.fcx.new_id_block("loop_body", body.id);
275 fcx.push_loop_cleanup_scope(loop_expr.id, [next_bcx_in, body_bcx_in]);
277 Br(bcx, body_bcx_in.llbb, loop_expr.debug_loc());
278 let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
279 Br(body_bcx_out, body_bcx_in.llbb, DebugLoc::None);
281 fcx.pop_loop_cleanup_scope(loop_expr.id);
286 pub fn trans_break_cont<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
288 opt_label: Option<Ident>,
290 -> Block<'blk, 'tcx> {
291 let _icx = push_ctxt("trans_break_cont");
294 if bcx.unreachable.get() {
298 // Locate loop that we will break to
299 let loop_id = match opt_label {
300 None => fcx.top_loop_scope(),
302 match bcx.tcx().def_map.borrow().get(&expr.id) {
303 Some(&def::DefLabel(loop_id)) => loop_id,
305 bcx.tcx().sess.bug(&format!("{:?} in def-map for label",
312 // Generate appropriate cleanup code and branch
313 let cleanup_llbb = fcx.normal_exit_block(loop_id, exit);
314 Br(bcx, cleanup_llbb, expr.debug_loc());
315 Unreachable(bcx); // anything afterwards should be ignored
319 pub fn trans_break<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
321 label_opt: Option<Ident>)
322 -> Block<'blk, 'tcx> {
323 return trans_break_cont(bcx, expr, label_opt, cleanup::EXIT_BREAK);
326 pub fn trans_cont<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
328 label_opt: Option<Ident>)
329 -> Block<'blk, 'tcx> {
330 return trans_break_cont(bcx, expr, label_opt, cleanup::EXIT_LOOP);
333 pub fn trans_ret<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
334 return_expr: &ast::Expr,
335 retval_expr: Option<&ast::Expr>)
336 -> Block<'blk, 'tcx> {
337 let _icx = push_ctxt("trans_ret");
340 let dest = match (fcx.llretslotptr.get(), retval_expr) {
341 (Some(_), Some(retval_expr)) => {
342 let ret_ty = expr_ty_adjusted(bcx, &*retval_expr);
343 expr::SaveIn(fcx.get_ret_slot(bcx, ty::FnConverging(ret_ty), "ret_slot"))
347 if let Some(x) = retval_expr {
348 bcx = expr::trans_into(bcx, &*x, dest);
350 expr::SaveIn(slot) if fcx.needs_ret_allocas => {
351 Store(bcx, slot, fcx.llretslotptr.get().unwrap());
356 let cleanup_llbb = fcx.return_exit_block();
357 Br(bcx, cleanup_llbb, return_expr.debug_loc());
362 pub fn trans_fail<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
363 call_info: NodeIdAndSpan,
364 fail_str: InternedString)
365 -> Block<'blk, 'tcx> {
367 let _icx = push_ctxt("trans_fail_value");
369 let v_str = C_str_slice(ccx, fail_str);
370 let loc = bcx.sess().codemap().lookup_char_pos(call_info.span.lo);
371 let filename = token::intern_and_get_ident(&loc.file.name[]);
372 let filename = C_str_slice(ccx, filename);
373 let line = C_uint(ccx, loc.line);
374 let expr_file_line_const = C_struct(ccx, &[v_str, filename, line], false);
375 let expr_file_line = consts::addr_of(ccx, expr_file_line_const,
376 "panic_loc", call_info.id);
377 let args = vec!(expr_file_line);
378 let did = langcall(bcx, Some(call_info.span), "", PanicFnLangItem);
379 let bcx = callee::trans_lang_call(bcx,
383 call_info.debug_loc()).bcx;
388 pub fn trans_fail_bounds_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
389 call_info: NodeIdAndSpan,
392 -> Block<'blk, 'tcx> {
394 let _icx = push_ctxt("trans_fail_bounds_check");
396 // Extract the file/line from the span
397 let loc = bcx.sess().codemap().lookup_char_pos(call_info.span.lo);
398 let filename = token::intern_and_get_ident(&loc.file.name[]);
400 // Invoke the lang item
401 let filename = C_str_slice(ccx, filename);
402 let line = C_uint(ccx, loc.line);
403 let file_line_const = C_struct(ccx, &[filename, line], false);
404 let file_line = consts::addr_of(ccx, file_line_const,
405 "panic_bounds_check_loc", call_info.id);
406 let args = vec!(file_line, index, len);
407 let did = langcall(bcx, Some(call_info.span), "", PanicBoundsCheckFnLangItem);
408 let bcx = callee::trans_lang_call(bcx,
412 call_info.debug_loc()).bcx;