]> git.proxmox.com Git - rustc.git/blob - src/librustc_mir/build/expr/as_temp.rs
New upstream version 1.38.0+dfsg1
[rustc.git] / src / librustc_mir / build / expr / as_temp.rs
1 //! See docs in build/expr/mod.rs
2
3 use crate::build::{BlockAnd, BlockAndExtension, Builder};
4 use crate::build::scope::DropKind;
5 use crate::hair::*;
6 use rustc::hir;
7 use rustc::middle::region;
8 use rustc::mir::*;
9
10 impl<'a, 'tcx> Builder<'a, 'tcx> {
11 /// Compile `expr` into a fresh temporary. This is used when building
12 /// up rvalues so as to freeze the value that will be consumed.
13 pub fn as_temp<M>(
14 &mut self,
15 block: BasicBlock,
16 temp_lifetime: Option<region::Scope>,
17 expr: M,
18 mutability: Mutability,
19 ) -> BlockAnd<Local>
20 where
21 M: Mirror<'tcx, Output = Expr<'tcx>>,
22 {
23 let expr = self.hir.mirror(expr);
24 self.expr_as_temp(block, temp_lifetime, expr, mutability)
25 }
26
27 fn expr_as_temp(
28 &mut self,
29 mut block: BasicBlock,
30 temp_lifetime: Option<region::Scope>,
31 expr: Expr<'tcx>,
32 mutability: Mutability,
33 ) -> BlockAnd<Local> {
34 debug!(
35 "expr_as_temp(block={:?}, temp_lifetime={:?}, expr={:?}, mutability={:?})",
36 block, temp_lifetime, expr, mutability
37 );
38 let this = self;
39
40 let expr_span = expr.span;
41 let source_info = this.source_info(expr_span);
42 if let ExprKind::Scope {
43 region_scope,
44 lint_level,
45 value,
46 } = expr.kind
47 {
48 return this.in_scope((region_scope, source_info), lint_level, |this| {
49 this.as_temp(block, temp_lifetime, value, mutability)
50 });
51 }
52
53 let expr_ty = expr.ty;
54 let temp = {
55 let mut local_decl = LocalDecl::new_temp(expr_ty, expr_span);
56 if mutability == Mutability::Not {
57 local_decl = local_decl.immutable();
58 }
59
60 debug!("creating temp {:?} with block_context: {:?}", local_decl, this.block_context);
61 // Find out whether this temp is being created within the
62 // tail expression of a block whose result is ignored.
63 if let Some(tail_info) = this.block_context.currently_in_block_tail() {
64 local_decl = local_decl.block_tail(tail_info);
65 }
66 this.local_decls.push(local_decl)
67 };
68 let temp_place = &Place::from(temp);
69
70 match expr.kind {
71 // Don't bother with StorageLive and Dead for these temporaries,
72 // they are never assigned.
73 ExprKind::Break { .. } |
74 ExprKind::Continue { .. } |
75 ExprKind::Return { .. } => (),
76 ExprKind::Block {
77 body: hir::Block { expr: None, targeted_by_break: false, .. }
78 } if expr_ty.is_never() => (),
79 _ => {
80 this.cfg.push(
81 block,
82 Statement {
83 source_info,
84 kind: StatementKind::StorageLive(temp),
85 },
86 );
87
88 // In constants, `temp_lifetime` is `None` for temporaries that
89 // live for the `'static` lifetime. Thus we do not drop these
90 // temporaries and simply leak them.
91 // This is equivalent to what `let x = &foo();` does in
92 // functions. The temporary is lifted to their surrounding
93 // scope. In a function that means the temporary lives until
94 // just before the function returns. In constants that means it
95 // outlives the constant's initialization value computation.
96 // Anything outliving a constant must have the `'static`
97 // lifetime and live forever.
98 // Anything with a shorter lifetime (e.g the `&foo()` in
99 // `bar(&foo())` or anything within a block will keep the
100 // regular drops just like runtime code.
101 if let Some(temp_lifetime) = temp_lifetime {
102 this.schedule_drop(
103 expr_span,
104 temp_lifetime,
105 temp,
106 expr_ty,
107 DropKind::Storage,
108 );
109 }
110 }
111 }
112
113 unpack!(block = this.into(temp_place, block, expr));
114
115 if let Some(temp_lifetime) = temp_lifetime {
116 this.schedule_drop(
117 expr_span,
118 temp_lifetime,
119 temp,
120 expr_ty,
121 DropKind::Value,
122 );
123 }
124
125 block.and(temp)
126 }
127 }