1 // Copyright 2015 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.
11 //! See docs in build/expr/mod.rs
13 use build
::{BlockAnd, BlockAndExtension, Builder}
;
14 use build
::expr
::category
::{Category, RvalueFunc}
;
15 use build
::scope
::LoopScope
;
17 use rustc
::middle
::region
::CodeExtent
;
19 use rustc
::mir
::repr
::*;
20 use syntax
::codemap
::Span
;
22 impl<'a
,'tcx
> Builder
<'a
,'tcx
> {
23 /// Compile `expr`, storing the result into `destination`, which
24 /// is assumed to be uninitialized.
25 pub fn into_expr(&mut self,
26 destination
: &Lvalue
<'tcx
>,
27 mut block
: BasicBlock
,
31 debug
!("into_expr(destination={:?}, block={:?}, expr={:?})",
32 destination
, block
, expr
);
34 // since we frequently have to reference `self` from within a
35 // closure, where `self` would be shadowed, it's easier to
36 // just use the name `this` uniformly
38 let expr_span
= expr
.span
;
39 let scope_id
= this
.innermost_scope_id();
42 ExprKind
::Scope { extent, value }
=> {
43 this
.in_scope(extent
, block
, |this
, _
| this
.into(destination
, block
, value
))
45 ExprKind
::Block { body: ast_block }
=> {
46 this
.ast_block(destination
, block
, ast_block
)
48 ExprKind
::Match { discriminant, arms }
=> {
49 this
.match_expr(destination
, expr_span
, block
, discriminant
, arms
)
51 ExprKind
::If { condition: cond_expr, then: then_expr, otherwise: else_expr }
=> {
52 let operand
= unpack
!(block
= this
.as_operand(block
, cond_expr
));
54 let mut then_block
= this
.cfg
.start_new_block();
55 let mut else_block
= this
.cfg
.start_new_block();
56 this
.cfg
.terminate(block
, scope_id
, expr_span
, TerminatorKind
::If
{
58 targets
: (then_block
, else_block
)
61 unpack
!(then_block
= this
.into(destination
, then_block
, then_expr
));
62 else_block
= if let Some(else_expr
) = else_expr
{
63 unpack
!(this
.into(destination
, else_block
, else_expr
))
65 // Body of the `if` expression without an `else` clause must return `()`, thus
66 // we implicitly generate a `else {}` if it is not specified.
67 let scope_id
= this
.innermost_scope_id();
68 this
.cfg
.push_assign_unit(else_block
, scope_id
, expr_span
, destination
);
72 let join_block
= this
.cfg
.start_new_block();
73 this
.cfg
.terminate(then_block
,
76 TerminatorKind
::Goto { target: join_block }
);
77 this
.cfg
.terminate(else_block
,
80 TerminatorKind
::Goto { target: join_block }
);
84 ExprKind
::LogicalOp { op, lhs, rhs }
=> {
87 // [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block]
89 // +----------false-----------+------------------> [false_block]
93 // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
95 // +----------true------------+-------------------> [false_block]
97 let (true_block
, false_block
, mut else_block
, join_block
) =
98 (this
.cfg
.start_new_block(), this
.cfg
.start_new_block(),
99 this
.cfg
.start_new_block(), this
.cfg
.start_new_block());
101 let lhs
= unpack
!(block
= this
.as_operand(block
, lhs
));
102 let blocks
= match op
{
103 LogicalOp
::And
=> (else_block
, false_block
),
104 LogicalOp
::Or
=> (true_block
, else_block
),
106 this
.cfg
.terminate(block
,
109 TerminatorKind
::If { cond: lhs, targets: blocks }
);
111 let rhs
= unpack
!(else_block
= this
.as_operand(else_block
, rhs
));
112 this
.cfg
.terminate(else_block
, scope_id
, expr_span
, TerminatorKind
::If
{
114 targets
: (true_block
, false_block
)
117 this
.cfg
.push_assign_constant(
118 true_block
, scope_id
, expr_span
, destination
,
121 ty
: this
.hir
.bool_ty(),
122 literal
: this
.hir
.true_literal(),
125 this
.cfg
.push_assign_constant(
126 false_block
, scope_id
, expr_span
, destination
,
129 ty
: this
.hir
.bool_ty(),
130 literal
: this
.hir
.false_literal(),
133 this
.cfg
.terminate(true_block
,
136 TerminatorKind
::Goto { target: join_block }
);
137 this
.cfg
.terminate(false_block
,
140 TerminatorKind
::Goto { target: join_block }
);
144 ExprKind
::Loop { condition: opt_cond_expr, body }
=> {
145 // [block] --> [loop_block] ~~> [loop_block_end] -1-> [exit_block]
150 // [body_block_end] <~~~ [body_block]
152 // If `opt_cond_expr` is `None`, then the graph is somewhat simplified:
154 // [block] --> [loop_block / body_block ] ~~> [body_block_end] [exit_block]
157 // +--------------------------+
160 let loop_block
= this
.cfg
.start_new_block();
161 let exit_block
= this
.cfg
.start_new_block();
164 this
.cfg
.terminate(block
,
167 TerminatorKind
::Goto { target: loop_block }
);
169 let might_break
= this
.in_loop_scope(loop_block
, exit_block
, move |this
| {
170 // conduct the test, if necessary
172 if let Some(cond_expr
) = opt_cond_expr
{
173 // This loop has a condition, ergo its exit_block is reachable.
174 this
.find_loop_scope(expr_span
, None
).might_break
= true;
177 let cond
= unpack
!(loop_block_end
= this
.as_operand(loop_block
, cond_expr
));
178 body_block
= this
.cfg
.start_new_block();
179 this
.cfg
.terminate(loop_block_end
,
184 targets
: (body_block
, exit_block
)
187 body_block
= loop_block
;
190 // The “return” value of the loop body must always be an unit, but we cannot
191 // reuse that as a “return” value of the whole loop expressions, because some
192 // loops are diverging (e.g. `loop {}`). Thus, we introduce a unit temporary as
193 // the destination for the loop body and assign the loop’s own “return” value
194 // immediately after the iteration is finished.
195 let tmp
= this
.get_unit_temp();
196 // Execute the body, branching back to the test.
197 let body_block_end
= unpack
!(this
.into(&tmp
, body_block
, body
));
198 this
.cfg
.terminate(body_block_end
,
201 TerminatorKind
::Goto { target: loop_block }
);
203 // If the loop may reach its exit_block, we assign an empty tuple to the
204 // destination to keep the MIR well-formed.
206 this
.cfg
.push_assign_unit(exit_block
, scope_id
, expr_span
, destination
);
210 ExprKind
::Assign { lhs, rhs }
=> {
211 // Note: we evaluate assignments right-to-left. This
212 // is better for borrowck interaction with overloaded
213 // operators like x[j] = x[i].
214 let lhs
= this
.hir
.mirror(lhs
);
215 let lhs_span
= lhs
.span
;
216 let rhs
= unpack
!(block
= this
.as_operand(block
, rhs
));
217 let lhs
= unpack
!(block
= this
.as_lvalue(block
, lhs
));
218 unpack
!(block
= this
.build_drop(block
, lhs_span
, lhs
.clone()));
219 this
.cfg
.push_assign(block
, scope_id
, expr_span
, &lhs
, Rvalue
::Use(rhs
));
222 ExprKind
::AssignOp { op, lhs, rhs }
=> {
223 // FIXME(#28160) there is an interesting semantics
224 // question raised here -- should we "freeze" the
225 // value of the lhs here? I'm inclined to think not,
226 // since it seems closer to the semantics of the
227 // overloaded version, which takes `&mut self`. This
228 // only affects weird things like `x += {x += 1; x}`
229 // -- is that equal to `x + (x + 1)` or `2*(x+1)`?
232 let rhs
= unpack
!(block
= this
.as_operand(block
, rhs
));
233 let lhs
= unpack
!(block
= this
.as_lvalue(block
, lhs
));
235 // we don't have to drop prior contents or anything
236 // because AssignOp is only legal for Copy types
237 // (overloaded ops should be desugared into a call).
238 this
.cfg
.push_assign(block
, scope_id
, expr_span
, &lhs
,
240 Operand
::Consume(lhs
.clone()),
245 ExprKind
::Continue { label }
=> {
246 this
.break_or_continue(expr_span
, label
, block
,
247 |loop_scope
| loop_scope
.continue_block
)
249 ExprKind
::Break { label }
=> {
250 this
.break_or_continue(expr_span
, label
, block
, |loop_scope
| {
251 loop_scope
.might_break
= true;
252 loop_scope
.break_block
255 ExprKind
::Return { value }
=> {
256 block
= match value
{
257 Some(value
) => unpack
!(this
.into(&Lvalue
::ReturnPointer
, block
, value
)),
259 this
.cfg
.push_assign_unit(block
, scope_id
,
260 expr_span
, &Lvalue
::ReturnPointer
);
264 let extent
= this
.extent_of_return_scope();
265 this
.exit_scope(expr_span
, extent
, block
, END_BLOCK
);
266 this
.cfg
.start_new_block().unit()
268 ExprKind
::Call { ty, fun, args }
=> {
269 let diverges
= match ty
.sty
{
270 ty
::TyFnDef(_
, _
, ref f
) | ty
::TyFnPtr(ref f
) => {
271 f
.sig
.0.output
.diverges()
275 let fun
= unpack
!(block
= this
.as_operand(block
, fun
));
278 .map(|arg
| unpack
!(block
= this
.as_operand(block
, arg
)))
281 let success
= this
.cfg
.start_new_block();
282 let cleanup
= this
.diverge_cleanup();
283 this
.cfg
.terminate(block
, scope_id
, expr_span
, TerminatorKind
::Call
{
287 destination
: if diverges
{
290 Some ((destination
.clone(), success
))
296 // these are the cases that are more naturally handled by some other mode
297 ExprKind
::Unary { .. }
|
298 ExprKind
::Binary { .. }
|
299 ExprKind
::Box { .. }
|
300 ExprKind
::Cast { .. }
|
301 ExprKind
::ReifyFnPointer { .. }
|
302 ExprKind
::UnsafeFnPointer { .. }
|
303 ExprKind
::Unsize { .. }
|
304 ExprKind
::Repeat { .. }
|
305 ExprKind
::Borrow { .. }
|
306 ExprKind
::VarRef { .. }
|
308 ExprKind
::StaticRef { .. }
|
309 ExprKind
::Vec { .. }
|
310 ExprKind
::Tuple { .. }
|
311 ExprKind
::Adt { .. }
|
312 ExprKind
::Closure { .. }
|
313 ExprKind
::Index { .. }
|
314 ExprKind
::Deref { .. }
|
315 ExprKind
::Literal { .. }
|
316 ExprKind
::InlineAsm { .. }
|
317 ExprKind
::Field { .. }
=> {
318 debug_assert
!(match Category
::of(&expr
.kind
).unwrap() {
319 Category
::Rvalue(RvalueFunc
::Into
) => false,
323 let rvalue
= unpack
!(block
= this
.as_rvalue(block
, expr
));
324 this
.cfg
.push_assign(block
, scope_id
, expr_span
, destination
, rvalue
);
330 fn break_or_continue
<F
>(&mut self,
332 label
: Option
<CodeExtent
>,
336 where F
: FnOnce(&mut LoopScope
) -> BasicBlock
338 let (exit_block
, extent
) = {
339 let loop_scope
= self.find_loop_scope(span
, label
);
340 (exit_selector(loop_scope
), loop_scope
.extent
)
342 self.exit_scope(span
, extent
, block
, exit_block
);
343 self.cfg
.start_new_block().unit()