summary refs log tree commit diff
path: root/src/librustc_mir/build/expr/as_temp.rs
blob: 169b71f281c3e532bfd5883da79be9b79161e1d8 (plain)
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
120
121
122
123
124
125
126
127
128
129
130
131
//! See docs in build/expr/mod.rs

use crate::build::{BlockAnd, BlockAndExtension, Builder};
use crate::build::scope::DropKind;
use crate::hair::*;
use rustc::hir;
use rustc::middle::region;
use rustc::mir::*;
use syntax_pos::symbol::sym;

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.
    pub 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);
        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().has_attr(def_id, sym::thread_local);
                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)
    }
}