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