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.
4 //
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.
10
11 use lib::llvm::*;
12 use driver::session::FullDebugInfo;
13 use middle::lang_items::{FailFnLangItem, FailBoundsCheckFnLangItem};
14 use middle::trans::base::*;
15 use middle::trans::build::*;
16 use middle::trans::callee;
17 use middle::trans::common::*;
18 use middle::trans::debuginfo;
19 use middle::trans::cleanup;
20 use middle::trans::cleanup::CleanupMethods;
21 use middle::trans::expr;
22 use middle::ty;
23 use util::ppaux::Repr;
24
25 use middle::trans::type_::Type;
26
27 use syntax::ast;
28 use syntax::ast::Ident;
29 use syntax::ast_util;
30 use syntax::codemap::Span;
31 use syntax::parse::token::InternedString;
32 use syntax::parse::token;
33 use syntax::visit::Visitor;
34
35 pub fn trans_stmt<'a>(cx: &'a Block<'a>,
36 s: &ast::Stmt)
37 -> &'a Block<'a> {
38 let _icx = push_ctxt("trans_stmt");
39 let fcx = cx.fcx;
40 debug!("trans_stmt({})", s.repr(cx.tcx()));
41
42 if cx.sess().asm_comments() {
43 add_span_comment(cx, s.span, s.repr(cx.tcx()));
44 }
45
46 let mut bcx = cx;
47
48 let id = ast_util::stmt_id(s);
49 fcx.push_ast_cleanup_scope(id);
50
51 match s.node {
52 ast::StmtExpr(e, _) | ast::StmtSemi(e, _) => {
53 bcx = trans_stmt_semi(bcx, e);
54 }
55 ast::StmtDecl(d, _) => {
56 match d.node {
57 ast::DeclLocal(ref local) => {
58 bcx = init_local(bcx, *local);
59 if cx.sess().opts.debuginfo == FullDebugInfo {
60 debuginfo::create_local_var_metadata(bcx, *local);
61 }
62 }
63 ast::DeclItem(i) => trans_item(cx.fcx.ccx, i)
64 }
65 }
66 ast::StmtMac(..) => cx.tcx().sess.bug("unexpanded macro")
67 }
68
69 bcx = fcx.pop_and_trans_ast_cleanup_scope(
70 bcx, ast_util::stmt_id(s));
71
72 return bcx;
73 }
74
75 pub fn trans_stmt_semi<'a>(cx: &'a Block<'a>, e: &ast::Expr) -> &'a Block<'a> {
76 let _icx = push_ctxt("trans_stmt_semi");
77 let ty = expr_ty(cx, e);
78 if ty::type_needs_drop(cx.tcx(), ty) {
79 expr::trans_to_lvalue(cx, e, "stmt").bcx
80 } else {
81 expr::trans_into(cx, e, expr::Ignore)
82 }
83 }
84
85 pub fn trans_block<'a>(bcx: &'a Block<'a>,
86 b: &ast::Block,
87 mut dest: expr::Dest)
88 -> &'a Block<'a> {
89 let _icx = push_ctxt("trans_block");
90 let fcx = bcx.fcx;
91 let mut bcx = bcx;
92
93 fcx.push_ast_cleanup_scope(b.id);
94
95 for s in b.stmts.iter() {
96 bcx = trans_stmt(bcx, *s);
97 }
98
99 if dest != expr::Ignore {
100 let block_ty = node_id_type(bcx, b.id);
101 if b.expr.is_none() || type_is_zero_size(bcx.ccx(), block_ty) {
102 dest = expr::Ignore;
103 }
104 }
105
106 match b.expr {
107 Some(e) => {
108 bcx = expr::trans_into(bcx, e, dest);
109 }
110 None => {
111 assert!(dest == expr::Ignore || bcx.unreachable.get());
112 }
113 }
114
115 bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, b.id);
116
117 return bcx;
118 }
119
120 pub fn trans_if<'a>(bcx: &'a Block<'a>,
121 if_id: ast::NodeId,
122 cond: &ast::Expr,
123 thn: ast::P<ast::Block>,
124 els: Option<@ast::Expr>,
125 dest: expr::Dest)
126 -> &'a Block<'a> {
127 debug!("trans_if(bcx={}, if_id={}, cond={}, thn={:?}, dest={})",
128 bcx.to_str(), if_id, bcx.expr_to_str(cond), thn.id,
129 dest.to_str(bcx.ccx()));
130 let _icx = push_ctxt("trans_if");
131 let mut bcx = bcx;
132
133 let cond_val = unpack_result!(bcx, expr::trans(bcx, cond).to_llbool());
134
135 // Drop branches that are known to be impossible
136 if is_const(cond_val) && !is_undef(cond_val) {
137 if const_to_uint(cond_val) == 1 {
138 match els {
139 Some(elexpr) => {
140 let mut trans = TransItemVisitor { ccx: bcx.fcx.ccx };
141 trans.visit_expr(elexpr, ());
142 }
143 None => {}
144 }
145 // if true { .. } [else { .. }]
146 bcx = trans_block(bcx, thn, dest);
147 debuginfo::clear_source_location(bcx.fcx);
148 } else {
149 let mut trans = TransItemVisitor { ccx: bcx.fcx.ccx } ;
150 trans.visit_block(thn, ());
151
152 match els {
153 // if false { .. } else { .. }
154 Some(elexpr) => {
155 bcx = expr::trans_into(bcx, elexpr, dest);
156 debuginfo::clear_source_location(bcx.fcx);
157 }
158
159 // if false { .. }
160 None => { }
161 }
162 }
163
164 return bcx;
165 }
166
167 let name = format!("then-block-{}-", thn.id);
168 let then_bcx_in = bcx.fcx.new_id_block(name, thn.id);
169 let then_bcx_out = trans_block(then_bcx_in, thn, dest);
170 debuginfo::clear_source_location(bcx.fcx);
171
172 let next_bcx;
173 match els {
174 Some(elexpr) => {
175 let else_bcx_in = bcx.fcx.new_id_block("else-block", elexpr.id);
176 let else_bcx_out = expr::trans_into(else_bcx_in, elexpr, dest);
177 next_bcx = bcx.fcx.join_blocks(if_id,
178 [then_bcx_out, else_bcx_out]);
179 CondBr(bcx, cond_val, then_bcx_in.llbb, else_bcx_in.llbb);
180 }
181
182 None => {
183 next_bcx = bcx.fcx.new_id_block("next-block", if_id);
184 Br(then_bcx_out, next_bcx.llbb);
185 CondBr(bcx, cond_val, then_bcx_in.llbb, next_bcx.llbb);
186 }
187 }
188
189 // Clear the source location because it is still set to whatever has been translated
190 // right before.
191 debuginfo::clear_source_location(next_bcx.fcx);
192
193 next_bcx
194 }
195
196 pub fn trans_while<'a>(bcx: &'a Block<'a>,
197 loop_id: ast::NodeId,
198 cond: &ast::Expr,
199 body: &ast::Block)
200 -> &'a Block<'a> {
201 let _icx = push_ctxt("trans_while");
202 let fcx = bcx.fcx;
203
204 // bcx
205 // |
206 // cond_bcx_in <--------+
207 // | |
208 // cond_bcx_out |
209 // | | |
210 // | body_bcx_in |
211 // cleanup_blk | |
212 // | body_bcx_out --+
213 // next_bcx_in
214
215 let next_bcx_in = fcx.new_id_block("while_exit", loop_id);
216 let cond_bcx_in = fcx.new_id_block("while_cond", cond.id);
217 let body_bcx_in = fcx.new_id_block("while_body", body.id);
218
219 fcx.push_loop_cleanup_scope(loop_id, [next_bcx_in, cond_bcx_in]);
220
221 Br(bcx, cond_bcx_in.llbb);
222
223 // compile the block where we will handle loop cleanups
224 let cleanup_llbb = fcx.normal_exit_block(loop_id, cleanup::EXIT_BREAK);
225
226 // compile the condition
227 let Result {bcx: cond_bcx_out, val: cond_val} =
228 expr::trans(cond_bcx_in, cond).to_llbool();
229 CondBr(cond_bcx_out, cond_val, body_bcx_in.llbb, cleanup_llbb);
230
231 // loop body:
232 let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
233 Br(body_bcx_out, cond_bcx_in.llbb);
234
235 fcx.pop_loop_cleanup_scope(loop_id);
236 return next_bcx_in;
237 }
238
239 pub fn trans_loop<'a>(bcx:&'a Block<'a>,
240 loop_id: ast::NodeId,
241 body: &ast::Block)
242 -> &'a Block<'a> {
243 let _icx = push_ctxt("trans_loop");
244 let fcx = bcx.fcx;
245
246 // bcx
247 // |
248 // body_bcx_in
249 // |
250 // body_bcx_out
251 //
252 // next_bcx
253 //
254 // Links between body_bcx_in and next_bcx are created by
255 // break statements.
256
257 let next_bcx_in = bcx.fcx.new_id_block("loop_exit", loop_id);
258 let body_bcx_in = bcx.fcx.new_id_block("loop_body", body.id);
259
260 fcx.push_loop_cleanup_scope(loop_id, [next_bcx_in, body_bcx_in]);
261
262 Br(bcx, body_bcx_in.llbb);
263 let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
264 Br(body_bcx_out, body_bcx_in.llbb);
265
266 fcx.pop_loop_cleanup_scope(loop_id);
267
268 return next_bcx_in;
269 }
270
271 pub fn trans_break_cont<'a>(bcx: &'a Block<'a>,
272 expr_id: ast::NodeId,
273 opt_label: Option<Ident>,
274 exit: uint)
275 -> &'a Block<'a> {
276 let _icx = push_ctxt("trans_break_cont");
277 let fcx = bcx.fcx;
278
279 if bcx.unreachable.get() {
280 return bcx;
281 }
282
283 // Locate loop that we will break to
284 let loop_id = match opt_label {
285 None => fcx.top_loop_scope(),
286 Some(_) => {
287 match bcx.tcx().def_map.borrow().find(&expr_id) {
288 Some(&ast::DefLabel(loop_id)) => loop_id,
289 ref r => {
290 bcx.tcx().sess.bug(format!("{:?} in def-map for label", r))
291 }
292 }
293 }
294 };
295
296 // Generate appropriate cleanup code and branch
297 let cleanup_llbb = fcx.normal_exit_block(loop_id, exit);
298 Br(bcx, cleanup_llbb);
299 Unreachable(bcx); // anything afterwards should be ignored
300 return bcx;
301 }
302
303 pub fn trans_break<'a>(bcx: &'a Block<'a>,
304 expr_id: ast::NodeId,
305 label_opt: Option<Ident>)
306 -> &'a Block<'a> {
307 return trans_break_cont(bcx, expr_id, label_opt, cleanup::EXIT_BREAK);
308 }
309
310 pub fn trans_cont<'a>(bcx: &'a Block<'a>,
311 expr_id: ast::NodeId,
312 label_opt: Option<Ident>)
313 -> &'a Block<'a> {
314 return trans_break_cont(bcx, expr_id, label_opt, cleanup::EXIT_LOOP);
315 }
316
317 pub fn trans_ret<'a>(bcx: &'a Block<'a>,
318 e: Option<@ast::Expr>)
319 -> &'a Block<'a> {
320 let _icx = push_ctxt("trans_ret");
321 let fcx = bcx.fcx;
322 let mut bcx = bcx;
323 let dest = match bcx.fcx.llretptr.get() {
324 None => expr::Ignore,
325 Some(retptr) => expr::SaveIn(retptr),
326 };
327 match e {
328 Some(x) => {
329 bcx = expr::trans_into(bcx, x, dest);
330 }
331 _ => {}
332 }
333 let cleanup_llbb = fcx.return_exit_block();
334 Br(bcx, cleanup_llbb);
335 Unreachable(bcx);
336 return bcx;
337 }
338
339 pub fn trans_fail<'a>(
340 bcx: &'a Block<'a>,
341 sp: Span,
342 fail_str: InternedString)
343 -> &'a Block<'a> {
344 let ccx = bcx.ccx();
345 let v_fail_str = C_cstr(ccx, fail_str, true);
346 let _icx = push_ctxt("trans_fail_value");
347 let loc = bcx.sess().codemap().lookup_char_pos(sp.lo);
348 let v_filename = C_cstr(ccx,
349 token::intern_and_get_ident(loc.file
350 .name
351 .as_slice()),
352 true);
353 let v_line = loc.line as int;
354 let v_str = PointerCast(bcx, v_fail_str, Type::i8p(ccx));
355 let v_filename = PointerCast(bcx, v_filename, Type::i8p(ccx));
356 let args = vec!(v_str, v_filename, C_int(ccx, v_line));
357 let did = langcall(bcx, Some(sp), "", FailFnLangItem);
358 let bcx = callee::trans_lang_call(bcx,
359 did,
360 args.as_slice(),
361 Some(expr::Ignore)).bcx;
362 Unreachable(bcx);
363 return bcx;
364 }
365
366 pub fn trans_fail_bounds_check<'a>(
367 bcx: &'a Block<'a>,
368 sp: Span,
369 index: ValueRef,
370 len: ValueRef)
371 -> &'a Block<'a> {
372 let _icx = push_ctxt("trans_fail_bounds_check");
373 let (filename, line) = filename_and_line_num_from_span(bcx, sp);
374 let args = vec!(filename, line, index, len);
375 let did = langcall(bcx, Some(sp), "", FailBoundsCheckFnLangItem);
376 let bcx = callee::trans_lang_call(bcx,
377 did,
378 args.as_slice(),
379 Some(expr::Ignore)).bcx;
380 Unreachable(bcx);
381 return bcx;
382 }
librustc/middle/trans/controlflow.rs:74:1-74:1 -fn- definition:
pub fn trans_stmt_semi<'a>(cx: &'a Block<'a>, e: &ast::Expr) -> &'a Block<'a> {
let _icx = push_ctxt("trans_stmt_semi");
let ty = expr_ty(cx, e);
references:- 2librustc/middle/trans/base.rs:
976: Some(init) => {
977: return controlflow::trans_stmt_semi(bcx, init)
978: }
librustc/middle/trans/controlflow.rs:
52: ast::StmtExpr(e, _) | ast::StmtSemi(e, _) => {
53: bcx = trans_stmt_semi(bcx, e);
54: }
librustc/middle/trans/controlflow.rs:270:1-270:1 -fn- definition:
pub fn trans_break_cont<'a>(bcx: &'a Block<'a>,
expr_id: ast::NodeId,
opt_label: Option<Ident>,
references:- 2306: -> &'a Block<'a> {
307: return trans_break_cont(bcx, expr_id, label_opt, cleanup::EXIT_BREAK);
308: }
--
313: -> &'a Block<'a> {
314: return trans_break_cont(bcx, expr_id, label_opt, cleanup::EXIT_LOOP);
315: }
librustc/middle/trans/controlflow.rs:338:1-338:1 -fn- definition:
pub fn trans_fail<'a>(
bcx: &'a Block<'a>,
sp: Span,
references:- 2librustc/middle/trans/_match.rs:
1147: let fail_cx = fcx.new_block(false, "case_fallthrough", None);
1148: controlflow::trans_fail(fail_cx, self.sp, self.msg.clone());
1149: self.finished.set(Some(fail_cx.llbb));
librustc/middle/trans/base.rs:
840: with_cond(cx, is_zero, |bcx| {
841: controlflow::trans_fail(bcx, span, InternedString::new(text))
842: })
librustc/middle/trans/controlflow.rs:84:1-84:1 -fn- definition:
pub fn trans_block<'a>(bcx: &'a Block<'a>,
b: &ast::Block,
mut dest: expr::Dest)
references:- 6168: let then_bcx_in = bcx.fcx.new_id_block(name, thn.id);
169: let then_bcx_out = trans_block(then_bcx_in, thn, dest);
170: debuginfo::clear_source_location(bcx.fcx);
--
262: Br(bcx, body_bcx_in.llbb);
263: let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
264: Br(body_bcx_out, body_bcx_in.llbb);
librustc/middle/trans/expr.rs:
666: ast::ExprBlock(blk) => {
667: controlflow::trans_block(bcx, blk, dest)
668: }
librustc/middle/trans/base.rs:
1429: // (trans_block, trans_expr, et cetera).
1430: bcx = controlflow::trans_block(bcx, body, dest);