rustc_middle/ty/
rvalue_scopes.rs

1use rustc_hir as hir;
2use rustc_hir::ItemLocalMap;
3use rustc_macros::{HashStable, TyDecodable, TyEncodable};
4use tracing::debug;
5
6use crate::middle::region::{Scope, ScopeData, ScopeTree};
7
8/// `RvalueScopes` is a mapping from sub-expressions to _extended_ lifetime as determined by
9/// rules laid out in `rustc_hir_analysis::check::rvalue_scopes`.
10#[derive(TyEncodable, TyDecodable, Clone, Debug, Default, Eq, PartialEq, HashStable)]
11pub struct RvalueScopes {
12    map: ItemLocalMap<Option<Scope>>,
13}
14
15impl RvalueScopes {
16    pub fn new() -> Self {
17        Self { map: <_>::default() }
18    }
19
20    /// Returns the scope when the temp created by `expr_id` will be cleaned up.
21    /// It also emits a lint on potential backwards incompatible change to the temporary scope
22    /// which is *for now* always shortening.
23    pub fn temporary_scope(
24        &self,
25        region_scope_tree: &ScopeTree,
26        expr_id: hir::ItemLocalId,
27    ) -> (Option<Scope>, Option<Scope>) {
28        // Check for a designated rvalue scope.
29        if let Some(&s) = self.map.get(&expr_id) {
30            debug!("temporary_scope({expr_id:?}) = {s:?} [custom]");
31            return (s, None);
32        }
33
34        // Otherwise, locate the innermost terminating scope
35        // if there's one. Static items, for instance, won't
36        // have an enclosing scope, hence no scope will be
37        // returned.
38        let mut id = Scope { local_id: expr_id, data: ScopeData::Node };
39        let mut backwards_incompatible = None;
40
41        while let Some(&(p, _)) = region_scope_tree.parent_map.get(&id) {
42            match p.data {
43                ScopeData::Destruction => {
44                    debug!("temporary_scope({expr_id:?}) = {id:?} [enclosing]");
45                    return (Some(id), backwards_incompatible);
46                }
47                ScopeData::IfThenRescope => {
48                    debug!("temporary_scope({expr_id:?}) = {p:?} [enclosing]");
49                    return (Some(p), backwards_incompatible);
50                }
51                ScopeData::Node
52                | ScopeData::CallSite
53                | ScopeData::Arguments
54                | ScopeData::IfThen
55                | ScopeData::Remainder(_) => {
56                    // If we haven't already passed through a backwards-incompatible node,
57                    // then check if we are passing through one now and record it if so.
58                    // This is for now only working for cases where a temporary lifetime is
59                    // *shortened*.
60                    if backwards_incompatible.is_none() {
61                        backwards_incompatible = region_scope_tree
62                            .backwards_incompatible_scope
63                            .get(&p.local_id)
64                            .copied();
65                    }
66                    id = p
67                }
68            }
69        }
70
71        debug!("temporary_scope({expr_id:?}) = None");
72        (None, backwards_incompatible)
73    }
74
75    /// Make an association between a sub-expression and an extended lifetime
76    pub fn record_rvalue_scope(&mut self, var: hir::ItemLocalId, lifetime: Option<Scope>) {
77        debug!("record_rvalue_scope(var={var:?}, lifetime={lifetime:?})");
78        if let Some(lifetime) = lifetime {
79            assert!(var != lifetime.local_id);
80        }
81        self.map.insert(var, lifetime);
82    }
83}