1 //! See docs in build/expr/mod.rs
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::*;
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 pub(crate) fn as_temp(
16 temp_lifetime: Option<region::Scope>,
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))
26 #[instrument(skip(self), level = "debug")]
29 mut block: BasicBlock,
30 temp_lifetime: Option<region::Scope>,
32 mutability: Mutability,
33 ) -> BlockAnd<Local> {
36 let expr_span = expr.span;
37 let source_info = this.source_info(expr_span);
38 if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
39 return this.in_scope((region_scope, source_info), lint_level, |this| {
40 this.as_temp(block, temp_lifetime, &this.thir[value], mutability)
44 let expr_ty = expr.ty;
46 let mut local_decl = LocalDecl::new(expr_ty, expr_span);
47 if mutability == Mutability::Not {
48 local_decl = local_decl.immutable();
51 debug!("creating temp {:?} with block_context: {:?}", local_decl, this.block_context);
52 // Find out whether this temp is being created within the
53 // tail expression of a block whose result is ignored.
54 if let Some(tail_info) = this.block_context.currently_in_block_tail() {
55 local_decl = local_decl.block_tail(tail_info);
58 ExprKind::StaticRef { def_id, .. } => {
59 assert!(!this.tcx.is_thread_local_static(def_id));
60 local_decl.internal = true;
61 local_decl.local_info =
62 Some(Box::new(LocalInfo::StaticRef { def_id, is_thread_local: false }));
64 ExprKind::ThreadLocalRef(def_id) => {
65 assert!(this.tcx.is_thread_local_static(def_id));
66 local_decl.internal = true;
67 local_decl.local_info =
68 Some(Box::new(LocalInfo::StaticRef { def_id, is_thread_local: true }));
70 ExprKind::NamedConst { def_id, .. } | ExprKind::ConstParam { def_id, .. } => {
71 local_decl.local_info = Some(Box::new(LocalInfo::ConstRef { def_id }));
75 this.local_decls.push(local_decl)
77 let temp_place = Place::from(temp);
80 // Don't bother with StorageLive and Dead for these temporaries,
81 // they are never assigned.
82 ExprKind::Break { .. } | ExprKind::Continue { .. } | ExprKind::Return { .. } => (),
83 ExprKind::Block { block }
84 if let Block { expr: None, targeted_by_break: false, .. } = this.thir[block]
85 && expr_ty.is_never() => {}
88 .push(block, Statement { source_info, kind: StatementKind::StorageLive(temp) });
90 // In constants, `temp_lifetime` is `None` for temporaries that
91 // live for the `'static` lifetime. Thus we do not drop these
92 // temporaries and simply leak them.
93 // This is equivalent to what `let x = &foo();` does in
94 // functions. The temporary is lifted to their surrounding
95 // scope. In a function that means the temporary lives until
96 // just before the function returns. In constants that means it
97 // outlives the constant's initialization value computation.
98 // Anything outliving a constant must have the `'static`
99 // lifetime and live forever.
100 // Anything with a shorter lifetime (e.g the `&foo()` in
101 // `bar(&foo())` or anything within a block will keep the
102 // regular drops just like runtime code.
103 if let Some(temp_lifetime) = temp_lifetime {
104 this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Storage);
109 unpack!(block = this.expr_into_dest(temp_place, block, expr));
111 if let Some(temp_lifetime) = temp_lifetime {
112 this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Value);