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.
12 use driver::config::FullDebugInfo;
14 use middle::lang_items::{FailFnLangItem, FailBoundsCheckFnLangItem};
15 use middle::trans::_match;
16 use middle::trans::adt;
17 use middle::trans::base::*;
18 use middle::trans::build::*;
19 use middle::trans::callee;
20 use middle::trans::cleanup::CleanupMethods;
21 use middle::trans::cleanup;
22 use middle::trans::common::*;
23 use middle::trans::consts;
24 use middle::trans::datum;
25 use middle::trans::expr;
26 use middle::trans::meth;
27 use middle::trans::type_::Type;
30 use middle::typeck::MethodCall;
31 use util::ppaux::Repr;
35 use syntax::ast::Ident;
37 use syntax::codemap::Span;
38 use syntax::parse::token::InternedString;
39 use syntax::parse::token;
40 use syntax::visit::Visitor;
42 pub fn trans_stmt<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
44 -> Block<'blk, 'tcx> {
45 let _icx = push_ctxt("trans_stmt");
47 debug!("trans_stmt({})", s.repr(cx.tcx()));
49 if cx.sess().asm_comments() {
50 add_span_comment(cx, s.span, s.repr(cx.tcx()).as_slice());
55 let id = ast_util::stmt_id(s);
56 fcx.push_ast_cleanup_scope(id);
59 ast::StmtExpr(ref e, _) | ast::StmtSemi(ref e, _) => {
60 bcx = trans_stmt_semi(bcx, &**e);
62 ast::StmtDecl(ref d, _) => {
64 ast::DeclLocal(ref local) => {
65 bcx = init_local(bcx, &**local);
66 if cx.sess().opts.debuginfo == FullDebugInfo {
67 trans::debuginfo::create_local_var_metadata(bcx,
71 // Inner items are visited by `trans_item`/`trans_meth`.
72 ast::DeclItem(_) => {},
75 ast::StmtMac(..) => cx.tcx().sess.bug("unexpanded macro")
78 bcx = fcx.pop_and_trans_ast_cleanup_scope(
79 bcx, ast_util::stmt_id(s));
84 pub fn trans_stmt_semi<'blk, 'tcx>(cx: Block<'blk, 'tcx>, e: &ast::Expr)
85 -> Block<'blk, 'tcx> {
86 let _icx = push_ctxt("trans_stmt_semi");
87 let ty = expr_ty(cx, e);
88 if ty::type_needs_drop(cx.tcx(), ty) {
89 expr::trans_to_lvalue(cx, e, "stmt").bcx
91 expr::trans_into(cx, e, expr::Ignore)
95 pub fn trans_block<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
98 -> Block<'blk, 'tcx> {
99 let _icx = push_ctxt("trans_block");
103 fcx.push_ast_cleanup_scope(b.id);
105 for s in b.stmts.iter() {
106 bcx = trans_stmt(bcx, &**s);
109 if dest != expr::Ignore {
110 let block_ty = node_id_type(bcx, b.id);
111 if b.expr.is_none() || type_is_zero_size(bcx.ccx(), block_ty) {
118 bcx = expr::trans_into(bcx, &**e, dest);
121 assert!(dest == expr::Ignore || bcx.unreachable.get());
125 bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, b.id);
130 pub fn trans_if<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
134 els: Option<&ast::Expr>,
136 -> Block<'blk, 'tcx> {
137 debug!("trans_if(bcx={}, if_id={}, cond={}, thn={:?}, dest={})",
138 bcx.to_str(), if_id, bcx.expr_to_string(cond), thn.id,
139 dest.to_string(bcx.ccx()));
140 let _icx = push_ctxt("trans_if");
143 let cond_val = unpack_result!(bcx, expr::trans(bcx, cond).to_llbool());
145 // Drop branches that are known to be impossible
146 if is_const(cond_val) && !is_undef(cond_val) {
147 if const_to_uint(cond_val) == 1 {
150 let mut trans = TransItemVisitor { ccx: bcx.fcx.ccx };
151 trans.visit_expr(&*elexpr);
155 // if true { .. } [else { .. }]
156 bcx = trans_block(bcx, &*thn, dest);
157 trans::debuginfo::clear_source_location(bcx.fcx);
159 let mut trans = TransItemVisitor { ccx: bcx.fcx.ccx } ;
160 trans.visit_block(&*thn);
163 // if false { .. } else { .. }
165 bcx = expr::trans_into(bcx, &*elexpr, dest);
166 trans::debuginfo::clear_source_location(bcx.fcx);
177 let name = format!("then-block-{}-", thn.id);
178 let then_bcx_in = bcx.fcx.new_id_block(name.as_slice(), thn.id);
179 let then_bcx_out = trans_block(then_bcx_in, &*thn, dest);
180 trans::debuginfo::clear_source_location(bcx.fcx);
185 let else_bcx_in = bcx.fcx.new_id_block("else-block", elexpr.id);
186 let else_bcx_out = expr::trans_into(else_bcx_in, &*elexpr, dest);
187 next_bcx = bcx.fcx.join_blocks(if_id,
188 [then_bcx_out, else_bcx_out]);
189 CondBr(bcx, cond_val, then_bcx_in.llbb, else_bcx_in.llbb);
193 next_bcx = bcx.fcx.new_id_block("next-block", if_id);
194 Br(then_bcx_out, next_bcx.llbb);
195 CondBr(bcx, cond_val, then_bcx_in.llbb, next_bcx.llbb);
199 // Clear the source location because it is still set to whatever has been translated
201 trans::debuginfo::clear_source_location(next_bcx.fcx);
206 pub fn trans_while<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
207 loop_id: ast::NodeId,
210 -> Block<'blk, 'tcx> {
211 let _icx = push_ctxt("trans_while");
216 // cond_bcx_in <--------+
222 // | body_bcx_out --+
225 let next_bcx_in = fcx.new_id_block("while_exit", loop_id);
226 let cond_bcx_in = fcx.new_id_block("while_cond", cond.id);
227 let body_bcx_in = fcx.new_id_block("while_body", body.id);
229 fcx.push_loop_cleanup_scope(loop_id, [next_bcx_in, cond_bcx_in]);
231 Br(bcx, cond_bcx_in.llbb);
233 // compile the block where we will handle loop cleanups
234 let cleanup_llbb = fcx.normal_exit_block(loop_id, cleanup::EXIT_BREAK);
236 // compile the condition
237 let Result {bcx: cond_bcx_out, val: cond_val} =
238 expr::trans(cond_bcx_in, cond).to_llbool();
239 CondBr(cond_bcx_out, cond_val, body_bcx_in.llbb, cleanup_llbb);
242 let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
243 Br(body_bcx_out, cond_bcx_in.llbb);
245 fcx.pop_loop_cleanup_scope(loop_id);
249 /// Translates a `for` loop.
250 pub fn trans_for<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
255 -> Block<'blk, 'tcx> {
256 let _icx = push_ctxt("trans_for");
260 // loopback_bcx_in <-------+
262 // loopback_bcx_out |
266 // | body_bcx_out --+
269 // Codegen the head to create the iterator value.
271 unpack_datum!(bcx, expr::trans_to_lvalue(bcx, head, "for_head"));
272 let iterator_type = node_id_type(bcx, head.id);
273 debug!("iterator type is {}, datum type is {}",
274 ppaux::ty_to_string(bcx.tcx(), iterator_type),
275 ppaux::ty_to_string(bcx.tcx(), iterator_datum.ty));
276 let lliterator = load_ty(bcx, iterator_datum.val, iterator_datum.ty);
278 // Create our basic blocks and set up our loop cleanups.
279 let next_bcx_in = bcx.fcx.new_id_block("for_exit", loop_info.id);
280 let loopback_bcx_in = bcx.fcx.new_id_block("for_loopback", head.id);
281 let body_bcx_in = bcx.fcx.new_id_block("for_body", body.id);
282 bcx.fcx.push_loop_cleanup_scope(loop_info.id,
283 [next_bcx_in, loopback_bcx_in]);
284 Br(bcx, loopback_bcx_in.llbb);
285 let cleanup_llbb = bcx.fcx.normal_exit_block(loop_info.id,
286 cleanup::EXIT_BREAK);
288 // Set up the method call (to `.next()`).
289 let method_call = MethodCall::expr(loop_info.id);
290 let method_type = loopback_bcx_in.tcx()
295 let method_type = monomorphize_type(loopback_bcx_in, method_type);
296 let method_result_type = ty::ty_fn_ret(method_type);
297 let option_cleanup_scope = body_bcx_in.fcx.push_custom_cleanup_scope();
298 let option_cleanup_scope_id = cleanup::CustomScope(option_cleanup_scope);
300 // Compile the method call (to `.next()`).
301 let mut loopback_bcx_out = loopback_bcx_in;
303 unpack_datum!(loopback_bcx_out,
304 datum::lvalue_scratch_datum(loopback_bcx_out,
308 option_cleanup_scope_id,
310 |(), bcx, lloption| {
314 } = callee::trans_call_inner(bcx,
317 |bcx, arg_cleanup_scope| {
318 meth::trans_method_callee(
324 callee::ArgVals([lliterator]),
325 Some(expr::SaveIn(lloption)));
329 // Check the discriminant; if the `None` case, exit the loop.
330 let option_representation = adt::represent_type(loopback_bcx_out.ccx(),
332 let lldiscriminant = adt::trans_get_discr(loopback_bcx_out,
333 &*option_representation,
336 let i1_type = Type::i1(loopback_bcx_out.ccx());
337 let llcondition = Trunc(loopback_bcx_out, lldiscriminant, i1_type);
338 CondBr(loopback_bcx_out, llcondition, body_bcx_in.llbb, cleanup_llbb);
340 // Now we're in the body. Unpack the `Option` value into the programmer-
342 let llpayload = adt::trans_field_ptr(body_bcx_in,
343 &*option_representation,
347 let binding_cleanup_scope = body_bcx_in.fcx.push_custom_cleanup_scope();
348 let binding_cleanup_scope_id =
349 cleanup::CustomScope(binding_cleanup_scope);
350 let mut body_bcx_out =
351 _match::store_for_loop_binding(body_bcx_in,
354 binding_cleanup_scope_id);
357 body_bcx_out = trans_block(body_bcx_out, body, expr::Ignore);
358 body_bcx_out.fcx.pop_custom_cleanup_scope(binding_cleanup_scope);
361 .pop_and_trans_custom_cleanup_scope(body_bcx_out,
362 option_cleanup_scope);
363 Br(body_bcx_out, loopback_bcx_in.llbb);
365 // Codegen cleanups and leave.
366 next_bcx_in.fcx.pop_loop_cleanup_scope(loop_info.id);
370 pub fn trans_loop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
371 loop_id: ast::NodeId,
373 -> Block<'blk, 'tcx> {
374 let _icx = push_ctxt("trans_loop");
385 // Links between body_bcx_in and next_bcx are created by
388 let next_bcx_in = bcx.fcx.new_id_block("loop_exit", loop_id);
389 let body_bcx_in = bcx.fcx.new_id_block("loop_body", body.id);
391 fcx.push_loop_cleanup_scope(loop_id, [next_bcx_in, body_bcx_in]);
393 Br(bcx, body_bcx_in.llbb);
394 let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
395 Br(body_bcx_out, body_bcx_in.llbb);
397 fcx.pop_loop_cleanup_scope(loop_id);
399 if ty::type_is_bot(node_id_type(bcx, loop_id)) {
400 Unreachable(next_bcx_in);
406 pub fn trans_break_cont<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
407 expr_id: ast::NodeId,
408 opt_label: Option<Ident>,
410 -> Block<'blk, 'tcx> {
411 let _icx = push_ctxt("trans_break_cont");
414 if bcx.unreachable.get() {
418 // Locate loop that we will break to
419 let loop_id = match opt_label {
420 None => fcx.top_loop_scope(),
422 match bcx.tcx().def_map.borrow().find(&expr_id) {
423 Some(&def::DefLabel(loop_id)) => loop_id,
425 bcx.tcx().sess.bug(format!("{:?} in def-map for label",
432 // Generate appropriate cleanup code and branch
433 let cleanup_llbb = fcx.normal_exit_block(loop_id, exit);
434 Br(bcx, cleanup_llbb);
435 Unreachable(bcx); // anything afterwards should be ignored
439 pub fn trans_break<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
440 expr_id: ast::NodeId,
441 label_opt: Option<Ident>)
442 -> Block<'blk, 'tcx> {
443 return trans_break_cont(bcx, expr_id, label_opt, cleanup::EXIT_BREAK);
446 pub fn trans_cont<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
447 expr_id: ast::NodeId,
448 label_opt: Option<Ident>)
449 -> Block<'blk, 'tcx> {
450 return trans_break_cont(bcx, expr_id, label_opt, cleanup::EXIT_LOOP);
453 pub fn trans_ret<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
454 e: Option<&ast::Expr>)
455 -> Block<'blk, 'tcx> {
456 let _icx = push_ctxt("trans_ret");
459 let dest = match (fcx.llretslotptr.get(), e) {
460 (Some(_), Some(e)) => {
461 let ret_ty = expr_ty(bcx, &*e);
462 expr::SaveIn(fcx.get_ret_slot(bcx, ret_ty, "ret_slot"))
468 bcx = expr::trans_into(bcx, &*x, dest);
470 expr::SaveIn(slot) if fcx.needs_ret_allocas => {
471 Store(bcx, slot, fcx.llretslotptr.get().unwrap());
478 let cleanup_llbb = fcx.return_exit_block();
479 Br(bcx, cleanup_llbb);
484 pub fn trans_fail<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
486 fail_str: InternedString)
487 -> Block<'blk, 'tcx> {
489 let _icx = push_ctxt("trans_fail_value");
491 let v_str = C_str_slice(ccx, fail_str);
492 let loc = bcx.sess().codemap().lookup_char_pos(sp.lo);
493 let filename = token::intern_and_get_ident(loc.file.name.as_slice());
494 let filename = C_str_slice(ccx, filename);
495 let line = C_int(ccx, loc.line as int);
496 let expr_file_line_const = C_struct(ccx, &[v_str, filename, line], false);
497 let expr_file_line = consts::const_addr_of(ccx, expr_file_line_const, ast::MutImmutable);
498 let args = vec!(expr_file_line);
499 let did = langcall(bcx, Some(sp), "", FailFnLangItem);
500 let bcx = callee::trans_lang_call(bcx,
503 Some(expr::Ignore)).bcx;
508 pub fn trans_fail_bounds_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
512 -> Block<'blk, 'tcx> {
514 let _icx = push_ctxt("trans_fail_bounds_check");
516 // Extract the file/line from the span
517 let loc = bcx.sess().codemap().lookup_char_pos(sp.lo);
518 let filename = token::intern_and_get_ident(loc.file.name.as_slice());
520 // Invoke the lang item
521 let filename = C_str_slice(ccx, filename);
522 let line = C_int(ccx, loc.line as int);
523 let file_line_const = C_struct(ccx, &[filename, line], false);
524 let file_line = consts::const_addr_of(ccx, file_line_const, ast::MutImmutable);
525 let args = vec!(file_line, index, len);
526 let did = langcall(bcx, Some(sp), "", FailBoundsCheckFnLangItem);
527 let bcx = callee::trans_lang_call(bcx,
530 Some(expr::Ignore)).bcx;