rust_verify_test coverage (7c1d655)

Coverage Report

Created: 2026-07-20 10:58

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