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::{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;
19use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary};
20
21impl<'a, 'tcx> Builder<'a, 'tcx> {
22 #[instrument(level = "debug", skip(self))]
25 pub(crate) fn expr_into_dest(
26 &mut self,
27 destination: Place<'tcx>,
28 mut block: BasicBlock,
29 expr_id: ExprId,
30 ) -> BlockAnd<()> {
31 let this = self;
35 let expr = &this.thir[expr_id];
36 let expr_span = expr.span;
37 let source_info = this.source_info(expr_span);
38
39 let expr_is_block_or_scope =
40 matches!(expr.kind, ExprKind::Block { .. } | ExprKind::Scope { .. });
41
42 if !expr_is_block_or_scope {
43 this.block_context.push(BlockFrame::SubExpr);
44 }
45
46 let block_and = match expr.kind {
47 ExprKind::Scope { region_scope, lint_level, value } => {
48 let region_scope = (region_scope, source_info);
49 ensure_sufficient_stack(|| {
50 this.in_scope(region_scope, lint_level, |this| {
51 this.expr_into_dest(destination, block, value)
52 })
53 })
54 }
55 ExprKind::Block { block: ast_block } => {
56 this.ast_block(destination, block, ast_block, source_info)
57 }
58 ExprKind::Match { scrutinee, ref arms, .. } => this.match_expr(
59 destination,
60 block,
61 scrutinee,
62 arms,
63 expr_span,
64 this.thir[scrutinee].span,
65 ),
66 ExprKind::If { cond, then, else_opt, if_then_scope } => {
67 let then_span = this.thir[then].span;
68 let then_source_info = this.source_info(then_span);
69 let condition_scope = this.local_scope();
70
71 let then_and_else_blocks = this.in_scope(
72 (if_then_scope, then_source_info),
73 LintLevel::Inherited,
74 |this| {
75 let source_info = if this.is_let(cond) {
77 let variable_scope =
78 this.new_source_scope(then_span, LintLevel::Inherited);
79 this.source_scope = variable_scope;
80 SourceInfo { span: then_span, scope: variable_scope }
81 } else {
82 this.source_info(then_span)
83 };
84
85 let (then_block, else_block) =
87 this.in_if_then_scope(condition_scope, then_span, |this| {
88 let then_blk = this
89 .then_else_break(
90 block,
91 cond,
92 Some(condition_scope), source_info,
94 DeclareLetBindings::Yes, )
96 .into_block();
97
98 this.expr_into_dest(destination, then_blk, then)
100 });
101
102 then_block.and(else_block)
104 },
105 );
106
107 let (then_blk, mut else_blk);
109 else_blk = unpack!(then_blk = then_and_else_blocks);
110
111 if let Some(else_expr) = else_opt {
113 else_blk = this.expr_into_dest(destination, else_blk, else_expr).into_block();
114 } else {
115 let correct_si = this.source_info(expr_span.shrink_to_hi());
118 this.cfg.push_assign_unit(else_blk, correct_si, destination, this.tcx);
119 }
120
121 let join_block = this.cfg.start_new_block();
124 this.cfg.goto(then_blk, source_info, join_block);
125 this.cfg.goto(else_blk, source_info, join_block);
126 join_block.unit()
127 }
128 ExprKind::Let { .. } => {
129 span_bug!(expr_span, "unexpected let expression outside of if or match-guard");
134 }
135 ExprKind::NeverToAny { source } => {
136 let source_expr = &this.thir[source];
137 let is_call =
138 matches!(source_expr.kind, ExprKind::Call { .. } | ExprKind::InlineAsm { .. });
139
140 unpack!(
143 block =
144 this.as_temp(block, this.local_temp_lifetime(), source, Mutability::Mut)
145 );
146
147 if is_call {
150 block.unit()
151 } else {
152 this.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
153 let end_block = this.cfg.start_new_block();
154 end_block.unit()
155 }
156 }
157 ExprKind::LogicalOp { op, lhs, rhs } => {
158 let condition_scope = this.local_scope();
159 let source_info = this.source_info(expr.span);
160
161 this.visit_coverage_branch_operation(op, expr.span);
162
163 let (then_block, else_block) =
165 this.in_if_then_scope(condition_scope, expr.span, |this| {
166 this.then_else_break(
167 block,
168 lhs,
169 Some(condition_scope), source_info,
171 DeclareLetBindings::LetNotPermitted,
174 )
175 });
176 let (short_circuit, continuation, constant) = match op {
177 LogicalOp::And => (else_block, then_block, false),
178 LogicalOp::Or => (then_block, else_block, true),
179 };
180 this.cfg.push_assign_constant(
187 short_circuit,
188 source_info,
189 destination,
190 ConstOperand {
191 span: expr.span,
192 user_ty: None,
193 const_: Const::from_bool(this.tcx, constant),
194 },
195 );
196 let mut rhs_block =
197 this.expr_into_dest(destination, continuation, rhs).into_block();
198 this.visit_coverage_standalone_condition(rhs, destination, &mut rhs_block);
201
202 let target = this.cfg.start_new_block();
203 this.cfg.goto(rhs_block, source_info, target);
204 this.cfg.goto(short_circuit, source_info, target);
205 target.unit()
206 }
207 ExprKind::Loop { body } => {
208 let loop_block = this.cfg.start_new_block();
219
220 this.cfg.goto(block, source_info, loop_block);
222
223 this.in_breakable_scope(Some(loop_block), destination, expr_span, move |this| {
224 let body_block = this.cfg.start_new_block();
226 this.cfg.terminate(
227 loop_block,
228 source_info,
229 TerminatorKind::FalseUnwind {
230 real_target: body_block,
231 unwind: UnwindAction::Continue,
232 },
233 );
234 this.diverge_from(loop_block);
235
236 let tmp = this.get_unit_temp();
239 let body_block_end = this.expr_into_dest(tmp, body_block, body).into_block();
241 this.cfg.goto(body_block_end, source_info, loop_block);
242
243 None
245 })
246 }
247 ExprKind::Call { ty: _, fun, ref args, from_hir_call, fn_span } => {
248 let fun = unpack!(block = this.as_local_operand(block, fun));
249 let args: Box<[_]> = args
250 .into_iter()
251 .copied()
252 .map(|arg| Spanned {
253 node: unpack!(block = this.as_local_call_operand(block, arg)),
254 span: this.thir.exprs[arg].span,
255 })
256 .collect();
257
258 let success = this.cfg.start_new_block();
259
260 this.record_operands_moved(&args);
261
262 debug!("expr_into_dest: fn_span={:?}", fn_span);
263
264 this.cfg.terminate(
265 block,
266 source_info,
267 TerminatorKind::Call {
268 func: fun,
269 args,
270 unwind: UnwindAction::Continue,
271 destination,
272 target: expr
277 .ty
278 .is_inhabited_from(
279 this.tcx,
280 this.parent_module,
281 this.infcx.typing_env(this.param_env),
282 )
283 .then_some(success),
284 call_source: if from_hir_call {
285 CallSource::Normal
286 } else {
287 CallSource::OverloadedOperator
288 },
289 fn_span,
290 },
291 );
292 this.diverge_from(block);
293 success.unit()
294 }
295 ExprKind::ByUse { expr, span } => {
296 let place = unpack!(block = this.as_place(block, expr));
297 let ty = place.ty(&this.local_decls, this.tcx).ty;
298
299 if this.tcx.type_is_copy_modulo_regions(this.infcx.typing_env(this.param_env), ty) {
300 this.cfg.push_assign(
301 block,
302 source_info,
303 destination,
304 Rvalue::Use(Operand::Copy(place)),
305 );
306 block.unit()
307 } else if this.infcx.type_is_use_cloned_modulo_regions(this.param_env, ty) {
308 let success = this.cfg.start_new_block();
310 let clone_trait = this.tcx.require_lang_item(LangItem::Clone, None);
311 let clone_fn = this.tcx.associated_item_def_ids(clone_trait)[0];
312 let func = Operand::function_handle(this.tcx, clone_fn, [ty.into()], expr_span);
313 let ref_ty = Ty::new_imm_ref(this.tcx, this.tcx.lifetimes.re_erased, ty);
314 let ref_place = this.temp(ref_ty, span);
315 this.cfg.push_assign(
316 block,
317 source_info,
318 ref_place,
319 Rvalue::Ref(this.tcx.lifetimes.re_erased, BorrowKind::Shared, place),
320 );
321 this.cfg.terminate(
322 block,
323 source_info,
324 TerminatorKind::Call {
325 func,
326 args: [Spanned { node: Operand::Move(ref_place), span: DUMMY_SP }]
327 .into(),
328 destination,
329 target: Some(success),
330 unwind: UnwindAction::Unreachable,
331 call_source: CallSource::Misc,
332 fn_span: expr_span,
333 },
334 );
335 success.unit()
336 } else {
337 this.cfg.push_assign(
338 block,
339 source_info,
340 destination,
341 Rvalue::Use(Operand::Move(place)),
342 );
343 block.unit()
344 }
345 }
346 ExprKind::Use { source } => this.expr_into_dest(destination, block, source),
347 ExprKind::Borrow { arg, borrow_kind } => {
348 let arg_place = match borrow_kind {
354 BorrowKind::Shared => {
355 unpack!(block = this.as_read_only_place(block, arg))
356 }
357 _ => unpack!(block = this.as_place(block, arg)),
358 };
359 let borrow = Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind, arg_place);
360 this.cfg.push_assign(block, source_info, destination, borrow);
361 block.unit()
362 }
363 ExprKind::RawBorrow { mutability, arg } => {
364 let place = match mutability {
365 hir::Mutability::Not => this.as_read_only_place(block, arg),
366 hir::Mutability::Mut => this.as_place(block, arg),
367 };
368 let address_of = Rvalue::RawPtr(mutability.into(), unpack!(block = place));
369 this.cfg.push_assign(block, source_info, destination, address_of);
370 block.unit()
371 }
372 ExprKind::Adt(box AdtExpr {
373 adt_def,
374 variant_index,
375 args,
376 ref user_ty,
377 ref fields,
378 ref base,
379 }) => {
380 let is_union = adt_def.is_union();
383 let active_field_index = is_union.then(|| fields[0].name);
384
385 let scope = this.local_temp_lifetime();
386
387 let fields_map: FxHashMap<_, _> = fields
390 .into_iter()
391 .map(|f| {
392 (
393 f.name,
394 unpack!(
395 block = this.as_operand(
396 block,
397 scope,
398 f.expr,
399 LocalInfo::AggregateTemp,
400 NeedsTemporary::Maybe,
401 )
402 ),
403 )
404 })
405 .collect();
406
407 let variant = adt_def.variant(variant_index);
408 let field_names = variant.fields.indices();
409
410 let fields = match base {
411 AdtExprBase::None => {
412 field_names.filter_map(|n| fields_map.get(&n).cloned()).collect()
413 }
414 AdtExprBase::Base(FruInfo { base, field_types }) => {
415 let place_builder = unpack!(block = this.as_place_builder(block, *base));
416
417 itertools::zip_eq(field_names, &**field_types)
421 .map(|(n, ty)| match fields_map.get(&n) {
422 Some(v) => v.clone(),
423 None => {
424 let place =
425 place_builder.clone_project(PlaceElem::Field(n, *ty));
426 this.consume_by_copy_or_move(place.to_place(this))
427 }
428 })
429 .collect()
430 }
431 AdtExprBase::DefaultFields(field_types) => {
432 itertools::zip_eq(field_names, field_types)
433 .map(|(n, &ty)| match fields_map.get(&n) {
434 Some(v) => v.clone(),
435 None => match variant.fields[n].value {
436 Some(def) => {
437 let value = Const::Unevaluated(
438 UnevaluatedConst::new(def, args),
439 ty,
440 );
441 Operand::Constant(Box::new(ConstOperand {
442 span: expr_span,
443 user_ty: None,
444 const_: value,
445 }))
446 }
447 None => {
448 let name = variant.fields[n].name;
449 span_bug!(
450 expr_span,
451 "missing mandatory field `{name}` of type `{ty}`",
452 );
453 }
454 },
455 })
456 .collect()
457 }
458 };
459
460 let inferred_ty = expr.ty;
461 let user_ty = user_ty.as_ref().map(|user_ty| {
462 this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
463 span: source_info.span,
464 user_ty: user_ty.clone(),
465 inferred_ty,
466 })
467 });
468 let adt = Box::new(AggregateKind::Adt(
469 adt_def.did(),
470 variant_index,
471 args,
472 user_ty,
473 active_field_index,
474 ));
475 this.cfg.push_assign(
476 block,
477 source_info,
478 destination,
479 Rvalue::Aggregate(adt, fields),
480 );
481 block.unit()
482 }
483 ExprKind::InlineAsm(box InlineAsmExpr {
484 asm_macro,
485 template,
486 ref operands,
487 options,
488 line_spans,
489 }) => {
490 use rustc_middle::{mir, thir};
491
492 let destination_block = this.cfg.start_new_block();
493 let mut targets =
494 if asm_macro.diverges(options) { vec![] } else { vec![destination_block] };
495
496 let operands = operands
497 .into_iter()
498 .map(|op| match *op {
499 thir::InlineAsmOperand::In { reg, expr } => mir::InlineAsmOperand::In {
500 reg,
501 value: unpack!(block = this.as_local_operand(block, expr)),
502 },
503 thir::InlineAsmOperand::Out { reg, late, expr } => {
504 mir::InlineAsmOperand::Out {
505 reg,
506 late,
507 place: expr.map(|expr| unpack!(block = this.as_place(block, expr))),
508 }
509 }
510 thir::InlineAsmOperand::InOut { reg, late, expr } => {
511 let place = unpack!(block = this.as_place(block, expr));
512 mir::InlineAsmOperand::InOut {
513 reg,
514 late,
515 in_value: Operand::Copy(place),
517 out_place: Some(place),
518 }
519 }
520 thir::InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
521 mir::InlineAsmOperand::InOut {
522 reg,
523 late,
524 in_value: unpack!(block = this.as_local_operand(block, in_expr)),
525 out_place: out_expr.map(|out_expr| {
526 unpack!(block = this.as_place(block, out_expr))
527 }),
528 }
529 }
530 thir::InlineAsmOperand::Const { value, span } => {
531 mir::InlineAsmOperand::Const {
532 value: Box::new(ConstOperand {
533 span,
534 user_ty: None,
535 const_: value,
536 }),
537 }
538 }
539 thir::InlineAsmOperand::SymFn { value } => mir::InlineAsmOperand::SymFn {
540 value: Box::new(this.as_constant(&this.thir[value])),
541 },
542 thir::InlineAsmOperand::SymStatic { def_id } => {
543 mir::InlineAsmOperand::SymStatic { def_id }
544 }
545 thir::InlineAsmOperand::Label { block } => {
546 let target = this.cfg.start_new_block();
547 let target_index = targets.len();
548 targets.push(target);
549
550 let tmp = this.get_unit_temp();
551 let target =
552 this.ast_block(tmp, target, block, source_info).into_block();
553 this.cfg.terminate(
554 target,
555 source_info,
556 TerminatorKind::Goto { target: destination_block },
557 );
558
559 mir::InlineAsmOperand::Label { target_index }
560 }
561 })
562 .collect();
563
564 if !expr.ty.is_never() {
565 this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
566 }
567
568 let asm_macro = match asm_macro {
569 AsmMacro::Asm | AsmMacro::GlobalAsm => InlineAsmMacro::Asm,
570 AsmMacro::NakedAsm => InlineAsmMacro::NakedAsm,
571 };
572
573 this.cfg.terminate(
574 block,
575 source_info,
576 TerminatorKind::InlineAsm {
577 asm_macro,
578 template,
579 operands,
580 options,
581 line_spans,
582 targets: targets.into_boxed_slice(),
583 unwind: if options.contains(InlineAsmOptions::MAY_UNWIND) {
584 UnwindAction::Continue
585 } else {
586 UnwindAction::Unreachable
587 },
588 },
589 );
590 if options.contains(InlineAsmOptions::MAY_UNWIND) {
591 this.diverge_from(block);
592 }
593 destination_block.unit()
594 }
595
596 ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
598 block = this.stmt_expr(block, expr_id, None).into_block();
599 this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
600 block.unit()
601 }
602
603 ExprKind::Continue { .. }
604 | ExprKind::Break { .. }
605 | ExprKind::Return { .. }
606 | ExprKind::Become { .. } => {
607 block = this.stmt_expr(block, expr_id, None).into_block();
608 block.unit()
610 }
611
612 ExprKind::VarRef { .. }
614 | ExprKind::UpvarRef { .. }
615 | ExprKind::PlaceTypeAscription { .. }
616 | ExprKind::ValueTypeAscription { .. }
617 | ExprKind::PlaceUnwrapUnsafeBinder { .. }
618 | ExprKind::ValueUnwrapUnsafeBinder { .. } => {
619 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
620
621 let place = unpack!(block = this.as_place(block, expr_id));
622 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
623 this.cfg.push_assign(block, source_info, destination, rvalue);
624 block.unit()
625 }
626 ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
627 debug_assert_eq!(Category::of(&expr.kind), Some(Category::Place));
628
629 if !destination.projection.is_empty() {
633 this.local_decls.push(LocalDecl::new(expr.ty, expr.span));
634 }
635
636 let place = unpack!(block = this.as_place(block, expr_id));
637 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
638 this.cfg.push_assign(block, source_info, destination, rvalue);
639 block.unit()
640 }
641
642 ExprKind::Yield { value } => {
643 let scope = this.local_temp_lifetime();
644 let value = unpack!(
645 block =
646 this.as_operand(block, scope, value, LocalInfo::Boring, NeedsTemporary::No)
647 );
648 let resume = this.cfg.start_new_block();
649 this.cfg.terminate(
650 block,
651 source_info,
652 TerminatorKind::Yield { value, resume, resume_arg: destination, drop: None },
653 );
654 this.coroutine_drop_cleanup(block);
655 resume.unit()
656 }
657
658 ExprKind::Unary { .. }
660 | ExprKind::Binary { .. }
661 | ExprKind::Box { .. }
662 | ExprKind::Cast { .. }
663 | ExprKind::PointerCoercion { .. }
664 | ExprKind::Repeat { .. }
665 | ExprKind::Array { .. }
666 | ExprKind::Tuple { .. }
667 | ExprKind::Closure { .. }
668 | ExprKind::ConstBlock { .. }
669 | ExprKind::Literal { .. }
670 | ExprKind::NamedConst { .. }
671 | ExprKind::NonHirLiteral { .. }
672 | ExprKind::ZstLiteral { .. }
673 | ExprKind::ConstParam { .. }
674 | ExprKind::ThreadLocalRef(_)
675 | ExprKind::StaticRef { .. }
676 | ExprKind::OffsetOf { .. }
677 | ExprKind::WrapUnsafeBinder { .. } => {
678 debug_assert!(match Category::of(&expr.kind).unwrap() {
679 Category::Rvalue(RvalueFunc::Into) => false,
681
682 Category::Place => false,
686
687 _ => true,
688 });
689
690 let rvalue = unpack!(block = this.as_local_rvalue(block, expr_id));
691 this.cfg.push_assign(block, source_info, destination, rvalue);
692 block.unit()
693 }
694 };
695
696 if !expr_is_block_or_scope {
697 let popped = this.block_context.pop();
698 assert!(popped.is_some());
699 }
700
701 block_and
702 }
703
704 fn is_let(&self, expr: ExprId) -> bool {
705 match self.thir[expr].kind {
706 ExprKind::Let { .. } => true,
707 ExprKind::Scope { value, .. } => self.is_let(value),
708 _ => false,
709 }
710 }
711}