]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_build/src/build/expr/into.rs
Rollup merge of #103445 - fmease:fix-50291, r=estebank
[rust.git] / compiler / rustc_mir_build / src / build / expr / into.rs
1 //! See docs in build/expr/mod.rs
2
3 use crate::build::expr::category::{Category, RvalueFunc};
4 use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary};
5 use rustc_ast::InlineAsmOptions;
6 use rustc_data_structures::fx::FxHashMap;
7 use rustc_data_structures::stack::ensure_sufficient_stack;
8 use rustc_hir as hir;
9 use rustc_index::vec::Idx;
10 use rustc_middle::mir::*;
11 use rustc_middle::thir::*;
12 use rustc_middle::ty::CanonicalUserTypeAnnotation;
13 use std::iter;
14
15 impl<'a, 'tcx> Builder<'a, 'tcx> {
16     /// Compile `expr`, storing the result into `destination`, which
17     /// is assumed to be uninitialized.
18     #[instrument(level = "debug", skip(self))]
19     pub(crate) fn expr_into_dest(
20         &mut self,
21         destination: Place<'tcx>,
22         mut block: BasicBlock,
23         expr: &Expr<'tcx>,
24     ) -> BlockAnd<()> {
25         // since we frequently have to reference `self` from within a
26         // closure, where `self` would be shadowed, it's easier to
27         // just use the name `this` uniformly
28         let this = self;
29         let expr_span = expr.span;
30         let source_info = this.source_info(expr_span);
31
32         let expr_is_block_or_scope =
33             matches!(expr.kind, ExprKind::Block { .. } | ExprKind::Scope { .. });
34
35         if !expr_is_block_or_scope {
36             this.block_context.push(BlockFrame::SubExpr);
37         }
38
39         let block_and = match expr.kind {
40             ExprKind::Scope { region_scope, lint_level, value } => {
41                 let region_scope = (region_scope, source_info);
42                 ensure_sufficient_stack(|| {
43                     this.in_scope(region_scope, lint_level, |this| {
44                         this.expr_into_dest(destination, block, &this.thir[value])
45                     })
46                 })
47             }
48             ExprKind::Block { block: ast_block } => {
49                 this.ast_block(destination, block, ast_block, source_info)
50             }
51             ExprKind::Match { scrutinee, ref arms } => {
52                 this.match_expr(destination, expr_span, block, &this.thir[scrutinee], arms)
53             }
54             ExprKind::If { cond, then, else_opt, if_then_scope } => {
55                 let then_blk;
56                 let then_expr = &this.thir[then];
57                 let then_source_info = this.source_info(then_expr.span);
58                 let condition_scope = this.local_scope();
59
60                 let mut else_blk = unpack!(
61                     then_blk = this.in_scope(
62                         (if_then_scope, then_source_info),
63                         LintLevel::Inherited,
64                         |this| {
65                             let source_info = if this.is_let(cond) {
66                                 let variable_scope = this.new_source_scope(
67                                     then_expr.span,
68                                     LintLevel::Inherited,
69                                     None,
70                                 );
71                                 this.source_scope = variable_scope;
72                                 SourceInfo { span: then_expr.span, scope: variable_scope }
73                             } else {
74                                 this.source_info(then_expr.span)
75                             };
76                             let (then_block, else_block) =
77                                 this.in_if_then_scope(condition_scope, then_expr.span, |this| {
78                                     let then_blk = unpack!(this.then_else_break(
79                                         block,
80                                         &this.thir[cond],
81                                         Some(condition_scope),
82                                         condition_scope,
83                                         source_info
84                                     ));
85
86                                     this.expr_into_dest(destination, then_blk, then_expr)
87                                 });
88                             then_block.and(else_block)
89                         },
90                     )
91                 );
92
93                 else_blk = if let Some(else_opt) = else_opt {
94                     unpack!(this.expr_into_dest(destination, else_blk, &this.thir[else_opt]))
95                 } else {
96                     // Body of the `if` expression without an `else` clause must return `()`, thus
97                     // we implicitly generate an `else {}` if it is not specified.
98                     let correct_si = this.source_info(expr_span.shrink_to_hi());
99                     this.cfg.push_assign_unit(else_blk, correct_si, destination, this.tcx);
100                     else_blk
101                 };
102
103                 let join_block = this.cfg.start_new_block();
104                 this.cfg.goto(then_blk, source_info, join_block);
105                 this.cfg.goto(else_blk, source_info, join_block);
106                 join_block.unit()
107             }
108             ExprKind::Let { expr, ref pat } => {
109                 let scope = this.local_scope();
110                 let (true_block, false_block) = this.in_if_then_scope(scope, expr_span, |this| {
111                     this.lower_let_expr(block, &this.thir[expr], pat, scope, None, expr_span)
112                 });
113
114                 this.cfg.push_assign_constant(
115                     true_block,
116                     source_info,
117                     destination,
118                     Constant {
119                         span: expr_span,
120                         user_ty: None,
121                         literal: ConstantKind::from_bool(this.tcx, true),
122                     },
123                 );
124
125                 this.cfg.push_assign_constant(
126                     false_block,
127                     source_info,
128                     destination,
129                     Constant {
130                         span: expr_span,
131                         user_ty: None,
132                         literal: ConstantKind::from_bool(this.tcx, false),
133                     },
134                 );
135
136                 let join_block = this.cfg.start_new_block();
137                 this.cfg.goto(true_block, source_info, join_block);
138                 this.cfg.goto(false_block, source_info, join_block);
139                 join_block.unit()
140             }
141             ExprKind::NeverToAny { source } => {
142                 let source = &this.thir[source];
143                 let is_call =
144                     matches!(source.kind, ExprKind::Call { .. } | ExprKind::InlineAsm { .. });
145
146                 // (#66975) Source could be a const of type `!`, so has to
147                 // exist in the generated MIR.
148                 unpack!(
149                     block = this.as_temp(block, Some(this.local_scope()), source, Mutability::Mut,)
150                 );
151
152                 // This is an optimization. If the expression was a call then we already have an
153                 // unreachable block. Don't bother to terminate it and create a new one.
154                 if is_call {
155                     block.unit()
156                 } else {
157                     this.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
158                     let end_block = this.cfg.start_new_block();
159                     end_block.unit()
160                 }
161             }
162             ExprKind::LogicalOp { op, lhs, rhs } => {
163                 // And:
164                 //
165                 // [block: If(lhs)] -true-> [else_block: dest = (rhs)]
166                 //        | (false)
167                 //  [shortcurcuit_block: dest = false]
168                 //
169                 // Or:
170                 //
171                 // [block: If(lhs)] -false-> [else_block: dest = (rhs)]
172                 //        | (true)
173                 //  [shortcurcuit_block: dest = true]
174
175                 let (shortcircuit_block, mut else_block, join_block) = (
176                     this.cfg.start_new_block(),
177                     this.cfg.start_new_block(),
178                     this.cfg.start_new_block(),
179                 );
180
181                 let lhs = unpack!(block = this.as_local_operand(block, &this.thir[lhs]));
182                 let blocks = match op {
183                     LogicalOp::And => (else_block, shortcircuit_block),
184                     LogicalOp::Or => (shortcircuit_block, else_block),
185                 };
186                 let term = TerminatorKind::if_(this.tcx, lhs, blocks.0, blocks.1);
187                 this.cfg.terminate(block, source_info, term);
188
189                 this.cfg.push_assign_constant(
190                     shortcircuit_block,
191                     source_info,
192                     destination,
193                     Constant {
194                         span: expr_span,
195                         user_ty: None,
196                         literal: match op {
197                             LogicalOp::And => ConstantKind::from_bool(this.tcx, false),
198                             LogicalOp::Or => ConstantKind::from_bool(this.tcx, true),
199                         },
200                     },
201                 );
202                 this.cfg.goto(shortcircuit_block, source_info, join_block);
203
204                 let rhs = unpack!(else_block = this.as_local_operand(else_block, &this.thir[rhs]));
205                 this.cfg.push_assign(else_block, source_info, destination, Rvalue::Use(rhs));
206                 this.cfg.goto(else_block, source_info, join_block);
207
208                 join_block.unit()
209             }
210             ExprKind::Loop { body } => {
211                 // [block]
212                 //    |
213                 //   [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
214                 //    |        ^                                         |
215                 // false link  |                                         |
216                 //    |        +-----------------------------------------+
217                 //    +-> [diverge_cleanup]
218                 // The false link is required to make sure borrowck considers unwinds through the
219                 // body, even when the exact code in the body cannot unwind
220
221                 let loop_block = this.cfg.start_new_block();
222
223                 // Start the loop.
224                 this.cfg.goto(block, source_info, loop_block);
225
226                 this.in_breakable_scope(Some(loop_block), destination, expr_span, move |this| {
227                     // conduct the test, if necessary
228                     let body_block = this.cfg.start_new_block();
229                     this.cfg.terminate(
230                         loop_block,
231                         source_info,
232                         TerminatorKind::FalseUnwind { real_target: body_block, unwind: None },
233                     );
234                     this.diverge_from(loop_block);
235
236                     // The “return” value of the loop body must always be a unit. We therefore
237                     // introduce a unit temporary as the destination for the loop body.
238                     let tmp = this.get_unit_temp();
239                     // Execute the body, branching back to the test.
240                     let body_block_end =
241                         unpack!(this.expr_into_dest(tmp, body_block, &this.thir[body]));
242                     this.cfg.goto(body_block_end, source_info, loop_block);
243
244                     // Loops are only exited by `break` expressions.
245                     None
246                 })
247             }
248             ExprKind::Call { ty: _, fun, ref args, from_hir_call, fn_span } => {
249                 let fun = unpack!(block = this.as_local_operand(block, &this.thir[fun]));
250                 let args: Vec<_> = args
251                     .into_iter()
252                     .copied()
253                     .map(|arg| unpack!(block = this.as_local_call_operand(block, &this.thir[arg])))
254                     .collect();
255
256                 let success = this.cfg.start_new_block();
257
258                 this.record_operands_moved(&args);
259
260                 debug!("expr_into_dest: fn_span={:?}", fn_span);
261
262                 this.cfg.terminate(
263                     block,
264                     source_info,
265                     TerminatorKind::Call {
266                         func: fun,
267                         args,
268                         cleanup: None,
269                         destination,
270                         // The presence or absence of a return edge affects control-flow sensitive
271                         // MIR checks and ultimately whether code is accepted or not. We can only
272                         // omit the return edge if a return type is visibly uninhabited to a module
273                         // that makes the call.
274                         target: if this.tcx.is_ty_uninhabited_from(
275                             this.parent_module,
276                             expr.ty,
277                             this.param_env,
278                         ) {
279                             None
280                         } else {
281                             Some(success)
282                         },
283                         from_hir_call,
284                         fn_span,
285                     },
286                 );
287                 this.diverge_from(block);
288                 success.unit()
289             }
290             ExprKind::Use { source } => this.expr_into_dest(destination, block, &this.thir[source]),
291             ExprKind::Borrow { arg, borrow_kind } => {
292                 let arg = &this.thir[arg];
293                 // We don't do this in `as_rvalue` because we use `as_place`
294                 // for borrow expressions, so we cannot create an `RValue` that
295                 // remains valid across user code. `as_rvalue` is usually called
296                 // by this method anyway, so this shouldn't cause too many
297                 // unnecessary temporaries.
298                 let arg_place = match borrow_kind {
299                     BorrowKind::Shared => unpack!(block = this.as_read_only_place(block, arg)),
300                     _ => unpack!(block = this.as_place(block, arg)),
301                 };
302                 let borrow = Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind, arg_place);
303                 this.cfg.push_assign(block, source_info, destination, borrow);
304                 block.unit()
305             }
306             ExprKind::AddressOf { mutability, arg } => {
307                 let arg = &this.thir[arg];
308                 let place = match mutability {
309                     hir::Mutability::Not => this.as_read_only_place(block, arg),
310                     hir::Mutability::Mut => this.as_place(block, arg),
311                 };
312                 let address_of = Rvalue::AddressOf(mutability, unpack!(block = place));
313                 this.cfg.push_assign(block, source_info, destination, address_of);
314                 block.unit()
315             }
316             ExprKind::Adt(box AdtExpr {
317                 adt_def,
318                 variant_index,
319                 substs,
320                 ref user_ty,
321                 ref fields,
322                 ref base,
323             }) => {
324                 // See the notes for `ExprKind::Array` in `as_rvalue` and for
325                 // `ExprKind::Borrow` above.
326                 let is_union = adt_def.is_union();
327                 let active_field_index = if is_union { Some(fields[0].name.index()) } else { None };
328
329                 let scope = this.local_scope();
330
331                 // first process the set of fields that were provided
332                 // (evaluating them in order given by user)
333                 let fields_map: FxHashMap<_, _> = fields
334                     .into_iter()
335                     .map(|f| {
336                         let local_info = Box::new(LocalInfo::AggregateTemp);
337                         (
338                             f.name,
339                             unpack!(
340                                 block = this.as_operand(
341                                     block,
342                                     Some(scope),
343                                     &this.thir[f.expr],
344                                     Some(local_info),
345                                     NeedsTemporary::Maybe,
346                                 )
347                             ),
348                         )
349                     })
350                     .collect();
351
352                 let field_names: Vec<_> =
353                     (0..adt_def.variant(variant_index).fields.len()).map(Field::new).collect();
354
355                 let fields: Vec<_> = if let Some(FruInfo { base, field_types }) = base {
356                     let place_builder =
357                         unpack!(block = this.as_place_builder(block, &this.thir[*base]));
358
359                     // MIR does not natively support FRU, so for each
360                     // base-supplied field, generate an operand that
361                     // reads it from the base.
362                     iter::zip(field_names, &**field_types)
363                         .map(|(n, ty)| match fields_map.get(&n) {
364                             Some(v) => v.clone(),
365                             None => {
366                                 let place_builder = place_builder.clone();
367                                 this.consume_by_copy_or_move(
368                                     place_builder.field(n, *ty).into_place(this),
369                                 )
370                             }
371                         })
372                         .collect()
373                 } else {
374                     field_names.iter().filter_map(|n| fields_map.get(n).cloned()).collect()
375                 };
376
377                 let inferred_ty = expr.ty;
378                 let user_ty = user_ty.as_ref().map(|user_ty| {
379                     this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
380                         span: source_info.span,
381                         user_ty: user_ty.clone(),
382                         inferred_ty,
383                     })
384                 });
385                 let adt = Box::new(AggregateKind::Adt(
386                     adt_def.did(),
387                     variant_index,
388                     substs,
389                     user_ty,
390                     active_field_index,
391                 ));
392                 this.cfg.push_assign(
393                     block,
394                     source_info,
395                     destination,
396                     Rvalue::Aggregate(adt, fields),
397                 );
398                 block.unit()
399             }
400             ExprKind::InlineAsm(box InlineAsmExpr {
401                 template,
402                 ref operands,
403                 options,
404                 line_spans,
405             }) => {
406                 use rustc_middle::{mir, thir};
407                 let operands = operands
408                     .into_iter()
409                     .map(|op| match *op {
410                         thir::InlineAsmOperand::In { reg, expr } => mir::InlineAsmOperand::In {
411                             reg,
412                             value: unpack!(block = this.as_local_operand(block, &this.thir[expr])),
413                         },
414                         thir::InlineAsmOperand::Out { reg, late, expr } => {
415                             mir::InlineAsmOperand::Out {
416                                 reg,
417                                 late,
418                                 place: expr.map(|expr| {
419                                     unpack!(block = this.as_place(block, &this.thir[expr]))
420                                 }),
421                             }
422                         }
423                         thir::InlineAsmOperand::InOut { reg, late, expr } => {
424                             let place = unpack!(block = this.as_place(block, &this.thir[expr]));
425                             mir::InlineAsmOperand::InOut {
426                                 reg,
427                                 late,
428                                 // This works because asm operands must be Copy
429                                 in_value: Operand::Copy(place),
430                                 out_place: Some(place),
431                             }
432                         }
433                         thir::InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
434                             mir::InlineAsmOperand::InOut {
435                                 reg,
436                                 late,
437                                 in_value: unpack!(
438                                     block = this.as_local_operand(block, &this.thir[in_expr])
439                                 ),
440                                 out_place: out_expr.map(|out_expr| {
441                                     unpack!(block = this.as_place(block, &this.thir[out_expr]))
442                                 }),
443                             }
444                         }
445                         thir::InlineAsmOperand::Const { value, span } => {
446                             mir::InlineAsmOperand::Const {
447                                 value: Box::new(Constant { span, user_ty: None, literal: value }),
448                             }
449                         }
450                         thir::InlineAsmOperand::SymFn { value, span } => {
451                             mir::InlineAsmOperand::SymFn {
452                                 value: Box::new(Constant { span, user_ty: None, literal: value }),
453                             }
454                         }
455                         thir::InlineAsmOperand::SymStatic { def_id } => {
456                             mir::InlineAsmOperand::SymStatic { def_id }
457                         }
458                     })
459                     .collect();
460
461                 if !options.contains(InlineAsmOptions::NORETURN) {
462                     this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
463                 }
464
465                 let destination_block = this.cfg.start_new_block();
466                 this.cfg.terminate(
467                     block,
468                     source_info,
469                     TerminatorKind::InlineAsm {
470                         template,
471                         operands,
472                         options,
473                         line_spans,
474                         destination: if options.contains(InlineAsmOptions::NORETURN) {
475                             None
476                         } else {
477                             Some(destination_block)
478                         },
479                         cleanup: None,
480                     },
481                 );
482                 if options.contains(InlineAsmOptions::MAY_UNWIND) {
483                     this.diverge_from(block);
484                 }
485                 destination_block.unit()
486             }
487
488             // These cases don't actually need a destination
489             ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
490                 unpack!(block = this.stmt_expr(block, expr, None));
491                 this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
492                 block.unit()
493             }
494
495             ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Return { .. } => {
496                 unpack!(block = this.stmt_expr(block, expr, None));
497                 // No assign, as these have type `!`.
498                 block.unit()
499             }
500
501             // Avoid creating a temporary
502             ExprKind::VarRef { .. }
503             | ExprKind::UpvarRef { .. }
504             | ExprKind::PlaceTypeAscription { .. }
505             | ExprKind::ValueTypeAscription { .. } => {
506                 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
507
508                 let place = unpack!(block = this.as_place(block, expr));
509                 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
510                 this.cfg.push_assign(block, source_info, destination, rvalue);
511                 block.unit()
512             }
513             ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
514                 debug_assert_eq!(Category::of(&expr.kind), Some(Category::Place));
515
516                 // Create a "fake" temporary variable so that we check that the
517                 // value is Sized. Usually, this is caught in type checking, but
518                 // in the case of box expr there is no such check.
519                 if !destination.projection.is_empty() {
520                     this.local_decls.push(LocalDecl::new(expr.ty, expr.span));
521                 }
522
523                 let place = unpack!(block = this.as_place(block, expr));
524                 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
525                 this.cfg.push_assign(block, source_info, destination, rvalue);
526                 block.unit()
527             }
528
529             ExprKind::Yield { value } => {
530                 let scope = this.local_scope();
531                 let value = unpack!(
532                     block = this.as_operand(
533                         block,
534                         Some(scope),
535                         &this.thir[value],
536                         None,
537                         NeedsTemporary::No
538                     )
539                 );
540                 let resume = this.cfg.start_new_block();
541                 this.cfg.terminate(
542                     block,
543                     source_info,
544                     TerminatorKind::Yield { value, resume, resume_arg: destination, drop: None },
545                 );
546                 this.generator_drop_cleanup(block);
547                 resume.unit()
548             }
549
550             // these are the cases that are more naturally handled by some other mode
551             ExprKind::Unary { .. }
552             | ExprKind::Binary { .. }
553             | ExprKind::Box { .. }
554             | ExprKind::Cast { .. }
555             | ExprKind::Pointer { .. }
556             | ExprKind::Repeat { .. }
557             | ExprKind::Array { .. }
558             | ExprKind::Tuple { .. }
559             | ExprKind::Closure { .. }
560             | ExprKind::ConstBlock { .. }
561             | ExprKind::Literal { .. }
562             | ExprKind::NamedConst { .. }
563             | ExprKind::NonHirLiteral { .. }
564             | ExprKind::ZstLiteral { .. }
565             | ExprKind::ConstParam { .. }
566             | ExprKind::ThreadLocalRef(_)
567             | ExprKind::StaticRef { .. } => {
568                 debug_assert!(match Category::of(&expr.kind).unwrap() {
569                     // should be handled above
570                     Category::Rvalue(RvalueFunc::Into) => false,
571
572                     // must be handled above or else we get an
573                     // infinite loop in the builder; see
574                     // e.g., `ExprKind::VarRef` above
575                     Category::Place => false,
576
577                     _ => true,
578                 });
579
580                 let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
581                 this.cfg.push_assign(block, source_info, destination, rvalue);
582                 block.unit()
583             }
584         };
585
586         if !expr_is_block_or_scope {
587             let popped = this.block_context.pop();
588             assert!(popped.is_some());
589         }
590
591         block_and
592     }
593
594     fn is_let(&self, expr: ExprId) -> bool {
595         match self.thir[expr].kind {
596             ExprKind::Let { .. } => true,
597             ExprKind::Scope { value, .. } => self.is_let(value),
598             _ => false,
599         }
600     }
601 }