]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir_build/build/expr/into.rs
5ef338c624da26cff15e64d8af66010fcc64ec28
[rust.git] / src / librustc_mir_build / 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::hair::*;
6 use rustc::mir::*;
7 use rustc::ty::{self, CanonicalUserTypeAnnotation};
8 use rustc_data_structures::fx::FxHashMap;
9 use rustc_hir as hir;
10 use rustc_span::symbol::sym;
11
12 use rustc_target::spec::abi::Abi;
13
14 impl<'a, 'tcx> Builder<'a, 'tcx> {
15     /// Compile `expr`, storing the result into `destination`, which
16     /// is assumed to be uninitialized.
17     crate fn into_expr(
18         &mut self,
19         destination: &Place<'tcx>,
20         mut block: BasicBlock,
21         expr: Expr<'tcx>,
22     ) -> BlockAnd<()> {
23         debug!("into_expr(destination={:?}, block={:?}, expr={:?})", destination, block, expr);
24
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 = match expr.kind {
33             ExprKind::Block { .. } => true,
34             ExprKind::Scope { .. } => true,
35             _ => false,
36         };
37
38         if !expr_is_block_or_scope {
39             this.block_context.push(BlockFrame::SubExpr);
40         }
41
42         let block_and = match expr.kind {
43             ExprKind::Scope { region_scope, lint_level, value } => {
44                 let region_scope = (region_scope, source_info);
45                 this.in_scope(region_scope, lint_level, |this| this.into(destination, block, value))
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::NeverToAny { source } => {
54                 let source = this.hir.mirror(source);
55                 let is_call = match source.kind {
56                     ExprKind::Call { .. } => true,
57                     _ => false,
58                 };
59
60                 // (#66975) Source could be a const of type `!`, so has to
61                 // exist in the generated MIR.
62                 unpack!(block = this.as_temp(block, this.local_scope(), source, Mutability::Mut,));
63
64                 // This is an optimization. If the expression was a call then we already have an
65                 // unreachable block. Don't bother to terminate it and create a new one.
66                 if is_call {
67                     block.unit()
68                 } else {
69                     this.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
70                     let end_block = this.cfg.start_new_block();
71                     end_block.unit()
72                 }
73             }
74             ExprKind::LogicalOp { op, lhs, rhs } => {
75                 // And:
76                 //
77                 // [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block]
78                 //        |                          | (false)
79                 //        +----------false-----------+------------------> [false_block]
80                 //
81                 // Or:
82                 //
83                 // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
84                 //        | (true)                   | (false)
85                 //  [true_block]               [false_block]
86
87                 let (true_block, false_block, mut else_block, join_block) = (
88                     this.cfg.start_new_block(),
89                     this.cfg.start_new_block(),
90                     this.cfg.start_new_block(),
91                     this.cfg.start_new_block(),
92                 );
93
94                 let lhs = unpack!(block = this.as_local_operand(block, lhs));
95                 let blocks = match op {
96                     LogicalOp::And => (else_block, false_block),
97                     LogicalOp::Or => (true_block, else_block),
98                 };
99                 let term = TerminatorKind::if_(this.hir.tcx(), lhs, blocks.0, blocks.1);
100                 this.cfg.terminate(block, source_info, term);
101
102                 let rhs = unpack!(else_block = this.as_local_operand(else_block, rhs));
103                 let term = TerminatorKind::if_(this.hir.tcx(), rhs, true_block, false_block);
104                 this.cfg.terminate(else_block, source_info, term);
105
106                 this.cfg.push_assign_constant(
107                     true_block,
108                     source_info,
109                     destination,
110                     Constant { span: expr_span, user_ty: None, literal: this.hir.true_literal() },
111                 );
112
113                 this.cfg.push_assign_constant(
114                     false_block,
115                     source_info,
116                     destination,
117                     Constant { span: expr_span, user_ty: None, literal: this.hir.false_literal() },
118                 );
119
120                 // Link up both branches:
121                 this.cfg.goto(true_block, source_info, join_block);
122                 this.cfg.goto(false_block, source_info, join_block);
123                 join_block.unit()
124             }
125             ExprKind::Loop { body } => {
126                 // [block]
127                 //    |
128                 //   [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
129                 //    |        ^                                         |
130                 // false link  |                                         |
131                 //    |        +-----------------------------------------+
132                 //    +-> [diverge_cleanup]
133                 // The false link is required to make sure borrowck considers unwinds through the
134                 // body, even when the exact code in the body cannot unwind
135
136                 let loop_block = this.cfg.start_new_block();
137                 let exit_block = this.cfg.start_new_block();
138
139                 // Start the loop.
140                 this.cfg.goto(block, source_info, loop_block);
141
142                 this.in_breakable_scope(
143                     Some(loop_block),
144                     exit_block,
145                     destination.clone(),
146                     move |this| {
147                         // conduct the test, if necessary
148                         let body_block = this.cfg.start_new_block();
149                         let diverge_cleanup = this.diverge_cleanup();
150                         this.cfg.terminate(
151                             loop_block,
152                             source_info,
153                             TerminatorKind::FalseUnwind {
154                                 real_target: body_block,
155                                 unwind: Some(diverge_cleanup),
156                             },
157                         );
158
159                         // The “return” value of the loop body must always be an unit. We therefore
160                         // introduce a unit temporary as the destination for the loop body.
161                         let tmp = this.get_unit_temp();
162                         // Execute the body, branching back to the test.
163                         let body_block_end = unpack!(this.into(&tmp, body_block, body));
164                         this.cfg.goto(body_block_end, source_info, loop_block);
165                     },
166                 );
167                 exit_block.unit()
168             }
169             ExprKind::Call { ty, fun, args, from_hir_call } => {
170                 let intrinsic = match ty.kind {
171                     ty::FnDef(def_id, _) => {
172                         let f = ty.fn_sig(this.hir.tcx());
173                         if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic {
174                             Some(this.hir.tcx().item_name(def_id))
175                         } else {
176                             None
177                         }
178                     }
179                     _ => None,
180                 };
181                 let fun = unpack!(block = this.as_local_operand(block, fun));
182                 if let Some(sym::move_val_init) = intrinsic {
183                     // `move_val_init` has "magic" semantics - the second argument is
184                     // always evaluated "directly" into the first one.
185
186                     let mut args = args.into_iter();
187                     let ptr = args.next().expect("0 arguments to `move_val_init`");
188                     let val = args.next().expect("1 argument to `move_val_init`");
189                     assert!(args.next().is_none(), ">2 arguments to `move_val_init`");
190
191                     let ptr = this.hir.mirror(ptr);
192                     let ptr_ty = ptr.ty;
193                     // Create an *internal* temp for the pointer, so that unsafety
194                     // checking won't complain about the raw pointer assignment.
195                     let ptr_temp = this.local_decls.push(LocalDecl {
196                         mutability: Mutability::Mut,
197                         ty: ptr_ty,
198                         user_ty: UserTypeProjections::none(),
199                         source_info,
200                         internal: true,
201                         local_info: LocalInfo::Other,
202                         is_block_tail: None,
203                     });
204                     let ptr_temp = Place::from(ptr_temp);
205                     let block = unpack!(this.into(&ptr_temp, block, ptr));
206                     this.into(&this.hir.tcx().mk_place_deref(ptr_temp), block, val)
207                 } else {
208                     let args: Vec<_> = args
209                         .into_iter()
210                         .map(|arg| unpack!(block = this.as_local_operand(block, arg)))
211                         .collect();
212
213                     let success = this.cfg.start_new_block();
214                     let cleanup = this.diverge_cleanup();
215
216                     this.record_operands_moved(&args);
217
218                     this.cfg.terminate(
219                         block,
220                         source_info,
221                         TerminatorKind::Call {
222                             func: fun,
223                             args,
224                             cleanup: Some(cleanup),
225                             // FIXME(varkor): replace this with an uninhabitedness-based check.
226                             // This requires getting access to the current module to call
227                             // `tcx.is_ty_uninhabited_from`, which is currently tricky to do.
228                             destination: if expr.ty.is_never() {
229                                 None
230                             } else {
231                                 Some((*destination, success))
232                             },
233                             from_hir_call,
234                         },
235                     );
236                     success.unit()
237                 }
238             }
239             ExprKind::Use { source } => this.into(destination, block, source),
240             ExprKind::Borrow { arg, borrow_kind } => {
241                 // We don't do this in `as_rvalue` because we use `as_place`
242                 // for borrow expressions, so we cannot create an `RValue` that
243                 // remains valid across user code. `as_rvalue` is usually called
244                 // by this method anyway, so this shouldn't cause too many
245                 // unnecessary temporaries.
246                 let arg_place = match borrow_kind {
247                     BorrowKind::Shared => unpack!(block = this.as_read_only_place(block, arg)),
248                     _ => unpack!(block = this.as_place(block, arg)),
249                 };
250                 let borrow =
251                     Rvalue::Ref(this.hir.tcx().lifetimes.re_erased, borrow_kind, arg_place);
252                 this.cfg.push_assign(block, source_info, destination, borrow);
253                 block.unit()
254             }
255             ExprKind::AddressOf { mutability, arg } => {
256                 let place = match mutability {
257                     hir::Mutability::Not => this.as_read_only_place(block, arg),
258                     hir::Mutability::Mut => this.as_place(block, arg),
259                 };
260                 let address_of = Rvalue::AddressOf(mutability, unpack!(block = place));
261                 this.cfg.push_assign(block, source_info, destination, address_of);
262                 block.unit()
263             }
264             ExprKind::Adt { adt_def, variant_index, substs, user_ty, fields, base } => {
265                 // See the notes for `ExprKind::Array` in `as_rvalue` and for
266                 // `ExprKind::Borrow` above.
267                 let is_union = adt_def.is_union();
268                 let active_field_index = if is_union { Some(fields[0].name.index()) } else { None };
269
270                 let scope = this.local_scope();
271
272                 // first process the set of fields that were provided
273                 // (evaluating them in order given by user)
274                 let fields_map: FxHashMap<_, _> = fields
275                     .into_iter()
276                     .map(|f| (f.name, unpack!(block = this.as_operand(block, scope, f.expr))))
277                     .collect();
278
279                 let field_names = this.hir.all_fields(adt_def, variant_index);
280
281                 let fields =
282                     if let Some(FruInfo { base, field_types }) = base {
283                         let base = unpack!(block = this.as_place(block, base));
284
285                         // MIR does not natively support FRU, so for each
286                         // base-supplied field, generate an operand that
287                         // reads it from the base.
288                         field_names
289                             .into_iter()
290                             .zip(field_types.into_iter())
291                             .map(|(n, ty)| match fields_map.get(&n) {
292                                 Some(v) => v.clone(),
293                                 None => this.consume_by_copy_or_move(
294                                     this.hir.tcx().mk_place_field(base.clone(), n, ty),
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
326             // These cases don't actually need a destination
327             ExprKind::Assign { .. }
328             | ExprKind::AssignOp { .. }
329             | ExprKind::Continue { .. }
330             | ExprKind::Break { .. }
331             | ExprKind::InlineAsm { .. }
332             | ExprKind::Return { .. } => {
333                 unpack!(block = this.stmt_expr(block, expr, None));
334                 this.cfg.push_assign_unit(block, source_info, destination);
335                 block.unit()
336             }
337
338             // Avoid creating a temporary
339             ExprKind::VarRef { .. }
340             | ExprKind::SelfRef
341             | ExprKind::PlaceTypeAscription { .. }
342             | ExprKind::ValueTypeAscription { .. } => {
343                 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
344
345                 let place = unpack!(block = this.as_place(block, expr));
346                 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
347                 this.cfg.push_assign(block, source_info, destination, rvalue);
348                 block.unit()
349             }
350             ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
351                 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
352
353                 // Create a "fake" temporary variable so that we check that the
354                 // value is Sized. Usually, this is caught in type checking, but
355                 // in the case of box expr there is no such check.
356                 if !destination.projection.is_empty() {
357                     this.local_decls.push(LocalDecl::new_temp(expr.ty, expr.span));
358                 }
359
360                 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
361
362                 let place = unpack!(block = this.as_place(block, expr));
363                 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
364                 this.cfg.push_assign(block, source_info, destination, rvalue);
365                 block.unit()
366             }
367
368             // these are the cases that are more naturally handled by some other mode
369             ExprKind::Unary { .. }
370             | ExprKind::Binary { .. }
371             | ExprKind::Box { .. }
372             | ExprKind::Cast { .. }
373             | ExprKind::Pointer { .. }
374             | ExprKind::Repeat { .. }
375             | ExprKind::Array { .. }
376             | ExprKind::Tuple { .. }
377             | ExprKind::Closure { .. }
378             | ExprKind::Literal { .. }
379             | ExprKind::StaticRef { .. }
380             | ExprKind::Yield { .. } => {
381                 debug_assert!(match Category::of(&expr.kind).unwrap() {
382                     // should be handled above
383                     Category::Rvalue(RvalueFunc::Into) => false,
384
385                     // must be handled above or else we get an
386                     // infinite loop in the builder; see
387                     // e.g., `ExprKind::VarRef` above
388                     Category::Place => false,
389
390                     _ => true,
391                 });
392
393                 let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
394                 this.cfg.push_assign(block, source_info, destination, rvalue);
395                 block.unit()
396             }
397         };
398
399         if !expr_is_block_or_scope {
400             let popped = this.block_context.pop();
401             assert!(popped.is_some());
402         }
403
404         block_and
405     }
406 }