|
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};
|
12 | 13 | use rustc_hir::intravisit::{self, Visitor};
|
13 | 14 | use rustc_hir::Node;
|
14 | 15 | use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
|
15 | 16 | use rustc_middle::middle::privacy::{self, Level};
|
16 |
| -use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc}; |
| 17 | +use rustc_middle::mir::interpret::{ConstAllocation, ErrorHandled, GlobalAlloc}; |
17 | 18 | use rustc_middle::query::Providers;
|
18 |
| -use rustc_middle::ty::{self, TyCtxt}; |
| 19 | +use rustc_middle::ty::{self, ExistentialTraitRef, TyCtxt}; |
| 20 | +use rustc_privacy::DefIdVisitor; |
19 | 21 | use rustc_session::config::CrateType;
|
20 | 22 | use rustc_target::spec::abi::Abi;
|
21 | 23 |
|
@@ -65,23 +67,8 @@ impl<'tcx> Visitor<'tcx> for ReachableContext<'tcx> {
|
65 | 67 | _ => None,
|
66 | 68 | };
|
67 | 69 |
|
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 |
| - } |
| 70 | + if let Some(res) = res { |
| 71 | + self.propagate_item(res); |
85 | 72 | }
|
86 | 73 |
|
87 | 74 | intravisit::walk_expr(self, expr)
|
@@ -198,20 +185,22 @@ impl<'tcx> ReachableContext<'tcx> {
|
198 | 185 | // Reachable constants will be inlined into other crates
|
199 | 186 | // unconditionally, so we need to make sure that their
|
200 | 187 | // contents are also reachable.
|
201 |
| - hir::ItemKind::Const(_, _, init) => { |
202 |
| - self.visit_nested_body(init); |
| 188 | + hir::ItemKind::Const(..) => { |
| 189 | + match self.tcx.const_eval_poly_to_alloc(item.owner_id.def_id.into()) { |
| 190 | + Ok(alloc) => { |
| 191 | + let alloc = self.tcx.global_alloc(alloc.alloc_id).unwrap_memory(); |
| 192 | + self.propagate_from_alloc(alloc); |
| 193 | + } |
| 194 | + Err(ErrorHandled::TooGeneric(span)) => span_bug!( |
| 195 | + span, |
| 196 | + "generic constants aren't implemented in reachability" |
| 197 | + ), |
| 198 | + Err(ErrorHandled::Reported(..)) => {} |
| 199 | + } |
203 | 200 | }
|
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); |
| 201 | + hir::ItemKind::Static(..) => { |
213 | 202 | 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); |
| 203 | + self.propagate_from_alloc(alloc); |
215 | 204 | }
|
216 | 205 | }
|
217 | 206 |
|
@@ -282,28 +271,89 @@ impl<'tcx> ReachableContext<'tcx> {
|
282 | 271 | }
|
283 | 272 | }
|
284 | 273 |
|
285 |
| - /// Finds anonymous nested statics created for nested allocations and adds them to `reachable_symbols`. |
286 |
| - fn propagate_statics_from_alloc(&mut self, root: LocalDefId, alloc: ConstAllocation<'tcx>) { |
| 274 | + /// Finds things to add to `reachable_symbols` within allocations. |
| 275 | + /// In contrast to visit_nested_body this ignores things that were only needed to evaluate |
| 276 | + /// the allocation. |
| 277 | + fn propagate_from_alloc(&mut self, alloc: ConstAllocation<'tcx>) { |
287 | 278 | if !self.any_library {
|
288 | 279 | return;
|
289 | 280 | }
|
290 | 281 | for (_, prov) in alloc.0.provenance().ptrs().iter() {
|
291 | 282 | match self.tcx.global_alloc(prov.alloc_id()) {
|
292 | 283 | GlobalAlloc::Static(def_id) => {
|
293 |
| - if let Some(def_id) = def_id.as_local() |
294 |
| - && self.tcx.local_parent(def_id) == root |
295 |
| - // This is the main purpose of this function: add the def_id we find |
296 |
| - // to `reachable_symbols`. |
297 |
| - && self.reachable_symbols.insert(def_id) |
298 |
| - && let Ok(alloc) = self.tcx.eval_static_initializer(def_id) |
299 |
| - { |
300 |
| - self.propagate_statics_from_alloc(root, alloc); |
| 284 | + self.propagate_item(Res::Def(self.tcx.def_kind(def_id), def_id)) |
| 285 | + } |
| 286 | + GlobalAlloc::Function(instance) => { |
| 287 | + // Manually visit to actually see the instance's `DefId`. Type visitors won't see it |
| 288 | + self.propagate_item(Res::Def( |
| 289 | + self.tcx.def_kind(instance.def_id()), |
| 290 | + instance.def_id(), |
| 291 | + )); |
| 292 | + self.visit(instance.args); |
| 293 | + } |
| 294 | + GlobalAlloc::VTable(ty, trait_ref) => { |
| 295 | + self.visit(ty); |
| 296 | + // Manually visit to actually see the trait's `DefId`. Type visitors won't see it |
| 297 | + if let Some(trait_ref) = trait_ref { |
| 298 | + let ExistentialTraitRef { def_id, args } = trait_ref.skip_binder(); |
| 299 | + self.visit_def_id(def_id, "", &""); |
| 300 | + self.visit(args); |
301 | 301 | }
|
302 | 302 | }
|
303 |
| - GlobalAlloc::Function(_) | GlobalAlloc::VTable(_, _) | GlobalAlloc::Memory(_) => {} |
| 303 | + GlobalAlloc::Memory(alloc) => self.propagate_from_alloc(alloc), |
304 | 304 | }
|
305 | 305 | }
|
306 | 306 | }
|
| 307 | + |
| 308 | + fn propagate_item(&mut self, res: Res) { |
| 309 | + let Res::Def(kind, def_id) = res else { return }; |
| 310 | + let Some(def_id) = def_id.as_local() else { return }; |
| 311 | + match kind { |
| 312 | + DefKind::Static { nested: true, .. } => { |
| 313 | + // This is the main purpose of this function: add the def_id we find |
| 314 | + // to `reachable_symbols`. |
| 315 | + if self.reachable_symbols.insert(def_id) { |
| 316 | + if let Ok(alloc) = self.tcx.eval_static_initializer(def_id) { |
| 317 | + // This cannot cause infinite recursion, because we abort by inserting into the |
| 318 | + // work list once we hit a normal static. Nested statics, even if they somehow |
| 319 | + // become recursive, are also not infinitely recursing, because of the |
| 320 | + // `reachable_symbols` check above. |
| 321 | + // We still need to protect against stack overflow due to deeply nested statics. |
| 322 | + ensure_sufficient_stack(|| self.propagate_from_alloc(alloc)); |
| 323 | + } |
| 324 | + } |
| 325 | + } |
| 326 | + // Reachable constants and reachable statics can have their contents inlined |
| 327 | + // into other crates. Mark them as reachable and recurse into their body. |
| 328 | + DefKind::Const | DefKind::AssocConst | DefKind::Static { .. } => { |
| 329 | + self.worklist.push(def_id); |
| 330 | + } |
| 331 | + _ => { |
| 332 | + if self.def_id_represents_local_inlined_item(def_id.to_def_id()) { |
| 333 | + self.worklist.push(def_id); |
| 334 | + } else { |
| 335 | + self.reachable_symbols.insert(def_id); |
| 336 | + } |
| 337 | + } |
| 338 | + } |
| 339 | + } |
| 340 | +} |
| 341 | + |
| 342 | +impl<'tcx> DefIdVisitor<'tcx> for ReachableContext<'tcx> { |
| 343 | + type Result = (); |
| 344 | + |
| 345 | + fn tcx(&self) -> TyCtxt<'tcx> { |
| 346 | + self.tcx |
| 347 | + } |
| 348 | + |
| 349 | + fn visit_def_id( |
| 350 | + &mut self, |
| 351 | + def_id: DefId, |
| 352 | + _kind: &str, |
| 353 | + _descr: &dyn std::fmt::Display, |
| 354 | + ) -> Self::Result { |
| 355 | + self.propagate_item(Res::Def(self.tcx.def_kind(def_id), def_id)) |
| 356 | + } |
307 | 357 | }
|
308 | 358 |
|
309 | 359 | fn check_item<'tcx>(
|
|
0 commit comments