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