]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_mir_build/src/build/expr/as_temp.rs
New upstream version 1.49.0+dfsg1
[rustc.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 crate::thir::*;
6 use rustc_data_structures::stack::ensure_sufficient_stack;
7 use rustc_hir as hir;
8 use rustc_middle::middle::region;
9 use rustc_middle::mir::*;
10
11 impl<'a, 'tcx> Builder<'a, 'tcx> {
12 /// Compile `expr` into a fresh temporary. This is used when building
13 /// up rvalues so as to freeze the value that will be consumed.
14 crate fn as_temp<M>(
15 &mut self,
16 block: BasicBlock,
17 temp_lifetime: Option<region::Scope>,
18 expr: M,
19 mutability: Mutability,
20 ) -> BlockAnd<Local>
21 where
22 M: Mirror<'tcx, Output = Expr<'tcx>>,
23 {
24 let expr = self.hir.mirror(expr);
25 //
26 // this is the only place in mir building that we need to truly need to worry about
27 // infinite recursion. Everything else does recurse, too, but it always gets broken up
28 // at some point by inserting an intermediate temporary
29 ensure_sufficient_stack(|| self.expr_as_temp(block, temp_lifetime, expr, mutability))
30 }
31
32 fn expr_as_temp(
33 &mut self,
34 mut block: BasicBlock,
35 temp_lifetime: Option<region::Scope>,
36 expr: Expr<'tcx>,
37 mutability: Mutability,
38 ) -> BlockAnd<Local> {
39 debug!(
40 "expr_as_temp(block={:?}, temp_lifetime={:?}, expr={:?}, mutability={:?})",
41 block, temp_lifetime, expr, mutability
42 );
43 let this = self;
44
45 let expr_span = expr.span;
46 let source_info = this.source_info(expr_span);
47 if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
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(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 match expr.kind {
67 ExprKind::StaticRef { def_id, .. } => {
68 assert!(!this.hir.tcx().is_thread_local_static(def_id));
69 local_decl.internal = true;
70 local_decl.local_info =
71 Some(box LocalInfo::StaticRef { def_id, is_thread_local: false });
72 }
73 ExprKind::ThreadLocalRef(def_id) => {
74 assert!(this.hir.tcx().is_thread_local_static(def_id));
75 local_decl.internal = true;
76 local_decl.local_info =
77 Some(box LocalInfo::StaticRef { def_id, is_thread_local: true });
78 }
79 ExprKind::Literal { const_id: Some(def_id), .. } => {
80 local_decl.local_info = Some(box LocalInfo::ConstRef { def_id });
81 }
82 _ => {}
83 }
84 this.local_decls.push(local_decl)
85 };
86 let temp_place = Place::from(temp);
87
88 match expr.kind {
89 // Don't bother with StorageLive and Dead for these temporaries,
90 // they are never assigned.
91 ExprKind::Break { .. } | ExprKind::Continue { .. } | ExprKind::Return { .. } => (),
92 ExprKind::Block { body: hir::Block { expr: None, targeted_by_break: false, .. } }
93 if expr_ty.is_never() => {}
94 _ => {
95 this.cfg
96 .push(block, Statement { source_info, kind: StatementKind::StorageLive(temp) });
97
98 // In constants, `temp_lifetime` is `None` for temporaries that
99 // live for the `'static` lifetime. Thus we do not drop these
100 // temporaries and simply leak them.
101 // This is equivalent to what `let x = &foo();` does in
102 // functions. The temporary is lifted to their surrounding
103 // scope. In a function that means the temporary lives until
104 // just before the function returns. In constants that means it
105 // outlives the constant's initialization value computation.
106 // Anything outliving a constant must have the `'static`
107 // lifetime and live forever.
108 // Anything with a shorter lifetime (e.g the `&foo()` in
109 // `bar(&foo())` or anything within a block will keep the
110 // regular drops just like runtime code.
111 if let Some(temp_lifetime) = temp_lifetime {
112 this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Storage);
113 }
114 }
115 }
116
117 unpack!(block = this.into(temp_place, block, expr));
118
119 if let Some(temp_lifetime) = temp_lifetime {
120 this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Value);
121 }
122
123 block.and(temp)
124 }
125 }