rustc_mir_build/src/builder/expr/as_temp.rs
Line | Count | Source |
1 | | //! See docs in build/expr/mod.rs |
2 | | |
3 | | use rustc_data_structures::stack::ensure_sufficient_stack; |
4 | | use rustc_hir::HirId; |
5 | | use rustc_middle::middle::region::{Scope, ScopeData, TempLifetime}; |
6 | | use rustc_middle::mir::*; |
7 | | use rustc_middle::thir::*; |
8 | | use tracing::{debug, instrument}; |
9 | | |
10 | | use crate::builder::scope::{DropKind, LintLevel}; |
11 | | use crate::builder::{BlockAnd, BlockAndExtension, Builder}; |
12 | | |
13 | | impl<'a, 'tcx> Builder<'a, 'tcx> { |
14 | | /// Compile `expr` into a fresh temporary. This is used when building |
15 | | /// up rvalues so as to freeze the value that will be consumed. |
16 | 1.73M | pub(crate) fn as_temp( |
17 | 1.73M | &mut self, |
18 | 1.73M | block: BasicBlock, |
19 | 1.73M | temp_lifetime: TempLifetime, |
20 | 1.73M | expr_id: ExprId, |
21 | 1.73M | mutability: Mutability, |
22 | 1.73M | ) -> BlockAnd<Local> { |
23 | | // this is the only place in mir building that we need to truly need to worry about |
24 | | // infinite recursion. Everything else does recurse, too, but it always gets broken up |
25 | | // at some point by inserting an intermediate temporary |
26 | 1.73M | ensure_sufficient_stack(|| self.as_temp_inner(block, temp_lifetime, expr_id, mutability)) |
27 | 1.73M | } |
28 | | |
29 | | #[instrument(skip(self), level = "debug")] |
30 | 1.73M | fn as_temp_inner( |
31 | 1.73M | &mut self, |
32 | 1.73M | mut block: BasicBlock, |
33 | 1.73M | temp_lifetime: TempLifetime, |
34 | 1.73M | expr_id: ExprId, |
35 | 1.73M | mutability: Mutability, |
36 | 1.73M | ) -> BlockAnd<Local> { |
37 | 1.73M | let this = self; // See "LET_THIS_SELF". |
38 | | |
39 | 1.73M | let expr = &this.thir[expr_id]; |
40 | 1.73M | let expr_span = expr.span; |
41 | 1.73M | let source_info = this.source_info(expr_span); |
42 | 1.73M | if let ExprKind::Scope { region_scope, hir_id, value } = expr.kind { |
43 | 497 | return this.in_scope( |
44 | 497 | (region_scope, source_info), |
45 | 497 | LintLevel::Explicit(hir_id), |
46 | 497 | |this| this.as_temp(block, temp_lifetime, value, mutability), |
47 | | ); |
48 | 1.73M | } |
49 | | |
50 | 1.73M | let expr_ty = expr.ty; |
51 | 1.73M | let deduplicate_temps = this.fixed_temps_scope.is_some() |
52 | 190 | && this.fixed_temps_scope == temp_lifetime.temp_lifetime; |
53 | 1.73M | let temp = if deduplicate_temps && let Some(temp_index) = this.fixed_temps.get(&expr_id) { |
54 | 0 | *temp_index |
55 | | } else { |
56 | 1.73M | let mut local_decl = LocalDecl::new(expr_ty, expr_span); |
57 | 1.73M | if mutability.is_not() { |
58 | 312k | local_decl = local_decl.immutable(); |
59 | 1.41M | } |
60 | | |
61 | 1.73M | debug!("creating temp {:?} with block_context: {:?}", local_decl, this.block_context); |
62 | 1.73M | let local_info = match expr.kind { |
63 | 15 | ExprKind::StaticRef { def_id, .. } => { |
64 | 15 | assert!(!this.tcx.is_thread_local_static(def_id)); |
65 | 15 | LocalInfo::StaticRef { def_id, is_thread_local: false } |
66 | | } |
67 | 0 | ExprKind::ThreadLocalRef(def_id) => { |
68 | 0 | assert!(this.tcx.is_thread_local_static(def_id)); |
69 | 0 | LocalInfo::StaticRef { def_id, is_thread_local: true } |
70 | | } |
71 | 3 | ExprKind::NamedConst { def_id, .. } | ExprKind::ConstParam { def_id, .. } => { |
72 | 3 | LocalInfo::ConstRef { def_id } |
73 | | } |
74 | | // Find out whether this temp is being created within the |
75 | | // tail expression of a block whose result is ignored. |
76 | 1.73M | _ if let Some(tail_info) = this.block_context.currently_in_block_tail() => { |
77 | 395k | LocalInfo::BlockTailTemp(tail_info) |
78 | | } |
79 | | |
80 | 0 | _ if let Some(Scope { data: ScopeData::IfThenRescope, local_id }) = |
81 | 0 | temp_lifetime.temp_lifetime => |
82 | | { |
83 | 0 | LocalInfo::IfThenRescopeTemp { |
84 | 0 | if_then: HirId { owner: this.hir_id.owner, local_id }, |
85 | 0 | } |
86 | | } |
87 | | |
88 | 1.33M | _ => LocalInfo::Boring, |
89 | | }; |
90 | 1.73M | **local_decl.local_info.as_mut().unwrap_crate_local() = local_info; |
91 | 1.73M | this.local_decls.push(local_decl) |
92 | | }; |
93 | 1.73M | debug!(?temp); |
94 | 1.73M | if deduplicate_temps { |
95 | 68 | this.fixed_temps.insert(expr_id, temp); |
96 | 1.73M | } |
97 | 1.73M | let temp_place = Place::from(temp); |
98 | | |
99 | 70.4k | match expr.kind { |
100 | | // Don't bother with StorageLive and Dead for these temporaries, |
101 | | // they are never assigned. |
102 | 467 | ExprKind::Break { .. } | ExprKind::Continue { .. } | ExprKind::Return { .. } => (), |
103 | 1.30k | ExprKind::Block { block } |
104 | 70.4k | if let Block { expr: None, targeted_by_break: false, .. } = this.thir[block] |
105 | 44.8k | && expr_ty.is_never() => {} |
106 | | _ => { |
107 | 1.72M | this.cfg.push(block, Statement::new(source_info, StatementKind::StorageLive(temp))); |
108 | | |
109 | | // In constants, `temp_lifetime` is `None` for temporaries that |
110 | | // live for the `'static` lifetime. Thus we do not drop these |
111 | | // temporaries and simply leak them. |
112 | | // This is equivalent to what `let x = &foo();` does in |
113 | | // functions. The temporary is lifted to their surrounding |
114 | | // scope. In a function that means the temporary lives until |
115 | | // just before the function returns. In constants that means it |
116 | | // outlives the constant's initialization value computation. |
117 | | // Anything outliving a constant must have the `'static` |
118 | | // lifetime and live forever. |
119 | | // Anything with a shorter lifetime (e.g the `&foo()` in |
120 | | // `bar(&foo())` or anything within a block will keep the |
121 | | // regular drops just like runtime code. |
122 | 1.72M | if let Some(temp_lifetime) = temp_lifetime.temp_lifetime { |
123 | 1.72M | this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Storage); |
124 | 1.72M | } |
125 | | } |
126 | | } |
127 | | |
128 | 1.73M | block = this.expr_into_dest(temp_place, block, expr_id).into_block(); |
129 | | |
130 | 1.73M | if let Some(temp_lifetime) = temp_lifetime.temp_lifetime { |
131 | 1.73M | this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Value); |
132 | 1.73M | } |
133 | | |
134 | 1.73M | if let Some(backwards_incompatible) = temp_lifetime.backwards_incompatible { |
135 | 0 | this.schedule_backwards_incompatible_drop(expr_span, backwards_incompatible, temp); |
136 | 1.73M | } |
137 | | |
138 | 1.73M | block.and(temp) |
139 | 1.73M | } |
140 | | } |