convert inherent-impl-related things to on-demand queries
There are now 3 queries: - inherent_impls(def-id): for a given type, get a `Rc<Vec<DefId>>` with all its inherent impls. This internally uses `crate_inherent_impls`, doing some hacks to keep the current deps (which, btw, are not clearly correct). - crate_inherent_impls(crate): gathers up a map from types to `Rc<Vec<DefId>>`, touching the entire krate, possibly generating errors. - crate_inherent_impls_overlap_check(crate): performs overlap checks between the inherent impls for a given type, generating errors.
This commit is contained in:
parent
8e6b10a6cb
commit
a29ae3052a
11 changed files with 227 additions and 186 deletions
|
|
@ -74,7 +74,6 @@ pub enum DepNode<D: Clone + Debug> {
|
|||
CoherenceCheckImpl(D),
|
||||
CoherenceOverlapCheck(D),
|
||||
CoherenceOverlapCheckSpecial(D),
|
||||
CoherenceOverlapInherentCheck(D),
|
||||
CoherenceOrphanCheck(D),
|
||||
Variance,
|
||||
WfCheck(D),
|
||||
|
|
@ -251,7 +250,6 @@ impl<D: Clone + Debug> DepNode<D> {
|
|||
CoherenceCheckImpl(ref d) => op(d).map(CoherenceCheckImpl),
|
||||
CoherenceOverlapCheck(ref d) => op(d).map(CoherenceOverlapCheck),
|
||||
CoherenceOverlapCheckSpecial(ref d) => op(d).map(CoherenceOverlapCheckSpecial),
|
||||
CoherenceOverlapInherentCheck(ref d) => op(d).map(CoherenceOverlapInherentCheck),
|
||||
CoherenceOrphanCheck(ref d) => op(d).map(CoherenceOrphanCheck),
|
||||
WfCheck(ref d) => op(d).map(WfCheck),
|
||||
TypeckItemType(ref d) => op(d).map(TypeckItemType),
|
||||
|
|
|
|||
|
|
@ -81,21 +81,6 @@ impl<M: DepTrackingMapConfig> DepTrackingMap<M> {
|
|||
pub fn keys(&self) -> Vec<M::Key> {
|
||||
self.map.keys().cloned().collect()
|
||||
}
|
||||
|
||||
/// Append `elem` to the vector stored for `k`, creating a new vector if needed.
|
||||
/// This is considered a write to `k`.
|
||||
///
|
||||
/// NOTE: Caution is required when using this method. You should
|
||||
/// be sure that nobody is **reading from the vector** while you
|
||||
/// are writing to it. Eventually, it'd be nice to remove this.
|
||||
pub fn push<E: Clone>(&mut self, k: M::Key, elem: E)
|
||||
where M: DepTrackingMapConfig<Value=Vec<E>>
|
||||
{
|
||||
self.write(&k);
|
||||
self.map.entry(k)
|
||||
.or_insert(Vec::new())
|
||||
.push(elem);
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: DepTrackingMapConfig> MemoizationMap for RefCell<DepTrackingMap<M>> {
|
||||
|
|
|
|||
|
|
@ -176,7 +176,6 @@ pub trait CrateStore {
|
|||
fn item_generics_cloned(&self, def: DefId) -> ty::Generics;
|
||||
fn item_attrs(&self, def_id: DefId) -> Vec<ast::Attribute>;
|
||||
fn fn_arg_names(&self, did: DefId) -> Vec<ast::Name>;
|
||||
fn inherent_implementations_for_type(&self, def_id: DefId) -> Vec<DefId>;
|
||||
|
||||
// trait info
|
||||
fn implementations_of_trait(&self, filter: Option<DefId>) -> Vec<DefId>;
|
||||
|
|
@ -310,7 +309,6 @@ impl CrateStore for DummyCrateStore {
|
|||
{ bug!("item_generics_cloned") }
|
||||
fn item_attrs(&self, def_id: DefId) -> Vec<ast::Attribute> { bug!("item_attrs") }
|
||||
fn fn_arg_names(&self, did: DefId) -> Vec<ast::Name> { bug!("fn_arg_names") }
|
||||
fn inherent_implementations_for_type(&self, def_id: DefId) -> Vec<DefId> { vec![] }
|
||||
|
||||
// trait info
|
||||
fn implementations_of_trait(&self, filter: Option<DefId>) -> Vec<DefId> { vec![] }
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use dep_graph::{DepGraph, DepNode, DepTrackingMap, DepTrackingMapConfig};
|
|||
use hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
|
||||
use middle::const_val::ConstVal;
|
||||
use mir;
|
||||
use ty::{self, Ty, TyCtxt};
|
||||
use ty::{self, CrateInherentImpls, Ty, TyCtxt};
|
||||
|
||||
use rustc_data_structures::indexed_vec::IndexVec;
|
||||
use std::cell::{RefCell, RefMut};
|
||||
|
|
@ -176,9 +176,15 @@ impl<'tcx> QueryDescription for queries::coherent_trait<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> QueryDescription for queries::coherent_inherent_impls<'tcx> {
|
||||
impl<'tcx> QueryDescription for queries::crate_inherent_impls<'tcx> {
|
||||
fn describe(_: TyCtxt, k: CrateNum) -> String {
|
||||
format!("all inherent impls defined in crate `{:?}`", k)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> QueryDescription for queries::crate_inherent_impls_overlap_check<'tcx> {
|
||||
fn describe(_: TyCtxt, _: CrateNum) -> String {
|
||||
format!("coherence checking all inherent impls")
|
||||
format!("check for overlap between inherent impls defined in this crate")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -368,7 +374,7 @@ define_maps! { <'tcx>
|
|||
/// Maps a DefId of a type to a list of its inherent impls.
|
||||
/// Contains implementations of methods that are inherent to a type.
|
||||
/// Methods in these implementations don't need to be exported.
|
||||
pub inherent_impls: InherentImpls(DefId) -> Vec<DefId>,
|
||||
pub inherent_impls: InherentImpls(DefId) -> Rc<Vec<DefId>>,
|
||||
|
||||
/// Maps from the def-id of a function/method or const/static
|
||||
/// to its MIR. Mutation is done at an item granularity to
|
||||
|
|
@ -400,7 +406,15 @@ define_maps! { <'tcx>
|
|||
|
||||
pub coherent_trait: coherent_trait_dep_node((CrateNum, DefId)) -> (),
|
||||
|
||||
pub coherent_inherent_impls: coherent_inherent_impls_dep_node(CrateNum) -> (),
|
||||
/// Gets a complete map from all types to their inherent impls.
|
||||
/// Not meant to be used directly outside of coherence.
|
||||
/// (Defined only for LOCAL_CRATE)
|
||||
pub crate_inherent_impls: crate_inherent_impls_dep_node(CrateNum) -> CrateInherentImpls,
|
||||
|
||||
/// Checks all types in the krate for overlap in their inherent impls. Reports errors.
|
||||
/// Not meant to be used directly outside of coherence.
|
||||
/// (Defined only for LOCAL_CRATE)
|
||||
pub crate_inherent_impls_overlap_check: crate_inherent_impls_dep_node(CrateNum) -> (),
|
||||
|
||||
/// Results of evaluating monomorphic constants embedded in
|
||||
/// other items, such as enum variant explicit discriminants.
|
||||
|
|
@ -413,7 +427,7 @@ fn coherent_trait_dep_node((_, def_id): (CrateNum, DefId)) -> DepNode<DefId> {
|
|||
DepNode::CoherenceCheckTrait(def_id)
|
||||
}
|
||||
|
||||
fn coherent_inherent_impls_dep_node(_: CrateNum) -> DepNode<DefId> {
|
||||
fn crate_inherent_impls_dep_node(_: CrateNum) -> DepNode<DefId> {
|
||||
DepNode::Coherence
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ use ty::subst::{Subst, Substs};
|
|||
use ty::util::IntTypeExt;
|
||||
use ty::walk::TypeWalker;
|
||||
use util::common::MemoizationMap;
|
||||
use util::nodemap::{NodeSet, FxHashMap};
|
||||
use util::nodemap::{NodeSet, DefIdMap, FxHashMap};
|
||||
|
||||
use serialize::{self, Encodable, Encoder};
|
||||
use std::borrow::Cow;
|
||||
|
|
@ -2345,34 +2345,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
|
|||
def.flags.get().intersects(TraitFlags::HAS_DEFAULT_IMPL)
|
||||
}
|
||||
|
||||
/// Populates the type context with all the inherent implementations for
|
||||
/// the given type if necessary.
|
||||
pub fn populate_inherent_implementations_for_type_if_necessary(self,
|
||||
span: Span,
|
||||
type_id: DefId) {
|
||||
if type_id.is_local() {
|
||||
// Make sure coherence of inherent impls ran already.
|
||||
ty::queries::coherent_inherent_impls::force(self, span, LOCAL_CRATE);
|
||||
return
|
||||
}
|
||||
|
||||
// The type is not local, hence we are reading this out of
|
||||
// metadata and don't need to track edges.
|
||||
let _ignore = self.dep_graph.in_ignore();
|
||||
|
||||
if self.populated_external_types.borrow().contains(&type_id) {
|
||||
return
|
||||
}
|
||||
|
||||
debug!("populate_inherent_implementations_for_type_if_necessary: searching for {:?}",
|
||||
type_id);
|
||||
|
||||
let inherent_impls = self.sess.cstore.inherent_implementations_for_type(type_id);
|
||||
|
||||
self.maps.inherent_impls.borrow_mut().insert(type_id, inherent_impls);
|
||||
self.populated_external_types.borrow_mut().insert(type_id);
|
||||
}
|
||||
|
||||
/// Populates the type context with all the implementations for the given
|
||||
/// trait if necessary.
|
||||
pub fn populate_implementations_for_trait_if_necessary(self, trait_id: DefId) {
|
||||
|
|
@ -2637,3 +2609,16 @@ pub fn provide(providers: &mut ty::maps::Providers) {
|
|||
..*providers
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// A map for the local crate mapping each type to a vector of its
|
||||
/// inherent impls. This is not meant to be used outside of coherence;
|
||||
/// rather, you should request the vector for a specific type via
|
||||
/// `ty::queries::inherent_impls::get(def_id)` so as to minimize your
|
||||
/// dependencies (constructing this map requires touching the entire
|
||||
/// crate).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CrateInherentImpls {
|
||||
pub inherent_impls: DefIdMap<Rc<Vec<DefId>>>,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ provide! { <'tcx> tcx, def_id, cdata
|
|||
typeck_tables => { cdata.item_body_tables(def_id.index, tcx) }
|
||||
closure_kind => { cdata.closure_kind(def_id.index) }
|
||||
closure_type => { cdata.closure_ty(def_id.index, tcx) }
|
||||
inherent_impls => { Rc::new(cdata.get_inherent_implementations_for_type(def_id.index)) }
|
||||
}
|
||||
|
||||
impl CrateStore for cstore::CStore {
|
||||
|
|
@ -162,12 +163,6 @@ impl CrateStore for cstore::CStore {
|
|||
self.get_crate_data(did.krate).get_fn_arg_names(did.index)
|
||||
}
|
||||
|
||||
fn inherent_implementations_for_type(&self, def_id: DefId) -> Vec<DefId>
|
||||
{
|
||||
self.dep_graph.read(DepNode::MetaData(def_id));
|
||||
self.get_crate_data(def_id.krate).get_inherent_implementations_for_type(def_id.index)
|
||||
}
|
||||
|
||||
fn implementations_of_trait(&self, filter: Option<DefId>) -> Vec<DefId>
|
||||
{
|
||||
if let Some(def_id) = filter {
|
||||
|
|
|
|||
|
|
@ -479,14 +479,9 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
|
||||
fn assemble_inherent_impl_candidates_for_type(&mut self, def_id: DefId) {
|
||||
// Read the inherent implementation candidates for this type from the
|
||||
// metadata if necessary.
|
||||
self.tcx.populate_inherent_implementations_for_type_if_necessary(self.span, def_id);
|
||||
|
||||
if let Some(impl_infos) = self.tcx.maps.inherent_impls.borrow().get(&def_id) {
|
||||
for &impl_def_id in impl_infos.iter() {
|
||||
self.assemble_inherent_impl_probe(impl_def_id);
|
||||
}
|
||||
let impl_def_ids = ty::queries::inherent_impls::get(self.tcx, self.span, def_id);
|
||||
for &impl_def_id in impl_def_ids.iter() {
|
||||
self.assemble_inherent_impl_probe(impl_def_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,26 +8,82 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! The code in this module gathers up all of the inherent impls in
|
||||
//! the current crate and organizes them in a map. It winds up
|
||||
//! touching the whole crate and thus must be recomputed completely
|
||||
//! for any change, but it is very cheap to compute. In practice, most
|
||||
//! code in the compiler never *directly* requests this map. Instead,
|
||||
//! it requests the inherent impls specific to some type (via
|
||||
//! `ty::queries::inherent_impls::get(def_id)`). That value, however,
|
||||
//! is computed by selecting an idea from this table.
|
||||
|
||||
use rustc::dep_graph::DepNode;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
|
||||
use rustc::hir;
|
||||
use rustc::hir::itemlikevisit::ItemLikeVisitor;
|
||||
use rustc::lint;
|
||||
use rustc::traits::{self, Reveal};
|
||||
use rustc::ty::{self, TyCtxt};
|
||||
use rustc::ty::{self, CrateInherentImpls, TyCtxt};
|
||||
use rustc::util::nodemap::DefIdMap;
|
||||
|
||||
use std::rc::Rc;
|
||||
use syntax::ast;
|
||||
use syntax_pos::Span;
|
||||
use syntax_pos::{DUMMY_SP, Span};
|
||||
|
||||
pub fn check<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
|
||||
tcx.visit_all_item_likes_in_krate(DepNode::CoherenceCheckImpl,
|
||||
&mut InherentCollect { tcx });
|
||||
tcx.visit_all_item_likes_in_krate(DepNode::CoherenceOverlapCheckSpecial,
|
||||
&mut InherentOverlapChecker { tcx });
|
||||
/// On-demand query: yields a map containing all types mapped to their inherent impls.
|
||||
pub fn crate_inherent_impls<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
crate_num: CrateNum)
|
||||
-> CrateInherentImpls {
|
||||
assert_eq!(crate_num, LOCAL_CRATE);
|
||||
|
||||
let krate = tcx.hir.krate();
|
||||
let mut collect = InherentCollect {
|
||||
tcx,
|
||||
impls_map: CrateInherentImpls {
|
||||
inherent_impls: DefIdMap()
|
||||
}
|
||||
};
|
||||
krate.visit_all_item_likes(&mut collect);
|
||||
collect.impls_map
|
||||
}
|
||||
|
||||
/// On-demand query: yields a vector of the inherent impls for a specific type.
|
||||
pub fn inherent_impls<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
ty_def_id: DefId)
|
||||
-> Rc<Vec<DefId>> {
|
||||
assert!(ty_def_id.is_local());
|
||||
|
||||
// NB. Until we adopt the red-green dep-tracking algorithm (see
|
||||
// [the plan] for details on that), we do some hackery here to get
|
||||
// the dependencies correct. Basically, we use a `with_ignore` to
|
||||
// read the result we want. If we didn't have the `with_ignore`,
|
||||
// we would wind up with a dependency on the entire crate, which
|
||||
// we don't want. Then we go and add dependencies on all the impls
|
||||
// in the result (which is what we wanted).
|
||||
//
|
||||
// The result is a graph with an edge from `Hir(I)` for every impl
|
||||
// `I` defined on some type `T` to `CoherentInherentImpls(T)`,
|
||||
// thus ensuring that if any of those impls change, the set of
|
||||
// inherent impls is considered dirty.
|
||||
//
|
||||
// [the plan]: https://github.com/rust-lang/rust-roadmap/issues/4
|
||||
|
||||
let result = tcx.dep_graph.with_ignore(|| {
|
||||
let crate_map = ty::queries::crate_inherent_impls::get(tcx, DUMMY_SP, ty_def_id.krate);
|
||||
match crate_map.inherent_impls.get(&ty_def_id) {
|
||||
Some(v) => v.clone(),
|
||||
None => Rc::new(vec![]),
|
||||
}
|
||||
});
|
||||
|
||||
for &impl_def_id in &result[..] {
|
||||
tcx.dep_graph.read(DepNode::Hir(impl_def_id));
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
struct InherentCollect<'a, 'tcx: 'a> {
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
impls_map: CrateInherentImpls,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> {
|
||||
|
|
@ -216,25 +272,19 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> {
|
|||
}
|
||||
|
||||
impl<'a, 'tcx> InherentCollect<'a, 'tcx> {
|
||||
fn check_def_id(&self, item: &hir::Item, def_id: DefId) {
|
||||
fn check_def_id(&mut self, item: &hir::Item, def_id: DefId) {
|
||||
if def_id.is_local() {
|
||||
// Add the implementation to the mapping from implementation to base
|
||||
// type def ID, if there is a base type for this implementation and
|
||||
// the implementation does not have any associated traits.
|
||||
let impl_def_id = self.tcx.hir.local_def_id(item.id);
|
||||
let mut rc_vec = self.impls_map.inherent_impls
|
||||
.entry(def_id)
|
||||
.or_insert_with(|| Rc::new(vec![]));
|
||||
|
||||
// Subtle: it'd be better to collect these into a local map
|
||||
// and then write the vector only once all items are known,
|
||||
// but that leads to degenerate dep-graphs. The problem is
|
||||
// that the write of that big vector winds up having reads
|
||||
// from *all* impls in the krate, since we've lost the
|
||||
// precision basically. This would be ok in the firewall
|
||||
// model so once we've made progess towards that we can modify
|
||||
// the strategy here. In the meantime, using `push` is ok
|
||||
// because we are doing this as a pre-pass before anyone
|
||||
// actually reads from `inherent_impls` -- and we know this is
|
||||
// true beacuse we hold the refcell lock.
|
||||
self.tcx.maps.inherent_impls.borrow_mut().push(def_id, impl_def_id);
|
||||
// At this point, there should not be any clones of the
|
||||
// `Rc`, so we can still safely push into it in place:
|
||||
Rc::get_mut(&mut rc_vec).unwrap().push(impl_def_id);
|
||||
} else {
|
||||
struct_span_err!(self.tcx.sess,
|
||||
item.span,
|
||||
|
|
@ -273,85 +323,3 @@ impl<'a, 'tcx> InherentCollect<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
struct InherentOverlapChecker<'a, 'tcx: 'a> {
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> InherentOverlapChecker<'a, 'tcx> {
|
||||
fn check_for_common_items_in_impls(&self, impl1: DefId, impl2: DefId) {
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
enum Namespace {
|
||||
Type,
|
||||
Value,
|
||||
}
|
||||
|
||||
let name_and_namespace = |def_id| {
|
||||
let item = self.tcx.associated_item(def_id);
|
||||
(item.name, match item.kind {
|
||||
ty::AssociatedKind::Type => Namespace::Type,
|
||||
ty::AssociatedKind::Const |
|
||||
ty::AssociatedKind::Method => Namespace::Value,
|
||||
})
|
||||
};
|
||||
|
||||
let impl_items1 = self.tcx.associated_item_def_ids(impl1);
|
||||
let impl_items2 = self.tcx.associated_item_def_ids(impl2);
|
||||
|
||||
for &item1 in &impl_items1[..] {
|
||||
let (name, namespace) = name_and_namespace(item1);
|
||||
|
||||
for &item2 in &impl_items2[..] {
|
||||
if (name, namespace) == name_and_namespace(item2) {
|
||||
let msg = format!("duplicate definitions with name `{}`", name);
|
||||
let node_id = self.tcx.hir.as_local_node_id(item1).unwrap();
|
||||
self.tcx.sess.add_lint(lint::builtin::OVERLAPPING_INHERENT_IMPLS,
|
||||
node_id,
|
||||
self.tcx.span_of_impl(item1).unwrap(),
|
||||
msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_for_overlapping_inherent_impls(&self, ty_def_id: DefId) {
|
||||
let _task = self.tcx.dep_graph.in_task(DepNode::CoherenceOverlapInherentCheck(ty_def_id));
|
||||
|
||||
let inherent_impls = self.tcx.maps.inherent_impls.borrow();
|
||||
let impls = match inherent_impls.get(&ty_def_id) {
|
||||
Some(impls) => impls,
|
||||
None => return,
|
||||
};
|
||||
|
||||
for (i, &impl1_def_id) in impls.iter().enumerate() {
|
||||
for &impl2_def_id in &impls[(i + 1)..] {
|
||||
self.tcx.infer_ctxt((), Reveal::UserFacing).enter(|infcx| {
|
||||
if traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id).is_some() {
|
||||
self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentOverlapChecker<'a, 'tcx> {
|
||||
fn visit_item(&mut self, item: &'v hir::Item) {
|
||||
match item.node {
|
||||
hir::ItemEnum(..) |
|
||||
hir::ItemStruct(..) |
|
||||
hir::ItemTrait(..) |
|
||||
hir::ItemUnion(..) => {
|
||||
let type_def_id = self.tcx.hir.local_def_id(item.id);
|
||||
self.check_for_overlapping_inherent_impls(type_def_id);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem) {
|
||||
}
|
||||
|
||||
fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem) {
|
||||
}
|
||||
}
|
||||
|
||||
102
src/librustc_typeck/coherence/inherent_impls_overlap.rs
Normal file
102
src/librustc_typeck/coherence/inherent_impls_overlap.rs
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
|
||||
use rustc::hir;
|
||||
use rustc::hir::itemlikevisit::ItemLikeVisitor;
|
||||
use rustc::lint;
|
||||
use rustc::traits::{self, Reveal};
|
||||
use rustc::ty::{self, TyCtxt};
|
||||
|
||||
use syntax_pos::DUMMY_SP;
|
||||
|
||||
pub fn crate_inherent_impls_overlap_check<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
crate_num: CrateNum) {
|
||||
assert_eq!(crate_num, LOCAL_CRATE);
|
||||
let krate = tcx.hir.krate();
|
||||
krate.visit_all_item_likes(&mut InherentOverlapChecker { tcx });
|
||||
}
|
||||
|
||||
struct InherentOverlapChecker<'a, 'tcx: 'a> {
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> InherentOverlapChecker<'a, 'tcx> {
|
||||
fn check_for_common_items_in_impls(&self, impl1: DefId, impl2: DefId) {
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
enum Namespace {
|
||||
Type,
|
||||
Value,
|
||||
}
|
||||
|
||||
let name_and_namespace = |def_id| {
|
||||
let item = self.tcx.associated_item(def_id);
|
||||
(item.name, match item.kind {
|
||||
ty::AssociatedKind::Type => Namespace::Type,
|
||||
ty::AssociatedKind::Const |
|
||||
ty::AssociatedKind::Method => Namespace::Value,
|
||||
})
|
||||
};
|
||||
|
||||
let impl_items1 = self.tcx.associated_item_def_ids(impl1);
|
||||
let impl_items2 = self.tcx.associated_item_def_ids(impl2);
|
||||
|
||||
for &item1 in &impl_items1[..] {
|
||||
let (name, namespace) = name_and_namespace(item1);
|
||||
|
||||
for &item2 in &impl_items2[..] {
|
||||
if (name, namespace) == name_and_namespace(item2) {
|
||||
let msg = format!("duplicate definitions with name `{}`", name);
|
||||
let node_id = self.tcx.hir.as_local_node_id(item1).unwrap();
|
||||
self.tcx.sess.add_lint(lint::builtin::OVERLAPPING_INHERENT_IMPLS,
|
||||
node_id,
|
||||
self.tcx.span_of_impl(item1).unwrap(),
|
||||
msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_for_overlapping_inherent_impls(&self, ty_def_id: DefId) {
|
||||
let impls = ty::queries::inherent_impls::get(self.tcx, DUMMY_SP, ty_def_id);
|
||||
|
||||
for (i, &impl1_def_id) in impls.iter().enumerate() {
|
||||
for &impl2_def_id in &impls[(i + 1)..] {
|
||||
self.tcx.infer_ctxt((), Reveal::UserFacing).enter(|infcx| {
|
||||
if traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id).is_some() {
|
||||
self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentOverlapChecker<'a, 'tcx> {
|
||||
fn visit_item(&mut self, item: &'v hir::Item) {
|
||||
match item.node {
|
||||
hir::ItemEnum(..) |
|
||||
hir::ItemStruct(..) |
|
||||
hir::ItemTrait(..) |
|
||||
hir::ItemUnion(..) => {
|
||||
let type_def_id = self.tcx.hir.local_def_id(item.id);
|
||||
self.check_for_overlapping_inherent_impls(type_def_id);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem) {
|
||||
}
|
||||
|
||||
fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem) {
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -24,7 +24,8 @@ use syntax::ast;
|
|||
use syntax_pos::DUMMY_SP;
|
||||
|
||||
mod builtin;
|
||||
mod inherent;
|
||||
mod inherent_impls;
|
||||
mod inherent_impls_overlap;
|
||||
mod orphan;
|
||||
mod overlap;
|
||||
mod unsafety;
|
||||
|
|
@ -103,10 +104,14 @@ fn enforce_trait_manually_implementable(tcx: TyCtxt, impl_def_id: DefId, trait_d
|
|||
|
||||
pub fn provide(providers: &mut Providers) {
|
||||
use self::builtin::coerce_unsized_info;
|
||||
use self::inherent_impls::{crate_inherent_impls, inherent_impls};
|
||||
use self::inherent_impls_overlap::crate_inherent_impls_overlap_check;
|
||||
|
||||
*providers = Providers {
|
||||
coherent_trait,
|
||||
coherent_inherent_impls,
|
||||
crate_inherent_impls,
|
||||
inherent_impls,
|
||||
crate_inherent_impls_overlap_check,
|
||||
coerce_unsized_info,
|
||||
..*providers
|
||||
};
|
||||
|
|
@ -126,10 +131,6 @@ fn coherent_trait<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||
builtin::check_trait(tcx, def_id);
|
||||
}
|
||||
|
||||
fn coherent_inherent_impls<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, _: CrateNum) {
|
||||
inherent::check(tcx);
|
||||
}
|
||||
|
||||
pub fn check_coherence<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
|
||||
let _task = tcx.dep_graph.in_task(DepNode::Coherence);
|
||||
for &trait_def_id in tcx.hir.krate().trait_impls.keys() {
|
||||
|
|
@ -140,5 +141,7 @@ pub fn check_coherence<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
|
|||
orphan::check(tcx);
|
||||
overlap::check_default_impls(tcx);
|
||||
|
||||
ty::queries::coherent_inherent_impls::get(tcx, DUMMY_SP, LOCAL_CRATE);
|
||||
// these queries are executed for side-effects (error reporting):
|
||||
ty::queries::crate_inherent_impls::get(tcx, DUMMY_SP, LOCAL_CRATE);
|
||||
ty::queries::crate_inherent_impls_overlap_check::get(tcx, DUMMY_SP, LOCAL_CRATE);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -232,14 +232,12 @@ fn build_type_alias(cx: &DocContext, did: DefId) -> clean::Typedef {
|
|||
|
||||
pub fn build_impls(cx: &DocContext, did: DefId) -> Vec<clean::Item> {
|
||||
let tcx = cx.tcx;
|
||||
tcx.populate_inherent_implementations_for_type_if_necessary(DUMMY_SP, did);
|
||||
let mut impls = Vec::new();
|
||||
|
||||
if let Some(i) = tcx.maps.inherent_impls.borrow().get(&did) {
|
||||
for &did in i.iter() {
|
||||
build_impl(cx, did, &mut impls);
|
||||
}
|
||||
for &did in ty::queries::inherent_impls::get(tcx, DUMMY_SP, did).iter() {
|
||||
build_impl(cx, did, &mut impls);
|
||||
}
|
||||
|
||||
// If this is the first time we've inlined something from another crate, then
|
||||
// we inline *all* impls from all the crates into this crate. Note that there's
|
||||
// currently no way for us to filter this based on type, and we likely need
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue