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