|
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