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};
19 use trans::cleanup::CleanupMethods;
27 use trans::type_::Type;
30 use middle::ty::MethodCall;
31 use session::config::FullDebugInfo;
32 use util::ppaux::Repr;
36 use syntax::ast::Ident;
38 use syntax::codemap::Span;
39 use syntax::parse::token::InternedString;
40 use syntax::parse::token;
41 use syntax::visit::Visitor;
43 pub fn trans_stmt<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
45 -> Block<'blk, 'tcx> {
46 let _icx = push_ctxt("trans_stmt");
48 debug!("trans_stmt({})", s.repr(cx.tcx()));
50 if cx.sess().asm_comments() {
51 add_span_comment(cx, s.span, &s.repr(cx.tcx())[]);
56 let id = ast_util::stmt_id(s);
57 let cleanup_debug_loc =
58 debuginfo::get_cleanup_debug_loc_for_ast_node(bcx.ccx(), id, s.span, false);
59 fcx.push_ast_cleanup_scope(cleanup_debug_loc);
62 ast::StmtExpr(ref e, _) | ast::StmtSemi(ref e, _) => {
63 bcx = trans_stmt_semi(bcx, &**e);
65 ast::StmtDecl(ref d, _) => {
67 ast::DeclLocal(ref local) => {
68 bcx = init_local(bcx, &**local);
69 if cx.sess().opts.debuginfo == FullDebugInfo {
70 trans::debuginfo::create_local_var_metadata(bcx,
74 // Inner items are visited by `trans_item`/`trans_meth`.
75 ast::DeclItem(_) => {},
78 ast::StmtMac(..) => cx.tcx().sess.bug("unexpanded macro")
81 bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, ast_util::stmt_id(s));
86 pub fn trans_stmt_semi<'blk, 'tcx>(cx: Block<'blk, 'tcx>, e: &ast::Expr)
87 -> Block<'blk, 'tcx> {
88 let _icx = push_ctxt("trans_stmt_semi");
89 let ty = expr_ty(cx, e);
90 if type_needs_drop(cx.tcx(), ty) {
91 expr::trans_to_lvalue(cx, e, "stmt").bcx
93 expr::trans_into(cx, e, expr::Ignore)
97 pub fn trans_block<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
100 -> Block<'blk, 'tcx> {
101 let _icx = push_ctxt("trans_block");
105 let cleanup_debug_loc =
106 debuginfo::get_cleanup_debug_loc_for_ast_node(bcx.ccx(), b.id, b.span, true);
107 fcx.push_ast_cleanup_scope(cleanup_debug_loc);
109 for s in b.stmts.iter() {
110 bcx = trans_stmt(bcx, &**s);
113 if dest != expr::Ignore {
114 let block_ty = node_id_type(bcx, b.id);
116 if b.expr.is_none() || type_is_zero_size(bcx.ccx(), block_ty) {
118 } else if b.expr.is_some() {
119 // If the block has an expression, but that expression isn't reachable,
120 // don't save into the destination given, ignore it.
121 if let Some(ref cfg) = bcx.fcx.cfg {
122 if !cfg.node_is_reachable(b.expr.as_ref().unwrap().id) {
131 bcx = expr::trans_into(bcx, &**e, dest);
134 assert!(dest == expr::Ignore || bcx.unreachable.get());
138 bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, b.id);
143 pub fn trans_if<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
147 els: Option<&ast::Expr>,
149 -> Block<'blk, 'tcx> {
150 debug!("trans_if(bcx={}, if_id={}, cond={}, thn={}, dest={})",
151 bcx.to_str(), if_id, bcx.expr_to_string(cond), thn.id,
152 dest.to_string(bcx.ccx()));
153 let _icx = push_ctxt("trans_if");
156 let cond_val = unpack_result!(bcx, expr::trans(bcx, cond).to_llbool());
158 // Drop branches that are known to be impossible
159 if is_const(cond_val) && !is_undef(cond_val) {
160 if const_to_uint(cond_val) == 1 {
163 let mut trans = TransItemVisitor { ccx: bcx.fcx.ccx };
164 trans.visit_expr(&*elexpr);
168 // if true { .. } [else { .. }]
169 bcx = trans_block(bcx, &*thn, dest);
170 trans::debuginfo::clear_source_location(bcx.fcx);
172 let mut trans = TransItemVisitor { ccx: bcx.fcx.ccx } ;
173 trans.visit_block(&*thn);
176 // if false { .. } else { .. }
178 bcx = expr::trans_into(bcx, &*elexpr, dest);
179 trans::debuginfo::clear_source_location(bcx.fcx);
190 let name = format!("then-block-{}-", thn.id);
191 let then_bcx_in = bcx.fcx.new_id_block(&name[], thn.id);
192 let then_bcx_out = trans_block(then_bcx_in, &*thn, dest);
193 trans::debuginfo::clear_source_location(bcx.fcx);
198 let else_bcx_in = bcx.fcx.new_id_block("else-block", elexpr.id);
199 let else_bcx_out = expr::trans_into(else_bcx_in, &*elexpr, dest);
200 next_bcx = bcx.fcx.join_blocks(if_id,
201 &[then_bcx_out, else_bcx_out]);
202 CondBr(bcx, cond_val, then_bcx_in.llbb, else_bcx_in.llbb);
206 next_bcx = bcx.fcx.new_id_block("next-block", if_id);
207 Br(then_bcx_out, next_bcx.llbb);
208 CondBr(bcx, cond_val, then_bcx_in.llbb, next_bcx.llbb);
212 // Clear the source location because it is still set to whatever has been translated
214 trans::debuginfo::clear_source_location(next_bcx.fcx);
219 pub fn trans_while<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
220 loop_id: ast::NodeId,
223 -> Block<'blk, 'tcx> {
224 let _icx = push_ctxt("trans_while");
229 // cond_bcx_in <--------+
235 // | body_bcx_out --+
238 let next_bcx_in = fcx.new_id_block("while_exit", loop_id);
239 let cond_bcx_in = fcx.new_id_block("while_cond", cond.id);
240 let body_bcx_in = fcx.new_id_block("while_body", body.id);
242 fcx.push_loop_cleanup_scope(loop_id, [next_bcx_in, cond_bcx_in]);
244 Br(bcx, cond_bcx_in.llbb);
246 // compile the block where we will handle loop cleanups
247 let cleanup_llbb = fcx.normal_exit_block(loop_id, cleanup::EXIT_BREAK);
249 // compile the condition
250 let Result {bcx: cond_bcx_out, val: cond_val} =
251 expr::trans(cond_bcx_in, cond).to_llbool();
252 CondBr(cond_bcx_out, cond_val, body_bcx_in.llbb, cleanup_llbb);
255 let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
256 Br(body_bcx_out, cond_bcx_in.llbb);
258 fcx.pop_loop_cleanup_scope(loop_id);
262 /// Translates a `for` loop.
263 pub fn trans_for<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
270 let _icx = push_ctxt("trans_for");
274 // loopback_bcx_in <-------+
276 // loopback_bcx_out |
280 // | body_bcx_out --+
283 // Codegen the head to create the iterator value.
285 unpack_datum!(bcx, expr::trans_to_lvalue(bcx, head, "for_head"));
286 let iterator_type = node_id_type(bcx, head.id);
287 debug!("iterator type is {}, datum type is {}",
288 ppaux::ty_to_string(bcx.tcx(), iterator_type),
289 ppaux::ty_to_string(bcx.tcx(), iterator_datum.ty));
291 let lliterator = load_ty(bcx, iterator_datum.val, iterator_datum.ty);
293 // Create our basic blocks and set up our loop cleanups.
294 let next_bcx_in = bcx.fcx.new_id_block("for_exit", loop_info.id);
295 let loopback_bcx_in = bcx.fcx.new_id_block("for_loopback", head.id);
296 let body_bcx_in = bcx.fcx.new_id_block("for_body", body.id);
297 bcx.fcx.push_loop_cleanup_scope(loop_info.id,
298 [next_bcx_in, loopback_bcx_in]);
299 Br(bcx, loopback_bcx_in.llbb);
300 let cleanup_llbb = bcx.fcx.normal_exit_block(loop_info.id,
301 cleanup::EXIT_BREAK);
303 // Set up the method call (to `.next()`).
304 let method_call = MethodCall::expr(loop_info.id);
305 let method_type = (*loopback_bcx_in.tcx()
307 .borrow())[method_call]
309 let method_type = monomorphize_type(loopback_bcx_in, method_type);
310 let method_result_type =
311 ty::assert_no_late_bound_regions( // LB regions are instantiated in invoked methods
312 loopback_bcx_in.tcx(), &ty::ty_fn_ret(method_type)).unwrap();
313 let option_cleanup_scope = body_bcx_in.fcx.push_custom_cleanup_scope();
314 let option_cleanup_scope_id = cleanup::CustomScope(option_cleanup_scope);
316 // Compile the method call (to `.next()`).
317 let mut loopback_bcx_out = loopback_bcx_in;
319 unpack_datum!(loopback_bcx_out,
320 datum::lvalue_scratch_datum(loopback_bcx_out,
324 option_cleanup_scope_id,
326 |(), bcx, lloption| {
330 } = callee::trans_call_inner(bcx,
333 |bcx, arg_cleanup_scope| {
334 meth::trans_method_callee(
340 callee::ArgVals(&[lliterator]),
341 Some(expr::SaveIn(lloption)));
345 // Check the discriminant; if the `None` case, exit the loop.
346 let option_representation = adt::represent_type(loopback_bcx_out.ccx(),
348 let lldiscriminant = adt::trans_get_discr(loopback_bcx_out,
349 &*option_representation,
352 let i1_type = Type::i1(loopback_bcx_out.ccx());
353 let llcondition = Trunc(loopback_bcx_out, lldiscriminant, i1_type);
354 CondBr(loopback_bcx_out, llcondition, body_bcx_in.llbb, cleanup_llbb);
356 // Now we're in the body. Unpack the `Option` value into the programmer-
358 let llpayload = adt::trans_field_ptr(body_bcx_in,
359 &*option_representation,
363 let binding_cleanup_scope = body_bcx_in.fcx.push_custom_cleanup_scope();
364 let binding_cleanup_scope_id =
365 cleanup::CustomScope(binding_cleanup_scope);
366 let mut body_bcx_out =
367 _match::store_for_loop_binding(body_bcx_in,
370 binding_cleanup_scope_id);
372 debuginfo::create_for_loop_var_metadata(body_bcx_in, pat);
375 body_bcx_out = trans_block(body_bcx_out, body, expr::Ignore);
378 .pop_and_trans_custom_cleanup_scope(body_bcx_out,
379 binding_cleanup_scope);
382 .pop_and_trans_custom_cleanup_scope(body_bcx_out,
383 option_cleanup_scope);
384 Br(body_bcx_out, loopback_bcx_in.llbb);
386 // Codegen cleanups and leave.
387 next_bcx_in.fcx.pop_loop_cleanup_scope(loop_info.id);
391 pub fn trans_loop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
392 loop_id: ast::NodeId,
394 -> Block<'blk, 'tcx> {
395 let _icx = push_ctxt("trans_loop");
406 // Links between body_bcx_in and next_bcx are created by
409 let next_bcx_in = bcx.fcx.new_id_block("loop_exit", loop_id);
410 let body_bcx_in = bcx.fcx.new_id_block("loop_body", body.id);
412 fcx.push_loop_cleanup_scope(loop_id, [next_bcx_in, body_bcx_in]);
414 Br(bcx, body_bcx_in.llbb);
415 let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
416 Br(body_bcx_out, body_bcx_in.llbb);
418 fcx.pop_loop_cleanup_scope(loop_id);
423 pub fn trans_break_cont<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
424 expr_id: ast::NodeId,
425 opt_label: Option<Ident>,
427 -> Block<'blk, 'tcx> {
428 let _icx = push_ctxt("trans_break_cont");
431 if bcx.unreachable.get() {
435 // Locate loop that we will break to
436 let loop_id = match opt_label {
437 None => fcx.top_loop_scope(),
439 match bcx.tcx().def_map.borrow().get(&expr_id) {
440 Some(&def::DefLabel(loop_id)) => loop_id,
442 bcx.tcx().sess.bug(&format!("{:?} in def-map for label",
449 // Generate appropriate cleanup code and branch
450 let cleanup_llbb = fcx.normal_exit_block(loop_id, exit);
451 Br(bcx, cleanup_llbb);
452 Unreachable(bcx); // anything afterwards should be ignored
456 pub fn trans_break<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
457 expr_id: ast::NodeId,
458 label_opt: Option<Ident>)
459 -> Block<'blk, 'tcx> {
460 return trans_break_cont(bcx, expr_id, label_opt, cleanup::EXIT_BREAK);
463 pub fn trans_cont<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
464 expr_id: ast::NodeId,
465 label_opt: Option<Ident>)
466 -> Block<'blk, 'tcx> {
467 return trans_break_cont(bcx, expr_id, label_opt, cleanup::EXIT_LOOP);
470 pub fn trans_ret<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
471 e: Option<&ast::Expr>)
472 -> Block<'blk, 'tcx> {
473 let _icx = push_ctxt("trans_ret");
476 let dest = match (fcx.llretslotptr.get(), e) {
477 (Some(_), Some(e)) => {
478 let ret_ty = expr_ty(bcx, &*e);
479 expr::SaveIn(fcx.get_ret_slot(bcx, ty::FnConverging(ret_ty), "ret_slot"))
484 bcx = expr::trans_into(bcx, &*x, dest);
486 expr::SaveIn(slot) if fcx.needs_ret_allocas => {
487 Store(bcx, slot, fcx.llretslotptr.get().unwrap());
492 let cleanup_llbb = fcx.return_exit_block();
493 Br(bcx, cleanup_llbb);
498 pub fn trans_fail<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
500 fail_str: InternedString)
501 -> Block<'blk, 'tcx> {
503 let _icx = push_ctxt("trans_fail_value");
505 let v_str = C_str_slice(ccx, fail_str);
506 let loc = bcx.sess().codemap().lookup_char_pos(sp.lo);
507 let filename = token::intern_and_get_ident(&loc.file.name[]);
508 let filename = C_str_slice(ccx, filename);
509 let line = C_uint(ccx, loc.line);
510 let expr_file_line_const = C_struct(ccx, &[v_str, filename, line], false);
511 let expr_file_line = consts::const_addr_of(ccx, expr_file_line_const, ast::MutImmutable);
512 let args = vec!(expr_file_line);
513 let did = langcall(bcx, Some(sp), "", PanicFnLangItem);
514 let bcx = callee::trans_lang_call(bcx,
517 Some(expr::Ignore)).bcx;
522 pub fn trans_fail_bounds_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
526 -> Block<'blk, 'tcx> {
528 let _icx = push_ctxt("trans_fail_bounds_check");
530 // Extract the file/line from the span
531 let loc = bcx.sess().codemap().lookup_char_pos(sp.lo);
532 let filename = token::intern_and_get_ident(&loc.file.name[]);
534 // Invoke the lang item
535 let filename = C_str_slice(ccx, filename);
536 let line = C_uint(ccx, loc.line);
537 let file_line_const = C_struct(ccx, &[filename, line], false);
538 let file_line = consts::const_addr_of(ccx, file_line_const, ast::MutImmutable);
539 let args = vec!(file_line, index, len);
540 let did = langcall(bcx, Some(sp), "", PanicBoundsCheckFnLangItem);
541 let bcx = callee::trans_lang_call(bcx,
544 Some(expr::Ignore)).bcx;