rustc_middle/ty/
rvalue_scopes.rs

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
use rustc_hir as hir;
use rustc_hir::ItemLocalMap;
use rustc_macros::{HashStable, TyDecodable, TyEncodable};
use tracing::debug;

use crate::middle::region::{Scope, ScopeData, ScopeTree};

/// `RvalueScopes` is a mapping from sub-expressions to _extended_ lifetime as determined by
/// rules laid out in `rustc_hir_analysis::check::rvalue_scopes`.
#[derive(TyEncodable, TyDecodable, Clone, Debug, Default, Eq, PartialEq, HashStable)]
pub struct RvalueScopes {
    map: ItemLocalMap<Option<Scope>>,
}

impl RvalueScopes {
    pub fn new() -> Self {
        Self { map: <_>::default() }
    }

    /// Returns the scope when the temp created by `expr_id` will be cleaned up.
    /// It also emits a lint on potential backwards incompatible change to the temporary scope
    /// which is *for now* always shortening.
    pub fn temporary_scope(
        &self,
        region_scope_tree: &ScopeTree,
        expr_id: hir::ItemLocalId,
    ) -> (Option<Scope>, Option<Scope>) {
        // Check for a designated rvalue scope.
        if let Some(&s) = self.map.get(&expr_id) {
            debug!("temporary_scope({expr_id:?}) = {s:?} [custom]");
            return (s, None);
        }

        // Otherwise, locate the innermost terminating scope
        // if there's one. Static items, for instance, won't
        // have an enclosing scope, hence no scope will be
        // returned.
        let mut id = Scope { id: expr_id, data: ScopeData::Node };
        let mut backwards_incompatible = None;

        while let Some(&(p, _)) = region_scope_tree.parent_map.get(&id) {
            match p.data {
                ScopeData::Destruction => {
                    debug!("temporary_scope({expr_id:?}) = {id:?} [enclosing]");
                    return (Some(id), backwards_incompatible);
                }
                ScopeData::IfThenRescope => {
                    debug!("temporary_scope({expr_id:?}) = {p:?} [enclosing]");
                    return (Some(p), backwards_incompatible);
                }
                ScopeData::Node
                | ScopeData::CallSite
                | ScopeData::Arguments
                | ScopeData::IfThen
                | ScopeData::Remainder(_) => {
                    // If we haven't already passed through a backwards-incompatible node,
                    // then check if we are passing through one now and record it if so.
                    // This is for now only working for cases where a temporary lifetime is
                    // *shortened*.
                    if backwards_incompatible.is_none() {
                        backwards_incompatible = region_scope_tree
                            .backwards_incompatible_scope
                            .get(&p.item_local_id())
                            .copied();
                    }
                    id = p
                }
            }
        }

        debug!("temporary_scope({expr_id:?}) = None");
        (None, backwards_incompatible)
    }

    /// Make an association between a sub-expression and an extended lifetime
    pub fn record_rvalue_scope(&mut self, var: hir::ItemLocalId, lifetime: Option<Scope>) {
        debug!("record_rvalue_scope(var={var:?}, lifetime={lifetime:?})");
        if let Some(lifetime) = lifetime {
            assert!(var != lifetime.item_local_id());
        }
        self.map.insert(var, lifetime);
    }
}