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