rustc_mir_build/builder/expr/
as_temp.rs

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