1use 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 #[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 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 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 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), source_info,
95 DeclareLetBindings::Yes, )
97 .into_block();
98
99 this.expr_into_dest(destination, then_blk, then)
101 });
102
103 then_block.and(else_block)
105 },
106 );
107
108 let (then_blk, mut else_blk);
110 else_blk = unpack!(then_blk = then_and_else_blocks);
111
112 if let Some(else_expr) = else_opt {
114 else_blk = this.expr_into_dest(destination, else_blk, else_expr).into_block();
115 } else {
116 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 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 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 unpack!(
144 block =
145 this.as_temp(block, this.local_temp_lifetime(), source, Mutability::Mut)
146 );
147
148 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 this.visit_coverage_branch_operation(op, expr.span);
163
164 let (then_block, else_block) =
166 this.in_if_then_scope(condition_scope, expr.span, |this| {
167 this.then_else_break(
168 block,
169 lhs,
170 Some(condition_scope), source_info,
172 DeclareLetBindings::LetNotPermitted,
175 )
176 });
177 let (short_circuit, continuation, constant) = match op {
178 LogicalOp::And => (else_block, then_block, false),
179 LogicalOp::Or => (then_block, else_block, true),
180 };
181 this.cfg.push_assign_constant(
188 short_circuit,
189 source_info,
190 destination,
191 ConstOperand {
192 span: expr.span,
193 user_ty: None,
194 const_: Const::from_bool(this.tcx, constant),
195 },
196 );
197 let mut rhs_block =
198 this.expr_into_dest(destination, continuation, rhs).into_block();
199 this.visit_coverage_standalone_condition(rhs, destination, &mut rhs_block);
202
203 let target = this.cfg.start_new_block();
204 this.cfg.goto(rhs_block, source_info, target);
205 this.cfg.goto(short_circuit, source_info, target);
206 target.unit()
207 }
208 ExprKind::Loop { body } => {
209 let loop_block = this.cfg.start_new_block();
220
221 this.cfg.goto(block, source_info, loop_block);
223
224 this.in_breakable_scope(Some(loop_block), destination, expr_span, move |this| {
225 let body_block = this.cfg.start_new_block();
227 this.cfg.terminate(
228 loop_block,
229 source_info,
230 TerminatorKind::FalseUnwind {
231 real_target: body_block,
232 unwind: UnwindAction::Continue,
233 },
234 );
235 this.diverge_from(loop_block);
236
237 let tmp = this.get_unit_temp();
240 let body_block_end = this.expr_into_dest(tmp, body_block, body).into_block();
242 this.cfg.goto(body_block_end, source_info, loop_block);
243
244 None
246 })
247 }
248 ExprKind::LoopMatch {
249 state,
250 region_scope,
251 match_data: box LoopMatchMatchData { box ref arms, span: match_span, scrutinee },
252 } => {
253 fn is_supported_loop_match_type(ty: Ty<'_>) -> bool {
261 match ty.kind() {
262 ty::Uint(_) | ty::Int(_) | ty::Float(_) | ty::Bool | ty::Char => true,
263 ty::Adt(adt_def, _) => match adt_def.adt_kind() {
264 ty::AdtKind::Struct | ty::AdtKind::Union => false,
265 ty::AdtKind::Enum => {
266 adt_def.variants().iter().all(|v| v.fields.is_empty())
267 }
268 },
269 _ => false,
270 }
271 }
272
273 let state_ty = this.thir.exprs[state].ty;
274 if !is_supported_loop_match_type(state_ty) {
275 let span = this.thir.exprs[state].span;
276 this.tcx.dcx().emit_fatal(LoopMatchUnsupportedType { span, ty: state_ty })
277 }
278
279 let loop_block = this.cfg.start_new_block();
280
281 this.cfg.goto(block, source_info, loop_block);
283
284 this.in_breakable_scope(Some(loop_block), destination, expr_span, |this| {
285 let mut body_block = this.cfg.start_new_block();
287 this.cfg.terminate(
288 loop_block,
289 source_info,
290 TerminatorKind::FalseUnwind {
291 real_target: body_block,
292 unwind: UnwindAction::Continue,
293 },
294 );
295 this.diverge_from(loop_block);
296
297 let scrutinee_place_builder =
299 unpack!(body_block = this.as_place_builder(body_block, scrutinee));
300 let scrutinee_span = this.thir.exprs[scrutinee].span;
301 let match_start_span = match_span.shrink_to_lo().to(scrutinee_span);
302
303 let mut patterns = Vec::with_capacity(arms.len());
304 for &arm_id in arms.iter() {
305 let arm = &this.thir[arm_id];
306
307 if let Some(guard) = arm.guard {
308 let span = this.thir.exprs[guard].span;
309 this.tcx.dcx().emit_fatal(LoopMatchArmWithGuard { span })
310 }
311
312 patterns.push((&*arm.pattern, HasMatchGuard::No));
313 }
314
315 let built_tree = this.lower_match_tree(
319 body_block,
320 scrutinee_span,
321 &scrutinee_place_builder,
322 match_start_span,
323 patterns,
324 false,
325 );
326
327 let state_place = scrutinee_place_builder.to_place(this);
328
329 unpack!(
336 body_block = this.in_scope(
337 (region_scope, source_info),
338 LintLevel::Inherited,
339 move |this| {
340 this.in_breakable_scope(None, state_place, expr_span, |this| {
341 Some(this.in_const_continuable_scope(
342 Box::from(arms),
343 built_tree.clone(),
344 state_place,
345 expr_span,
346 |this| {
347 this.lower_match_arms(
348 destination,
349 scrutinee_place_builder,
350 scrutinee_span,
351 arms,
352 built_tree,
353 this.source_info(match_span),
354 )
355 },
356 ))
357 })
358 }
359 )
360 );
361
362 this.cfg.goto(body_block, source_info, loop_block);
363
364 None
366 })
367 }
368 ExprKind::Call { ty: _, fun, ref args, from_hir_call, fn_span } => {
369 let fun = unpack!(block = this.as_local_operand(block, fun));
370 let args: Box<[_]> = args
371 .into_iter()
372 .copied()
373 .map(|arg| Spanned {
374 node: unpack!(block = this.as_local_call_operand(block, arg)),
375 span: this.thir.exprs[arg].span,
376 })
377 .collect();
378
379 let success = this.cfg.start_new_block();
380
381 this.record_operands_moved(&args);
382
383 debug!("expr_into_dest: fn_span={:?}", fn_span);
384
385 this.cfg.terminate(
386 block,
387 source_info,
388 TerminatorKind::Call {
389 func: fun,
390 args,
391 unwind: UnwindAction::Continue,
392 destination,
393 target: expr
398 .ty
399 .is_inhabited_from(
400 this.tcx,
401 this.parent_module,
402 this.infcx.typing_env(this.param_env),
403 )
404 .then_some(success),
405 call_source: if from_hir_call {
406 CallSource::Normal
407 } else {
408 CallSource::OverloadedOperator
409 },
410 fn_span,
411 },
412 );
413 this.diverge_from(block);
414 success.unit()
415 }
416 ExprKind::ByUse { expr, span } => {
417 let place = unpack!(block = this.as_place(block, expr));
418 let ty = place.ty(&this.local_decls, this.tcx).ty;
419
420 if this.tcx.type_is_copy_modulo_regions(this.infcx.typing_env(this.param_env), ty) {
421 this.cfg.push_assign(
422 block,
423 source_info,
424 destination,
425 Rvalue::Use(Operand::Copy(place)),
426 );
427 block.unit()
428 } else if this.infcx.type_is_use_cloned_modulo_regions(this.param_env, ty) {
429 let success = this.cfg.start_new_block();
431 let clone_trait = this.tcx.require_lang_item(LangItem::Clone, span);
432 let clone_fn = this.tcx.associated_item_def_ids(clone_trait)[0];
433 let func = Operand::function_handle(this.tcx, clone_fn, [ty.into()], expr_span);
434 let ref_ty = Ty::new_imm_ref(this.tcx, this.tcx.lifetimes.re_erased, ty);
435 let ref_place = this.temp(ref_ty, span);
436 this.cfg.push_assign(
437 block,
438 source_info,
439 ref_place,
440 Rvalue::Ref(this.tcx.lifetimes.re_erased, BorrowKind::Shared, place),
441 );
442 this.cfg.terminate(
443 block,
444 source_info,
445 TerminatorKind::Call {
446 func,
447 args: [Spanned { node: Operand::Move(ref_place), span: DUMMY_SP }]
448 .into(),
449 destination,
450 target: Some(success),
451 unwind: UnwindAction::Unreachable,
452 call_source: CallSource::Use,
453 fn_span: expr_span,
454 },
455 );
456 success.unit()
457 } else {
458 this.cfg.push_assign(
459 block,
460 source_info,
461 destination,
462 Rvalue::Use(Operand::Move(place)),
463 );
464 block.unit()
465 }
466 }
467 ExprKind::Use { source } => this.expr_into_dest(destination, block, source),
468 ExprKind::Borrow { arg, borrow_kind } => {
469 let arg_place = match borrow_kind {
475 BorrowKind::Shared => {
476 unpack!(block = this.as_read_only_place(block, arg))
477 }
478 _ => unpack!(block = this.as_place(block, arg)),
479 };
480 let borrow = Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind, arg_place);
481 this.cfg.push_assign(block, source_info, destination, borrow);
482 block.unit()
483 }
484 ExprKind::RawBorrow { mutability, arg } => {
485 let place = match mutability {
486 hir::Mutability::Not => this.as_read_only_place(block, arg),
487 hir::Mutability::Mut => this.as_place(block, arg),
488 };
489 let address_of = Rvalue::RawPtr(mutability.into(), unpack!(block = place));
490 this.cfg.push_assign(block, source_info, destination, address_of);
491 block.unit()
492 }
493 ExprKind::Adt(box AdtExpr {
494 adt_def,
495 variant_index,
496 args,
497 ref user_ty,
498 ref fields,
499 ref base,
500 }) => {
501 let is_union = adt_def.is_union();
504 let active_field_index = is_union.then(|| fields[0].name);
505
506 let scope = this.local_temp_lifetime();
507
508 let fields_map: FxHashMap<_, _> = fields
511 .into_iter()
512 .map(|f| {
513 (
514 f.name,
515 unpack!(
516 block = this.as_operand(
517 block,
518 scope,
519 f.expr,
520 LocalInfo::AggregateTemp,
521 NeedsTemporary::Maybe,
522 )
523 ),
524 )
525 })
526 .collect();
527
528 let variant = adt_def.variant(variant_index);
529 let field_names = variant.fields.indices();
530
531 let fields = match base {
532 AdtExprBase::None => {
533 field_names.filter_map(|n| fields_map.get(&n).cloned()).collect()
534 }
535 AdtExprBase::Base(FruInfo { base, field_types }) => {
536 let place_builder = unpack!(block = this.as_place_builder(block, *base));
537
538 itertools::zip_eq(field_names, &**field_types)
542 .map(|(n, ty)| match fields_map.get(&n) {
543 Some(v) => v.clone(),
544 None => {
545 let place =
546 place_builder.clone_project(PlaceElem::Field(n, *ty));
547 this.consume_by_copy_or_move(place.to_place(this))
548 }
549 })
550 .collect()
551 }
552 AdtExprBase::DefaultFields(field_types) => {
553 itertools::zip_eq(field_names, field_types)
554 .map(|(n, &ty)| match fields_map.get(&n) {
555 Some(v) => v.clone(),
556 None => match variant.fields[n].value {
557 Some(def) => {
558 let value = Const::Unevaluated(
559 UnevaluatedConst::new(def, args),
560 ty,
561 );
562 Operand::Constant(Box::new(ConstOperand {
563 span: expr_span,
564 user_ty: None,
565 const_: value,
566 }))
567 }
568 None => {
569 let name = variant.fields[n].name;
570 span_bug!(
571 expr_span,
572 "missing mandatory field `{name}` of type `{ty}`",
573 );
574 }
575 },
576 })
577 .collect()
578 }
579 };
580
581 let inferred_ty = expr.ty;
582 let user_ty = user_ty.as_ref().map(|user_ty| {
583 this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
584 span: source_info.span,
585 user_ty: user_ty.clone(),
586 inferred_ty,
587 })
588 });
589 let adt = Box::new(AggregateKind::Adt(
590 adt_def.did(),
591 variant_index,
592 args,
593 user_ty,
594 active_field_index,
595 ));
596 this.cfg.push_assign(
597 block,
598 source_info,
599 destination,
600 Rvalue::Aggregate(adt, fields),
601 );
602 block.unit()
603 }
604 ExprKind::InlineAsm(box InlineAsmExpr {
605 asm_macro,
606 template,
607 ref operands,
608 options,
609 line_spans,
610 }) => {
611 use rustc_middle::{mir, thir};
612
613 let destination_block = this.cfg.start_new_block();
614 let mut targets =
615 if asm_macro.diverges(options) { vec![] } else { vec![destination_block] };
616
617 let operands = operands
618 .into_iter()
619 .map(|op| match *op {
620 thir::InlineAsmOperand::In { reg, expr } => mir::InlineAsmOperand::In {
621 reg,
622 value: unpack!(block = this.as_local_operand(block, expr)),
623 },
624 thir::InlineAsmOperand::Out { reg, late, expr } => {
625 mir::InlineAsmOperand::Out {
626 reg,
627 late,
628 place: expr.map(|expr| unpack!(block = this.as_place(block, expr))),
629 }
630 }
631 thir::InlineAsmOperand::InOut { reg, late, expr } => {
632 let place = unpack!(block = this.as_place(block, expr));
633 mir::InlineAsmOperand::InOut {
634 reg,
635 late,
636 in_value: Operand::Copy(place),
638 out_place: Some(place),
639 }
640 }
641 thir::InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
642 mir::InlineAsmOperand::InOut {
643 reg,
644 late,
645 in_value: unpack!(block = this.as_local_operand(block, in_expr)),
646 out_place: out_expr.map(|out_expr| {
647 unpack!(block = this.as_place(block, out_expr))
648 }),
649 }
650 }
651 thir::InlineAsmOperand::Const { value, span } => {
652 mir::InlineAsmOperand::Const {
653 value: Box::new(ConstOperand {
654 span,
655 user_ty: None,
656 const_: value,
657 }),
658 }
659 }
660 thir::InlineAsmOperand::SymFn { value } => mir::InlineAsmOperand::SymFn {
661 value: Box::new(this.as_constant(&this.thir[value])),
662 },
663 thir::InlineAsmOperand::SymStatic { def_id } => {
664 mir::InlineAsmOperand::SymStatic { def_id }
665 }
666 thir::InlineAsmOperand::Label { block } => {
667 let target = this.cfg.start_new_block();
668 let target_index = targets.len();
669 targets.push(target);
670
671 let tmp = this.get_unit_temp();
672 let target =
673 this.ast_block(tmp, target, block, source_info).into_block();
674 this.cfg.terminate(
675 target,
676 source_info,
677 TerminatorKind::Goto { target: destination_block },
678 );
679
680 mir::InlineAsmOperand::Label { target_index }
681 }
682 })
683 .collect();
684
685 if !expr.ty.is_never() {
686 this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
687 }
688
689 let asm_macro = match asm_macro {
690 AsmMacro::Asm | AsmMacro::GlobalAsm => InlineAsmMacro::Asm,
691 AsmMacro::NakedAsm => InlineAsmMacro::NakedAsm,
692 };
693
694 this.cfg.terminate(
695 block,
696 source_info,
697 TerminatorKind::InlineAsm {
698 asm_macro,
699 template,
700 operands,
701 options,
702 line_spans,
703 targets: targets.into_boxed_slice(),
704 unwind: if options.contains(InlineAsmOptions::MAY_UNWIND) {
705 UnwindAction::Continue
706 } else {
707 UnwindAction::Unreachable
708 },
709 },
710 );
711 if options.contains(InlineAsmOptions::MAY_UNWIND) {
712 this.diverge_from(block);
713 }
714 destination_block.unit()
715 }
716
717 ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
719 block = this.stmt_expr(block, expr_id, None).into_block();
720 this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
721 block.unit()
722 }
723
724 ExprKind::Continue { .. }
725 | ExprKind::ConstContinue { .. }
726 | ExprKind::Break { .. }
727 | ExprKind::Return { .. }
728 | ExprKind::Become { .. } => {
729 block = this.stmt_expr(block, expr_id, None).into_block();
730 block.unit()
732 }
733
734 ExprKind::VarRef { .. }
736 | ExprKind::UpvarRef { .. }
737 | ExprKind::PlaceTypeAscription { .. }
738 | ExprKind::ValueTypeAscription { .. }
739 | ExprKind::PlaceUnwrapUnsafeBinder { .. }
740 | ExprKind::ValueUnwrapUnsafeBinder { .. } => {
741 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
742
743 let place = unpack!(block = this.as_place(block, expr_id));
744 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
745 this.cfg.push_assign(block, source_info, destination, rvalue);
746 block.unit()
747 }
748 ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
749 debug_assert_eq!(Category::of(&expr.kind), Some(Category::Place));
750
751 if !destination.projection.is_empty() {
755 this.local_decls.push(LocalDecl::new(expr.ty, expr.span));
756 }
757
758 let place = unpack!(block = this.as_place(block, expr_id));
759 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
760 this.cfg.push_assign(block, source_info, destination, rvalue);
761 block.unit()
762 }
763
764 ExprKind::Yield { value } => {
765 let scope = this.local_temp_lifetime();
766 let value = unpack!(
767 block =
768 this.as_operand(block, scope, value, LocalInfo::Boring, NeedsTemporary::No)
769 );
770 let resume = this.cfg.start_new_block();
771 this.cfg.terminate(
772 block,
773 source_info,
774 TerminatorKind::Yield { value, resume, resume_arg: destination, drop: None },
775 );
776 this.coroutine_drop_cleanup(block);
777 resume.unit()
778 }
779
780 ExprKind::Unary { .. }
782 | ExprKind::Binary { .. }
783 | ExprKind::Box { .. }
784 | ExprKind::Cast { .. }
785 | ExprKind::PointerCoercion { .. }
786 | ExprKind::Repeat { .. }
787 | ExprKind::Array { .. }
788 | ExprKind::Tuple { .. }
789 | ExprKind::Closure { .. }
790 | ExprKind::ConstBlock { .. }
791 | ExprKind::Literal { .. }
792 | ExprKind::NamedConst { .. }
793 | ExprKind::NonHirLiteral { .. }
794 | ExprKind::ZstLiteral { .. }
795 | ExprKind::ConstParam { .. }
796 | ExprKind::ThreadLocalRef(_)
797 | ExprKind::StaticRef { .. }
798 | ExprKind::OffsetOf { .. }
799 | ExprKind::WrapUnsafeBinder { .. } => {
800 debug_assert!(match Category::of(&expr.kind).unwrap() {
801 Category::Rvalue(RvalueFunc::Into) => false,
803
804 Category::Place => false,
808
809 _ => true,
810 });
811
812 let rvalue = unpack!(block = this.as_local_rvalue(block, expr_id));
813 this.cfg.push_assign(block, source_info, destination, rvalue);
814 block.unit()
815 }
816 };
817
818 if !expr_is_block_or_scope {
819 let popped = this.block_context.pop();
820 assert!(popped.is_some());
821 }
822
823 block_and
824 }
825
826 fn is_let(&self, expr: ExprId) -> bool {
827 match self.thir[expr].kind {
828 ExprKind::Let { .. } => true,
829 ExprKind::Scope { value, .. } => self.is_let(value),
830 _ => false,
831 }
832 }
833}