|
8 | 8 | // option. This file may not be copied, modified, or distributed
|
9 | 9 | // except according to those terms.
|
10 | 10 |
|
| 11 | +//! The code in this module gathers up all of the inherent impls in |
| 12 | +//! the current crate and organizes them in a map. It winds up |
| 13 | +//! touching the whole crate and thus must be recomputed completely |
| 14 | +//! for any change, but it is very cheap to compute. In practice, most |
| 15 | +//! code in the compiler never *directly* requests this map. Instead, |
| 16 | +//! it requests the inherent impls specific to some type (via |
| 17 | +//! `ty::queries::inherent_impls::get(def_id)`). That value, however, |
| 18 | +//! is computed by selecting an idea from this table. |
| 19 | +
|
11 | 20 | use rustc::dep_graph::DepNode;
|
12 |
| -use rustc::hir::def_id::DefId; |
| 21 | +use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; |
13 | 22 | use rustc::hir;
|
14 | 23 | use rustc::hir::itemlikevisit::ItemLikeVisitor;
|
15 |
| -use rustc::lint; |
16 |
| -use rustc::traits::{self, Reveal}; |
17 |
| -use rustc::ty::{self, TyCtxt}; |
| 24 | +use rustc::ty::{self, CrateInherentImpls, TyCtxt}; |
| 25 | +use rustc::util::nodemap::DefIdMap; |
18 | 26 |
|
| 27 | +use std::rc::Rc; |
19 | 28 | use syntax::ast;
|
20 |
| -use syntax_pos::Span; |
| 29 | +use syntax_pos::{DUMMY_SP, Span}; |
| 30 | + |
| 31 | +/// On-demand query: yields a map containing all types mapped to their inherent impls. |
| 32 | +pub fn crate_inherent_impls<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, |
| 33 | + crate_num: CrateNum) |
| 34 | + -> CrateInherentImpls { |
| 35 | + assert_eq!(crate_num, LOCAL_CRATE); |
| 36 | + |
| 37 | + let krate = tcx.hir.krate(); |
| 38 | + let mut collect = InherentCollect { |
| 39 | + tcx, |
| 40 | + impls_map: CrateInherentImpls { |
| 41 | + inherent_impls: DefIdMap() |
| 42 | + } |
| 43 | + }; |
| 44 | + krate.visit_all_item_likes(&mut collect); |
| 45 | + collect.impls_map |
| 46 | +} |
| 47 | + |
| 48 | +/// On-demand query: yields a vector of the inherent impls for a specific type. |
| 49 | +pub fn inherent_impls<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, |
| 50 | + ty_def_id: DefId) |
| 51 | + -> Rc<Vec<DefId>> { |
| 52 | + assert!(ty_def_id.is_local()); |
| 53 | + |
| 54 | + // NB. Until we adopt the red-green dep-tracking algorithm (see |
| 55 | + // [the plan] for details on that), we do some hackery here to get |
| 56 | + // the dependencies correct. Basically, we use a `with_ignore` to |
| 57 | + // read the result we want. If we didn't have the `with_ignore`, |
| 58 | + // we would wind up with a dependency on the entire crate, which |
| 59 | + // we don't want. Then we go and add dependencies on all the impls |
| 60 | + // in the result (which is what we wanted). |
| 61 | + // |
| 62 | + // The result is a graph with an edge from `Hir(I)` for every impl |
| 63 | + // `I` defined on some type `T` to `CoherentInherentImpls(T)`, |
| 64 | + // thus ensuring that if any of those impls change, the set of |
| 65 | + // inherent impls is considered dirty. |
| 66 | + // |
| 67 | + // [the plan]: https://github.com/rust-lang/rust-roadmap/issues/4 |
| 68 | + |
| 69 | + let result = tcx.dep_graph.with_ignore(|| { |
| 70 | + let crate_map = ty::queries::crate_inherent_impls::get(tcx, DUMMY_SP, ty_def_id.krate); |
| 71 | + match crate_map.inherent_impls.get(&ty_def_id) { |
| 72 | + Some(v) => v.clone(), |
| 73 | + None => Rc::new(vec![]), |
| 74 | + } |
| 75 | + }); |
| 76 | + |
| 77 | + for &impl_def_id in &result[..] { |
| 78 | + tcx.dep_graph.read(DepNode::Hir(impl_def_id)); |
| 79 | + } |
| 80 | + |
| 81 | + result |
| 82 | +} |
21 | 83 |
|
22 | 84 | struct InherentCollect<'a, 'tcx: 'a> {
|
23 |
| - tcx: TyCtxt<'a, 'tcx, 'tcx> |
| 85 | + tcx: TyCtxt<'a, 'tcx, 'tcx>, |
| 86 | + impls_map: CrateInherentImpls, |
24 | 87 | }
|
25 | 88 |
|
26 | 89 | impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> {
|
@@ -209,25 +272,19 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> {
|
209 | 272 | }
|
210 | 273 |
|
211 | 274 | impl<'a, 'tcx> InherentCollect<'a, 'tcx> {
|
212 |
| - fn check_def_id(&self, item: &hir::Item, def_id: DefId) { |
| 275 | + fn check_def_id(&mut self, item: &hir::Item, def_id: DefId) { |
213 | 276 | if def_id.is_local() {
|
214 | 277 | // Add the implementation to the mapping from implementation to base
|
215 | 278 | // type def ID, if there is a base type for this implementation and
|
216 | 279 | // the implementation does not have any associated traits.
|
217 | 280 | let impl_def_id = self.tcx.hir.local_def_id(item.id);
|
| 281 | + let mut rc_vec = self.impls_map.inherent_impls |
| 282 | + .entry(def_id) |
| 283 | + .or_insert_with(|| Rc::new(vec![])); |
218 | 284 |
|
219 |
| - // Subtle: it'd be better to collect these into a local map |
220 |
| - // and then write the vector only once all items are known, |
221 |
| - // but that leads to degenerate dep-graphs. The problem is |
222 |
| - // that the write of that big vector winds up having reads |
223 |
| - // from *all* impls in the krate, since we've lost the |
224 |
| - // precision basically. This would be ok in the firewall |
225 |
| - // model so once we've made progess towards that we can modify |
226 |
| - // the strategy here. In the meantime, using `push` is ok |
227 |
| - // because we are doing this as a pre-pass before anyone |
228 |
| - // actually reads from `inherent_impls` -- and we know this is |
229 |
| - // true beacuse we hold the refcell lock. |
230 |
| - self.tcx.maps.inherent_impls.borrow_mut().push(def_id, impl_def_id); |
| 285 | + // At this point, there should not be any clones of the |
| 286 | + // `Rc`, so we can still safely push into it in place: |
| 287 | + Rc::get_mut(&mut rc_vec).unwrap().push(impl_def_id); |
231 | 288 | } else {
|
232 | 289 | struct_span_err!(self.tcx.sess,
|
233 | 290 | item.span,
|
@@ -266,91 +323,3 @@ impl<'a, 'tcx> InherentCollect<'a, 'tcx> {
|
266 | 323 | }
|
267 | 324 | }
|
268 | 325 |
|
269 |
| -struct InherentOverlapChecker<'a, 'tcx: 'a> { |
270 |
| - tcx: TyCtxt<'a, 'tcx, 'tcx> |
271 |
| -} |
272 |
| - |
273 |
| -impl<'a, 'tcx> InherentOverlapChecker<'a, 'tcx> { |
274 |
| - fn check_for_common_items_in_impls(&self, impl1: DefId, impl2: DefId) { |
275 |
| - #[derive(Copy, Clone, PartialEq)] |
276 |
| - enum Namespace { |
277 |
| - Type, |
278 |
| - Value, |
279 |
| - } |
280 |
| - |
281 |
| - let name_and_namespace = |def_id| { |
282 |
| - let item = self.tcx.associated_item(def_id); |
283 |
| - (item.name, match item.kind { |
284 |
| - ty::AssociatedKind::Type => Namespace::Type, |
285 |
| - ty::AssociatedKind::Const | |
286 |
| - ty::AssociatedKind::Method => Namespace::Value, |
287 |
| - }) |
288 |
| - }; |
289 |
| - |
290 |
| - let impl_items1 = self.tcx.associated_item_def_ids(impl1); |
291 |
| - let impl_items2 = self.tcx.associated_item_def_ids(impl2); |
292 |
| - |
293 |
| - for &item1 in &impl_items1[..] { |
294 |
| - let (name, namespace) = name_and_namespace(item1); |
295 |
| - |
296 |
| - for &item2 in &impl_items2[..] { |
297 |
| - if (name, namespace) == name_and_namespace(item2) { |
298 |
| - let msg = format!("duplicate definitions with name `{}`", name); |
299 |
| - let node_id = self.tcx.hir.as_local_node_id(item1).unwrap(); |
300 |
| - self.tcx.sess.add_lint(lint::builtin::OVERLAPPING_INHERENT_IMPLS, |
301 |
| - node_id, |
302 |
| - self.tcx.span_of_impl(item1).unwrap(), |
303 |
| - msg); |
304 |
| - } |
305 |
| - } |
306 |
| - } |
307 |
| - } |
308 |
| - |
309 |
| - fn check_for_overlapping_inherent_impls(&self, ty_def_id: DefId) { |
310 |
| - let _task = self.tcx.dep_graph.in_task(DepNode::CoherenceOverlapInherentCheck(ty_def_id)); |
311 |
| - |
312 |
| - let inherent_impls = self.tcx.maps.inherent_impls.borrow(); |
313 |
| - let impls = match inherent_impls.get(&ty_def_id) { |
314 |
| - Some(impls) => impls, |
315 |
| - None => return, |
316 |
| - }; |
317 |
| - |
318 |
| - for (i, &impl1_def_id) in impls.iter().enumerate() { |
319 |
| - for &impl2_def_id in &impls[(i + 1)..] { |
320 |
| - self.tcx.infer_ctxt((), Reveal::UserFacing).enter(|infcx| { |
321 |
| - if traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id).is_some() { |
322 |
| - self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id) |
323 |
| - } |
324 |
| - }); |
325 |
| - } |
326 |
| - } |
327 |
| - } |
328 |
| -} |
329 |
| - |
330 |
| -impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentOverlapChecker<'a, 'tcx> { |
331 |
| - fn visit_item(&mut self, item: &'v hir::Item) { |
332 |
| - match item.node { |
333 |
| - hir::ItemEnum(..) | |
334 |
| - hir::ItemStruct(..) | |
335 |
| - hir::ItemTrait(..) | |
336 |
| - hir::ItemUnion(..) => { |
337 |
| - let type_def_id = self.tcx.hir.local_def_id(item.id); |
338 |
| - self.check_for_overlapping_inherent_impls(type_def_id); |
339 |
| - } |
340 |
| - _ => {} |
341 |
| - } |
342 |
| - } |
343 |
| - |
344 |
| - fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem) { |
345 |
| - } |
346 |
| - |
347 |
| - fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem) { |
348 |
| - } |
349 |
| -} |
350 |
| - |
351 |
| -pub fn check<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { |
352 |
| - tcx.visit_all_item_likes_in_krate(DepNode::CoherenceCheckImpl, |
353 |
| - &mut InherentCollect { tcx }); |
354 |
| - tcx.visit_all_item_likes_in_krate(DepNode::CoherenceOverlapCheckSpecial, |
355 |
| - &mut InherentOverlapChecker { tcx }); |
356 |
| -} |
0 commit comments