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