]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_build/src/build/expr/as_temp.rs
6067da2f69b489f9208ab2eb3ee5938ce030c4fc
[rust.git] / compiler / rustc_mir_build / src / build / expr / as_temp.rs
1 //! See docs in build/expr/mod.rs
2
3 use crate::build::scope::DropKind;
4 use crate::build::{BlockAnd, BlockAndExtension, Builder};
5 use rustc_data_structures::stack::ensure_sufficient_stack;
6 use rustc_middle::middle::region;
7 use rustc_middle::mir::*;
8 use rustc_middle::thir::*;
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     crate fn as_temp(
14         &mut self,
15         block: BasicBlock,
16         temp_lifetime: Option<region::Scope>,
17         expr: &Expr<'tcx>,
18         mutability: Mutability,
19     ) -> BlockAnd<Local> {
20         // this is the only place in mir building that we need to truly need to worry about
21         // infinite recursion. Everything else does recurse, too, but it always gets broken up
22         // at some point by inserting an intermediate temporary
23         ensure_sufficient_stack(|| self.as_temp_inner(block, temp_lifetime, expr, mutability))
24     }
25
26     fn as_temp_inner(
27         &mut self,
28         mut block: BasicBlock,
29         temp_lifetime: Option<region::Scope>,
30         expr: &Expr<'tcx>,
31         mutability: Mutability,
32     ) -> BlockAnd<Local> {
33         debug!(
34             "as_temp(block={:?}, temp_lifetime={:?}, expr={:?}, mutability={:?})",
35             block, temp_lifetime, expr, mutability
36         );
37         let this = self;
38
39         let expr_span = expr.span;
40         let source_info = this.source_info(expr_span);
41         if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
42             return this.in_scope((region_scope, source_info), lint_level, |this| {
43                 this.as_temp(block, temp_lifetime, &this.thir[value], mutability)
44             });
45         }
46
47         let expr_ty = expr.ty;
48         let temp = {
49             let mut local_decl = LocalDecl::new(expr_ty, expr_span);
50             if mutability == Mutability::Not {
51                 local_decl = local_decl.immutable();
52             }
53
54             debug!("creating temp {:?} with block_context: {:?}", local_decl, this.block_context);
55             // Find out whether this temp is being created within the
56             // tail expression of a block whose result is ignored.
57             if let Some(tail_info) = this.block_context.currently_in_block_tail() {
58                 local_decl = local_decl.block_tail(tail_info);
59             }
60             match expr.kind {
61                 ExprKind::StaticRef { def_id, .. } => {
62                     assert!(!this.tcx.is_thread_local_static(def_id));
63                     local_decl.internal = true;
64                     local_decl.local_info =
65                         Some(Box::new(LocalInfo::StaticRef { def_id, is_thread_local: false }));
66                 }
67                 ExprKind::ThreadLocalRef(def_id) => {
68                     assert!(this.tcx.is_thread_local_static(def_id));
69                     local_decl.internal = true;
70                     local_decl.local_info =
71                         Some(Box::new(LocalInfo::StaticRef { def_id, is_thread_local: true }));
72                 }
73                 ExprKind::NamedConst { def_id, .. } | ExprKind::ConstParam { def_id, .. } => {
74                     local_decl.local_info = Some(Box::new(LocalInfo::ConstRef { def_id }));
75                 }
76                 _ => {}
77             }
78             this.local_decls.push(local_decl)
79         };
80         let temp_place = Place::from(temp);
81
82         match expr.kind {
83             // Don't bother with StorageLive and Dead for these temporaries,
84             // they are never assigned.
85             ExprKind::Break { .. } | ExprKind::Continue { .. } | ExprKind::Return { .. } => (),
86             ExprKind::Block { body: Block { expr: None, targeted_by_break: false, .. } }
87                 if expr_ty.is_never() => {}
88             _ => {
89                 this.cfg
90                     .push(block, Statement { source_info, kind: StatementKind::StorageLive(temp) });
91
92                 // In constants, `temp_lifetime` is `None` for temporaries that
93                 // live for the `'static` lifetime. Thus we do not drop these
94                 // temporaries and simply leak them.
95                 // This is equivalent to what `let x = &foo();` does in
96                 // functions. The temporary is lifted to their surrounding
97                 // scope. In a function that means the temporary lives until
98                 // just before the function returns. In constants that means it
99                 // outlives the constant's initialization value computation.
100                 // Anything outliving a constant must have the `'static`
101                 // lifetime and live forever.
102                 // Anything with a shorter lifetime (e.g the `&foo()` in
103                 // `bar(&foo())` or anything within a block will keep the
104                 // regular drops just like runtime code.
105                 if let Some(temp_lifetime) = temp_lifetime {
106                     this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Storage);
107                 }
108             }
109         }
110
111         unpack!(block = this.expr_into_dest(temp_place, block, expr));
112
113         if let Some(temp_lifetime) = temp_lifetime {
114             this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Value);
115         }
116
117         block.and(temp)
118     }
119 }