1use rustc_hiras hir;
2use rustc_hir::ItemLocalMap;
3use rustc_macros::{HashStable, TyDecodable, TyEncodable};
4use tracing::debug;
56use crate::middle::region::{Scope, ScopeData, ScopeTree};
78/// `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}
1415impl RvalueScopes {
16pub fn new() -> Self {
17Self { map: <_>::default() }
18 }
1920/// 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.
23pub 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.
29if let Some(&s) = self.map.get(&expr_id) {
30debug!("temporary_scope({expr_id:?}) = {s:?} [custom]");
31return (s, None);
32 }
3334// 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.
38let mut id = Scope { local_id: expr_id, data: ScopeData::Node };
39let mut backwards_incompatible = None;
4041while let Some(&(p, _)) = region_scope_tree.parent_map.get(&id) {
42match p.data {
43 ScopeData::Destruction => {
44debug!("temporary_scope({expr_id:?}) = {id:?} [enclosing]");
45return (Some(id), backwards_incompatible);
46 }
47 ScopeData::IfThenRescope => {
48debug!("temporary_scope({expr_id:?}) = {p:?} [enclosing]");
49return (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*.
60if 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 }
7071debug!("temporary_scope({expr_id:?}) = None");
72 (None, backwards_incompatible)
73 }
7475/// Make an association between a sub-expression and an extended lifetime
76pub fn record_rvalue_scope(&mut self, var: hir::ItemLocalId, lifetime: Option<Scope>) {
77debug!("record_rvalue_scope(var={var:?}, lifetime={lifetime:?})");
78if let Some(lifetime) = lifetime {
79assert!(var != lifetime.local_id);
80 }
81self.map.insert(var, lifetime);
82 }
83}