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
113
114
115
116
117
118
119
//! See docs in build/expr/mod.rs

use crate::build::scope::DropKind;
use crate::build::{BlockAnd, BlockAndExtension, Builder};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_middle::middle::region;
use rustc_middle::mir::*;
use rustc_middle::thir::*;

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(
        &mut self,
        block: BasicBlock,
        temp_lifetime: Option<region::Scope>,
        expr: &Expr<'tcx>,
        mutability: Mutability,
    ) -> BlockAnd<Local> {
        // 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.as_temp_inner(block, temp_lifetime, expr, mutability))
    }

    fn as_temp_inner(
        &mut self,
        mut block: BasicBlock,
        temp_lifetime: Option<region::Scope>,
        expr: &Expr<'tcx>,
        mutability: Mutability,
    ) -> BlockAnd<Local> {
        debug!(
            "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, &this.thir[value], mutability)
            });
        }

        let expr_ty = expr.ty;
        let temp = {
            let mut local_decl = LocalDecl::new(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);
            }
            match expr.kind {
                ExprKind::StaticRef { def_id, .. } => {
                    assert!(!this.tcx.is_thread_local_static(def_id));
                    local_decl.internal = true;
                    local_decl.local_info =
                        Some(Box::new(LocalInfo::StaticRef { def_id, is_thread_local: false }));
                }
                ExprKind::ThreadLocalRef(def_id) => {
                    assert!(this.tcx.is_thread_local_static(def_id));
                    local_decl.internal = true;
                    local_decl.local_info =
                        Some(Box::new(LocalInfo::StaticRef { def_id, is_thread_local: true }));
                }
                ExprKind::Literal { const_id: Some(def_id), .. } => {
                    local_decl.local_info = Some(Box::new(LocalInfo::ConstRef { def_id }));
                }
                _ => {}
            }
            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: 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.expr_into_dest(temp_place, block, expr));

        if let Some(temp_lifetime) = temp_lifetime {
            this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Value);
        }

        block.and(temp)
    }
}