]>
Commit | Line | Data |
---|---|---|
970d7e83 LB |
1 | // Finds items that are externally reachable, to determine which items |
2 | // need to have their metadata (and possibly their AST) serialized. | |
3 | // All items that can be referred to through an exported name are | |
4 | // reachable, and when a reachable thing is inline or generic, it | |
5 | // makes all other generics or inline functions that it references | |
6 | // reachable as well. | |
7 | ||
dfeec247 | 8 | use rustc_data_structures::fx::FxHashSet; |
dfeec247 XL |
9 | use rustc_hir as hir; |
10 | use rustc_hir::def::{DefKind, Res}; | |
17df50a5 | 11 | use rustc_hir::def_id::{DefId, LocalDefId}; |
5099ac24 | 12 | use rustc_hir::intravisit::{self, Visitor}; |
3dfed10e | 13 | use rustc_hir::Node; |
ba9703b0 | 14 | use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; |
2b03887a | 15 | use rustc_middle::middle::privacy::{self, Level}; |
ba9703b0 | 16 | use rustc_middle::ty::query::Providers; |
3dfed10e | 17 | use rustc_middle::ty::{self, DefIdTree, TyCtxt}; |
f9f354fc | 18 | use rustc_session::config::CrateType; |
83c7162d | 19 | use rustc_target::spec::abi::Abi; |
970d7e83 | 20 | |
970d7e83 LB |
21 | // Returns true if the given item must be inlined because it may be |
22 | // monomorphized or it was marked with `#[inline]`. This will only return | |
23 | // true for functions. | |
a2a8927a | 24 | fn item_might_be_inlined(tcx: TyCtxt<'_>, item: &hir::Item<'_>, attrs: &CodegenFnAttrs) -> bool { |
0531ce1d | 25 | if attrs.requests_inline() { |
dfeec247 | 26 | return true; |
970d7e83 LB |
27 | } |
28 | ||
e74abb32 | 29 | match item.kind { |
ba9703b0 | 30 | hir::ItemKind::Fn(ref sig, ..) if sig.header.is_const() => true, |
dfeec247 | 31 | hir::ItemKind::Impl { .. } | hir::ItemKind::Fn(..) => { |
2b03887a | 32 | let generics = tcx.generics_of(item.owner_id); |
b7449926 | 33 | generics.requires_monomorphization(tcx) |
970d7e83 LB |
34 | } |
35 | _ => false, | |
36 | } | |
37 | } | |
38 | ||
416331ca XL |
39 | fn method_might_be_inlined( |
40 | tcx: TyCtxt<'_>, | |
dfeec247 | 41 | impl_item: &hir::ImplItem<'_>, |
ba9703b0 | 42 | impl_src: LocalDefId, |
dc9dc135 | 43 | ) -> bool { |
6a06907d | 44 | let codegen_fn_attrs = tcx.codegen_fn_attrs(impl_item.hir_id().owner.to_def_id()); |
2b03887a | 45 | let generics = tcx.generics_of(impl_item.owner_id); |
b7449926 | 46 | if codegen_fn_attrs.requests_inline() || generics.requires_monomorphization(tcx) { |
dfeec247 | 47 | return true; |
1a4d82fc | 48 | } |
ba9703b0 | 49 | if let hir::ImplItemKind::Fn(method_sig, _) = &impl_item.kind { |
e1599b0c | 50 | if method_sig.header.is_const() { |
dfeec247 | 51 | return true; |
e1599b0c XL |
52 | } |
53 | } | |
5099ac24 | 54 | match tcx.hir().find_by_def_id(impl_src) { |
f9f354fc XL |
55 | Some(Node::Item(item)) => item_might_be_inlined(tcx, &item, codegen_fn_attrs), |
56 | Some(..) | None => span_bug!(impl_item.span, "impl did is not an item"), | |
970d7e83 | 57 | } |
970d7e83 LB |
58 | } |
59 | ||
60 | // Information needed while computing reachability. | |
f035d41b | 61 | struct ReachableContext<'tcx> { |
970d7e83 | 62 | // The type context. |
dc9dc135 | 63 | tcx: TyCtxt<'tcx>, |
3dfed10e | 64 | maybe_typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>, |
970d7e83 | 65 | // The set of items which must be exported in the linkage sense. |
3dfed10e | 66 | reachable_symbols: FxHashSet<LocalDefId>, |
970d7e83 LB |
67 | // A worklist of item IDs. Each item ID in this worklist will be inlined |
68 | // and will be scanned for further references. | |
3dfed10e XL |
69 | // FIXME(eddyb) benchmark if this would be faster as a `VecDeque`. |
70 | worklist: Vec<LocalDefId>, | |
1a4d82fc JJ |
71 | // Whether any output of this compilation is a library |
72 | any_library: bool, | |
970d7e83 LB |
73 | } |
74 | ||
f035d41b | 75 | impl<'tcx> Visitor<'tcx> for ReachableContext<'tcx> { |
32a655c1 | 76 | fn visit_nested_body(&mut self, body: hir::BodyId) { |
3dfed10e XL |
77 | let old_maybe_typeck_results = |
78 | self.maybe_typeck_results.replace(self.tcx.typeck_body(body)); | |
0731742a | 79 | let body = self.tcx.hir().body(body); |
32a655c1 | 80 | self.visit_body(body); |
3dfed10e | 81 | self.maybe_typeck_results = old_maybe_typeck_results; |
476ff2be | 82 | } |
970d7e83 | 83 | |
dfeec247 | 84 | fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { |
e74abb32 | 85 | let res = match expr.kind { |
3dfed10e XL |
86 | hir::ExprKind::Path(ref qpath) => { |
87 | Some(self.typeck_results().qpath_res(qpath, expr.hir_id)) | |
88 | } | |
dfeec247 | 89 | hir::ExprKind::MethodCall(..) => self |
3dfed10e | 90 | .typeck_results() |
dfeec247 XL |
91 | .type_dependent_def(expr.hir_id) |
92 | .map(|(kind, def_id)| Res::Def(kind, def_id)), | |
93 | _ => None, | |
476ff2be SL |
94 | }; |
95 | ||
5e7ed085 FG |
96 | if let Some(res) = res && let Some(def_id) = res.opt_def_id().and_then(|el| el.as_local()) { |
97 | if self.def_id_represents_local_inlined_item(def_id.to_def_id()) { | |
98 | self.worklist.push(def_id); | |
99 | } else { | |
100 | match res { | |
101 | // If this path leads to a constant, then we need to | |
102 | // recurse into the constant to continue finding | |
103 | // items that are reachable. | |
104 | Res::Def(DefKind::Const | DefKind::AssocConst, _) => { | |
105 | self.worklist.push(def_id); | |
106 | } | |
92a42be0 | 107 | |
5e7ed085 FG |
108 | // If this wasn't a static, then the destination is |
109 | // surely reachable. | |
110 | _ => { | |
111 | self.reachable_symbols.insert(def_id); | |
970d7e83 | 112 | } |
970d7e83 | 113 | } |
1a4d82fc | 114 | } |
1a4d82fc | 115 | } |
970d7e83 | 116 | |
92a42be0 | 117 | intravisit::walk_expr(self, expr) |
1a4d82fc | 118 | } |
2b03887a FG |
119 | |
120 | fn visit_inline_asm(&mut self, asm: &'tcx hir::InlineAsm<'tcx>, id: hir::HirId) { | |
121 | for (op, _) in asm.operands { | |
122 | if let hir::InlineAsmOperand::SymStatic { def_id, .. } = op { | |
123 | if let Some(def_id) = def_id.as_local() { | |
124 | self.reachable_symbols.insert(def_id); | |
125 | } | |
126 | } | |
127 | } | |
128 | intravisit::walk_inline_asm(self, asm, id); | |
129 | } | |
1a4d82fc | 130 | } |
970d7e83 | 131 | |
f035d41b | 132 | impl<'tcx> ReachableContext<'tcx> { |
3dfed10e | 133 | /// Gets the type-checking results for the current body. |
f035d41b XL |
134 | /// As this will ICE if called outside bodies, only call when working with |
135 | /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies). | |
136 | #[track_caller] | |
3dfed10e XL |
137 | fn typeck_results(&self) -> &'tcx ty::TypeckResults<'tcx> { |
138 | self.maybe_typeck_results | |
139 | .expect("`ReachableContext::typeck_results` called outside of body") | |
f035d41b XL |
140 | } |
141 | ||
970d7e83 LB |
142 | // Returns true if the given def ID represents a local item that is |
143 | // eligible for inlining and false otherwise. | |
e9174d1e | 144 | fn def_id_represents_local_inlined_item(&self, def_id: DefId) -> bool { |
5099ac24 FG |
145 | let Some(def_id) = def_id.as_local() else { |
146 | return false; | |
b039eaaf | 147 | }; |
970d7e83 | 148 | |
5099ac24 | 149 | match self.tcx.hir().find_by_def_id(def_id) { |
dfeec247 XL |
150 | Some(Node::Item(item)) => match item.kind { |
151 | hir::ItemKind::Fn(..) => { | |
152 | item_might_be_inlined(self.tcx, &item, self.tcx.codegen_fn_attrs(def_id)) | |
970d7e83 | 153 | } |
dfeec247 XL |
154 | _ => false, |
155 | }, | |
156 | Some(Node::TraitItem(trait_method)) => match trait_method.kind { | |
157 | hir::TraitItemKind::Const(_, ref default) => default.is_some(), | |
ba9703b0 XL |
158 | hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)) => true, |
159 | hir::TraitItemKind::Fn(_, hir::TraitFn::Required(_)) | |
dfeec247 XL |
160 | | hir::TraitItemKind::Type(..) => false, |
161 | }, | |
923072b8 FG |
162 | Some(Node::ImplItem(impl_item)) => match impl_item.kind { |
163 | hir::ImplItemKind::Const(..) => true, | |
164 | hir::ImplItemKind::Fn(..) => { | |
165 | let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); | |
166 | let impl_did = self.tcx.hir().get_parent_item(hir_id); | |
2b03887a | 167 | method_might_be_inlined(self.tcx, impl_item, impl_did.def_id) |
970d7e83 | 168 | } |
2b03887a | 169 | hir::ImplItemKind::Type(_) => false, |
923072b8 | 170 | }, |
970d7e83 | 171 | Some(_) => false, |
dfeec247 | 172 | None => false, // This will happen for default methods. |
970d7e83 LB |
173 | } |
174 | } | |
175 | ||
1a4d82fc JJ |
176 | // Step 2: Mark all symbols that the symbols on the worklist touch. |
177 | fn propagate(&mut self) { | |
0bf4aa26 | 178 | let mut scanned = FxHashSet::default(); |
0531ce1d | 179 | while let Some(search_item) = self.worklist.pop() { |
1a4d82fc | 180 | if !scanned.insert(search_item) { |
dfeec247 | 181 | continue; |
1a4d82fc | 182 | } |
970d7e83 | 183 | |
5099ac24 | 184 | if let Some(ref item) = self.tcx.hir().find_by_def_id(search_item) { |
92a42be0 | 185 | self.propagate_node(item, search_item); |
1a4d82fc JJ |
186 | } |
187 | } | |
970d7e83 LB |
188 | } |
189 | ||
3dfed10e | 190 | fn propagate_node(&mut self, node: &Node<'tcx>, search_item: LocalDefId) { |
1a4d82fc | 191 | if !self.any_library { |
7453a54e SL |
192 | // If we are building an executable, only explicitly extern |
193 | // types need to be exported. | |
94222f64 XL |
194 | let reachable = |
195 | if let Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, ..), .. }) | |
196 | | Node::ImplItem(hir::ImplItem { | |
197 | kind: hir::ImplItemKind::Fn(sig, ..), .. | |
198 | }) = *node | |
199 | { | |
60c5eb7d | 200 | sig.header.abi != Abi::Rust |
7453a54e SL |
201 | } else { |
202 | false | |
203 | }; | |
04454e1e FG |
204 | let codegen_attrs = if self.tcx.def_kind(search_item).has_codegen_attrs() { |
205 | self.tcx.codegen_fn_attrs(search_item) | |
206 | } else { | |
207 | CodegenFnAttrs::EMPTY | |
208 | }; | |
94222f64 XL |
209 | let is_extern = codegen_attrs.contains_extern_indicator(); |
210 | let std_internal = | |
211 | codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL); | |
212 | if reachable || is_extern || std_internal { | |
213 | self.reachable_symbols.insert(search_item); | |
970d7e83 | 214 | } |
1a4d82fc JJ |
215 | } else { |
216 | // If we are building a library, then reachable symbols will | |
217 | // continue to participate in linkage after this product is | |
218 | // produced. In this case, we traverse the ast node, recursing on | |
219 | // all reachable nodes from this one. | |
970d7e83 | 220 | self.reachable_symbols.insert(search_item); |
1a4d82fc | 221 | } |
970d7e83 | 222 | |
1a4d82fc | 223 | match *node { |
b7449926 | 224 | Node::Item(item) => { |
e74abb32 | 225 | match item.kind { |
8faf50e0 | 226 | hir::ItemKind::Fn(.., body) => { |
6a06907d XL |
227 | if item_might_be_inlined( |
228 | self.tcx, | |
229 | &item, | |
2b03887a | 230 | self.tcx.codegen_fn_attrs(item.owner_id), |
6a06907d | 231 | ) { |
32a655c1 | 232 | self.visit_nested_body(body); |
970d7e83 LB |
233 | } |
234 | } | |
1a4d82fc JJ |
235 | |
236 | // Reachable constants will be inlined into other crates | |
237 | // unconditionally, so we need to make sure that their | |
238 | // contents are also reachable. | |
17df50a5 | 239 | hir::ItemKind::Const(_, init) | hir::ItemKind::Static(_, _, init) => { |
32a655c1 | 240 | self.visit_nested_body(init); |
1a4d82fc JJ |
241 | } |
242 | ||
243 | // These are normal, nothing reachable about these | |
244 | // inherently and their children are already in the | |
245 | // worklist, as determined by the privacy pass | |
dfeec247 XL |
246 | hir::ItemKind::ExternCrate(_) |
247 | | hir::ItemKind::Use(..) | |
248 | | hir::ItemKind::OpaqueTy(..) | |
249 | | hir::ItemKind::TyAlias(..) | |
94222f64 | 250 | | hir::ItemKind::Macro(..) |
dfeec247 | 251 | | hir::ItemKind::Mod(..) |
fc512014 | 252 | | hir::ItemKind::ForeignMod { .. } |
dfeec247 XL |
253 | | hir::ItemKind::Impl { .. } |
254 | | hir::ItemKind::Trait(..) | |
255 | | hir::ItemKind::TraitAlias(..) | |
256 | | hir::ItemKind::Struct(..) | |
257 | | hir::ItemKind::Enum(..) | |
258 | | hir::ItemKind::Union(..) | |
259 | | hir::ItemKind::GlobalAsm(..) => {} | |
970d7e83 | 260 | } |
1a4d82fc | 261 | } |
b7449926 | 262 | Node::TraitItem(trait_method) => { |
e74abb32 | 263 | match trait_method.kind { |
dfeec247 | 264 | hir::TraitItemKind::Const(_, None) |
ba9703b0 | 265 | | hir::TraitItemKind::Fn(_, hir::TraitFn::Required(_)) => { |
1a4d82fc JJ |
266 | // Keep going, nothing to get exported |
267 | } | |
dfeec247 | 268 | hir::TraitItemKind::Const(_, Some(body_id)) |
ba9703b0 | 269 | | hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body_id)) => { |
32a655c1 | 270 | self.visit_nested_body(body_id); |
d9579d0f | 271 | } |
32a655c1 | 272 | hir::TraitItemKind::Type(..) => {} |
970d7e83 | 273 | } |
1a4d82fc | 274 | } |
dfeec247 XL |
275 | Node::ImplItem(impl_item) => match impl_item.kind { |
276 | hir::ImplItemKind::Const(_, body) => { | |
277 | self.visit_nested_body(body); | |
278 | } | |
ba9703b0 | 279 | hir::ImplItemKind::Fn(_, body) => { |
04454e1e | 280 | let impl_def_id = self.tcx.local_parent(search_item); |
3dfed10e | 281 | if method_might_be_inlined(self.tcx, impl_item, impl_def_id) { |
dfeec247 | 282 | self.visit_nested_body(body) |
1a4d82fc | 283 | } |
970d7e83 | 284 | } |
2b03887a | 285 | hir::ImplItemKind::Type(_) => {} |
dfeec247 | 286 | }, |
064997fb FG |
287 | Node::Expr(&hir::Expr { |
288 | kind: hir::ExprKind::Closure(&hir::Closure { body, .. }), | |
289 | .. | |
290 | }) => { | |
3b2f2976 XL |
291 | self.visit_nested_body(body); |
292 | } | |
1a4d82fc | 293 | // Nothing to recurse on for these |
dfeec247 XL |
294 | Node::ForeignItem(_) |
295 | | Node::Variant(_) | |
296 | | Node::Ctor(..) | |
297 | | Node::Field(_) | |
298 | | Node::Ty(_) | |
94222f64 | 299 | | Node::Crate(_) => {} |
1a4d82fc | 300 | _ => { |
9fa01778 XL |
301 | bug!( |
302 | "found unexpected node kind in worklist: {} ({:?})", | |
3dfed10e XL |
303 | self.tcx |
304 | .hir() | |
305 | .node_to_string(self.tcx.hir().local_def_id_to_hir_id(search_item)), | |
9fa01778 XL |
306 | node, |
307 | ); | |
1a4d82fc | 308 | } |
970d7e83 LB |
309 | } |
310 | } | |
92a42be0 | 311 | } |
970d7e83 | 312 | |
923072b8 | 313 | fn check_item<'tcx>( |
dc9dc135 | 314 | tcx: TyCtxt<'tcx>, |
923072b8 FG |
315 | id: hir::ItemId, |
316 | worklist: &mut Vec<LocalDefId>, | |
2b03887a | 317 | effective_visibilities: &privacy::EffectiveVisibilities, |
923072b8 | 318 | ) { |
2b03887a FG |
319 | if has_custom_linkage(tcx, id.owner_id.def_id) { |
320 | worklist.push(id.owner_id.def_id); | |
923072b8 | 321 | } |
92a42be0 | 322 | |
2b03887a | 323 | if !matches!(tcx.def_kind(id.owner_id), DefKind::Impl) { |
923072b8 | 324 | return; |
94222f64 | 325 | } |
94222f64 | 326 | |
923072b8 FG |
327 | // We need only trait impls here, not inherent impls, and only non-exported ones |
328 | let item = tcx.hir().item(id); | |
329 | if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref trait_ref), ref items, .. }) = | |
330 | item.kind | |
331 | { | |
2b03887a FG |
332 | if !effective_visibilities.is_reachable(item.owner_id.def_id) { |
333 | worklist.extend(items.iter().map(|ii_ref| ii_ref.id.owner_id.def_id)); | |
476ff2be | 334 | |
923072b8 FG |
335 | let Res::Def(DefKind::Trait, trait_def_id) = trait_ref.path.res else { |
336 | unreachable!(); | |
337 | }; | |
476ff2be | 338 | |
923072b8 FG |
339 | if !trait_def_id.is_local() { |
340 | return; | |
970d7e83 | 341 | } |
32a655c1 | 342 | |
923072b8 FG |
343 | worklist.extend( |
344 | tcx.provided_trait_methods(trait_def_id).map(|assoc| assoc.def_id.expect_local()), | |
345 | ); | |
346 | } | |
476ff2be | 347 | } |
923072b8 | 348 | } |
fc512014 | 349 | |
923072b8 FG |
350 | fn has_custom_linkage<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> bool { |
351 | // Anything which has custom linkage gets thrown on the worklist no | |
352 | // matter where it is in the crate, along with "special std symbols" | |
353 | // which are currently akin to allocator symbols. | |
354 | if !tcx.def_kind(def_id).has_codegen_attrs() { | |
355 | return false; | |
fc512014 | 356 | } |
923072b8 FG |
357 | let codegen_attrs = tcx.codegen_fn_attrs(def_id); |
358 | codegen_attrs.contains_extern_indicator() | |
359 | || codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) | |
360 | // FIXME(nbdd0121): `#[used]` are marked as reachable here so it's picked up by | |
361 | // `linked_symbols` in cg_ssa. They won't be exported in binary or cdylib due to their | |
362 | // `SymbolExportLevel::Rust` export level but may end up being exported in dylibs. | |
363 | || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED) | |
364 | || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) | |
970d7e83 LB |
365 | } |
366 | ||
17df50a5 | 367 | fn reachable_set<'tcx>(tcx: TyCtxt<'tcx>, (): ()) -> FxHashSet<LocalDefId> { |
2b03887a | 368 | let effective_visibilities = &tcx.effective_visibilities(()); |
92a42be0 | 369 | |
f9f354fc XL |
370 | let any_library = |
371 | tcx.sess.crate_types().iter().any(|ty| { | |
372 | *ty == CrateType::Rlib || *ty == CrateType::Dylib || *ty == CrateType::ProcMacro | |
373 | }); | |
32a655c1 | 374 | let mut reachable_context = ReachableContext { |
041b39d2 | 375 | tcx, |
3dfed10e | 376 | maybe_typeck_results: None, |
a1dfa0c6 | 377 | reachable_symbols: Default::default(), |
32a655c1 | 378 | worklist: Vec::new(), |
041b39d2 | 379 | any_library, |
32a655c1 | 380 | }; |
1a4d82fc JJ |
381 | |
382 | // Step 1: Seed the worklist with all nodes which were found to be public as | |
92a42be0 SL |
383 | // a result of the privacy pass along with all local lang items and impl items. |
384 | // If other crates link to us, they're going to expect to be able to | |
1a4d82fc JJ |
385 | // use the lang items, so we need to be sure to mark them as |
386 | // exported. | |
2b03887a FG |
387 | reachable_context.worklist = effective_visibilities |
388 | .iter() | |
389 | .filter_map(|(&id, effective_vis)| { | |
390 | effective_vis.is_public_at_level(Level::ReachableThroughImplTrait).then_some(id) | |
391 | }) | |
392 | .collect::<Vec<_>>(); | |
393 | ||
487cf647 FG |
394 | for (_, def_id) in tcx.lang_items().iter() { |
395 | if let Some(def_id) = def_id.as_local() { | |
396 | reachable_context.worklist.push(def_id); | |
1a4d82fc JJ |
397 | } |
398 | } | |
92a42be0 | 399 | { |
923072b8 FG |
400 | // Some methods from non-exported (completely private) trait impls still have to be |
401 | // reachable if they are called from inlinable code. Generally, it's not known until | |
402 | // monomorphization if a specific trait impl item can be reachable or not. So, we | |
403 | // conservatively mark all of them as reachable. | |
404 | // FIXME: One possible strategy for pruning the reachable set is to avoid marking impl | |
405 | // items of non-exported traits (or maybe all local traits?) unless their respective | |
406 | // trait items are used from inlinable code through method call syntax or UFCS, or their | |
407 | // trait is a lang item. | |
408 | let crate_items = tcx.hir_crate_items(()); | |
409 | ||
410 | for id in crate_items.items() { | |
2b03887a | 411 | check_item(tcx, id, &mut reachable_context.worklist, effective_visibilities); |
923072b8 FG |
412 | } |
413 | ||
414 | for id in crate_items.impl_items() { | |
2b03887a FG |
415 | if has_custom_linkage(tcx, id.owner_id.def_id) { |
416 | reachable_context.worklist.push(id.owner_id.def_id); | |
923072b8 FG |
417 | } |
418 | } | |
92a42be0 | 419 | } |
970d7e83 LB |
420 | |
421 | // Step 2: Mark all symbols that the symbols on the worklist touch. | |
422 | reachable_context.propagate(); | |
423 | ||
b7449926 XL |
424 | debug!("Inline reachability shows: {:?}", reachable_context.reachable_symbols); |
425 | ||
970d7e83 | 426 | // Return the set of reachable symbols. |
3dfed10e | 427 | reachable_context.reachable_symbols |
cc61c64b XL |
428 | } |
429 | ||
f035d41b | 430 | pub fn provide(providers: &mut Providers) { |
dfeec247 | 431 | *providers = Providers { reachable_set, ..*providers }; |
970d7e83 | 432 | } |