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