1 //! See docs in build/expr/mod.rs
3 use crate::build
::expr
::category
::{Category, RvalueFunc}
;
4 use crate::build
::{BlockAnd, BlockAndExtension, BlockFrame, Builder}
;
9 use rustc_target
::spec
::abi
::Abi
;
11 impl<'a
, 'tcx
> Builder
<'a
, 'tcx
> {
12 /// Compile `expr`, storing the result into `destination`, which
13 /// is assumed to be uninitialized.
16 destination
: &Place
<'tcx
>,
17 mut block
: BasicBlock
,
21 "into_expr(destination={:?}, block={:?}, expr={:?})",
22 destination
, block
, expr
25 // since we frequently have to reference `self` from within a
26 // closure, where `self` would be shadowed, it's easier to
27 // just use the name `this` uniformly
29 let expr_span
= expr
.span
;
30 let source_info
= this
.source_info(expr_span
);
32 let expr_is_block_or_scope
= match expr
.kind
{
33 ExprKind
::Block { .. }
=> true,
34 ExprKind
::Scope { .. }
=> true,
38 if !expr_is_block_or_scope
{
39 this
.block_context
.push(BlockFrame
::SubExpr
);
42 let block_and
= match expr
.kind
{
48 let region_scope
= (region_scope
, source_info
);
49 this
.in_scope(region_scope
, lint_level
, |this
| {
50 this
.into(destination
, block
, value
)
53 ExprKind
::Block { body: ast_block }
=> {
54 this
.ast_block(destination
, block
, ast_block
, source_info
)
56 ExprKind
::Match { scrutinee, arms }
=> {
57 this
.match_expr(destination
, expr_span
, block
, scrutinee
, arms
)
59 ExprKind
::NeverToAny { source }
=> {
60 let source
= this
.hir
.mirror(source
);
61 let is_call
= match source
.kind
{
62 ExprKind
::Call { .. }
=> true,
66 unpack
!(block
= this
.as_local_rvalue(block
, source
));
68 // This is an optimization. If the expression was a call then we already have an
69 // unreachable block. Don't bother to terminate it and create a new one.
74 .terminate(block
, source_info
, TerminatorKind
::Unreachable
);
75 let end_block
= this
.cfg
.start_new_block();
79 ExprKind
::LogicalOp { op, lhs, rhs }
=> {
82 // [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block]
84 // +----------false-----------+------------------> [false_block]
88 // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
90 // [true_block] [false_block]
92 let (true_block
, false_block
, mut else_block
, join_block
) = (
93 this
.cfg
.start_new_block(),
94 this
.cfg
.start_new_block(),
95 this
.cfg
.start_new_block(),
96 this
.cfg
.start_new_block(),
99 let lhs
= unpack
!(block
= this
.as_local_operand(block
, lhs
));
100 let blocks
= match op
{
101 LogicalOp
::And
=> (else_block
, false_block
),
102 LogicalOp
::Or
=> (true_block
, else_block
),
104 let term
= TerminatorKind
::if_(this
.hir
.tcx(), lhs
, blocks
.0, blocks
.1);
105 this
.cfg
.terminate(block
, source_info
, term
);
107 let rhs
= unpack
!(else_block
= this
.as_local_operand(else_block
, rhs
));
108 let term
= TerminatorKind
::if_(this
.hir
.tcx(), rhs
, true_block
, false_block
);
109 this
.cfg
.terminate(else_block
, source_info
, term
);
111 this
.cfg
.push_assign_constant(
117 ty
: this
.hir
.bool_ty(),
119 literal
: this
.hir
.true_literal(),
123 this
.cfg
.push_assign_constant(
129 ty
: this
.hir
.bool_ty(),
131 literal
: this
.hir
.false_literal(),
138 TerminatorKind
::Goto { target: join_block }
,
143 TerminatorKind
::Goto { target: join_block }
,
148 ExprKind
::Loop { body }
=> {
151 // [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
154 // | +-----------------------------------------+
155 // +-> [diverge_cleanup]
156 // The false link is required to make sure borrowck considers unwinds through the
157 // body, even when the exact code in the body cannot unwind
159 let loop_block
= this
.cfg
.start_new_block();
160 let exit_block
= this
.cfg
.start_new_block();
166 TerminatorKind
::Goto { target: loop_block }
,
169 this
.in_breakable_scope(
174 // conduct the test, if necessary
175 let body_block
= this
.cfg
.start_new_block();
176 let diverge_cleanup
= this
.diverge_cleanup();
180 TerminatorKind
::FalseUnwind
{
181 real_target
: body_block
,
182 unwind
: Some(diverge_cleanup
),
186 // The “return” value of the loop body must always be an unit. We therefore
187 // introduce a unit temporary as the destination for the loop body.
188 let tmp
= this
.get_unit_temp();
189 // Execute the body, branching back to the test.
190 let body_block_end
= unpack
!(this
.into(&tmp
, body_block
, body
));
194 TerminatorKind
::Goto { target: loop_block }
,
200 ExprKind
::Call { ty, fun, args, from_hir_call }
=> {
201 let intrinsic
= match ty
.sty
{
202 ty
::FnDef(def_id
, _
) => {
203 let f
= ty
.fn_sig(this
.hir
.tcx());
204 if f
.abi() == Abi
::RustIntrinsic
|| f
.abi() == Abi
::PlatformIntrinsic
{
205 Some(this
.hir
.tcx().item_name(def_id
).as_str())
212 let intrinsic
= intrinsic
.as_ref().map(|s
| &s
[..]);
213 let fun
= unpack
!(block
= this
.as_local_operand(block
, fun
));
214 if intrinsic
== Some("move_val_init") {
215 // `move_val_init` has "magic" semantics - the second argument is
216 // always evaluated "directly" into the first one.
218 let mut args
= args
.into_iter();
219 let ptr
= args
.next().expect("0 arguments to `move_val_init`");
220 let val
= args
.next().expect("1 argument to `move_val_init`");
221 assert
!(args
.next().is_none(), ">2 arguments to `move_val_init`");
223 let ptr
= this
.hir
.mirror(ptr
);
225 // Create an *internal* temp for the pointer, so that unsafety
226 // checking won't complain about the raw pointer assignment.
227 let ptr_temp
= this
.local_decls
.push(LocalDecl
{
228 mutability
: Mutability
::Mut
,
230 user_ty
: UserTypeProjections
::none(),
233 visibility_scope
: source_info
.scope
,
235 is_user_variable
: None
,
238 let ptr_temp
= Place
::from(ptr_temp
);
239 let block
= unpack
!(this
.into(&ptr_temp
, block
, ptr
));
240 this
.into(&ptr_temp
.deref(), block
, val
)
242 let args
: Vec
<_
> = args
244 .map(|arg
| unpack
!(block
= this
.as_local_operand(block
, arg
)))
247 let success
= this
.cfg
.start_new_block();
248 let cleanup
= this
.diverge_cleanup();
252 TerminatorKind
::Call
{
255 cleanup
: Some(cleanup
),
256 // FIXME(varkor): replace this with an uninhabitedness-based check.
257 // This requires getting access to the current module to call
258 // `tcx.is_ty_uninhabited_from`, which is currently tricky to do.
259 destination
: if expr
.ty
.is_never() {
262 Some((destination
.clone(), success
))
270 ExprKind
::Use { source }
=> {
271 this
.into(destination
, block
, source
)
274 // These cases don't actually need a destination
275 ExprKind
::Assign { .. }
276 | ExprKind
::AssignOp { .. }
277 | ExprKind
::Continue { .. }
278 | ExprKind
::Break { .. }
279 | ExprKind
::InlineAsm { .. }
280 | ExprKind
::Return { .. }
=> {
281 unpack
!(block
= this
.stmt_expr(block
, expr
, None
));
282 this
.cfg
.push_assign_unit(block
, source_info
, destination
);
286 // Avoid creating a temporary
287 ExprKind
::VarRef { .. }
|
289 ExprKind
::StaticRef { .. }
|
290 ExprKind
::PlaceTypeAscription { .. }
|
291 ExprKind
::ValueTypeAscription { .. }
=> {
292 debug_assert
!(Category
::of(&expr
.kind
) == Some(Category
::Place
));
294 let place
= unpack
!(block
= this
.as_place(block
, expr
));
295 let rvalue
= Rvalue
::Use(this
.consume_by_copy_or_move(place
));
297 .push_assign(block
, source_info
, destination
, rvalue
);
300 ExprKind
::Index { .. }
| ExprKind
::Deref { .. }
| ExprKind
::Field { .. }
=> {
301 debug_assert
!(Category
::of(&expr
.kind
) == Some(Category
::Place
));
303 // Create a "fake" temporary variable so that we check that the
304 // value is Sized. Usually, this is caught in type checking, but
305 // in the case of box expr there is no such check.
306 if destination
.projection
.is_some() {
308 .push(LocalDecl
::new_temp(expr
.ty
, expr
.span
));
311 debug_assert
!(Category
::of(&expr
.kind
) == Some(Category
::Place
));
313 let place
= unpack
!(block
= this
.as_place(block
, expr
));
314 let rvalue
= Rvalue
::Use(this
.consume_by_copy_or_move(place
));
316 .push_assign(block
, source_info
, destination
, rvalue
);
320 // these are the cases that are more naturally handled by some other mode
321 ExprKind
::Unary { .. }
322 | ExprKind
::Binary { .. }
323 | ExprKind
::Box { .. }
324 | ExprKind
::Cast { .. }
325 | ExprKind
::Pointer { .. }
326 | ExprKind
::Repeat { .. }
327 | ExprKind
::Borrow { .. }
328 | ExprKind
::Array { .. }
329 | ExprKind
::Tuple { .. }
330 | ExprKind
::Adt { .. }
331 | ExprKind
::Closure { .. }
332 | ExprKind
::Literal { .. }
333 | ExprKind
::Yield { .. }
=> {
334 debug_assert
!(match Category
::of(&expr
.kind
).unwrap() {
335 // should be handled above
336 Category
::Rvalue(RvalueFunc
::Into
) => false,
338 // must be handled above or else we get an
339 // infinite loop in the builder; see
340 // e.g., `ExprKind::VarRef` above
341 Category
::Place
=> false,
346 let rvalue
= unpack
!(block
= this
.as_local_rvalue(block
, expr
));
347 this
.cfg
.push_assign(block
, source_info
, destination
, rvalue
);
352 if !expr_is_block_or_scope
{
353 let popped
= this
.block_context
.pop();
354 assert
!(popped
.is_some());