rustc_mir_build/builder/expr/into.rs
1//! See docs in build/expr/mod.rs
2
3use rustc_ast::{AsmMacro, InlineAsmOptions};
4use rustc_data_structures::fx::FxHashMap;
5use rustc_data_structures::stack::ensure_sufficient_stack;
6use rustc_hir as hir;
7use rustc_hir::lang_items::LangItem;
8use rustc_middle::mir::*;
9use rustc_middle::span_bug;
10use rustc_middle::thir::*;
11use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty};
12use rustc_span::DUMMY_SP;
13use rustc_span::source_map::Spanned;
14use rustc_trait_selection::infer::InferCtxtExt;
15use tracing::{debug, instrument};
16
17use crate::builder::expr::category::{Category, RvalueFunc};
18use crate::builder::matches::{DeclareLetBindings, HasMatchGuard};
19use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary};
20use crate::errors::{LoopMatchArmWithGuard, LoopMatchUnsupportedType};
21
22impl<'a, 'tcx> Builder<'a, 'tcx> {
23 /// Compile `expr`, storing the result into `destination`, which
24 /// is assumed to be uninitialized.
25 #[instrument(level = "debug", skip(self))]
26 pub(crate) fn expr_into_dest(
27 &mut self,
28 destination: Place<'tcx>,
29 mut block: BasicBlock,
30 expr_id: ExprId,
31 ) -> BlockAnd<()> {
32 // since we frequently have to reference `self` from within a
33 // closure, where `self` would be shadowed, it's easier to
34 // just use the name `this` uniformly
35 let this = self;
36 let expr = &this.thir[expr_id];
37 let expr_span = expr.span;
38 let source_info = this.source_info(expr_span);
39
40 let expr_is_block_or_scope =
41 matches!(expr.kind, ExprKind::Block { .. } | ExprKind::Scope { .. });
42
43 if !expr_is_block_or_scope {
44 this.block_context.push(BlockFrame::SubExpr);
45 }
46
47 let block_and = match expr.kind {
48 ExprKind::Scope { region_scope, lint_level, value } => {
49 let region_scope = (region_scope, source_info);
50 ensure_sufficient_stack(|| {
51 this.in_scope(region_scope, lint_level, |this| {
52 this.expr_into_dest(destination, block, value)
53 })
54 })
55 }
56 ExprKind::Block { block: ast_block } => {
57 this.ast_block(destination, block, ast_block, source_info)
58 }
59 ExprKind::Match { scrutinee, ref arms, .. } => this.match_expr(
60 destination,
61 block,
62 scrutinee,
63 arms,
64 expr_span,
65 this.thir[scrutinee].span,
66 ),
67 ExprKind::If { cond, then, else_opt, if_then_scope } => {
68 let then_span = this.thir[then].span;
69 let then_source_info = this.source_info(then_span);
70 let condition_scope = this.local_scope();
71
72 let then_and_else_blocks = this.in_scope(
73 (if_then_scope, then_source_info),
74 LintLevel::Inherited,
75 |this| {
76 // FIXME: Does this need extra logic to handle let-chains?
77 let source_info = if this.is_let(cond) {
78 let variable_scope =
79 this.new_source_scope(then_span, LintLevel::Inherited);
80 this.source_scope = variable_scope;
81 SourceInfo { span: then_span, scope: variable_scope }
82 } else {
83 this.source_info(then_span)
84 };
85
86 // Lower the condition, and have it branch into `then` and `else` blocks.
87 let (then_block, else_block) =
88 this.in_if_then_scope(condition_scope, then_span, |this| {
89 let then_blk = this
90 .then_else_break(
91 block,
92 cond,
93 Some(condition_scope), // Temp scope
94 source_info,
95 DeclareLetBindings::Yes, // Declare `let` bindings normally
96 )
97 .into_block();
98
99 // Lower the `then` arm into its block.
100 this.expr_into_dest(destination, then_blk, then)
101 });
102
103 // Pack `(then_block, else_block)` into `BlockAnd<BasicBlock>`.
104 then_block.and(else_block)
105 },
106 );
107
108 // Unpack `BlockAnd<BasicBlock>` into `(then_blk, else_blk)`.
109 let (then_blk, mut else_blk);
110 else_blk = unpack!(then_blk = then_and_else_blocks);
111
112 // If there is an `else` arm, lower it into `else_blk`.
113 if let Some(else_expr) = else_opt {
114 else_blk = this.expr_into_dest(destination, else_blk, else_expr).into_block();
115 } else {
116 // There is no `else` arm, so we know both arms have type `()`.
117 // Generate the implicit `else {}` by assigning unit.
118 let correct_si = this.source_info(expr_span.shrink_to_hi());
119 this.cfg.push_assign_unit(else_blk, correct_si, destination, this.tcx);
120 }
121
122 // The `then` and `else` arms have been lowered into their respective
123 // blocks, so make both of them meet up in a new block.
124 let join_block = this.cfg.start_new_block();
125 this.cfg.goto(then_blk, source_info, join_block);
126 this.cfg.goto(else_blk, source_info, join_block);
127 join_block.unit()
128 }
129 ExprKind::Let { .. } => {
130 // After desugaring, `let` expressions should only appear inside `if`
131 // expressions or `match` guards, possibly nested within a let-chain.
132 // In both cases they are specifically handled by the lowerings of
133 // those expressions, so this case is currently unreachable.
134 span_bug!(expr_span, "unexpected let expression outside of if or match-guard");
135 }
136 ExprKind::NeverToAny { source } => {
137 let source_expr = &this.thir[source];
138 let is_call =
139 matches!(source_expr.kind, ExprKind::Call { .. } | ExprKind::InlineAsm { .. });
140
141 // (#66975) Source could be a const of type `!`, so has to
142 // exist in the generated MIR.
143 unpack!(
144 block =
145 this.as_temp(block, this.local_temp_lifetime(), source, Mutability::Mut)
146 );
147
148 // This is an optimization. If the expression was a call then we already have an
149 // unreachable block. Don't bother to terminate it and create a new one.
150 if is_call {
151 block.unit()
152 } else {
153 this.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
154 let end_block = this.cfg.start_new_block();
155 end_block.unit()
156 }
157 }
158 ExprKind::LogicalOp { op, lhs, rhs } => {
159 let condition_scope = this.local_scope();
160 let source_info = this.source_info(expr.span);
161
162 // We first evaluate the left-hand side of the predicate ...
163 let (then_block, else_block) =
164 this.in_if_then_scope(condition_scope, expr.span, |this| {
165 this.then_else_break(
166 block,
167 lhs,
168 Some(condition_scope), // Temp scope
169 source_info,
170 // This flag controls how inner `let` expressions are lowered,
171 // but either way there shouldn't be any of those in here.
172 DeclareLetBindings::LetNotPermitted,
173 )
174 });
175 let (short_circuit, continuation, constant) = match op {
176 LogicalOp::And => (else_block, then_block, false),
177 LogicalOp::Or => (then_block, else_block, true),
178 };
179 // At this point, the control flow splits into a short-circuiting path
180 // and a continuation path.
181 // - If the operator is `&&`, passing `lhs` leads to continuation of evaluation on `rhs`;
182 // failing it leads to the short-circuting path which assigns `false` to the place.
183 // - If the operator is `||`, failing `lhs` leads to continuation of evaluation on `rhs`;
184 // passing it leads to the short-circuting path which assigns `true` to the place.
185 this.cfg.push_assign_constant(
186 short_circuit,
187 source_info,
188 destination,
189 ConstOperand {
190 span: expr.span,
191 user_ty: None,
192 const_: Const::from_bool(this.tcx, constant),
193 },
194 );
195 let mut rhs_block =
196 this.expr_into_dest(destination, continuation, rhs).into_block();
197 // Instrument the lowered RHS's value for condition coverage.
198 // (Does nothing if condition coverage is not enabled.)
199 this.visit_coverage_standalone_condition(rhs, destination, &mut rhs_block);
200
201 let target = this.cfg.start_new_block();
202 this.cfg.goto(rhs_block, source_info, target);
203 this.cfg.goto(short_circuit, source_info, target);
204 target.unit()
205 }
206 ExprKind::Loop { body } => {
207 // [block]
208 // |
209 // [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
210 // | ^ |
211 // false link | |
212 // | +-----------------------------------------+
213 // +-> [diverge_cleanup]
214 // The false link is required to make sure borrowck considers unwinds through the
215 // body, even when the exact code in the body cannot unwind
216
217 let loop_block = this.cfg.start_new_block();
218
219 // Start the loop.
220 this.cfg.goto(block, source_info, loop_block);
221
222 this.in_breakable_scope(Some(loop_block), destination, expr_span, move |this| {
223 // conduct the test, if necessary
224 let body_block = this.cfg.start_new_block();
225 this.cfg.terminate(
226 loop_block,
227 source_info,
228 TerminatorKind::FalseUnwind {
229 real_target: body_block,
230 unwind: UnwindAction::Continue,
231 },
232 );
233 this.diverge_from(loop_block);
234
235 // The “return” value of the loop body must always be a unit. We therefore
236 // introduce a unit temporary as the destination for the loop body.
237 let tmp = this.get_unit_temp();
238 // Execute the body, branching back to the test.
239 let body_block_end = this.expr_into_dest(tmp, body_block, body).into_block();
240 this.cfg.goto(body_block_end, source_info, loop_block);
241
242 // Loops are only exited by `break` expressions.
243 None
244 })
245 }
246 ExprKind::LoopMatch {
247 state,
248 region_scope,
249 match_data: box LoopMatchMatchData { box ref arms, span: match_span, scrutinee },
250 } => {
251 // Intuitively, this is a combination of a loop containing a labeled block
252 // containing a match.
253 //
254 // The only new bit here is that the lowering of the match is wrapped in a
255 // `in_const_continuable_scope`, which makes the match arms and their target basic
256 // block available to the lowering of `#[const_continue]`.
257
258 fn is_supported_loop_match_type(ty: Ty<'_>) -> bool {
259 match ty.kind() {
260 ty::Uint(_) | ty::Int(_) | ty::Float(_) | ty::Bool | ty::Char => true,
261 ty::Adt(adt_def, _) => match adt_def.adt_kind() {
262 ty::AdtKind::Struct | ty::AdtKind::Union => false,
263 ty::AdtKind::Enum => {
264 adt_def.variants().iter().all(|v| v.fields.is_empty())
265 }
266 },
267 _ => false,
268 }
269 }
270
271 let state_ty = this.thir.exprs[state].ty;
272 if !is_supported_loop_match_type(state_ty) {
273 let span = this.thir.exprs[state].span;
274 this.tcx.dcx().emit_fatal(LoopMatchUnsupportedType { span, ty: state_ty })
275 }
276
277 let loop_block = this.cfg.start_new_block();
278
279 // Start the loop.
280 this.cfg.goto(block, source_info, loop_block);
281
282 this.in_breakable_scope(Some(loop_block), destination, expr_span, |this| {
283 // Logic for `loop`.
284 let mut body_block = this.cfg.start_new_block();
285 this.cfg.terminate(
286 loop_block,
287 source_info,
288 TerminatorKind::FalseUnwind {
289 real_target: body_block,
290 unwind: UnwindAction::Continue,
291 },
292 );
293 this.diverge_from(loop_block);
294
295 // Logic for `match`.
296 let scrutinee_place_builder =
297 unpack!(body_block = this.as_place_builder(body_block, scrutinee));
298 let scrutinee_span = this.thir.exprs[scrutinee].span;
299 let match_start_span = match_span.shrink_to_lo().to(scrutinee_span);
300
301 let mut patterns = Vec::with_capacity(arms.len());
302 for &arm_id in arms.iter() {
303 let arm = &this.thir[arm_id];
304
305 if let Some(guard) = arm.guard {
306 let span = this.thir.exprs[guard].span;
307 this.tcx.dcx().emit_fatal(LoopMatchArmWithGuard { span })
308 }
309
310 patterns.push((&*arm.pattern, HasMatchGuard::No));
311 }
312
313 // The `built_tree` maps match arms to their basic block (where control flow
314 // jumps to when a value matches the arm). This structure is stored so that a
315 // `#[const_continue]` can figure out what basic block to jump to.
316 let built_tree = this.lower_match_tree(
317 body_block,
318 scrutinee_span,
319 &scrutinee_place_builder,
320 match_start_span,
321 patterns,
322 false,
323 );
324
325 let state_place = scrutinee_place_builder.to_place(this);
326
327 // This is logic for the labeled block: a block is a drop scope, hence
328 // `in_scope`, and a labeled block can be broken out of with a `break 'label`,
329 // hence the `in_breakable_scope`.
330 //
331 // Then `in_const_continuable_scope` stores information for the lowering of
332 // `#[const_continue]`, and finally the match is lowered in the standard way.
333 unpack!(
334 body_block = this.in_scope(
335 (region_scope, source_info),
336 LintLevel::Inherited,
337 move |this| {
338 this.in_breakable_scope(None, state_place, expr_span, |this| {
339 Some(this.in_const_continuable_scope(
340 Box::from(arms),
341 built_tree.clone(),
342 state_place,
343 expr_span,
344 |this| {
345 this.lower_match_arms(
346 destination,
347 scrutinee_place_builder,
348 scrutinee_span,
349 arms,
350 built_tree,
351 this.source_info(match_span),
352 )
353 },
354 ))
355 })
356 }
357 )
358 );
359
360 this.cfg.goto(body_block, source_info, loop_block);
361
362 // Loops are only exited by `break` expressions.
363 None
364 })
365 }
366 ExprKind::Call { ty: _, fun, ref args, from_hir_call, fn_span } => {
367 let fun = unpack!(block = this.as_local_operand(block, fun));
368 let args: Box<[_]> = args
369 .into_iter()
370 .copied()
371 .map(|arg| Spanned {
372 node: unpack!(block = this.as_local_call_operand(block, arg)),
373 span: this.thir.exprs[arg].span,
374 })
375 .collect();
376
377 let success = this.cfg.start_new_block();
378
379 this.record_operands_moved(&args);
380
381 debug!("expr_into_dest: fn_span={:?}", fn_span);
382
383 this.cfg.terminate(
384 block,
385 source_info,
386 TerminatorKind::Call {
387 func: fun,
388 args,
389 unwind: UnwindAction::Continue,
390 destination,
391 // The presence or absence of a return edge affects control-flow sensitive
392 // MIR checks and ultimately whether code is accepted or not. We can only
393 // omit the return edge if a return type is visibly uninhabited to a module
394 // that makes the call.
395 target: expr
396 .ty
397 .is_inhabited_from(
398 this.tcx,
399 this.parent_module,
400 this.infcx.typing_env(this.param_env),
401 )
402 .then_some(success),
403 call_source: if from_hir_call {
404 CallSource::Normal
405 } else {
406 CallSource::OverloadedOperator
407 },
408 fn_span,
409 },
410 );
411 this.diverge_from(block);
412 success.unit()
413 }
414 ExprKind::ByUse { expr, span } => {
415 let place = unpack!(block = this.as_place(block, expr));
416 let ty = place.ty(&this.local_decls, this.tcx).ty;
417
418 if this.tcx.type_is_copy_modulo_regions(this.infcx.typing_env(this.param_env), ty) {
419 this.cfg.push_assign(
420 block,
421 source_info,
422 destination,
423 Rvalue::Use(Operand::Copy(place)),
424 );
425 block.unit()
426 } else if this.infcx.type_is_use_cloned_modulo_regions(this.param_env, ty) {
427 // Convert `expr.use` to a call like `Clone::clone(&expr)`
428 let success = this.cfg.start_new_block();
429 let clone_trait = this.tcx.require_lang_item(LangItem::Clone, span);
430 let clone_fn = this.tcx.associated_item_def_ids(clone_trait)[0];
431 let func = Operand::function_handle(this.tcx, clone_fn, [ty.into()], expr_span);
432 let ref_ty = Ty::new_imm_ref(this.tcx, this.tcx.lifetimes.re_erased, ty);
433 let ref_place = this.temp(ref_ty, span);
434 this.cfg.push_assign(
435 block,
436 source_info,
437 ref_place,
438 Rvalue::Ref(this.tcx.lifetimes.re_erased, BorrowKind::Shared, place),
439 );
440 this.cfg.terminate(
441 block,
442 source_info,
443 TerminatorKind::Call {
444 func,
445 args: [Spanned { node: Operand::Move(ref_place), span: DUMMY_SP }]
446 .into(),
447 destination,
448 target: Some(success),
449 unwind: UnwindAction::Unreachable,
450 call_source: CallSource::Use,
451 fn_span: expr_span,
452 },
453 );
454 success.unit()
455 } else {
456 this.cfg.push_assign(
457 block,
458 source_info,
459 destination,
460 Rvalue::Use(Operand::Move(place)),
461 );
462 block.unit()
463 }
464 }
465 ExprKind::Use { source } => this.expr_into_dest(destination, block, source),
466 ExprKind::Borrow { arg, borrow_kind } => {
467 // We don't do this in `as_rvalue` because we use `as_place`
468 // for borrow expressions, so we cannot create an `RValue` that
469 // remains valid across user code. `as_rvalue` is usually called
470 // by this method anyway, so this shouldn't cause too many
471 // unnecessary temporaries.
472 let arg_place = match borrow_kind {
473 BorrowKind::Shared => {
474 unpack!(block = this.as_read_only_place(block, arg))
475 }
476 _ => unpack!(block = this.as_place(block, arg)),
477 };
478 let borrow = Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind, arg_place);
479 this.cfg.push_assign(block, source_info, destination, borrow);
480 block.unit()
481 }
482 ExprKind::RawBorrow { mutability, arg } => {
483 let place = match mutability {
484 hir::Mutability::Not => this.as_read_only_place(block, arg),
485 hir::Mutability::Mut => this.as_place(block, arg),
486 };
487 let address_of = Rvalue::RawPtr(mutability.into(), unpack!(block = place));
488 this.cfg.push_assign(block, source_info, destination, address_of);
489 block.unit()
490 }
491 ExprKind::Adt(box AdtExpr {
492 adt_def,
493 variant_index,
494 args,
495 ref user_ty,
496 ref fields,
497 ref base,
498 }) => {
499 // See the notes for `ExprKind::Array` in `as_rvalue` and for
500 // `ExprKind::Borrow` above.
501 let is_union = adt_def.is_union();
502 let active_field_index = is_union.then(|| fields[0].name);
503
504 let scope = this.local_temp_lifetime();
505
506 // first process the set of fields that were provided
507 // (evaluating them in order given by user)
508 let fields_map: FxHashMap<_, _> = fields
509 .into_iter()
510 .map(|f| {
511 (
512 f.name,
513 unpack!(
514 block = this.as_operand(
515 block,
516 scope,
517 f.expr,
518 LocalInfo::AggregateTemp,
519 NeedsTemporary::Maybe,
520 )
521 ),
522 )
523 })
524 .collect();
525
526 let variant = adt_def.variant(variant_index);
527 let field_names = variant.fields.indices();
528
529 let fields = match base {
530 AdtExprBase::None => {
531 field_names.filter_map(|n| fields_map.get(&n).cloned()).collect()
532 }
533 AdtExprBase::Base(FruInfo { base, field_types }) => {
534 let place_builder = unpack!(block = this.as_place_builder(block, *base));
535
536 // We desugar FRU as we lower to MIR, so for each
537 // base-supplied field, generate an operand that
538 // reads it from the base.
539 itertools::zip_eq(field_names, &**field_types)
540 .map(|(n, ty)| match fields_map.get(&n) {
541 Some(v) => v.clone(),
542 None => {
543 let place =
544 place_builder.clone_project(PlaceElem::Field(n, *ty));
545 this.consume_by_copy_or_move(place.to_place(this))
546 }
547 })
548 .collect()
549 }
550 AdtExprBase::DefaultFields(field_types) => {
551 itertools::zip_eq(field_names, field_types)
552 .map(|(n, &ty)| match fields_map.get(&n) {
553 Some(v) => v.clone(),
554 None => match variant.fields[n].value {
555 Some(def) => {
556 let value = Const::Unevaluated(
557 UnevaluatedConst::new(def, args),
558 ty,
559 );
560 Operand::Constant(Box::new(ConstOperand {
561 span: expr_span,
562 user_ty: None,
563 const_: value,
564 }))
565 }
566 None => {
567 let name = variant.fields[n].name;
568 span_bug!(
569 expr_span,
570 "missing mandatory field `{name}` of type `{ty}`",
571 );
572 }
573 },
574 })
575 .collect()
576 }
577 };
578
579 let inferred_ty = expr.ty;
580 let user_ty = user_ty.as_ref().map(|user_ty| {
581 this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
582 span: source_info.span,
583 user_ty: user_ty.clone(),
584 inferred_ty,
585 })
586 });
587 let adt = Box::new(AggregateKind::Adt(
588 adt_def.did(),
589 variant_index,
590 args,
591 user_ty,
592 active_field_index,
593 ));
594 this.cfg.push_assign(
595 block,
596 source_info,
597 destination,
598 Rvalue::Aggregate(adt, fields),
599 );
600 block.unit()
601 }
602 ExprKind::InlineAsm(box InlineAsmExpr {
603 asm_macro,
604 template,
605 ref operands,
606 options,
607 line_spans,
608 }) => {
609 use rustc_middle::{mir, thir};
610
611 let destination_block = this.cfg.start_new_block();
612 let mut targets =
613 if asm_macro.diverges(options) { vec![] } else { vec![destination_block] };
614
615 let operands = operands
616 .into_iter()
617 .map(|op| match *op {
618 thir::InlineAsmOperand::In { reg, expr } => mir::InlineAsmOperand::In {
619 reg,
620 value: unpack!(block = this.as_local_operand(block, expr)),
621 },
622 thir::InlineAsmOperand::Out { reg, late, expr } => {
623 mir::InlineAsmOperand::Out {
624 reg,
625 late,
626 place: expr.map(|expr| unpack!(block = this.as_place(block, expr))),
627 }
628 }
629 thir::InlineAsmOperand::InOut { reg, late, expr } => {
630 let place = unpack!(block = this.as_place(block, expr));
631 mir::InlineAsmOperand::InOut {
632 reg,
633 late,
634 // This works because asm operands must be Copy
635 in_value: Operand::Copy(place),
636 out_place: Some(place),
637 }
638 }
639 thir::InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
640 mir::InlineAsmOperand::InOut {
641 reg,
642 late,
643 in_value: unpack!(block = this.as_local_operand(block, in_expr)),
644 out_place: out_expr.map(|out_expr| {
645 unpack!(block = this.as_place(block, out_expr))
646 }),
647 }
648 }
649 thir::InlineAsmOperand::Const { value, span } => {
650 mir::InlineAsmOperand::Const {
651 value: Box::new(ConstOperand {
652 span,
653 user_ty: None,
654 const_: value,
655 }),
656 }
657 }
658 thir::InlineAsmOperand::SymFn { value } => mir::InlineAsmOperand::SymFn {
659 value: Box::new(this.as_constant(&this.thir[value])),
660 },
661 thir::InlineAsmOperand::SymStatic { def_id } => {
662 mir::InlineAsmOperand::SymStatic { def_id }
663 }
664 thir::InlineAsmOperand::Label { block } => {
665 let target = this.cfg.start_new_block();
666 let target_index = targets.len();
667 targets.push(target);
668
669 let tmp = this.get_unit_temp();
670 let target =
671 this.ast_block(tmp, target, block, source_info).into_block();
672 this.cfg.terminate(
673 target,
674 source_info,
675 TerminatorKind::Goto { target: destination_block },
676 );
677
678 mir::InlineAsmOperand::Label { target_index }
679 }
680 })
681 .collect();
682
683 if !expr.ty.is_never() {
684 this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
685 }
686
687 let asm_macro = match asm_macro {
688 AsmMacro::Asm | AsmMacro::GlobalAsm => InlineAsmMacro::Asm,
689 AsmMacro::NakedAsm => InlineAsmMacro::NakedAsm,
690 };
691
692 this.cfg.terminate(
693 block,
694 source_info,
695 TerminatorKind::InlineAsm {
696 asm_macro,
697 template,
698 operands,
699 options,
700 line_spans,
701 targets: targets.into_boxed_slice(),
702 unwind: if options.contains(InlineAsmOptions::MAY_UNWIND) {
703 UnwindAction::Continue
704 } else {
705 UnwindAction::Unreachable
706 },
707 },
708 );
709 if options.contains(InlineAsmOptions::MAY_UNWIND) {
710 this.diverge_from(block);
711 }
712 destination_block.unit()
713 }
714
715 // These cases don't actually need a destination
716 ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
717 block = this.stmt_expr(block, expr_id, None).into_block();
718 this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
719 block.unit()
720 }
721
722 ExprKind::Continue { .. }
723 | ExprKind::ConstContinue { .. }
724 | ExprKind::Break { .. }
725 | ExprKind::Return { .. }
726 | ExprKind::Become { .. } => {
727 block = this.stmt_expr(block, expr_id, None).into_block();
728 // No assign, as these have type `!`.
729 block.unit()
730 }
731
732 // Avoid creating a temporary
733 ExprKind::VarRef { .. }
734 | ExprKind::UpvarRef { .. }
735 | ExprKind::PlaceTypeAscription { .. }
736 | ExprKind::ValueTypeAscription { .. }
737 | ExprKind::PlaceUnwrapUnsafeBinder { .. }
738 | ExprKind::ValueUnwrapUnsafeBinder { .. } => {
739 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
740
741 let place = unpack!(block = this.as_place(block, expr_id));
742 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
743 this.cfg.push_assign(block, source_info, destination, rvalue);
744 block.unit()
745 }
746 ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
747 debug_assert_eq!(Category::of(&expr.kind), Some(Category::Place));
748
749 // Create a "fake" temporary variable so that we check that the
750 // value is Sized. Usually, this is caught in type checking, but
751 // in the case of box expr there is no such check.
752 if !destination.projection.is_empty() {
753 this.local_decls.push(LocalDecl::new(expr.ty, expr.span));
754 }
755
756 let place = unpack!(block = this.as_place(block, expr_id));
757 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
758 this.cfg.push_assign(block, source_info, destination, rvalue);
759 block.unit()
760 }
761
762 ExprKind::Yield { value } => {
763 let scope = this.local_temp_lifetime();
764 let value = unpack!(
765 block =
766 this.as_operand(block, scope, value, LocalInfo::Boring, NeedsTemporary::No)
767 );
768 let resume = this.cfg.start_new_block();
769 this.cfg.terminate(
770 block,
771 source_info,
772 TerminatorKind::Yield { value, resume, resume_arg: destination, drop: None },
773 );
774 this.coroutine_drop_cleanup(block);
775 resume.unit()
776 }
777
778 // these are the cases that are more naturally handled by some other mode
779 ExprKind::Unary { .. }
780 | ExprKind::Binary { .. }
781 | ExprKind::Box { .. }
782 | ExprKind::Cast { .. }
783 | ExprKind::PointerCoercion { .. }
784 | ExprKind::Repeat { .. }
785 | ExprKind::Array { .. }
786 | ExprKind::Tuple { .. }
787 | ExprKind::Closure { .. }
788 | ExprKind::ConstBlock { .. }
789 | ExprKind::Literal { .. }
790 | ExprKind::NamedConst { .. }
791 | ExprKind::NonHirLiteral { .. }
792 | ExprKind::ZstLiteral { .. }
793 | ExprKind::ConstParam { .. }
794 | ExprKind::ThreadLocalRef(_)
795 | ExprKind::StaticRef { .. }
796 | ExprKind::OffsetOf { .. }
797 | ExprKind::WrapUnsafeBinder { .. } => {
798 debug_assert!(match Category::of(&expr.kind).unwrap() {
799 // should be handled above
800 Category::Rvalue(RvalueFunc::Into) => false,
801
802 // must be handled above or else we get an
803 // infinite loop in the builder; see
804 // e.g., `ExprKind::VarRef` above
805 Category::Place => false,
806
807 _ => true,
808 });
809
810 let rvalue = unpack!(block = this.as_local_rvalue(block, expr_id));
811 this.cfg.push_assign(block, source_info, destination, rvalue);
812 block.unit()
813 }
814 };
815
816 if !expr_is_block_or_scope {
817 let popped = this.block_context.pop();
818 assert!(popped.is_some());
819 }
820
821 block_and
822 }
823
824 fn is_let(&self, expr: ExprId) -> bool {
825 match self.thir[expr].kind {
826 ExprKind::Let { .. } => true,
827 ExprKind::Scope { value, .. } => self.is_let(value),
828 _ => false,
829 }
830 }
831}