1 //! See docs in build/expr/mod.rs
3 use crate::build
::expr
::category
::{Category, RvalueFunc}
;
4 use crate::build
::{BlockAnd, BlockAndExtension, BlockFrame, Builder}
;
6 use rustc_ast
::InlineAsmOptions
;
7 use rustc_data_structures
::fx
::FxHashMap
;
8 use rustc_data_structures
::stack
::ensure_sufficient_stack
;
10 use rustc_middle
::mir
::*;
11 use rustc_middle
::ty
::{self, CanonicalUserTypeAnnotation}
;
12 use rustc_span
::symbol
::sym
;
14 use rustc_target
::spec
::abi
::Abi
;
16 impl<'a
, 'tcx
> Builder
<'a
, 'tcx
> {
17 /// Compile `expr`, storing the result into `destination`, which
18 /// is assumed to be uninitialized.
21 destination
: Place
<'tcx
>,
22 mut block
: BasicBlock
,
25 debug
!("into_expr(destination={:?}, block={:?}, expr={:?})", destination
, block
, expr
);
27 // since we frequently have to reference `self` from within a
28 // closure, where `self` would be shadowed, it's easier to
29 // just use the name `this` uniformly
31 let expr_span
= expr
.span
;
32 let source_info
= this
.source_info(expr_span
);
34 let expr_is_block_or_scope
= match expr
.kind
{
35 ExprKind
::Block { .. }
=> true,
36 ExprKind
::Scope { .. }
=> true,
40 if !expr_is_block_or_scope
{
41 this
.block_context
.push(BlockFrame
::SubExpr
);
44 let block_and
= match expr
.kind
{
45 ExprKind
::Scope { region_scope, lint_level, value }
=> {
46 let region_scope
= (region_scope
, source_info
);
47 ensure_sufficient_stack(|| {
48 this
.in_scope(region_scope
, lint_level
, |this
| {
49 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 { .. }
| ExprKind
::InlineAsm { .. }
=> true,
66 // (#66975) Source could be a const of type `!`, so has to
67 // exist in the generated MIR.
68 unpack
!(block
= this
.as_temp(block
, this
.local_scope(), source
, Mutability
::Mut
,));
70 // This is an optimization. If the expression was a call then we already have an
71 // unreachable block. Don't bother to terminate it and create a new one.
75 this
.cfg
.terminate(block
, source_info
, TerminatorKind
::Unreachable
);
76 let end_block
= this
.cfg
.start_new_block();
80 ExprKind
::LogicalOp { op, lhs, rhs }
=> {
83 // [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block]
85 // +----------false-----------+------------------> [false_block]
89 // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
91 // [true_block] [false_block]
93 let (true_block
, false_block
, mut else_block
, join_block
) = (
94 this
.cfg
.start_new_block(),
95 this
.cfg
.start_new_block(),
96 this
.cfg
.start_new_block(),
97 this
.cfg
.start_new_block(),
100 let lhs
= unpack
!(block
= this
.as_local_operand(block
, lhs
));
101 let blocks
= match op
{
102 LogicalOp
::And
=> (else_block
, false_block
),
103 LogicalOp
::Or
=> (true_block
, else_block
),
105 let term
= TerminatorKind
::if_(this
.hir
.tcx(), lhs
, blocks
.0, blocks
.1);
106 this
.cfg
.terminate(block
, source_info
, term
);
108 let rhs
= unpack
!(else_block
= this
.as_local_operand(else_block
, rhs
));
109 let term
= TerminatorKind
::if_(this
.hir
.tcx(), rhs
, true_block
, false_block
);
110 this
.cfg
.terminate(else_block
, source_info
, term
);
112 this
.cfg
.push_assign_constant(
116 Constant { span: expr_span, user_ty: None, literal: this.hir.true_literal() }
,
119 this
.cfg
.push_assign_constant(
123 Constant { span: expr_span, user_ty: None, literal: this.hir.false_literal() }
,
126 // Link up both branches:
127 this
.cfg
.goto(true_block
, source_info
, join_block
);
128 this
.cfg
.goto(false_block
, source_info
, join_block
);
131 ExprKind
::Loop { body }
=> {
134 // [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
137 // | +-----------------------------------------+
138 // +-> [diverge_cleanup]
139 // The false link is required to make sure borrowck considers unwinds through the
140 // body, even when the exact code in the body cannot unwind
142 let loop_block
= this
.cfg
.start_new_block();
143 let exit_block
= this
.cfg
.start_new_block();
146 this
.cfg
.goto(block
, source_info
, loop_block
);
148 this
.in_breakable_scope(Some(loop_block
), exit_block
, destination
, move |this
| {
149 // conduct the test, if necessary
150 let body_block
= this
.cfg
.start_new_block();
151 let diverge_cleanup
= this
.diverge_cleanup();
155 TerminatorKind
::FalseUnwind
{
156 real_target
: body_block
,
157 unwind
: Some(diverge_cleanup
),
161 // The “return” value of the loop body must always be an unit. We therefore
162 // introduce a unit temporary as the destination for the loop body.
163 let tmp
= this
.get_unit_temp();
164 // Execute the body, branching back to the test.
165 let body_block_end
= unpack
!(this
.into(tmp
, body_block
, body
));
166 this
.cfg
.goto(body_block_end
, source_info
, loop_block
);
170 ExprKind
::Call { ty, fun, args, from_hir_call, fn_span }
=> {
171 let intrinsic
= match ty
.kind
{
172 ty
::FnDef(def_id
, _
) => {
173 let f
= ty
.fn_sig(this
.hir
.tcx());
174 if f
.abi() == Abi
::RustIntrinsic
|| f
.abi() == Abi
::PlatformIntrinsic
{
175 Some(this
.hir
.tcx().item_name(def_id
))
182 let fun
= unpack
!(block
= this
.as_local_operand(block
, fun
));
183 if let Some(sym
::move_val_init
) = intrinsic
{
184 // `move_val_init` has "magic" semantics - the second argument is
185 // always evaluated "directly" into the first one.
187 let mut args
= args
.into_iter();
188 let ptr
= args
.next().expect("0 arguments to `move_val_init`");
189 let val
= args
.next().expect("1 argument to `move_val_init`");
190 assert
!(args
.next().is_none(), ">2 arguments to `move_val_init`");
192 let ptr
= this
.hir
.mirror(ptr
);
194 // Create an *internal* temp for the pointer, so that unsafety
195 // checking won't complain about the raw pointer assignment.
198 .push(LocalDecl
::with_source_info(ptr_ty
, source_info
).internal());
199 let ptr_temp
= Place
::from(ptr_temp
);
200 let block
= unpack
!(this
.into(ptr_temp
, block
, ptr
));
201 this
.into(this
.hir
.tcx().mk_place_deref(ptr_temp
), block
, val
)
203 let args
: Vec
<_
> = args
205 .map(|arg
| unpack
!(block
= this
.as_local_call_operand(block
, arg
)))
208 let success
= this
.cfg
.start_new_block();
209 let cleanup
= this
.diverge_cleanup();
211 this
.record_operands_moved(&args
);
213 debug
!("into_expr: fn_span={:?}", fn_span
);
218 TerminatorKind
::Call
{
221 cleanup
: Some(cleanup
),
222 // FIXME(varkor): replace this with an uninhabitedness-based check.
223 // This requires getting access to the current module to call
224 // `tcx.is_ty_uninhabited_from`, which is currently tricky to do.
225 destination
: if expr
.ty
.is_never() {
228 Some((destination
, success
))
237 ExprKind
::Use { source }
=> this
.into(destination
, block
, source
),
238 ExprKind
::Borrow { arg, borrow_kind }
=> {
239 // We don't do this in `as_rvalue` because we use `as_place`
240 // for borrow expressions, so we cannot create an `RValue` that
241 // remains valid across user code. `as_rvalue` is usually called
242 // by this method anyway, so this shouldn't cause too many
243 // unnecessary temporaries.
244 let arg_place
= match borrow_kind
{
245 BorrowKind
::Shared
=> unpack
!(block
= this
.as_read_only_place(block
, arg
)),
246 _
=> unpack
!(block
= this
.as_place(block
, arg
)),
249 Rvalue
::Ref(this
.hir
.tcx().lifetimes
.re_erased
, borrow_kind
, arg_place
);
250 this
.cfg
.push_assign(block
, source_info
, destination
, borrow
);
253 ExprKind
::AddressOf { mutability, arg }
=> {
254 let place
= match mutability
{
255 hir
::Mutability
::Not
=> this
.as_read_only_place(block
, arg
),
256 hir
::Mutability
::Mut
=> this
.as_place(block
, arg
),
258 let address_of
= Rvalue
::AddressOf(mutability
, unpack
!(block
= place
));
259 this
.cfg
.push_assign(block
, source_info
, destination
, address_of
);
262 ExprKind
::Adt { adt_def, variant_index, substs, user_ty, fields, base }
=> {
263 // See the notes for `ExprKind::Array` in `as_rvalue` and for
264 // `ExprKind::Borrow` above.
265 let is_union
= adt_def
.is_union();
266 let active_field_index
= if is_union { Some(fields[0].name.index()) }
else { None }
;
268 let scope
= this
.local_scope();
270 // first process the set of fields that were provided
271 // (evaluating them in order given by user)
272 let fields_map
: FxHashMap
<_
, _
> = fields
274 .map(|f
| (f
.name
, unpack
!(block
= this
.as_operand(block
, scope
, f
.expr
))))
277 let field_names
= this
.hir
.all_fields(adt_def
, variant_index
);
279 let fields
= if let Some(FruInfo { base, field_types }
) = base
{
280 let base
= unpack
!(block
= this
.as_place(block
, base
));
282 // MIR does not natively support FRU, so for each
283 // base-supplied field, generate an operand that
284 // reads it from the base.
287 .zip(field_types
.into_iter())
288 .map(|(n
, ty
)| match fields_map
.get(&n
) {
289 Some(v
) => v
.clone(),
290 None
=> this
.consume_by_copy_or_move(
291 this
.hir
.tcx().mk_place_field(base
, n
, ty
),
296 field_names
.iter().filter_map(|n
| fields_map
.get(n
).cloned()).collect()
299 let inferred_ty
= expr
.ty
;
300 let user_ty
= user_ty
.map(|ty
| {
301 this
.canonical_user_type_annotations
.push(CanonicalUserTypeAnnotation
{
302 span
: source_info
.span
,
307 let adt
= box AggregateKind
::Adt(
314 this
.cfg
.push_assign(
318 Rvalue
::Aggregate(adt
, fields
),
322 ExprKind
::InlineAsm { template, operands, options, line_spans }
=> {
324 use rustc_middle
::mir
;
325 let operands
= operands
328 thir
::InlineAsmOperand
::In { reg, expr }
=> mir
::InlineAsmOperand
::In
{
330 value
: unpack
!(block
= this
.as_local_operand(block
, expr
)),
332 thir
::InlineAsmOperand
::Out { reg, late, expr }
=> {
333 mir
::InlineAsmOperand
::Out
{
336 place
: expr
.map(|expr
| unpack
!(block
= this
.as_place(block
, expr
))),
339 thir
::InlineAsmOperand
::InOut { reg, late, expr }
=> {
340 let place
= unpack
!(block
= this
.as_place(block
, expr
));
341 mir
::InlineAsmOperand
::InOut
{
344 // This works because asm operands must be Copy
345 in_value
: Operand
::Copy(place
),
346 out_place
: Some(place
),
349 thir
::InlineAsmOperand
::SplitInOut { reg, late, in_expr, out_expr }
=> {
350 mir
::InlineAsmOperand
::InOut
{
353 in_value
: unpack
!(block
= this
.as_local_operand(block
, in_expr
)),
354 out_place
: out_expr
.map(|out_expr
| {
355 unpack
!(block
= this
.as_place(block
, out_expr
))
359 thir
::InlineAsmOperand
::Const { expr }
=> mir
::InlineAsmOperand
::Const
{
360 value
: unpack
!(block
= this
.as_local_operand(block
, expr
)),
362 thir
::InlineAsmOperand
::SymFn { expr }
=> {
363 mir
::InlineAsmOperand
::SymFn { value: box this.as_constant(expr) }
365 thir
::InlineAsmOperand
::SymStatic { def_id }
=> {
366 mir
::InlineAsmOperand
::SymStatic { def_id }
371 let destination
= this
.cfg
.start_new_block();
376 TerminatorKind
::InlineAsm
{
381 destination
: if options
.contains(InlineAsmOptions
::NORETURN
) {
391 // These cases don't actually need a destination
392 ExprKind
::Assign { .. }
393 | ExprKind
::AssignOp { .. }
394 | ExprKind
::LlvmInlineAsm { .. }
=> {
395 unpack
!(block
= this
.stmt_expr(block
, expr
, None
));
396 this
.cfg
.push_assign_unit(block
, source_info
, destination
, this
.hir
.tcx());
400 ExprKind
::Continue { .. }
| ExprKind
::Break { .. }
| ExprKind
::Return { .. }
=> {
401 unpack
!(block
= this
.stmt_expr(block
, expr
, None
));
402 // No assign, as these have type `!`.
406 // Avoid creating a temporary
407 ExprKind
::VarRef { .. }
409 | ExprKind
::PlaceTypeAscription { .. }
410 | ExprKind
::ValueTypeAscription { .. }
=> {
411 debug_assert
!(Category
::of(&expr
.kind
) == Some(Category
::Place
));
413 let place
= unpack
!(block
= this
.as_place(block
, expr
));
414 let rvalue
= Rvalue
::Use(this
.consume_by_copy_or_move(place
));
415 this
.cfg
.push_assign(block
, source_info
, destination
, rvalue
);
418 ExprKind
::Index { .. }
| ExprKind
::Deref { .. }
| ExprKind
::Field { .. }
=> {
419 debug_assert
!(Category
::of(&expr
.kind
) == Some(Category
::Place
));
421 // Create a "fake" temporary variable so that we check that the
422 // value is Sized. Usually, this is caught in type checking, but
423 // in the case of box expr there is no such check.
424 if !destination
.projection
.is_empty() {
425 this
.local_decls
.push(LocalDecl
::new(expr
.ty
, expr
.span
));
428 debug_assert
!(Category
::of(&expr
.kind
) == Some(Category
::Place
));
430 let place
= unpack
!(block
= this
.as_place(block
, expr
));
431 let rvalue
= Rvalue
::Use(this
.consume_by_copy_or_move(place
));
432 this
.cfg
.push_assign(block
, source_info
, destination
, rvalue
);
436 ExprKind
::Yield { value }
=> {
437 let scope
= this
.local_scope();
438 let value
= unpack
!(block
= this
.as_operand(block
, scope
, value
));
439 let resume
= this
.cfg
.start_new_block();
440 let cleanup
= this
.generator_drop_cleanup();
444 TerminatorKind
::Yield { value, resume, resume_arg: destination, drop: cleanup }
,
449 // these are the cases that are more naturally handled by some other mode
450 ExprKind
::Unary { .. }
451 | ExprKind
::Binary { .. }
452 | ExprKind
::Box { .. }
453 | ExprKind
::Cast { .. }
454 | ExprKind
::Pointer { .. }
455 | ExprKind
::Repeat { .. }
456 | ExprKind
::Array { .. }
457 | ExprKind
::Tuple { .. }
458 | ExprKind
::Closure { .. }
459 | ExprKind
::Literal { .. }
460 | ExprKind
::ThreadLocalRef(_
)
461 | ExprKind
::StaticRef { .. }
=> {
462 debug_assert
!(match Category
::of(&expr
.kind
).unwrap() {
463 // should be handled above
464 Category
::Rvalue(RvalueFunc
::Into
) => false,
466 // must be handled above or else we get an
467 // infinite loop in the builder; see
468 // e.g., `ExprKind::VarRef` above
469 Category
::Place
=> false,
474 let rvalue
= unpack
!(block
= this
.as_local_rvalue(block
, expr
));
475 this
.cfg
.push_assign(block
, source_info
, destination
, rvalue
);
480 if !expr_is_block_or_scope
{
481 let popped
= this
.block_context
.pop();
482 assert
!(popped
.is_some());