Initial incorporation of specialization:
- Rewrites the overlap checker to instead build up a specialization graph, checking for overlap errors in the process. - Use the specialization order during impl selection. This commit does not yet handle associated types correctly, and assumes that all items are `default` and are overridden.
This commit is contained in:
parent
c5849e4dff
commit
1f34086e94
@ -51,7 +51,7 @@ could invalidate work done for other items. So, for example:
|
||||
not shared state, because if it changes it does not itself
|
||||
invalidate other functions (though it may be that it causes new
|
||||
monomorphizations to occur, but that's handled independently).
|
||||
|
||||
|
||||
Put another way: if the HIR for an item changes, we are going to
|
||||
recompile that item for sure. But we need the dep tracking map to tell
|
||||
us what *else* we have to recompile. Shared state is anything that is
|
||||
@ -177,7 +177,7 @@ reads from `item`, there would be missing edges in the graph:
|
||||
| ^
|
||||
| |
|
||||
+---------------------------------+ // added by `visit_all_items_in_krate`
|
||||
|
||||
|
||||
In particular, the edge from `Hir(X)` to `ItemSignature(X)` is only
|
||||
present because we called `read` ourselves when entering the `ItemSignature(X)`
|
||||
task.
|
||||
@ -273,8 +273,8 @@ should not exist. In contrast, using the memoized helper, you get:
|
||||
... -> MapVariant(key) -> A
|
||||
|
|
||||
+----------> B
|
||||
|
||||
which is much cleaner.
|
||||
|
||||
which is much cleaner.
|
||||
|
||||
**Be aware though that the closure is executed with `MapVariant(key)`
|
||||
pushed onto the stack as the current task!** That means that you must
|
||||
@ -387,4 +387,3 @@ RUST_DEP_GRAPH_FILTER='Hir&foo -> TypeckItemBody & bar'
|
||||
This will dump out all the nodes that lead from `Hir(foo)` to
|
||||
`TypeckItemBody(bar)`, from which you can (hopefully) see the source
|
||||
of the erroneous edge.
|
||||
|
||||
|
@ -176,6 +176,7 @@ pub trait CrateStore<'tcx> : Any {
|
||||
-> Option<ty::adjustment::CustomCoerceUnsized>;
|
||||
fn associated_consts(&self, tcx: &TyCtxt<'tcx>, def: DefId)
|
||||
-> Vec<Rc<ty::AssociatedConst<'tcx>>>;
|
||||
fn impl_parent(&self, impl_def_id: DefId) -> Option<DefId>;
|
||||
|
||||
// trait/impl-item info
|
||||
fn trait_of_item(&self, tcx: &TyCtxt<'tcx>, def_id: DefId)
|
||||
@ -346,6 +347,7 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
|
||||
{ unimplemented!() }
|
||||
fn associated_consts(&self, tcx: &TyCtxt<'tcx>, def: DefId)
|
||||
-> Vec<Rc<ty::AssociatedConst<'tcx>>> { unimplemented!() }
|
||||
fn impl_parent(&self, def: DefId) -> Option<DefId> { unimplemented!() }
|
||||
|
||||
// trait/impl-item info
|
||||
fn trait_of_item(&self, tcx: &TyCtxt<'tcx>, def_id: DefId)
|
||||
|
@ -23,8 +23,8 @@ use syntax::codemap::DUMMY_SP;
|
||||
#[derive(Copy, Clone)]
|
||||
struct InferIsLocal(bool);
|
||||
|
||||
/// If there are types that satisfy both impls, returns an `ImplTy`
|
||||
/// with those types substituted (by updating the given `infcx`)
|
||||
/// If there are types that satisfy both impls, returns a suitably-freshened
|
||||
/// `ImplHeader` with those types substituted
|
||||
pub fn overlapping_impls<'cx, 'tcx>(infcx: &InferCtxt<'cx, 'tcx>,
|
||||
impl1_def_id: DefId,
|
||||
impl2_def_id: DefId)
|
||||
@ -85,7 +85,10 @@ fn overlap<'cx, 'tcx>(selcx: &mut SelectionContext<'cx, 'tcx>,
|
||||
return None
|
||||
}
|
||||
|
||||
Some(selcx.infcx().resolve_type_vars_if_possible(&a_impl_header))
|
||||
let substituted = selcx.infcx().resolve_type_vars_if_possible(&a_impl_header);
|
||||
let freshened = selcx.infcx().freshen(substituted);
|
||||
|
||||
Some(freshened)
|
||||
}
|
||||
|
||||
pub fn trait_ref_is_knowable<'tcx>(tcx: &TyCtxt<'tcx>, trait_ref: &ty::TraitRef<'tcx>) -> bool
|
||||
|
@ -50,6 +50,7 @@ pub use self::select::SelectionContext;
|
||||
pub use self::select::SelectionCache;
|
||||
pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch};
|
||||
pub use self::select::{MethodMatchedData}; // intentionally don't export variants
|
||||
pub use self::specialize::{Overlap, SpecializationGraph, specializes};
|
||||
pub use self::util::elaborate_predicates;
|
||||
pub use self::util::get_vtable_index_of_object_method;
|
||||
pub use self::util::trait_ref_for_builtin_bound;
|
||||
@ -67,6 +68,7 @@ mod fulfill;
|
||||
mod project;
|
||||
mod object_safety;
|
||||
mod select;
|
||||
mod specialize;
|
||||
mod structural_impls;
|
||||
mod util;
|
||||
|
||||
|
@ -40,6 +40,7 @@ use middle::infer;
|
||||
use middle::infer::{InferCtxt, TypeFreshener, TypeOrigin};
|
||||
use middle::subst::{Subst, Substs, TypeSpace};
|
||||
use middle::ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable};
|
||||
use middle::traits;
|
||||
use middle::ty::fast_reject;
|
||||
use middle::ty::relate::TypeRelation;
|
||||
|
||||
@ -224,6 +225,12 @@ struct SelectionCandidateSet<'tcx> {
|
||||
ambiguous: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq,Eq,Debug,Clone)]
|
||||
struct EvaluatedCandidate<'tcx> {
|
||||
candidate: SelectionCandidate<'tcx>,
|
||||
evaluation: EvaluationResult,
|
||||
}
|
||||
|
||||
enum BuiltinBoundConditions<'tcx> {
|
||||
If(ty::Binder<Vec<Ty<'tcx>>>),
|
||||
ParameterBuiltin,
|
||||
@ -746,6 +753,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
candidate
|
||||
}
|
||||
|
||||
// Treat negative impls as unimplemented
|
||||
fn filter_negative_impls(&self, candidate: SelectionCandidate<'tcx>)
|
||||
-> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
|
||||
if let ImplCandidate(def_id) = candidate {
|
||||
if self.tcx().trait_impl_polarity(def_id) == Some(hir::ImplPolarity::Negative) {
|
||||
return Err(Unimplemented)
|
||||
}
|
||||
}
|
||||
Ok(Some(candidate))
|
||||
}
|
||||
|
||||
fn candidate_from_obligation_no_cache<'o>(&mut self,
|
||||
stack: &TraitObligationStack<'o, 'tcx>)
|
||||
-> SelectionResult<'tcx, SelectionCandidate<'tcx>>
|
||||
@ -803,12 +821,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
// we were to winnow, we'd wind up with zero candidates.
|
||||
// Instead, we select the right impl now but report `Bar does
|
||||
// not implement Clone`.
|
||||
if candidates.len() > 1 {
|
||||
candidates.retain(|c| self.evaluate_candidate(stack, c).may_apply())
|
||||
if candidates.len() == 1 {
|
||||
return self.filter_negative_impls(candidates.pop().unwrap());
|
||||
}
|
||||
|
||||
// If there are STILL multiple candidate, we can further reduce
|
||||
// the list by dropping duplicates.
|
||||
// Winnow, but record the exact outcome of evaluation, which
|
||||
// is needed for specialization.
|
||||
let mut candidates: Vec<_> = candidates.into_iter().filter_map(|c| {
|
||||
let eval = self.evaluate_candidate(stack, &c);
|
||||
if eval.may_apply() {
|
||||
Some(EvaluatedCandidate {
|
||||
candidate: c,
|
||||
evaluation: eval,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).collect();
|
||||
|
||||
// If there are STILL multiple candidate, we can further
|
||||
// reduce the list by dropping duplicates -- including
|
||||
// resolving specializations.
|
||||
if candidates.len() > 1 {
|
||||
let mut i = 0;
|
||||
while i < candidates.len() {
|
||||
@ -850,19 +883,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
}
|
||||
|
||||
// Just one candidate left.
|
||||
let candidate = candidates.pop().unwrap();
|
||||
|
||||
match candidate {
|
||||
ImplCandidate(def_id) => {
|
||||
match self.tcx().trait_impl_polarity(def_id) {
|
||||
Some(hir::ImplPolarity::Negative) => return Err(Unimplemented),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(Some(candidate))
|
||||
self.filter_negative_impls(candidates.pop().unwrap().candidate)
|
||||
}
|
||||
|
||||
fn is_knowable<'o>(&mut self,
|
||||
@ -1564,41 +1585,55 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
/// candidates and prefer where-clause candidates.
|
||||
///
|
||||
/// See the comment for "SelectionCandidate" for more details.
|
||||
fn candidate_should_be_dropped_in_favor_of<'o>(&mut self,
|
||||
victim: &SelectionCandidate<'tcx>,
|
||||
other: &SelectionCandidate<'tcx>)
|
||||
-> bool
|
||||
fn candidate_should_be_dropped_in_favor_of<'o>(
|
||||
&mut self,
|
||||
victim: &EvaluatedCandidate<'tcx>,
|
||||
other: &EvaluatedCandidate<'tcx>)
|
||||
-> bool
|
||||
{
|
||||
if victim == other {
|
||||
if victim.candidate == other.candidate {
|
||||
return true;
|
||||
}
|
||||
|
||||
match other {
|
||||
&ObjectCandidate |
|
||||
&ParamCandidate(_) | &ProjectionCandidate => match victim {
|
||||
&DefaultImplCandidate(..) => {
|
||||
match other.candidate {
|
||||
ObjectCandidate |
|
||||
ParamCandidate(_) | ProjectionCandidate => match victim.candidate {
|
||||
DefaultImplCandidate(..) => {
|
||||
self.tcx().sess.bug(
|
||||
"default implementations shouldn't be recorded \
|
||||
when there are other valid candidates");
|
||||
}
|
||||
&ImplCandidate(..) |
|
||||
&ClosureCandidate(..) |
|
||||
&FnPointerCandidate |
|
||||
&BuiltinObjectCandidate |
|
||||
&BuiltinUnsizeCandidate |
|
||||
&DefaultImplObjectCandidate(..) |
|
||||
&BuiltinCandidate(..) => {
|
||||
ImplCandidate(..) |
|
||||
ClosureCandidate(..) |
|
||||
FnPointerCandidate |
|
||||
BuiltinObjectCandidate |
|
||||
BuiltinUnsizeCandidate |
|
||||
DefaultImplObjectCandidate(..) |
|
||||
BuiltinCandidate(..) => {
|
||||
// We have a where-clause so don't go around looking
|
||||
// for impls.
|
||||
true
|
||||
}
|
||||
&ObjectCandidate |
|
||||
&ProjectionCandidate => {
|
||||
ObjectCandidate |
|
||||
ProjectionCandidate => {
|
||||
// Arbitrarily give param candidates priority
|
||||
// over projection and object candidates.
|
||||
true
|
||||
},
|
||||
&ParamCandidate(..) => false,
|
||||
ParamCandidate(..) => false,
|
||||
ErrorCandidate => false // propagate errors
|
||||
},
|
||||
ImplCandidate(other_def) => {
|
||||
// See if we can toss out `victim` based on specialization.
|
||||
// This requires us to know *for sure* that the `other` impl applies
|
||||
// i.e. EvaluatedToOk:
|
||||
if other.evaluation == EvaluatedToOk {
|
||||
if let ImplCandidate(victim_def) = victim.candidate {
|
||||
return traits::specializes(self.infcx(), other_def, victim_def);
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
},
|
||||
_ => false
|
||||
}
|
||||
|
@ -381,7 +381,6 @@ pub fn trait_ref_for_builtin_bound<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn predicate_for_trait_ref<'tcx>(
|
||||
cause: ObligationCause<'tcx>,
|
||||
trait_ref: ty::TraitRef<'tcx>,
|
||||
|
@ -2456,8 +2456,13 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
for impl_def_id in self.sess.cstore.implementations_of_trait(trait_id) {
|
||||
let impl_items = self.sess.cstore.impl_items(impl_def_id);
|
||||
let trait_ref = self.impl_trait_ref(impl_def_id).unwrap();
|
||||
|
||||
// Record the trait->implementation mapping.
|
||||
def.record_impl(self, impl_def_id, trait_ref);
|
||||
if let Some(parent) = self.sess.cstore.impl_parent(impl_def_id) {
|
||||
def.record_remote_impl(self, impl_def_id, trait_ref, parent);
|
||||
} else {
|
||||
def.record_remote_impl(self, impl_def_id, trait_ref, trait_id);
|
||||
}
|
||||
|
||||
// For any methods that use a default implementation, add them to
|
||||
// the map. This is a bit unfortunate.
|
||||
|
@ -1106,6 +1106,13 @@ impl<'tcx> TyS<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_concrete_skeleton(&self) -> bool {
|
||||
match self.sty {
|
||||
TyParam(_) | TyInfer(_) | TyError => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the type and mutability of *ty.
|
||||
//
|
||||
// The parameter `explicit` indicates if this is an *explicit* dereference.
|
||||
|
@ -59,6 +59,9 @@ pub struct TraitDef<'tcx> {
|
||||
/// Blanket impls associated with the trait.
|
||||
blanket_impls: RefCell<Vec<DefId>>,
|
||||
|
||||
/// The specialization order for impls of this trait.
|
||||
pub specialization_graph: RefCell<traits::SpecializationGraph>,
|
||||
|
||||
/// Various flags
|
||||
pub flags: Cell<TraitFlags>
|
||||
}
|
||||
@ -78,7 +81,8 @@ impl<'tcx> TraitDef<'tcx> {
|
||||
associated_type_names: associated_type_names,
|
||||
nonblanket_impls: RefCell::new(FnvHashMap()),
|
||||
blanket_impls: RefCell::new(vec![]),
|
||||
flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS)
|
||||
flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS),
|
||||
specialization_graph: RefCell::new(traits::SpecializationGraph::new()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,42 +118,80 @@ impl<'tcx> TraitDef<'tcx> {
|
||||
tcx.dep_graph.read(DepNode::TraitImpls(self.trait_ref.def_id));
|
||||
}
|
||||
|
||||
/// Records a trait-to-implementation mapping.
|
||||
pub fn record_impl(&self,
|
||||
tcx: &TyCtxt<'tcx>,
|
||||
impl_def_id: DefId,
|
||||
impl_trait_ref: ty::TraitRef<'tcx>) {
|
||||
/// Records a basic trait-to-implementation mapping.
|
||||
///
|
||||
/// Returns `true` iff the impl has not previously been recorded.
|
||||
fn record_impl(&self,
|
||||
tcx: &TyCtxt<'tcx>,
|
||||
impl_def_id: DefId,
|
||||
impl_trait_ref: TraitRef<'tcx>) -> bool {
|
||||
debug!("TraitDef::record_impl for {:?}, from {:?}",
|
||||
self, impl_trait_ref);
|
||||
|
||||
// Record the write into the impl set, but only for local
|
||||
// impls: external impls are handled differently.
|
||||
if impl_def_id.is_local() {
|
||||
self.write_trait_impls(tcx);
|
||||
}
|
||||
|
||||
// We don't want to borrow_mut after we already populated all impls,
|
||||
// so check if an impl is present with an immutable borrow first.
|
||||
if let Some(sty) = fast_reject::simplify_type(tcx,
|
||||
impl_trait_ref.self_ty(), false) {
|
||||
if let Some(is) = self.nonblanket_impls.borrow().get(&sty) {
|
||||
if is.contains(&impl_def_id) {
|
||||
return // duplicate - skip
|
||||
return false; // duplicate - skip
|
||||
}
|
||||
}
|
||||
|
||||
self.nonblanket_impls.borrow_mut().entry(sty).or_insert(vec![]).push(impl_def_id)
|
||||
} else {
|
||||
if self.blanket_impls.borrow().contains(&impl_def_id) {
|
||||
return // duplicate - skip
|
||||
return false; // duplicate - skip
|
||||
}
|
||||
self.blanket_impls.borrow_mut().push(impl_def_id)
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Records a trait-to-implementation mapping for a crate-local impl.
|
||||
pub fn record_local_impl(&self,
|
||||
tcx: &TyCtxt<'tcx>,
|
||||
impl_def_id: DefId,
|
||||
impl_trait_ref: TraitRef<'tcx>) {
|
||||
self.record_impl(tcx, impl_def_id, impl_trait_ref);
|
||||
}
|
||||
|
||||
/// Records a trait-to-implementation mapping for a non-local impl.
|
||||
///
|
||||
/// The `parent_impl` is the immediately-less-specialized impl, or the
|
||||
/// trait's def ID if the impl is maximally-specialized -- information that
|
||||
/// should be pulled from the metadata.
|
||||
pub fn record_remote_impl(&self,
|
||||
tcx: &TyCtxt<'tcx>,
|
||||
impl_def_id: DefId,
|
||||
impl_trait_ref: TraitRef<'tcx>,
|
||||
parent_impl: DefId) {
|
||||
// if the impl has not previously been recorded
|
||||
if self.record_impl(tcx, impl_def_id, impl_trait_ref) {
|
||||
// if the impl is non-local, it's placed directly into the
|
||||
// specialization graph using parent information drawn from metadata.
|
||||
self.specialization_graph.borrow_mut()
|
||||
.record_impl_from_cstore(parent_impl, impl_def_id)
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a local impl into the specialization graph, returning an error with
|
||||
/// overlap information if the impl overlaps but does not specialize an
|
||||
/// existing impl.
|
||||
pub fn add_impl_for_specialization(&self,
|
||||
tcx: &ctxt<'tcx>,
|
||||
impl_def_id: DefId,
|
||||
impl_trait_ref: TraitRef<'tcx>)
|
||||
-> Result<(), traits::Overlap<'tcx>> {
|
||||
assert!(impl_def_id.is_local());
|
||||
|
||||
self.specialization_graph.borrow_mut()
|
||||
.insert(tcx, impl_def_id, impl_trait_ref)
|
||||
}
|
||||
|
||||
pub fn for_each_impl<F: FnMut(DefId)>(&self, tcx: &TyCtxt<'tcx>, mut f: F) {
|
||||
self.read_trait_impls(tcx);
|
||||
|
||||
tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id);
|
||||
|
||||
for &impl_def_id in self.blanket_impls.borrow().iter() {
|
||||
@ -223,4 +265,3 @@ bitflags! {
|
||||
const IMPLS_VALID = 1 << 3,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -243,6 +243,8 @@ pub const tag_items_data_item_deprecation: usize = 0xa7;
|
||||
|
||||
pub const tag_items_data_item_defaultness: usize = 0xa8;
|
||||
|
||||
pub const tag_items_data_parent_impl: usize = 0xa9;
|
||||
|
||||
pub const tag_rustc_version: usize = 0x10f;
|
||||
pub fn rustc_version() -> String {
|
||||
format!(
|
||||
|
@ -225,6 +225,11 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
|
||||
decoder::get_associated_consts(self.intr.clone(), &cdata, def.index, tcx)
|
||||
}
|
||||
|
||||
fn impl_parent(&self, impl_def: DefId) -> Option<DefId> {
|
||||
let cdata = self.get_crate_data(impl_def.krate);
|
||||
decoder::get_parent_impl(&*cdata, impl_def.index)
|
||||
}
|
||||
|
||||
fn trait_of_item(&self, tcx: &TyCtxt<'tcx>, def_id: DefId) -> Option<DefId>
|
||||
{
|
||||
let cdata = self.get_crate_data(def_id.krate);
|
||||
|
@ -34,6 +34,7 @@ use middle::lang_items;
|
||||
use middle::subst;
|
||||
use middle::ty::{ImplContainer, TraitContainer};
|
||||
use middle::ty::{self, Ty, TyCtxt, TypeFoldable, VariantKind};
|
||||
use middle::traits;
|
||||
|
||||
use rustc_const_eval::ConstInt;
|
||||
|
||||
@ -564,6 +565,13 @@ pub fn get_visibility(cdata: Cmd, id: DefIndex) -> hir::Visibility {
|
||||
item_visibility(cdata.lookup_item(id))
|
||||
}
|
||||
|
||||
pub fn get_parent_impl(cdata: Cmd, id: DefIndex) -> Option<DefId> {
|
||||
let item = cdata.lookup_item(id);
|
||||
reader::maybe_get_doc(item, tag_items_data_parent_impl).map(|doc| {
|
||||
translated_def_id(cdata, doc)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_repr_attrs(cdata: Cmd, id: DefIndex) -> Vec<attr::ReprAttr> {
|
||||
let item = cdata.lookup_item(id);
|
||||
match reader::maybe_get_doc(item, tag_items_data_item_repr).map(|doc| {
|
||||
|
@ -884,6 +884,12 @@ fn encode_deprecation(rbml_w: &mut Encoder, depr_opt: Option<attr::Deprecation>)
|
||||
});
|
||||
}
|
||||
|
||||
fn encode_parent_impl(rbml_w: &mut Encoder, parent_opt: Option<DefId>) {
|
||||
parent_opt.map(|parent| {
|
||||
rbml_w.wr_tagged_u64(tag_items_data_parent_impl, def_to_u64(parent));
|
||||
});
|
||||
}
|
||||
|
||||
fn encode_xrefs<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
|
||||
rbml_w: &mut Encoder,
|
||||
xrefs: FnvHashMap<XRef<'tcx>, u32>)
|
||||
@ -1161,8 +1167,12 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
|
||||
}
|
||||
rbml_w.end_tag();
|
||||
}
|
||||
if let Some(trait_ref) = tcx.impl_trait_ref(ecx.tcx.map.local_def_id(item.id)) {
|
||||
let did = ecx.tcx.map.local_def_id(item.id);
|
||||
if let Some(trait_ref) = tcx.impl_trait_ref(did) {
|
||||
encode_trait_ref(rbml_w, ecx, trait_ref, tag_item_trait_ref);
|
||||
|
||||
let parent = tcx.lookup_trait_def(trait_ref.def_id).parent_of_impl(did);
|
||||
encode_parent_impl(rbml_w, parent);
|
||||
}
|
||||
encode_path(rbml_w, path.clone());
|
||||
encode_stability(rbml_w, stab);
|
||||
|
@ -15,7 +15,6 @@
|
||||
// done by the orphan and overlap modules. Then we build up various
|
||||
// mappings. That mapping code resides here.
|
||||
|
||||
|
||||
use middle::def_id::DefId;
|
||||
use middle::lang_items::UnsizeTraitLangItem;
|
||||
use middle::subst::{self, Subst};
|
||||
@ -197,7 +196,7 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> {
|
||||
debug!("add_trait_impl: impl_trait_ref={:?} impl_def_id={:?}",
|
||||
impl_trait_ref, impl_def_id);
|
||||
let trait_def = self.crate_context.tcx.lookup_trait_def(impl_trait_ref.def_id);
|
||||
trait_def.record_impl(self.crate_context.tcx, impl_def_id, impl_trait_ref);
|
||||
trait_def.record_local_impl(self.crate_context.tcx, impl_def_id, impl_trait_ref);
|
||||
}
|
||||
|
||||
// Converts an implementation in the AST to a vector of items.
|
||||
|
@ -12,7 +12,7 @@
|
||||
//! same type. Likewise, no two inherent impls for a given type
|
||||
//! constructor provide a method with the same name.
|
||||
|
||||
use middle::cstore::{CrateStore, LOCAL_CRATE};
|
||||
use middle::cstore::CrateStore;
|
||||
use middle::def_id::DefId;
|
||||
use middle::traits;
|
||||
use middle::ty::{self, TyCtxt};
|
||||
@ -50,121 +50,6 @@ struct OverlapChecker<'cx, 'tcx:'cx> {
|
||||
}
|
||||
|
||||
impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
|
||||
fn check_for_overlapping_impls_of_trait(&mut self, trait_def_id: DefId) {
|
||||
debug!("check_for_overlapping_impls_of_trait(trait_def_id={:?})",
|
||||
trait_def_id);
|
||||
|
||||
let _task = self.tcx.dep_graph.in_task(DepNode::CoherenceOverlapCheck(trait_def_id));
|
||||
if !self.traits_checked.insert(trait_def_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
let trait_def = self.tcx.lookup_trait_def(trait_def_id);
|
||||
self.tcx.populate_implementations_for_trait_if_necessary(
|
||||
trait_def.trait_ref.def_id);
|
||||
|
||||
// We should already know all impls of this trait, so these
|
||||
// borrows are safe.
|
||||
let (blanket_impls, nonblanket_impls) = trait_def.borrow_impl_lists(self.tcx);
|
||||
|
||||
// Conflicts can only occur between a blanket impl and another impl,
|
||||
// or between 2 non-blanket impls of the same kind.
|
||||
|
||||
for (i, &impl1_def_id) in blanket_impls.iter().enumerate() {
|
||||
for &impl2_def_id in &blanket_impls[(i+1)..] {
|
||||
self.check_if_impls_overlap(impl1_def_id,
|
||||
impl2_def_id);
|
||||
}
|
||||
|
||||
for v in nonblanket_impls.values() {
|
||||
for &impl2_def_id in v {
|
||||
self.check_if_impls_overlap(impl1_def_id,
|
||||
impl2_def_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for impl_group in nonblanket_impls.values() {
|
||||
for (i, &impl1_def_id) in impl_group.iter().enumerate() {
|
||||
for &impl2_def_id in &impl_group[(i+1)..] {
|
||||
self.check_if_impls_overlap(impl1_def_id,
|
||||
impl2_def_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We need to coherently pick which impl will be displayed
|
||||
// as causing the error message, and it must be the in the current
|
||||
// crate. Just pick the smaller impl in the file.
|
||||
fn order_impls(&self, impl1_def_id: DefId, impl2_def_id: DefId)
|
||||
-> Option<(DefId, DefId)> {
|
||||
if impl1_def_id.krate != LOCAL_CRATE {
|
||||
if impl2_def_id.krate != LOCAL_CRATE {
|
||||
// we don't need to check impls if both are external;
|
||||
// that's the other crate's job.
|
||||
None
|
||||
} else {
|
||||
Some((impl2_def_id, impl1_def_id))
|
||||
}
|
||||
} else if impl2_def_id.krate != LOCAL_CRATE {
|
||||
Some((impl1_def_id, impl2_def_id))
|
||||
} else if impl1_def_id < impl2_def_id {
|
||||
Some((impl1_def_id, impl2_def_id))
|
||||
} else {
|
||||
Some((impl2_def_id, impl1_def_id))
|
||||
}
|
||||
}
|
||||
|
||||
fn check_if_impls_overlap(&self,
|
||||
impl1_def_id: DefId,
|
||||
impl2_def_id: DefId)
|
||||
{
|
||||
if let Some((impl1_def_id, impl2_def_id)) = self.order_impls(
|
||||
impl1_def_id, impl2_def_id)
|
||||
{
|
||||
debug!("check_if_impls_overlap({:?}, {:?})",
|
||||
impl1_def_id,
|
||||
impl2_def_id);
|
||||
|
||||
let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None);
|
||||
if let Some(header) = traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id) {
|
||||
self.report_overlap_error(impl1_def_id, impl2_def_id, header.trait_ref.unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn report_overlap_error(&self,
|
||||
impl1: DefId,
|
||||
impl2: DefId,
|
||||
trait_ref: ty::TraitRef)
|
||||
{
|
||||
// only print the Self type if it's concrete; otherwise, it's not adding much information.
|
||||
let self_type = {
|
||||
trait_ref.substs.self_ty().and_then(|ty| {
|
||||
if let ty::TyInfer(_) = ty.sty {
|
||||
None
|
||||
} else {
|
||||
Some(format!(" for type `{}`", ty))
|
||||
}
|
||||
}).unwrap_or(String::new())
|
||||
};
|
||||
|
||||
let mut err = struct_span_err!(self.tcx.sess, self.span_of_def_id(impl1), E0119,
|
||||
"conflicting implementations of trait `{}`{}:",
|
||||
trait_ref,
|
||||
self_type);
|
||||
|
||||
if impl2.is_local() {
|
||||
span_note!(&mut err, self.span_of_def_id(impl2),
|
||||
"conflicting implementation is here:");
|
||||
} else {
|
||||
let cname = self.tcx.sess.cstore.crate_name(impl2.krate);
|
||||
err.note(&format!("conflicting implementation in crate `{}`", cname));
|
||||
}
|
||||
err.emit();
|
||||
}
|
||||
|
||||
fn span_of_def_id(&self, did: DefId) -> Span {
|
||||
let node_id = self.tcx.map.as_local_node_id(did).unwrap();
|
||||
self.tcx.map.span(node_id)
|
||||
@ -222,15 +107,9 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> {
|
||||
fn visit_item(&mut self, item: &'v hir::Item) {
|
||||
match item.node {
|
||||
hir::ItemTrait(..) => {
|
||||
let trait_def_id = self.tcx.map.local_def_id(item.id);
|
||||
self.check_for_overlapping_impls_of_trait(trait_def_id);
|
||||
}
|
||||
|
||||
hir::ItemEnum(..) | hir::ItemStruct(..) => {
|
||||
let type_def_id = self.tcx.map.local_def_id(item.id);
|
||||
self.check_for_overlapping_inherent_impls(type_def_id);
|
||||
@ -246,47 +125,84 @@ impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> {
|
||||
self.check_for_overlapping_impls_of_trait(trait_ref.def_id);
|
||||
|
||||
let prev_default_impl = self.default_impls.insert(trait_ref.def_id, item.id);
|
||||
match prev_default_impl {
|
||||
Some(prev_id) => {
|
||||
self.report_overlap_error(impl_def_id,
|
||||
self.tcx.map.local_def_id(prev_id),
|
||||
trait_ref);
|
||||
}
|
||||
None => { }
|
||||
if let Some(prev_id) = prev_default_impl {
|
||||
span_err!(self.tcx.sess,
|
||||
self.span_of_def_id(impl_def_id), E0519,
|
||||
"redundant default implementations of trait `{}`:",
|
||||
trait_ref);
|
||||
span_note!(self.tcx.sess,
|
||||
self.span_of_def_id(self.tcx.map.local_def_id(prev_id)),
|
||||
"redundant implementation is here:");
|
||||
}
|
||||
}
|
||||
hir::ItemImpl(_, _, _, Some(_), _, _) => {
|
||||
hir::ItemImpl(_, _, _, Some(_), ref self_ty, _) => {
|
||||
let impl_def_id = self.tcx.map.local_def_id(item.id);
|
||||
let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap();
|
||||
let trait_def_id = trait_ref.def_id;
|
||||
self.check_for_overlapping_impls_of_trait(trait_def_id);
|
||||
match trait_ref.self_ty().sty {
|
||||
ty::TyTrait(ref data) => {
|
||||
// This is something like impl Trait1 for Trait2. Illegal
|
||||
// if Trait1 is a supertrait of Trait2 or Trait2 is not object safe.
|
||||
|
||||
if !traits::is_object_safe(self.tcx, data.principal_def_id()) {
|
||||
// This is an error, but it will be
|
||||
// reported by wfcheck. Ignore it
|
||||
// here. This is tested by
|
||||
// `coherence-impl-trait-for-trait-object-safe.rs`.
|
||||
} else {
|
||||
let mut supertrait_def_ids =
|
||||
traits::supertrait_def_ids(self.tcx, data.principal_def_id());
|
||||
if supertrait_def_ids.any(|d| d == trait_def_id) {
|
||||
span_err!(self.tcx.sess, item.span, E0371,
|
||||
"the object type `{}` automatically \
|
||||
implements the trait `{}`",
|
||||
trait_ref.self_ty(),
|
||||
self.tcx.item_path_str(trait_def_id));
|
||||
let _task = self.tcx.dep_graph.in_task(DepNode::CoherenceOverlapCheck(trait_def_id));
|
||||
|
||||
let def = self.tcx.lookup_trait_def(trait_def_id);
|
||||
|
||||
// attempt to insert into the specialization graph
|
||||
let insert_result = def.add_impl_for_specialization(self.tcx,
|
||||
impl_def_id,
|
||||
trait_ref);
|
||||
|
||||
// insertion failed due to overlap
|
||||
if let Err(overlap) = insert_result {
|
||||
// only print the Self type if it has at least some outer
|
||||
// concrete shell; otherwise, it's not adding much
|
||||
// information.
|
||||
let self_type = {
|
||||
overlap.on_trait_ref.substs.self_ty().and_then(|ty| {
|
||||
if ty.has_concrete_skeleton() {
|
||||
Some(format!(" for type `{}`", ty))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).unwrap_or(String::new())
|
||||
};
|
||||
|
||||
span_err!(self.tcx.sess, self.span_of_def_id(impl_def_id), E0119,
|
||||
"conflicting implementations of trait `{}`{}:",
|
||||
overlap.on_trait_ref,
|
||||
self_type);
|
||||
|
||||
if overlap.with_impl.is_local() {
|
||||
span_note!(self.tcx.sess, self.span_of_def_id(overlap.with_impl),
|
||||
"conflicting implementation is here:");
|
||||
} else {
|
||||
let cname = self.tcx.sess.cstore.crate_name(overlap.with_impl.krate);
|
||||
self.tcx.sess.note(&format!("conflicting implementation in crate `{}`",
|
||||
cname));
|
||||
}
|
||||
}
|
||||
|
||||
// check for overlap with the automatic `impl Trait for Trait`
|
||||
if let ty::TyTrait(ref data) = trait_ref.self_ty().sty {
|
||||
// This is something like impl Trait1 for Trait2. Illegal
|
||||
// if Trait1 is a supertrait of Trait2 or Trait2 is not object safe.
|
||||
|
||||
if !traits::is_object_safe(self.tcx, data.principal_def_id()) {
|
||||
// This is an error, but it will be
|
||||
// reported by wfcheck. Ignore it
|
||||
// here. This is tested by
|
||||
// `coherence-impl-trait-for-trait-object-safe.rs`.
|
||||
} else {
|
||||
let mut supertrait_def_ids =
|
||||
traits::supertrait_def_ids(self.tcx, data.principal_def_id());
|
||||
if supertrait_def_ids.any(|d| d == trait_def_id) {
|
||||
span_err!(self.tcx.sess, item.span, E0371,
|
||||
"the object type `{}` automatically \
|
||||
implements the trait `{}`",
|
||||
trait_ref.self_ty(),
|
||||
self.tcx.item_path_str(trait_def_id));
|
||||
}
|
||||
}
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -69,6 +69,7 @@ use middle::resolve_lifetime;
|
||||
use middle::const_eval::{self, ConstVal};
|
||||
use middle::const_eval::EvalHint::UncheckedExprHint;
|
||||
use middle::subst::{Substs, FnSpace, ParamSpace, SelfSpace, TypeSpace, VecPerParamSpace};
|
||||
use middle::traits;
|
||||
use middle::ty::{ToPredicate, ImplContainer, ImplOrTraitItemContainer, TraitContainer};
|
||||
use middle::ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeScheme};
|
||||
use middle::ty::{VariantKind};
|
||||
|
@ -3695,5 +3695,6 @@ register_diagnostics! {
|
||||
E0399, // trait items need to be implemented because the associated
|
||||
// type `{}` was overridden
|
||||
E0436, // functional record update requires a struct
|
||||
E0513 // no type for local variable ..
|
||||
E0513, // no type for local variable ..
|
||||
E0519 // redundant default implementations of trait
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user