|
6 | 6 | // reachable as well. |
7 | 7 |
|
8 | 8 | use hir::def_id::LocalDefIdSet; |
| 9 | +use rustc_data_structures::stack::ensure_sufficient_stack; |
9 | 10 | use rustc_hir as hir; |
10 | 11 | use rustc_hir::def::{DefKind, Res}; |
11 | 12 | use rustc_hir::def_id::{DefId, LocalDefId}; |
@@ -65,23 +66,8 @@ impl<'tcx> Visitor<'tcx> for ReachableContext<'tcx> { |
65 | 66 | _ => None, |
66 | 67 | }; |
67 | 68 |
|
68 | | - if let Some(res) = res |
69 | | - && let Some(def_id) = res.opt_def_id().and_then(|el| el.as_local()) |
70 | | - { |
71 | | - if self.def_id_represents_local_inlined_item(def_id.to_def_id()) { |
72 | | - self.worklist.push(def_id); |
73 | | - } else { |
74 | | - match res { |
75 | | - // Reachable constants and reachable statics can have their contents inlined |
76 | | - // into other crates. Mark them as reachable and recurse into their body. |
77 | | - Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::Static { .. }, _) => { |
78 | | - self.worklist.push(def_id); |
79 | | - } |
80 | | - _ => { |
81 | | - self.reachable_symbols.insert(def_id); |
82 | | - } |
83 | | - } |
84 | | - } |
| 69 | + if let Some(res) = res { |
| 70 | + self.propagate_item(res); |
85 | 71 | } |
86 | 72 |
|
87 | 73 | intravisit::walk_expr(self, expr) |
@@ -201,17 +187,9 @@ impl<'tcx> ReachableContext<'tcx> { |
201 | 187 | hir::ItemKind::Const(_, _, init) => { |
202 | 188 | self.visit_nested_body(init); |
203 | 189 | } |
204 | | - |
205 | | - // Reachable statics are inlined if read from another constant or static |
206 | | - // in other crates. Additionally anonymous nested statics may be created |
207 | | - // when evaluating a static, so preserve those, too. |
208 | | - hir::ItemKind::Static(_, _, init) => { |
209 | | - // FIXME(oli-obk): remove this body walking and instead walk the evaluated initializer |
210 | | - // to find nested items that end up in the final value instead of also marking symbols |
211 | | - // as reachable that are only needed for evaluation. |
212 | | - self.visit_nested_body(init); |
| 190 | + hir::ItemKind::Static(..) => { |
213 | 191 | if let Ok(alloc) = self.tcx.eval_static_initializer(item.owner_id.def_id) { |
214 | | - self.propagate_statics_from_alloc(item.owner_id.def_id, alloc); |
| 192 | + self.propagate_from_alloc(alloc); |
215 | 193 | } |
216 | 194 | } |
217 | 195 |
|
@@ -281,25 +259,60 @@ impl<'tcx> ReachableContext<'tcx> { |
281 | 259 | } |
282 | 260 | } |
283 | 261 |
|
284 | | - /// Finds anonymous nested statics created for nested allocations and adds them to `reachable_symbols`. |
285 | | - fn propagate_statics_from_alloc(&mut self, root: LocalDefId, alloc: ConstAllocation<'tcx>) { |
| 262 | + /// Finds things to add to `reachable_symbols` within allocations. |
| 263 | + /// In contrast to visit_nested_body this ignores things that were only needed to evaluate |
| 264 | + /// the allocation. |
| 265 | + fn propagate_from_alloc(&mut self, alloc: ConstAllocation<'tcx>) { |
286 | 266 | if !self.any_library { |
287 | 267 | return; |
288 | 268 | } |
289 | 269 | for (_, prov) in alloc.0.provenance().ptrs().iter() { |
290 | 270 | match self.tcx.global_alloc(prov.alloc_id()) { |
291 | 271 | GlobalAlloc::Static(def_id) => { |
292 | | - if let Some(def_id) = def_id.as_local() |
293 | | - && self.tcx.local_parent(def_id) == root |
294 | | - // This is the main purpose of this function: add the def_id we find |
295 | | - // to `reachable_symbols`. |
296 | | - && self.reachable_symbols.insert(def_id) |
297 | | - && let Ok(alloc) = self.tcx.eval_static_initializer(def_id) |
298 | | - { |
299 | | - self.propagate_statics_from_alloc(root, alloc); |
| 272 | + self.propagate_item(Res::Def(self.tcx.def_kind(def_id), def_id)) |
| 273 | + } |
| 274 | + GlobalAlloc::Function(instance) => { |
| 275 | + self.propagate_item(Res::Def( |
| 276 | + self.tcx.def_kind(instance.def_id()), |
| 277 | + instance.def_id(), |
| 278 | + )) |
| 279 | + // TODO: walk generic args |
| 280 | + } |
| 281 | + GlobalAlloc::VTable(ty, trait_ref) => todo!("{ty:?}, {trait_ref:?}"), |
| 282 | + GlobalAlloc::Memory(alloc) => self.propagate_from_alloc(alloc), |
| 283 | + } |
| 284 | + } |
| 285 | + } |
| 286 | + |
| 287 | + fn propagate_item(&mut self, res: Res) { |
| 288 | + let Res::Def(kind, def_id) = res else { return }; |
| 289 | + let Some(def_id) = def_id.as_local() else { return }; |
| 290 | + match kind { |
| 291 | + DefKind::Static { nested: true, .. } => { |
| 292 | + // This is the main purpose of this function: add the def_id we find |
| 293 | + // to `reachable_symbols`. |
| 294 | + if self.reachable_symbols.insert(def_id) { |
| 295 | + if let Ok(alloc) = self.tcx.eval_static_initializer(def_id) { |
| 296 | + // This cannot cause infinite recursion, because we abort by inserting into the |
| 297 | + // work list once we hit a normal static. Nested statics, even if they somehow |
| 298 | + // become recursive, are also not infinitely recursing, because of the |
| 299 | + // `reachable_symbols` check above. |
| 300 | + // We still need to protect against stack overflow due to deeply nested statics. |
| 301 | + ensure_sufficient_stack(|| self.propagate_from_alloc(alloc)); |
300 | 302 | } |
301 | 303 | } |
302 | | - GlobalAlloc::Function(_) | GlobalAlloc::VTable(_, _) | GlobalAlloc::Memory(_) => {} |
| 304 | + } |
| 305 | + // Reachable constants and reachable statics can have their contents inlined |
| 306 | + // into other crates. Mark them as reachable and recurse into their body. |
| 307 | + DefKind::Const | DefKind::AssocConst | DefKind::Static { .. } => { |
| 308 | + self.worklist.push(def_id); |
| 309 | + } |
| 310 | + _ => { |
| 311 | + if self.def_id_represents_local_inlined_item(def_id.to_def_id()) { |
| 312 | + self.worklist.push(def_id); |
| 313 | + } else { |
| 314 | + self.reachable_symbols.insert(def_id); |
| 315 | + } |
303 | 316 | } |
304 | 317 | } |
305 | 318 | } |
|
0 commit comments