]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
0814793f27790159dd3aa6400fe3a6e28b6b3b4f
[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 Some(mir_place) = place_builder.try_to_place(this) {
373                         this.cfg.push_fake_read(
374                             block,
375                             this.source_info(this.tcx.hir().span(*hir_id)),
376                             *cause,
377                             mir_place,
378                         );
379                     }
380                 }
381
382                 // see (*) above
383                 let operands: Vec<_> = upvars
384                     .into_iter()
385                     .copied()
386                     .map(|upvar| {
387                         let upvar = &this.thir[upvar];
388                         match Category::of(&upvar.kind) {
389                             // Use as_place to avoid creating a temporary when
390                             // moving a variable into a closure, so that
391                             // borrowck knows which variables to mark as being
392                             // used as mut. This is OK here because the upvar
393                             // expressions have no side effects and act on
394                             // disjoint places.
395                             // This occurs when capturing by copy/move, while
396                             // by reference captures use as_operand
397                             Some(Category::Place) => {
398                                 let place = unpack!(block = this.as_place(block, upvar));
399                                 this.consume_by_copy_or_move(place)
400                             }
401                             _ => {
402                                 // Turn mutable borrow captures into unique
403                                 // borrow captures when capturing an immutable
404                                 // variable. This is sound because the mutation
405                                 // that caused the capture will cause an error.
406                                 match upvar.kind {
407                                     ExprKind::Borrow {
408                                         borrow_kind:
409                                             BorrowKind::Mut { allow_two_phase_borrow: false },
410                                         arg,
411                                     } => unpack!(
412                                         block = this.limit_capture_mutability(
413                                             upvar.span,
414                                             upvar.ty,
415                                             scope,
416                                             block,
417                                             &this.thir[arg],
418                                         )
419                                     ),
420                                     _ => {
421                                         unpack!(
422                                             block = this.as_operand(
423                                                 block,
424                                                 scope,
425                                                 upvar,
426                                                 None,
427                                                 NeedsTemporary::Maybe
428                                             )
429                                         )
430                                     }
431                                 }
432                             }
433                         }
434                     })
435                     .collect();
436
437                 let result = match substs {
438                     UpvarSubsts::Generator(substs) => {
439                         // We implicitly set the discriminant to 0. See
440                         // librustc_mir/transform/deaggregator.rs for details.
441                         let movability = movability.unwrap();
442                         Box::new(AggregateKind::Generator(closure_id, substs, movability))
443                     }
444                     UpvarSubsts::Closure(substs) => {
445                         Box::new(AggregateKind::Closure(closure_id, substs))
446                     }
447                 };
448                 block.and(Rvalue::Aggregate(result, operands))
449             }
450             ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
451                 block = unpack!(this.stmt_expr(block, expr, None));
452                 block.and(Rvalue::Use(Operand::Constant(Box::new(Constant {
453                     span: expr_span,
454                     user_ty: None,
455                     literal: ConstantKind::zero_sized(this.tcx.types.unit),
456                 }))))
457             }
458
459             ExprKind::Literal { .. }
460             | ExprKind::NamedConst { .. }
461             | ExprKind::NonHirLiteral { .. }
462             | ExprKind::ZstLiteral { .. }
463             | ExprKind::ConstParam { .. }
464             | ExprKind::ConstBlock { .. }
465             | ExprKind::StaticRef { .. } => {
466                 let constant = this.as_constant(expr);
467                 block.and(Rvalue::Use(Operand::Constant(Box::new(constant))))
468             }
469
470             ExprKind::Yield { .. }
471             | ExprKind::Block { .. }
472             | ExprKind::Match { .. }
473             | ExprKind::If { .. }
474             | ExprKind::NeverToAny { .. }
475             | ExprKind::Use { .. }
476             | ExprKind::Borrow { .. }
477             | ExprKind::AddressOf { .. }
478             | ExprKind::Adt { .. }
479             | ExprKind::Loop { .. }
480             | ExprKind::LogicalOp { .. }
481             | ExprKind::Call { .. }
482             | ExprKind::Field { .. }
483             | ExprKind::Let { .. }
484             | ExprKind::Deref { .. }
485             | ExprKind::Index { .. }
486             | ExprKind::VarRef { .. }
487             | ExprKind::UpvarRef { .. }
488             | ExprKind::Break { .. }
489             | ExprKind::Continue { .. }
490             | ExprKind::Return { .. }
491             | ExprKind::InlineAsm { .. }
492             | ExprKind::PlaceTypeAscription { .. }
493             | ExprKind::ValueTypeAscription { .. } => {
494                 // these do not have corresponding `Rvalue` variants,
495                 // so make an operand and then return that
496                 debug_assert!(!matches!(
497                     Category::of(&expr.kind),
498                     Some(Category::Rvalue(RvalueFunc::AsRvalue) | Category::Constant)
499                 ));
500                 let operand =
501                     unpack!(block = this.as_operand(block, scope, expr, None, NeedsTemporary::No));
502                 block.and(Rvalue::Use(operand))
503             }
504         }
505     }
506
507     pub(crate) fn build_binary_op(
508         &mut self,
509         mut block: BasicBlock,
510         op: BinOp,
511         span: Span,
512         ty: Ty<'tcx>,
513         lhs: Operand<'tcx>,
514         rhs: Operand<'tcx>,
515     ) -> BlockAnd<Rvalue<'tcx>> {
516         let source_info = self.source_info(span);
517         let bool_ty = self.tcx.types.bool;
518         if self.check_overflow && op.is_checkable() && ty.is_integral() {
519             let result_tup = self.tcx.intern_tup(&[ty, bool_ty]);
520             let result_value = self.temp(result_tup, span);
521
522             self.cfg.push_assign(
523                 block,
524                 source_info,
525                 result_value,
526                 Rvalue::CheckedBinaryOp(op, Box::new((lhs.to_copy(), rhs.to_copy()))),
527             );
528             let val_fld = Field::new(0);
529             let of_fld = Field::new(1);
530
531             let tcx = self.tcx;
532             let val = tcx.mk_place_field(result_value, val_fld, ty);
533             let of = tcx.mk_place_field(result_value, of_fld, bool_ty);
534
535             let err = AssertKind::Overflow(op, lhs, rhs);
536
537             block = self.assert(block, Operand::Move(of), false, err, span);
538
539             block.and(Rvalue::Use(Operand::Move(val)))
540         } else {
541             if ty.is_integral() && (op == BinOp::Div || op == BinOp::Rem) {
542                 // Checking division and remainder is more complex, since we 1. always check
543                 // and 2. there are two possible failure cases, divide-by-zero and overflow.
544
545                 let zero_err = if op == BinOp::Div {
546                     AssertKind::DivisionByZero(lhs.to_copy())
547                 } else {
548                     AssertKind::RemainderByZero(lhs.to_copy())
549                 };
550                 let overflow_err = AssertKind::Overflow(op, lhs.to_copy(), rhs.to_copy());
551
552                 // Check for / 0
553                 let is_zero = self.temp(bool_ty, span);
554                 let zero = self.zero_literal(span, ty);
555                 self.cfg.push_assign(
556                     block,
557                     source_info,
558                     is_zero,
559                     Rvalue::BinaryOp(BinOp::Eq, Box::new((rhs.to_copy(), zero))),
560                 );
561
562                 block = self.assert(block, Operand::Move(is_zero), false, zero_err, span);
563
564                 // We only need to check for the overflow in one case:
565                 // MIN / -1, and only for signed values.
566                 if ty.is_signed() {
567                     let neg_1 = self.neg_1_literal(span, ty);
568                     let min = self.minval_literal(span, ty);
569
570                     let is_neg_1 = self.temp(bool_ty, span);
571                     let is_min = self.temp(bool_ty, span);
572                     let of = self.temp(bool_ty, span);
573
574                     // this does (rhs == -1) & (lhs == MIN). It could short-circuit instead
575
576                     self.cfg.push_assign(
577                         block,
578                         source_info,
579                         is_neg_1,
580                         Rvalue::BinaryOp(BinOp::Eq, Box::new((rhs.to_copy(), neg_1))),
581                     );
582                     self.cfg.push_assign(
583                         block,
584                         source_info,
585                         is_min,
586                         Rvalue::BinaryOp(BinOp::Eq, Box::new((lhs.to_copy(), min))),
587                     );
588
589                     let is_neg_1 = Operand::Move(is_neg_1);
590                     let is_min = Operand::Move(is_min);
591                     self.cfg.push_assign(
592                         block,
593                         source_info,
594                         of,
595                         Rvalue::BinaryOp(BinOp::BitAnd, Box::new((is_neg_1, is_min))),
596                     );
597
598                     block = self.assert(block, Operand::Move(of), false, overflow_err, span);
599                 }
600             }
601
602             block.and(Rvalue::BinaryOp(op, Box::new((lhs, rhs))))
603         }
604     }
605
606     fn build_zero_repeat(
607         &mut self,
608         mut block: BasicBlock,
609         value: ExprId,
610         scope: Option<region::Scope>,
611         outer_source_info: SourceInfo,
612     ) -> BlockAnd<Rvalue<'tcx>> {
613         let this = self;
614         let value = &this.thir[value];
615         let elem_ty = value.ty;
616         if let Some(Category::Constant) = Category::of(&value.kind) {
617             // Repeating a const does nothing
618         } else {
619             // For a non-const, we may need to generate an appropriate `Drop`
620             let value_operand =
621                 unpack!(block = this.as_operand(block, scope, value, None, NeedsTemporary::No));
622             if let Operand::Move(to_drop) = value_operand {
623                 let success = this.cfg.start_new_block();
624                 this.cfg.terminate(
625                     block,
626                     outer_source_info,
627                     TerminatorKind::Drop { place: to_drop, target: success, unwind: None },
628                 );
629                 this.diverge_from(block);
630                 block = success;
631             }
632             this.record_operands_moved(&[value_operand]);
633         }
634         block.and(Rvalue::Aggregate(Box::new(AggregateKind::Array(elem_ty)), Vec::new()))
635     }
636
637     fn limit_capture_mutability(
638         &mut self,
639         upvar_span: Span,
640         upvar_ty: Ty<'tcx>,
641         temp_lifetime: Option<region::Scope>,
642         mut block: BasicBlock,
643         arg: &Expr<'tcx>,
644     ) -> BlockAnd<Operand<'tcx>> {
645         let this = self;
646
647         let source_info = this.source_info(upvar_span);
648         let temp = this.local_decls.push(LocalDecl::new(upvar_ty, upvar_span));
649
650         this.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(temp) });
651
652         let arg_place_builder = unpack!(block = this.as_place_builder(block, arg));
653
654         let mutability = match arg_place_builder.base() {
655             // We are capturing a path that starts off a local variable in the parent.
656             // The mutability of the current capture is same as the mutability
657             // of the local declaration in the parent.
658             PlaceBase::Local(local) => this.local_decls[local].mutability,
659             // Parent is a closure and we are capturing a path that is captured
660             // by the parent itself. The mutability of the current capture
661             // is same as that of the capture in the parent closure.
662             PlaceBase::Upvar { .. } => {
663                 let enclosing_upvars_resolved = arg_place_builder.to_place(this);
664
665                 match enclosing_upvars_resolved.as_ref() {
666                     PlaceRef {
667                         local,
668                         projection: &[ProjectionElem::Field(upvar_index, _), ..],
669                     }
670                     | PlaceRef {
671                         local,
672                         projection:
673                             &[ProjectionElem::Deref, ProjectionElem::Field(upvar_index, _), ..],
674                     } => {
675                         // Not in a closure
676                         debug_assert!(
677                             local == ty::CAPTURE_STRUCT_LOCAL,
678                             "Expected local to be Local(1), found {:?}",
679                             local
680                         );
681                         // Not in a closure
682                         debug_assert!(
683                             this.upvars.len() > upvar_index.index(),
684                             "Unexpected capture place, upvars={:#?}, upvar_index={:?}",
685                             this.upvars,
686                             upvar_index
687                         );
688                         this.upvars[upvar_index.index()].mutability
689                     }
690                     _ => bug!("Unexpected capture place"),
691                 }
692             }
693         };
694
695         let borrow_kind = match mutability {
696             Mutability::Not => BorrowKind::Unique,
697             Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
698         };
699
700         let arg_place = arg_place_builder.to_place(this);
701
702         this.cfg.push_assign(
703             block,
704             source_info,
705             Place::from(temp),
706             Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind, arg_place),
707         );
708
709         // See the comment in `expr_as_temp` and on the `rvalue_scopes` field for why
710         // this can be `None`.
711         if let Some(temp_lifetime) = temp_lifetime {
712             this.schedule_drop_storage_and_value(upvar_span, temp_lifetime, temp);
713         }
714
715         block.and(Operand::Move(Place::from(temp)))
716     }
717
718     // Helper to get a `-1` value of the appropriate type
719     fn neg_1_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> {
720         let param_ty = ty::ParamEnv::empty().and(ty);
721         let size = self.tcx.layout_of(param_ty).unwrap().size;
722         let literal = ConstantKind::from_bits(self.tcx, size.unsigned_int_max(), param_ty);
723
724         self.literal_operand(span, literal)
725     }
726
727     // Helper to get the minimum value of the appropriate type
728     fn minval_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> {
729         assert!(ty.is_signed());
730         let param_ty = ty::ParamEnv::empty().and(ty);
731         let bits = self.tcx.layout_of(param_ty).unwrap().size.bits();
732         let n = 1 << (bits - 1);
733         let literal = ConstantKind::from_bits(self.tcx, n, param_ty);
734
735         self.literal_operand(span, literal)
736     }
737 }