]>
Commit | Line | Data |
---|---|---|
e9174d1e SL |
1 | //! See docs in build/expr/mod.rs |
2 | ||
dc9dc135 | 3 | use crate::build::scope::DropKind; |
dfeec247 | 4 | use crate::build::{BlockAnd, BlockAndExtension, Builder}; |
3dfed10e | 5 | use crate::thir::*; |
f9f354fc XL |
6 | use rustc_data_structures::stack::ensure_sufficient_stack; |
7 | use rustc_hir as hir; | |
ba9703b0 XL |
8 | use rustc_middle::middle::region; |
9 | use rustc_middle::mir::*; | |
e9174d1e | 10 | |
dc9dc135 | 11 | impl<'a, 'tcx> Builder<'a, 'tcx> { |
e9174d1e SL |
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. | |
dfeec247 | 14 | crate fn as_temp<M>( |
b7449926 XL |
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>>, | |
e9174d1e SL |
23 | { |
24 | let expr = self.hir.mirror(expr); | |
f9f354fc XL |
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)) | |
e9174d1e SL |
30 | } |
31 | ||
b7449926 XL |
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 | ); | |
e9174d1e SL |
43 | let this = self; |
44 | ||
041b39d2 XL |
45 | let expr_span = expr.span; |
46 | let source_info = this.source_info(expr_span); | |
dfeec247 | 47 | if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind { |
dc9dc135 | 48 | return this.in_scope((region_scope, source_info), lint_level, |this| { |
b7449926 | 49 | this.as_temp(block, temp_lifetime, value, mutability) |
cc61c64b | 50 | }); |
e9174d1e SL |
51 | } |
52 | ||
ea8adc8c | 53 | let expr_ty = expr.ty; |
0bf4aa26 | 54 | let temp = { |
f9f354fc | 55 | let mut local_decl = LocalDecl::new(expr_ty, expr_span); |
0bf4aa26 XL |
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. | |
a1dfa0c6 XL |
63 | if let Some(tail_info) = this.block_context.currently_in_block_tail() { |
64 | local_decl = local_decl.block_tail(tail_info); | |
0bf4aa26 | 65 | } |
f9f354fc XL |
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; | |
3dfed10e XL |
70 | local_decl.local_info = |
71 | Some(box LocalInfo::StaticRef { def_id, is_thread_local: false }); | |
f9f354fc XL |
72 | } |
73 | ExprKind::ThreadLocalRef(def_id) => { | |
74 | assert!(this.hir.tcx().is_thread_local_static(def_id)); | |
75 | local_decl.internal = true; | |
3dfed10e XL |
76 | local_decl.local_info = |
77 | Some(box LocalInfo::StaticRef { def_id, is_thread_local: true }); | |
f9f354fc XL |
78 | } |
79 | _ => {} | |
60c5eb7d | 80 | } |
0bf4aa26 XL |
81 | this.local_decls.push(local_decl) |
82 | }; | |
ba9703b0 | 83 | let temp_place = Place::from(temp); |
dc9dc135 | 84 | |
416331ca XL |
85 | match expr.kind { |
86 | // Don't bother with StorageLive and Dead for these temporaries, | |
87 | // they are never assigned. | |
dfeec247 XL |
88 | ExprKind::Break { .. } | ExprKind::Continue { .. } | ExprKind::Return { .. } => (), |
89 | ExprKind::Block { body: hir::Block { expr: None, targeted_by_break: false, .. } } | |
ba9703b0 | 90 | if expr_ty.is_never() => {} |
416331ca | 91 | _ => { |
dfeec247 XL |
92 | this.cfg |
93 | .push(block, Statement { source_info, kind: StatementKind::StorageLive(temp) }); | |
416331ca XL |
94 | |
95 | // In constants, `temp_lifetime` is `None` for temporaries that | |
96 | // live for the `'static` lifetime. Thus we do not drop these | |
97 | // temporaries and simply leak them. | |
98 | // This is equivalent to what `let x = &foo();` does in | |
99 | // functions. The temporary is lifted to their surrounding | |
100 | // scope. In a function that means the temporary lives until | |
101 | // just before the function returns. In constants that means it | |
102 | // outlives the constant's initialization value computation. | |
103 | // Anything outliving a constant must have the `'static` | |
104 | // lifetime and live forever. | |
105 | // Anything with a shorter lifetime (e.g the `&foo()` in | |
106 | // `bar(&foo())` or anything within a block will keep the | |
107 | // regular drops just like runtime code. | |
108 | if let Some(temp_lifetime) = temp_lifetime { | |
dfeec247 | 109 | this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Storage); |
416331ca | 110 | } |
dc9dc135 | 111 | } |
5bcae85e | 112 | } |
e9174d1e | 113 | |
dc9dc135 | 114 | unpack!(block = this.into(temp_place, block, expr)); |
e9174d1e | 115 | |
a7813a04 | 116 | if let Some(temp_lifetime) = temp_lifetime { |
dfeec247 | 117 | this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Value); |
a7813a04 XL |
118 | } |
119 | ||
e9174d1e SL |
120 | block.and(temp) |
121 | } | |
122 | } |