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}
;
15 use trans
::basic_block
::BasicBlock
;
18 use trans
::cleanup
::CleanupMethods
;
23 use trans
::debuginfo
::{DebugLoc, ToDebugLoc}
;
30 use rustc_front
::util
as ast_util
;
33 use syntax
::parse
::token
::InternedString
;
34 use syntax
::parse
::token
;
36 pub fn trans_stmt
<'blk
, 'tcx
>(cx
: Block
<'blk
, 'tcx
>,
38 -> Block
<'blk
, 'tcx
> {
39 let _icx
= push_ctxt("trans_stmt");
41 debug
!("trans_stmt({:?})", s
);
43 if cx
.unreachable
.get() {
47 if cx
.sess().asm_comments() {
48 add_span_comment(cx
, s
.span
, &format
!("{:?}", s
));
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
);
59 hir
::StmtExpr(ref e
, _
) | hir
::StmtSemi(ref e
, _
) => {
60 bcx
= trans_stmt_semi(bcx
, &e
);
62 hir
::StmtDecl(ref d
, _
) => {
64 hir
::DeclLocal(ref local
) => {
65 bcx
= init_local(bcx
, &local
);
66 debuginfo
::create_local_var_metadata(bcx
, &local
);
68 // Inner items are visited by `trans_item`/`trans_meth`.
69 hir
::DeclItem(_
) => {}
,
74 bcx
= fcx
.pop_and_trans_ast_cleanup_scope(bcx
, ast_util
::stmt_id(s
));
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");
83 if cx
.unreachable
.get() {
87 let ty
= expr_ty(cx
, e
);
88 if cx
.fcx
.type_needs_drop(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");
101 if bcx
.unreachable
.get() {
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
);
113 bcx
= trans_stmt(bcx
, s
);
116 if dest
!= expr
::Ignore
{
117 let block_ty
= node_id_type(bcx
, b
.id
);
119 if b
.expr
.is_none() || type_is_zero_size(bcx
.ccx(), block_ty
) {
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
) {
134 if !bcx
.unreachable
.get() {
135 bcx
= expr
::trans_into(bcx
, &e
, dest
);
139 assert
!(dest
== expr
::Ignore
|| bcx
.unreachable
.get());
143 bcx
= fcx
.pop_and_trans_ast_cleanup_scope(bcx
, b
.id
);
148 pub fn trans_if
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
152 els
: Option
<&hir
::Expr
>,
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");
160 if bcx
.unreachable
.get() {
166 let cond_val
= unpack_result
!(bcx
, expr
::trans(bcx
, cond
).to_llbool());
168 // Drop branches that are known to be impossible
169 if let Some(cv
) = const_to_opt_uint(cond_val
) {
171 // if true { .. } [else { .. }]
172 bcx
= trans_block(bcx
, &thn
, dest
);
173 trans
::debuginfo
::clear_source_location(bcx
.fcx
);
175 if let Some(elexpr
) = els
{
176 bcx
= expr
::trans_into(bcx
, &elexpr
, dest
);
177 trans
::debuginfo
::clear_source_location(bcx
.fcx
);
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
);
189 let cond_source_loc
= cond
.debug_loc();
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
);
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
);
208 // Clear the source location because it is still set to whatever has been translated
210 trans
::debuginfo
::clear_source_location(next_bcx
.fcx
);
215 pub fn trans_while
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
216 loop_expr
: &hir
::Expr
,
219 -> Block
<'blk
, 'tcx
> {
220 let _icx
= push_ctxt("trans_while");
222 if bcx
.unreachable
.get() {
230 // cond_bcx_in <--------+
236 // | body_bcx_out --+
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
);
243 fcx
.push_loop_cleanup_scope(loop_expr
.id
, [next_bcx_in
, cond_bcx_in
]);
245 Br(bcx
, cond_bcx_in
.llbb
, loop_expr
.debug_loc());
247 // compile the block where we will handle loop cleanups
248 let cleanup_llbb
= fcx
.normal_exit_block(loop_expr
.id
, cleanup
::EXIT_BREAK
);
250 // compile the condition
251 let Result {bcx: cond_bcx_out, val: cond_val}
=
252 expr
::trans(cond_bcx_in
, cond
).to_llbool();
254 CondBr(cond_bcx_out
, cond_val
, body_bcx_in
.llbb
, cleanup_llbb
, cond
.debug_loc());
257 let body_bcx_out
= trans_block(body_bcx_in
, body
, expr
::Ignore
);
258 Br(body_bcx_out
, cond_bcx_in
.llbb
, DebugLoc
::None
);
260 fcx
.pop_loop_cleanup_scope(loop_expr
.id
);
264 pub fn trans_loop
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
265 loop_expr
: &hir
::Expr
,
267 -> Block
<'blk
, 'tcx
> {
268 let _icx
= push_ctxt("trans_loop");
270 if bcx
.unreachable
.get() {
284 // Links between body_bcx_in and next_bcx are created by
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
);
290 fcx
.push_loop_cleanup_scope(loop_expr
.id
, [next_bcx_in
, body_bcx_in
]);
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
);
296 fcx
.pop_loop_cleanup_scope(loop_expr
.id
);
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
);
307 pub fn trans_break_cont
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
309 opt_label
: Option
<ast
::Name
>,
311 -> Block
<'blk
, 'tcx
> {
312 let _icx
= push_ctxt("trans_break_cont");
314 if bcx
.unreachable
.get() {
320 // Locate loop that we will break to
321 let loop_id
= match opt_label
{
322 None
=> fcx
.top_loop_scope(),
324 match bcx
.tcx().def_map
.borrow().get(&expr
.id
).map(|d
| d
.full_def()) {
325 Some(Def
::Label(loop_id
)) => loop_id
,
327 bcx
.tcx().sess
.bug(&format
!("{:?} in def-map for label", r
))
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
340 pub fn trans_break
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
342 label_opt
: Option
<ast
::Name
>)
343 -> Block
<'blk
, 'tcx
> {
344 return trans_break_cont(bcx
, expr
, label_opt
, cleanup
::EXIT_BREAK
);
347 pub fn trans_cont
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
349 label_opt
: Option
<ast
::Name
>)
350 -> Block
<'blk
, 'tcx
> {
351 return trans_break_cont(bcx
, expr
, label_opt
, cleanup
::EXIT_LOOP
);
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");
360 if bcx
.unreachable
.get() {
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"))
373 if let Some(x
) = retval_expr
{
374 bcx
= expr
::trans_into(bcx
, &x
, dest
);
376 expr
::SaveIn(slot
) if fcx
.needs_ret_allocas
=> {
377 Store(bcx
, slot
, fcx
.llretslotptr
.get().unwrap());
382 let cleanup_llbb
= fcx
.return_exit_block();
383 Br(bcx
, cleanup_llbb
, return_expr
.debug_loc());
388 pub fn trans_fail
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
389 call_info
: NodeIdAndSpan
,
390 fail_str
: InternedString
)
391 -> Block
<'blk
, 'tcx
> {
393 let _icx
= push_ctxt("trans_fail_value");
395 if bcx
.unreachable
.get() {
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
,
413 call_info
.debug_loc()).bcx
;
418 pub fn trans_fail_bounds_check
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
419 call_info
: NodeIdAndSpan
,
422 -> Block
<'blk
, 'tcx
> {
424 let _icx
= push_ctxt("trans_fail_bounds_check");
426 if bcx
.unreachable
.get() {
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
);
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
,
446 call_info
.debug_loc()).bcx
;