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 rustc
::hir
::def
::Def
;
13 use middle
::lang_items
::{PanicFnLangItem, PanicBoundsCheckFnLangItem}
;
14 use rustc
::ty
::subst
::Substs
;
16 use basic_block
::BasicBlock
;
18 use callee
::{Callee, ArgVals}
;
19 use cleanup
::CleanupMethods
;
24 use debuginfo
::{DebugLoc, ToDebugLoc}
;
31 use syntax
::parse
::token
::InternedString
;
32 use syntax
::parse
::token
;
34 pub fn trans_stmt
<'blk
, 'tcx
>(cx
: Block
<'blk
, 'tcx
>,
36 -> Block
<'blk
, 'tcx
> {
37 let _icx
= push_ctxt("trans_stmt");
39 debug
!("trans_stmt({:?})", s
);
41 if cx
.unreachable
.get() {
45 if cx
.sess().asm_comments() {
46 add_span_comment(cx
, s
.span
, &format
!("{:?}", s
));
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
);
57 hir
::StmtExpr(ref e
, _
) | hir
::StmtSemi(ref e
, _
) => {
58 bcx
= trans_stmt_semi(bcx
, &e
);
60 hir
::StmtDecl(ref d
, _
) => {
62 hir
::DeclLocal(ref local
) => {
63 bcx
= init_local(bcx
, &local
);
64 debuginfo
::create_local_var_metadata(bcx
, &local
);
66 // Inner items are visited by `trans_item`/`trans_meth`.
67 hir
::DeclItem(_
) => {}
,
72 bcx
= fcx
.pop_and_trans_ast_cleanup_scope(bcx
, s
.node
.id());
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");
81 if cx
.unreachable
.get() {
85 let ty
= expr_ty(cx
, e
);
86 if cx
.fcx
.type_needs_drop(ty
) {
87 expr
::trans_to_lvalue(cx
, e
, "stmt").bcx
89 expr
::trans_into(cx
, e
, expr
::Ignore
)
93 pub fn trans_block
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
96 -> Block
<'blk
, 'tcx
> {
97 let _icx
= push_ctxt("trans_block");
99 if bcx
.unreachable
.get() {
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
);
111 bcx
= trans_stmt(bcx
, s
);
114 if dest
!= expr
::Ignore
{
115 let block_ty
= node_id_type(bcx
, b
.id
);
117 if b
.expr
.is_none() || type_is_zero_size(bcx
.ccx(), block_ty
) {
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
) {
132 if !bcx
.unreachable
.get() {
133 bcx
= expr
::trans_into(bcx
, &e
, dest
);
137 assert
!(dest
== expr
::Ignore
|| bcx
.unreachable
.get());
141 bcx
= fcx
.pop_and_trans_ast_cleanup_scope(bcx
, b
.id
);
146 pub fn trans_if
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
150 els
: Option
<&hir
::Expr
>,
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");
157 if bcx
.unreachable
.get() {
163 let cond_val
= unpack_result
!(bcx
, expr
::trans(bcx
, cond
).to_llbool());
165 // Drop branches that are known to be impossible
166 if let Some(cv
) = const_to_opt_uint(cond_val
) {
168 // if true { .. } [else { .. }]
169 bcx
= trans_block(bcx
, &thn
, dest
);
170 debuginfo
::clear_source_location(bcx
.fcx
);
172 if let Some(elexpr
) = els
{
173 bcx
= expr
::trans_into(bcx
, &elexpr
, dest
);
174 debuginfo
::clear_source_location(bcx
.fcx
);
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
);
186 let cond_source_loc
= cond
.debug_loc();
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
);
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
);
205 // Clear the source location because it is still set to whatever has been translated
207 debuginfo
::clear_source_location(next_bcx
.fcx
);
212 pub fn trans_while
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
213 loop_expr
: &hir
::Expr
,
216 -> Block
<'blk
, 'tcx
> {
217 let _icx
= push_ctxt("trans_while");
219 if bcx
.unreachable
.get() {
227 // cond_bcx_in <--------+
233 // | body_bcx_out --+
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
);
240 fcx
.push_loop_cleanup_scope(loop_expr
.id
, [next_bcx_in
, cond_bcx_in
]);
242 Br(bcx
, cond_bcx_in
.llbb
, loop_expr
.debug_loc());
244 // compile the block where we will handle loop cleanups
245 let cleanup_llbb
= fcx
.normal_exit_block(loop_expr
.id
, cleanup
::EXIT_BREAK
);
247 // compile the condition
248 let Result {bcx: cond_bcx_out, val: cond_val}
=
249 expr
::trans(cond_bcx_in
, cond
).to_llbool();
251 CondBr(cond_bcx_out
, cond_val
, body_bcx_in
.llbb
, cleanup_llbb
, cond
.debug_loc());
254 let body_bcx_out
= trans_block(body_bcx_in
, body
, expr
::Ignore
);
255 Br(body_bcx_out
, cond_bcx_in
.llbb
, DebugLoc
::None
);
257 fcx
.pop_loop_cleanup_scope(loop_expr
.id
);
261 pub fn trans_loop
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
262 loop_expr
: &hir
::Expr
,
264 -> Block
<'blk
, 'tcx
> {
265 let _icx
= push_ctxt("trans_loop");
267 if bcx
.unreachable
.get() {
281 // Links between body_bcx_in and next_bcx are created by
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
);
287 fcx
.push_loop_cleanup_scope(loop_expr
.id
, [next_bcx_in
, body_bcx_in
]);
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
);
293 fcx
.pop_loop_cleanup_scope(loop_expr
.id
);
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
);
304 pub fn trans_break_cont
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
306 opt_label
: Option
<ast
::Name
>,
308 -> Block
<'blk
, 'tcx
> {
309 let _icx
= push_ctxt("trans_break_cont");
311 if bcx
.unreachable
.get() {
317 // Locate loop that we will break to
318 let loop_id
= match opt_label
{
319 None
=> fcx
.top_loop_scope(),
321 match bcx
.tcx().def_map
.borrow().get(&expr
.id
).map(|d
| d
.full_def()) {
322 Some(Def
::Label(loop_id
)) => loop_id
,
324 bug
!("{:?} in def-map for label", r
)
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
337 pub fn trans_break
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
339 label_opt
: Option
<ast
::Name
>)
340 -> Block
<'blk
, 'tcx
> {
341 return trans_break_cont(bcx
, expr
, label_opt
, cleanup
::EXIT_BREAK
);
344 pub fn trans_cont
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
346 label_opt
: Option
<ast
::Name
>)
347 -> Block
<'blk
, 'tcx
> {
348 return trans_break_cont(bcx
, expr
, label_opt
, cleanup
::EXIT_LOOP
);
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");
357 if bcx
.unreachable
.get() {
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"))
369 bcx
= expr
::trans_into(bcx
, &x
, dest
);
371 expr
::SaveIn(slot
) if fcx
.needs_ret_allocas
=> {
372 Store(bcx
, slot
, fcx
.llretslotptr
.get().unwrap());
377 let cleanup_llbb
= fcx
.return_exit_block();
378 Br(bcx
, cleanup_llbb
, return_expr
.debug_loc());
383 pub fn trans_fail
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
384 call_info
: NodeIdAndSpan
,
385 fail_str
: InternedString
)
386 -> Block
<'blk
, 'tcx
> {
388 let _icx
= push_ctxt("trans_fail_value");
390 if bcx
.unreachable
.get() {
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
408 pub fn trans_fail_bounds_check
<'blk
, 'tcx
>(bcx
: Block
<'blk
, 'tcx
>,
409 call_info
: NodeIdAndSpan
,
412 -> Block
<'blk
, 'tcx
> {
414 let _icx
= push_ctxt("trans_fail_bounds_check");
416 if bcx
.unreachable
.get() {
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
);
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