]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
Auto merge of #104219 - bryangarza:async-track-caller-dup, r=eholk
[rust.git] / compiler / rustc_mir_build / src / build / expr / as_rvalue.rs
1 //! See docs in `build/expr/mod.rs`.
2
3 use rustc_index::vec::Idx;
4 use rustc_middle::ty::util::IntTypeExt;
5 use rustc_target::abi::{Abi, Primitive};
6
7 use crate::build::expr::as_place::PlaceBase;
8 use crate::build::expr::category::{Category, RvalueFunc};
9 use crate::build::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary};
10 use rustc_hir::lang_items::LangItem;
11 use rustc_middle::middle::region;
12 use rustc_middle::mir::AssertKind;
13 use rustc_middle::mir::Place;
14 use rustc_middle::mir::*;
15 use rustc_middle::thir::*;
16 use rustc_middle::ty::cast::{mir_cast_kind, CastTy};
17 use rustc_middle::ty::{self, Ty, UpvarSubsts};
18 use rustc_span::Span;
19
20 impl<'a, 'tcx> Builder<'a, 'tcx> {
21     /// Returns an rvalue suitable for use until the end of the current
22     /// scope expression.
23     ///
24     /// The operand returned from this function will *not be valid* after
25     /// an ExprKind::Scope is passed, so please do *not* return it from
26     /// functions to avoid bad miscompiles.
27     pub(crate) fn as_local_rvalue(
28         &mut self,
29         block: BasicBlock,
30         expr: &Expr<'tcx>,
31     ) -> BlockAnd<Rvalue<'tcx>> {
32         let local_scope = self.local_scope();
33         self.as_rvalue(block, Some(local_scope), expr)
34     }
35
36     /// Compile `expr`, yielding an rvalue.
37     pub(crate) fn as_rvalue(
38         &mut self,
39         mut block: BasicBlock,
40         scope: Option<region::Scope>,
41         expr: &Expr<'tcx>,
42     ) -> BlockAnd<Rvalue<'tcx>> {
43         debug!("expr_as_rvalue(block={:?}, scope={:?}, expr={:?})", block, scope, expr);
44
45         let this = self;
46         let expr_span = expr.span;
47         let source_info = this.source_info(expr_span);
48
49         match expr.kind {
50             ExprKind::ThreadLocalRef(did) => block.and(Rvalue::ThreadLocalRef(did)),
51             ExprKind::Scope { region_scope, lint_level, value } => {
52                 let region_scope = (region_scope, source_info);
53                 this.in_scope(region_scope, lint_level, |this| {
54                     this.as_rvalue(block, scope, &this.thir[value])
55                 })
56             }
57             ExprKind::Repeat { value, count } => {
58                 if Some(0) == count.try_eval_usize(this.tcx, this.param_env) {
59                     this.build_zero_repeat(block, value, scope, source_info)
60                 } else {
61                     let value_operand = unpack!(
62                         block = this.as_operand(
63                             block,
64                             scope,
65                             &this.thir[value],
66                             None,
67                             NeedsTemporary::No
68                         )
69                     );
70                     block.and(Rvalue::Repeat(value_operand, count))
71                 }
72             }
73             ExprKind::Binary { op, lhs, rhs } => {
74                 let lhs = unpack!(
75                     block =
76                         this.as_operand(block, scope, &this.thir[lhs], None, NeedsTemporary::Maybe)
77                 );
78                 let rhs = unpack!(
79                     block =
80                         this.as_operand(block, scope, &this.thir[rhs], None, NeedsTemporary::No)
81                 );
82                 this.build_binary_op(block, op, expr_span, expr.ty, lhs, rhs)
83             }
84             ExprKind::Unary { op, arg } => {
85                 let arg = unpack!(
86                     block =
87                         this.as_operand(block, scope, &this.thir[arg], None, NeedsTemporary::No)
88                 );
89                 // Check for -MIN on signed integers
90                 if this.check_overflow && op == UnOp::Neg && expr.ty.is_signed() {
91                     let bool_ty = this.tcx.types.bool;
92
93                     let minval = this.minval_literal(expr_span, expr.ty);
94                     let is_min = this.temp(bool_ty, expr_span);
95
96                     this.cfg.push_assign(
97                         block,
98                         source_info,
99                         is_min,
100                         Rvalue::BinaryOp(BinOp::Eq, Box::new((arg.to_copy(), minval))),
101                     );
102
103                     block = this.assert(
104                         block,
105                         Operand::Move(is_min),
106                         false,
107                         AssertKind::OverflowNeg(arg.to_copy()),
108                         expr_span,
109                     );
110                 }
111                 block.and(Rvalue::UnaryOp(op, arg))
112             }
113             ExprKind::Box { value } => {
114                 let value = &this.thir[value];
115                 let tcx = this.tcx;
116
117                 // `exchange_malloc` is unsafe but box is safe, so need a new scope.
118                 let synth_scope = this.new_source_scope(
119                     expr_span,
120                     LintLevel::Inherited,
121                     Some(Safety::BuiltinUnsafe),
122                 );
123                 let synth_info = SourceInfo { span: expr_span, scope: synth_scope };
124
125                 let size = this.temp(tcx.types.usize, expr_span);
126                 this.cfg.push_assign(
127                     block,
128                     synth_info,
129                     size,
130                     Rvalue::NullaryOp(NullOp::SizeOf, value.ty),
131                 );
132
133                 let align = this.temp(tcx.types.usize, expr_span);
134                 this.cfg.push_assign(
135                     block,
136                     synth_info,
137                     align,
138                     Rvalue::NullaryOp(NullOp::AlignOf, value.ty),
139                 );
140
141                 // malloc some memory of suitable size and align:
142                 let exchange_malloc = Operand::function_handle(
143                     tcx,
144                     tcx.require_lang_item(LangItem::ExchangeMalloc, Some(expr_span)),
145                     ty::List::empty(),
146                     expr_span,
147                 );
148                 let storage = this.temp(tcx.mk_mut_ptr(tcx.types.u8), expr_span);
149                 let success = this.cfg.start_new_block();
150                 this.cfg.terminate(
151                     block,
152                     synth_info,
153                     TerminatorKind::Call {
154                         func: exchange_malloc,
155                         args: vec![Operand::Move(size), Operand::Move(align)],
156                         destination: storage,
157                         target: Some(success),
158                         cleanup: None,
159                         from_hir_call: false,
160                         fn_span: expr_span,
161                     },
162                 );
163                 this.diverge_from(block);
164                 block = success;
165
166                 // The `Box<T>` temporary created here is not a part of the HIR,
167                 // and therefore is not considered during generator auto-trait
168                 // determination. See the comment about `box` at `yield_in_scope`.
169                 let result = this.local_decls.push(LocalDecl::new(expr.ty, expr_span).internal());
170                 this.cfg.push(
171                     block,
172                     Statement { source_info, kind: StatementKind::StorageLive(result) },
173                 );
174                 if let Some(scope) = scope {
175                     // schedule a shallow free of that memory, lest we unwind:
176                     this.schedule_drop_storage_and_value(expr_span, scope, result);
177                 }
178
179                 // Transmute `*mut u8` to the box (thus far, uninitialized):
180                 let box_ = Rvalue::ShallowInitBox(Operand::Move(storage), value.ty);
181                 this.cfg.push_assign(block, source_info, Place::from(result), box_);
182
183                 // initialize the box contents:
184                 unpack!(
185                     block = this.expr_into_dest(
186                         this.tcx.mk_place_deref(Place::from(result)),
187                         block,
188                         value
189                     )
190                 );
191                 block.and(Rvalue::Use(Operand::Move(Place::from(result))))
192             }
193             ExprKind::Cast { source } => {
194                 let source = &this.thir[source];
195
196                 // Casting an enum to an integer is equivalent to computing the discriminant and casting the
197                 // discriminant. Previously every backend had to repeat the logic for this operation. Now we
198                 // create all the steps directly in MIR with operations all backends need to support anyway.
199                 let (source, ty) = if let ty::Adt(adt_def, ..) = source.ty.kind() && adt_def.is_enum() {
200                     let discr_ty = adt_def.repr().discr_type().to_ty(this.tcx);
201                     let temp = unpack!(block = this.as_temp(block, scope, source, Mutability::Not));
202                     let layout = this.tcx.layout_of(this.param_env.and(source.ty));
203                     let discr = this.temp(discr_ty, source.span);
204                     this.cfg.push_assign(
205                         block,
206                         source_info,
207                         discr,
208                         Rvalue::Discriminant(temp.into()),
209                     );
210                     let (op,ty) = (Operand::Move(discr), discr_ty);
211
212                     if let Abi::Scalar(scalar) = layout.unwrap().abi{
213                         if let Primitive::Int(_, signed) = scalar.primitive() {
214                             let range = scalar.valid_range(&this.tcx);
215                             // FIXME: Handle wraparound cases too.
216                             if range.end >= range.start {
217                                 let mut assumer = |range: u128, bin_op: BinOp| {
218                                     // We will be overwriting this val if our scalar is signed value
219                                     // because sign extension on unsigned types might cause unintended things
220                                     let mut range_val =
221                                         ConstantKind::from_bits(this.tcx, range, ty::ParamEnv::empty().and(discr_ty));
222                                     let bool_ty = this.tcx.types.bool;
223                                     if signed {
224                                         let scalar_size_extend = scalar.size(&this.tcx).sign_extend(range);
225                                         let discr_layout = this.tcx.layout_of(this.param_env.and(discr_ty));
226                                         let truncated_val = discr_layout.unwrap().size.truncate(scalar_size_extend);
227                                         range_val = ConstantKind::from_bits(
228                                             this.tcx,
229                                             truncated_val,
230                                             ty::ParamEnv::empty().and(discr_ty),
231                                         );
232                                     }
233                                     let lit_op = this.literal_operand(expr.span, range_val);
234                                     let is_bin_op = this.temp(bool_ty, expr_span);
235                                     this.cfg.push_assign(
236                                         block,
237                                         source_info,
238                                         is_bin_op,
239                                         Rvalue::BinaryOp(bin_op, Box::new(((lit_op), (Operand::Copy(discr))))),
240                                     );
241                                     this.cfg.push(
242                                         block,
243                                         Statement {
244                                             source_info,
245                                             kind: StatementKind::Intrinsic(Box::new(NonDivergingIntrinsic::Assume(
246                                                 Operand::Copy(is_bin_op),
247                                             ))),
248                                         },
249                                     )
250                                 };
251                                 assumer(range.end, BinOp::Ge);
252                                 assumer(range.start, BinOp::Le);
253                             }
254                         }
255                     }
256
257                     (op,ty)
258
259                 } else {
260                     let ty = source.ty;
261                     let source = unpack!(
262                         block = this.as_operand(block, scope, source, None, NeedsTemporary::No)
263                     );
264                     (source, ty)
265                 };
266                 let from_ty = CastTy::from_ty(ty);
267                 let cast_ty = CastTy::from_ty(expr.ty);
268                 debug!("ExprKind::Cast from_ty={from_ty:?}, cast_ty={:?}/{cast_ty:?}", expr.ty,);
269                 let cast_kind = mir_cast_kind(ty, expr.ty);
270                 block.and(Rvalue::Cast(cast_kind, source, expr.ty))
271             }
272             ExprKind::Pointer { cast, source } => {
273                 let source = unpack!(
274                     block =
275                         this.as_operand(block, scope, &this.thir[source], None, NeedsTemporary::No)
276                 );
277                 block.and(Rvalue::Cast(CastKind::Pointer(cast), source, expr.ty))
278             }
279             ExprKind::Array { ref fields } => {
280                 // (*) We would (maybe) be closer to codegen if we
281                 // handled this and other aggregate cases via
282                 // `into()`, not `as_rvalue` -- in that case, instead
283                 // of generating
284                 //
285                 //     let tmp1 = ...1;
286                 //     let tmp2 = ...2;
287                 //     dest = Rvalue::Aggregate(Foo, [tmp1, tmp2])
288                 //
289                 // we could just generate
290                 //
291                 //     dest.f = ...1;
292                 //     dest.g = ...2;
293                 //
294                 // The problem is that then we would need to:
295                 //
296                 // (a) have a more complex mechanism for handling
297                 //     partial cleanup;
298                 // (b) distinguish the case where the type `Foo` has a
299                 //     destructor, in which case creating an instance
300                 //     as a whole "arms" the destructor, and you can't
301                 //     write individual fields; and,
302                 // (c) handle the case where the type Foo has no
303                 //     fields. We don't want `let x: ();` to compile
304                 //     to the same MIR as `let x = ();`.
305
306                 // first process the set of fields
307                 let el_ty = expr.ty.sequence_element_type(this.tcx);
308                 let fields: Vec<_> = fields
309                     .into_iter()
310                     .copied()
311                     .map(|f| {
312                         unpack!(
313                             block = this.as_operand(
314                                 block,
315                                 scope,
316                                 &this.thir[f],
317                                 None,
318                                 NeedsTemporary::Maybe
319                             )
320                         )
321                     })
322                     .collect();
323
324                 block.and(Rvalue::Aggregate(Box::new(AggregateKind::Array(el_ty)), fields))
325             }
326             ExprKind::Tuple { ref fields } => {
327                 // see (*) above
328                 // first process the set of fields
329                 let fields: Vec<_> = fields
330                     .into_iter()
331                     .copied()
332                     .map(|f| {
333                         unpack!(
334                             block = this.as_operand(
335                                 block,
336                                 scope,
337                                 &this.thir[f],
338                                 None,
339                                 NeedsTemporary::Maybe
340                             )
341                         )
342                     })
343                     .collect();
344
345                 block.and(Rvalue::Aggregate(Box::new(AggregateKind::Tuple), fields))
346             }
347             ExprKind::Closure(box ClosureExpr {
348                 closure_id,
349                 substs,
350                 ref upvars,
351                 movability,
352                 ref fake_reads,
353             }) => {
354                 // Convert the closure fake reads, if any, from `ExprRef` to mir `Place`
355                 // and push the fake reads.
356                 // This must come before creating the operands. This is required in case
357                 // there is a fake read and a borrow of the same path, since otherwise the
358                 // fake read might interfere with the borrow. Consider an example like this
359                 // one:
360                 // ```
361                 // let mut x = 0;
362                 // let c = || {
363                 //     &mut x; // mutable borrow of `x`
364                 //     match x { _ => () } // fake read of `x`
365                 // };
366                 // ```
367                 //
368                 for (thir_place, cause, hir_id) in fake_reads.into_iter() {
369                     let place_builder =
370                         unpack!(block = this.as_place_builder(block, &this.thir[*thir_place]));
371
372                     if let Ok(place_builder_resolved) = place_builder.try_upvars_resolved(this) {
373                         let mir_place = place_builder_resolved.into_place(this);
374                         this.cfg.push_fake_read(
375                             block,
376                             this.source_info(this.tcx.hir().span(*hir_id)),
377                             *cause,
378                             mir_place,
379                         );
380                     }
381                 }
382
383                 // see (*) above
384                 let operands: Vec<_> = upvars
385                     .into_iter()
386                     .copied()
387                     .map(|upvar| {
388                         let upvar = &this.thir[upvar];
389                         match Category::of(&upvar.kind) {
390                             // Use as_place to avoid creating a temporary when
391                             // moving a variable into a closure, so that
392                             // borrowck knows which variables to mark as being
393                             // used as mut. This is OK here because the upvar
394                             // expressions have no side effects and act on
395                             // disjoint places.
396                             // This occurs when capturing by copy/move, while
397                             // by reference captures use as_operand
398                             Some(Category::Place) => {
399                                 let place = unpack!(block = this.as_place(block, upvar));
400                                 this.consume_by_copy_or_move(place)
401                             }
402                             _ => {
403                                 // Turn mutable borrow captures into unique
404                                 // borrow captures when capturing an immutable
405                                 // variable. This is sound because the mutation
406                                 // that caused the capture will cause an error.
407                                 match upvar.kind {
408                                     ExprKind::Borrow {
409                                         borrow_kind:
410                                             BorrowKind::Mut { allow_two_phase_borrow: false },
411                                         arg,
412                                     } => unpack!(
413                                         block = this.limit_capture_mutability(
414                                             upvar.span,
415                                             upvar.ty,
416                                             scope,
417                                             block,
418                                             &this.thir[arg],
419                                         )
420                                     ),
421                                     _ => {
422                                         unpack!(
423                                             block = this.as_operand(
424                                                 block,
425                                                 scope,
426                                                 upvar,
427                                                 None,
428                                                 NeedsTemporary::Maybe
429                                             )
430                                         )
431                                     }
432                                 }
433                             }
434                         }
435                     })
436                     .collect();
437
438                 let result = match substs {
439                     UpvarSubsts::Generator(substs) => {
440                         // We implicitly set the discriminant to 0. See
441                         // librustc_mir/transform/deaggregator.rs for details.
442                         let movability = movability.unwrap();
443                         Box::new(AggregateKind::Generator(closure_id, substs, movability))
444                     }
445                     UpvarSubsts::Closure(substs) => {
446                         Box::new(AggregateKind::Closure(closure_id, substs))
447                     }
448                 };
449                 block.and(Rvalue::Aggregate(result, operands))
450             }
451             ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
452                 block = unpack!(this.stmt_expr(block, expr, None));
453                 block.and(Rvalue::Use(Operand::Constant(Box::new(Constant {
454                     span: expr_span,
455                     user_ty: None,
456                     literal: ConstantKind::zero_sized(this.tcx.types.unit),
457                 }))))
458             }
459
460             ExprKind::Literal { .. }
461             | ExprKind::NamedConst { .. }
462             | ExprKind::NonHirLiteral { .. }
463             | ExprKind::ZstLiteral { .. }
464             | ExprKind::ConstParam { .. }
465             | ExprKind::ConstBlock { .. }
466             | ExprKind::StaticRef { .. } => {
467                 let constant = this.as_constant(expr);
468                 block.and(Rvalue::Use(Operand::Constant(Box::new(constant))))
469             }
470
471             ExprKind::Yield { .. }
472             | ExprKind::Block { .. }
473             | ExprKind::Match { .. }
474             | ExprKind::If { .. }
475             | ExprKind::NeverToAny { .. }
476             | ExprKind::Use { .. }
477             | ExprKind::Borrow { .. }
478             | ExprKind::AddressOf { .. }
479             | ExprKind::Adt { .. }
480             | ExprKind::Loop { .. }
481             | ExprKind::LogicalOp { .. }
482             | ExprKind::Call { .. }
483             | ExprKind::Field { .. }
484             | ExprKind::Let { .. }
485             | ExprKind::Deref { .. }
486             | ExprKind::Index { .. }
487             | ExprKind::VarRef { .. }
488             | ExprKind::UpvarRef { .. }
489             | ExprKind::Break { .. }
490             | ExprKind::Continue { .. }
491             | ExprKind::Return { .. }
492             | ExprKind::InlineAsm { .. }
493             | ExprKind::PlaceTypeAscription { .. }
494             | ExprKind::ValueTypeAscription { .. } => {
495                 // these do not have corresponding `Rvalue` variants,
496                 // so make an operand and then return that
497                 debug_assert!(!matches!(
498                     Category::of(&expr.kind),
499                     Some(Category::Rvalue(RvalueFunc::AsRvalue) | Category::Constant)
500                 ));
501                 let operand =
502                     unpack!(block = this.as_operand(block, scope, expr, None, NeedsTemporary::No));
503                 block.and(Rvalue::Use(operand))
504             }
505         }
506     }
507
508     pub(crate) fn build_binary_op(
509         &mut self,
510         mut block: BasicBlock,
511         op: BinOp,
512         span: Span,
513         ty: Ty<'tcx>,
514         lhs: Operand<'tcx>,
515         rhs: Operand<'tcx>,
516     ) -> BlockAnd<Rvalue<'tcx>> {
517         let source_info = self.source_info(span);
518         let bool_ty = self.tcx.types.bool;
519         if self.check_overflow && op.is_checkable() && ty.is_integral() {
520             let result_tup = self.tcx.intern_tup(&[ty, bool_ty]);
521             let result_value = self.temp(result_tup, span);
522
523             self.cfg.push_assign(
524                 block,
525                 source_info,
526                 result_value,
527                 Rvalue::CheckedBinaryOp(op, Box::new((lhs.to_copy(), rhs.to_copy()))),
528             );
529             let val_fld = Field::new(0);
530             let of_fld = Field::new(1);
531
532             let tcx = self.tcx;
533             let val = tcx.mk_place_field(result_value, val_fld, ty);
534             let of = tcx.mk_place_field(result_value, of_fld, bool_ty);
535
536             let err = AssertKind::Overflow(op, lhs, rhs);
537
538             block = self.assert(block, Operand::Move(of), false, err, span);
539
540             block.and(Rvalue::Use(Operand::Move(val)))
541         } else {
542             if ty.is_integral() && (op == BinOp::Div || op == BinOp::Rem) {
543                 // Checking division and remainder is more complex, since we 1. always check
544                 // and 2. there are two possible failure cases, divide-by-zero and overflow.
545
546                 let zero_err = if op == BinOp::Div {
547                     AssertKind::DivisionByZero(lhs.to_copy())
548                 } else {
549                     AssertKind::RemainderByZero(lhs.to_copy())
550                 };
551                 let overflow_err = AssertKind::Overflow(op, lhs.to_copy(), rhs.to_copy());
552
553                 // Check for / 0
554                 let is_zero = self.temp(bool_ty, span);
555                 let zero = self.zero_literal(span, ty);
556                 self.cfg.push_assign(
557                     block,
558                     source_info,
559                     is_zero,
560                     Rvalue::BinaryOp(BinOp::Eq, Box::new((rhs.to_copy(), zero))),
561                 );
562
563                 block = self.assert(block, Operand::Move(is_zero), false, zero_err, span);
564
565                 // We only need to check for the overflow in one case:
566                 // MIN / -1, and only for signed values.
567                 if ty.is_signed() {
568                     let neg_1 = self.neg_1_literal(span, ty);
569                     let min = self.minval_literal(span, ty);
570
571                     let is_neg_1 = self.temp(bool_ty, span);
572                     let is_min = self.temp(bool_ty, span);
573                     let of = self.temp(bool_ty, span);
574
575                     // this does (rhs == -1) & (lhs == MIN). It could short-circuit instead
576
577                     self.cfg.push_assign(
578                         block,
579                         source_info,
580                         is_neg_1,
581                         Rvalue::BinaryOp(BinOp::Eq, Box::new((rhs.to_copy(), neg_1))),
582                     );
583                     self.cfg.push_assign(
584                         block,
585                         source_info,
586                         is_min,
587                         Rvalue::BinaryOp(BinOp::Eq, Box::new((lhs.to_copy(), min))),
588                     );
589
590                     let is_neg_1 = Operand::Move(is_neg_1);
591                     let is_min = Operand::Move(is_min);
592                     self.cfg.push_assign(
593                         block,
594                         source_info,
595                         of,
596                         Rvalue::BinaryOp(BinOp::BitAnd, Box::new((is_neg_1, is_min))),
597                     );
598
599                     block = self.assert(block, Operand::Move(of), false, overflow_err, span);
600                 }
601             }
602
603             block.and(Rvalue::BinaryOp(op, Box::new((lhs, rhs))))
604         }
605     }
606
607     fn build_zero_repeat(
608         &mut self,
609         mut block: BasicBlock,
610         value: ExprId,
611         scope: Option<region::Scope>,
612         outer_source_info: SourceInfo,
613     ) -> BlockAnd<Rvalue<'tcx>> {
614         let this = self;
615         let value = &this.thir[value];
616         let elem_ty = value.ty;
617         if let Some(Category::Constant) = Category::of(&value.kind) {
618             // Repeating a const does nothing
619         } else {
620             // For a non-const, we may need to generate an appropriate `Drop`
621             let value_operand =
622                 unpack!(block = this.as_operand(block, scope, value, None, NeedsTemporary::No));
623             if let Operand::Move(to_drop) = value_operand {
624                 let success = this.cfg.start_new_block();
625                 this.cfg.terminate(
626                     block,
627                     outer_source_info,
628                     TerminatorKind::Drop { place: to_drop, target: success, unwind: None },
629                 );
630                 this.diverge_from(block);
631                 block = success;
632             }
633             this.record_operands_moved(&[value_operand]);
634         }
635         block.and(Rvalue::Aggregate(Box::new(AggregateKind::Array(elem_ty)), Vec::new()))
636     }
637
638     fn limit_capture_mutability(
639         &mut self,
640         upvar_span: Span,
641         upvar_ty: Ty<'tcx>,
642         temp_lifetime: Option<region::Scope>,
643         mut block: BasicBlock,
644         arg: &Expr<'tcx>,
645     ) -> BlockAnd<Operand<'tcx>> {
646         let this = self;
647
648         let source_info = this.source_info(upvar_span);
649         let temp = this.local_decls.push(LocalDecl::new(upvar_ty, upvar_span));
650
651         this.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(temp) });
652
653         let arg_place_builder = unpack!(block = this.as_place_builder(block, arg));
654
655         let mutability = match arg_place_builder.base() {
656             // We are capturing a path that starts off a local variable in the parent.
657             // The mutability of the current capture is same as the mutability
658             // of the local declaration in the parent.
659             PlaceBase::Local(local) => this.local_decls[local].mutability,
660             // Parent is a closure and we are capturing a path that is captured
661             // by the parent itself. The mutability of the current capture
662             // is same as that of the capture in the parent closure.
663             PlaceBase::Upvar { .. } => {
664                 let enclosing_upvars_resolved = arg_place_builder.clone().into_place(this);
665
666                 match enclosing_upvars_resolved.as_ref() {
667                     PlaceRef {
668                         local,
669                         projection: &[ProjectionElem::Field(upvar_index, _), ..],
670                     }
671                     | PlaceRef {
672                         local,
673                         projection:
674                             &[ProjectionElem::Deref, ProjectionElem::Field(upvar_index, _), ..],
675                     } => {
676                         // Not in a closure
677                         debug_assert!(
678                             local == ty::CAPTURE_STRUCT_LOCAL,
679                             "Expected local to be Local(1), found {:?}",
680                             local
681                         );
682                         // Not in a closure
683                         debug_assert!(
684                             this.upvars.len() > upvar_index.index(),
685                             "Unexpected capture place, upvars={:#?}, upvar_index={:?}",
686                             this.upvars,
687                             upvar_index
688                         );
689                         this.upvars[upvar_index.index()].mutability
690                     }
691                     _ => bug!("Unexpected capture place"),
692                 }
693             }
694         };
695
696         let borrow_kind = match mutability {
697             Mutability::Not => BorrowKind::Unique,
698             Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
699         };
700
701         let arg_place = arg_place_builder.into_place(this);
702
703         this.cfg.push_assign(
704             block,
705             source_info,
706             Place::from(temp),
707             Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind, arg_place),
708         );
709
710         // See the comment in `expr_as_temp` and on the `rvalue_scopes` field for why
711         // this can be `None`.
712         if let Some(temp_lifetime) = temp_lifetime {
713             this.schedule_drop_storage_and_value(upvar_span, temp_lifetime, temp);
714         }
715
716         block.and(Operand::Move(Place::from(temp)))
717     }
718
719     // Helper to get a `-1` value of the appropriate type
720     fn neg_1_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> {
721         let param_ty = ty::ParamEnv::empty().and(ty);
722         let size = self.tcx.layout_of(param_ty).unwrap().size;
723         let literal = ConstantKind::from_bits(self.tcx, size.unsigned_int_max(), param_ty);
724
725         self.literal_operand(span, literal)
726     }
727
728     // Helper to get the minimum value of the appropriate type
729     fn minval_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> {
730         assert!(ty.is_signed());
731         let param_ty = ty::ParamEnv::empty().and(ty);
732         let bits = self.tcx.layout_of(param_ty).unwrap().size.bits();
733         let n = 1 << (bits - 1);
734         let literal = ConstantKind::from_bits(self.tcx, n, param_ty);
735
736         self.literal_operand(span, literal)
737     }
738 }