Auto merge of #31925 - aturon:inherent-overlap, r=nikomatsakis
Forbid items with the same name from appearing in overlapping inherent impl blocks
For example, the following is now correctly illegal:
```rust
struct Foo;
impl Foo {
fn id() {}
}
impl Foo {
fn id() {}
}
```
"Overlapping" here is determined the same way it is for traits (and in fact shares the same code path): roughly, there must be some way of substituting any generic types to unify the impls, such that none of the `where` clauses are provably unsatisfiable under such a unification.
Along the way, this PR also introduces an `ImplHeader` abstraction (the first commit) that makes it easier to work with impls abstractly (without caring whether they are trait or inherent impl blocks); see the first commit.
Closes #22889
r? @nikomatsakis
This commit is contained in:
commit
1a019dc86d
20 changed files with 253 additions and 109 deletions
|
|
@ -58,6 +58,7 @@ pub enum DepNode {
|
|||
CoherenceCheckImpl(DefId),
|
||||
CoherenceOverlapCheck(DefId),
|
||||
CoherenceOverlapCheckSpecial(DefId),
|
||||
CoherenceOverlapInherentCheck(DefId),
|
||||
CoherenceOrphanCheck(DefId),
|
||||
Variance,
|
||||
WfCheck(DefId),
|
||||
|
|
|
|||
|
|
@ -458,14 +458,13 @@ pub fn mk_eqty<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
|
|||
}
|
||||
|
||||
pub fn mk_eq_trait_refs<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
|
||||
a_is_expected: bool,
|
||||
origin: TypeOrigin,
|
||||
a: ty::TraitRef<'tcx>,
|
||||
b: ty::TraitRef<'tcx>)
|
||||
-> UnitResult<'tcx>
|
||||
a_is_expected: bool,
|
||||
origin: TypeOrigin,
|
||||
a: ty::TraitRef<'tcx>,
|
||||
b: ty::TraitRef<'tcx>)
|
||||
-> UnitResult<'tcx>
|
||||
{
|
||||
debug!("mk_eq_trait_refs({:?} <: {:?})",
|
||||
a, b);
|
||||
debug!("mk_eq_trait_refs({:?} = {:?})", a, b);
|
||||
cx.eq_trait_refs(a_is_expected, origin, a, b)
|
||||
}
|
||||
|
||||
|
|
@ -476,11 +475,25 @@ pub fn mk_sub_poly_trait_refs<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
|
|||
b: ty::PolyTraitRef<'tcx>)
|
||||
-> UnitResult<'tcx>
|
||||
{
|
||||
debug!("mk_sub_poly_trait_refs({:?} <: {:?})",
|
||||
a, b);
|
||||
debug!("mk_sub_poly_trait_refs({:?} <: {:?})", a, b);
|
||||
cx.sub_poly_trait_refs(a_is_expected, origin, a, b)
|
||||
}
|
||||
|
||||
pub fn mk_eq_impl_headers<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
|
||||
a_is_expected: bool,
|
||||
origin: TypeOrigin,
|
||||
a: &ty::ImplHeader<'tcx>,
|
||||
b: &ty::ImplHeader<'tcx>)
|
||||
-> UnitResult<'tcx>
|
||||
{
|
||||
debug!("mk_eq_impl_header({:?} = {:?})", a, b);
|
||||
match (a.trait_ref, b.trait_ref) {
|
||||
(Some(a_ref), Some(b_ref)) => mk_eq_trait_refs(cx, a_is_expected, origin, a_ref, b_ref),
|
||||
(None, None) => mk_eqty(cx, a_is_expected, origin, a.self_ty, b.self_ty),
|
||||
_ => cx.tcx.sess.bug("mk_eq_impl_headers given mismatched impl kinds"),
|
||||
}
|
||||
}
|
||||
|
||||
fn expected_found<T>(a_is_expected: bool,
|
||||
a: T,
|
||||
b: T)
|
||||
|
|
|
|||
|
|
@ -10,29 +10,25 @@
|
|||
|
||||
//! See `README.md` for high-level documentation
|
||||
|
||||
use super::Normalized;
|
||||
use super::SelectionContext;
|
||||
use super::ObligationCause;
|
||||
use super::PredicateObligation;
|
||||
use super::project;
|
||||
use super::util;
|
||||
use super::{SelectionContext};
|
||||
use super::{Obligation, ObligationCause};
|
||||
|
||||
use middle::cstore::LOCAL_CRATE;
|
||||
use middle::def_id::DefId;
|
||||
use middle::subst::{Subst, Substs, TypeSpace};
|
||||
use middle::subst::TypeSpace;
|
||||
use middle::ty::{self, Ty, TyCtxt};
|
||||
use middle::infer::{self, InferCtxt, TypeOrigin};
|
||||
use syntax::codemap::{DUMMY_SP, Span};
|
||||
use syntax::codemap::DUMMY_SP;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct InferIsLocal(bool);
|
||||
|
||||
/// If there are types that satisfy both impls, returns a `TraitRef`
|
||||
/// If there are types that satisfy both impls, returns an `ImplTy`
|
||||
/// with those types substituted (by updating the given `infcx`)
|
||||
pub fn overlapping_impls<'cx, 'tcx>(infcx: &InferCtxt<'cx, 'tcx>,
|
||||
impl1_def_id: DefId,
|
||||
impl2_def_id: DefId)
|
||||
-> Option<ty::TraitRef<'tcx>>
|
||||
-> Option<ty::ImplHeader<'tcx>>
|
||||
{
|
||||
debug!("impl_can_satisfy(\
|
||||
impl1_def_id={:?}, \
|
||||
|
|
@ -45,34 +41,28 @@ pub fn overlapping_impls<'cx, 'tcx>(infcx: &InferCtxt<'cx, 'tcx>,
|
|||
}
|
||||
|
||||
/// Can both impl `a` and impl `b` be satisfied by a common type (including
|
||||
/// `where` clauses)? If so, returns a `TraitRef` that unifies the two impls.
|
||||
/// `where` clauses)? If so, returns an `ImplHeader` that unifies the two impls.
|
||||
fn overlap<'cx, 'tcx>(selcx: &mut SelectionContext<'cx, 'tcx>,
|
||||
a_def_id: DefId,
|
||||
b_def_id: DefId)
|
||||
-> Option<ty::TraitRef<'tcx>>
|
||||
-> Option<ty::ImplHeader<'tcx>>
|
||||
{
|
||||
debug!("overlap(a_def_id={:?}, b_def_id={:?})",
|
||||
a_def_id,
|
||||
b_def_id);
|
||||
|
||||
let (a_trait_ref, a_obligations) = impl_trait_ref_and_oblig(selcx,
|
||||
a_def_id,
|
||||
util::fresh_type_vars_for_impl);
|
||||
let a_impl_header = ty::ImplHeader::with_fresh_ty_vars(selcx, a_def_id);
|
||||
let b_impl_header = ty::ImplHeader::with_fresh_ty_vars(selcx, b_def_id);
|
||||
|
||||
let (b_trait_ref, b_obligations) = impl_trait_ref_and_oblig(selcx,
|
||||
b_def_id,
|
||||
util::fresh_type_vars_for_impl);
|
||||
|
||||
debug!("overlap: a_trait_ref={:?} a_obligations={:?}", a_trait_ref, a_obligations);
|
||||
|
||||
debug!("overlap: b_trait_ref={:?} b_obligations={:?}", b_trait_ref, b_obligations);
|
||||
debug!("overlap: a_impl_header={:?}", a_impl_header);
|
||||
debug!("overlap: b_impl_header={:?}", b_impl_header);
|
||||
|
||||
// Do `a` and `b` unify? If not, no overlap.
|
||||
if let Err(_) = infer::mk_eq_trait_refs(selcx.infcx(),
|
||||
true,
|
||||
TypeOrigin::Misc(DUMMY_SP),
|
||||
a_trait_ref,
|
||||
b_trait_ref) {
|
||||
if let Err(_) = infer::mk_eq_impl_headers(selcx.infcx(),
|
||||
true,
|
||||
TypeOrigin::Misc(DUMMY_SP),
|
||||
&a_impl_header,
|
||||
&b_impl_header) {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
@ -81,9 +71,13 @@ fn overlap<'cx, 'tcx>(selcx: &mut SelectionContext<'cx, 'tcx>,
|
|||
// Are any of the obligations unsatisfiable? If so, no overlap.
|
||||
let infcx = selcx.infcx();
|
||||
let opt_failing_obligation =
|
||||
a_obligations.iter()
|
||||
.chain(&b_obligations)
|
||||
.map(|o| infcx.resolve_type_vars_if_possible(o))
|
||||
a_impl_header.predicates
|
||||
.iter()
|
||||
.chain(&b_impl_header.predicates)
|
||||
.map(|p| infcx.resolve_type_vars_if_possible(p))
|
||||
.map(|p| Obligation { cause: ObligationCause::dummy(),
|
||||
recursion_depth: 0,
|
||||
predicate: p })
|
||||
.find(|o| !selcx.evaluate_obligation(o));
|
||||
|
||||
if let Some(failing_obligation) = opt_failing_obligation {
|
||||
|
|
@ -91,7 +85,7 @@ fn overlap<'cx, 'tcx>(selcx: &mut SelectionContext<'cx, 'tcx>,
|
|||
return None
|
||||
}
|
||||
|
||||
Some(selcx.infcx().resolve_type_vars_if_possible(&a_trait_ref))
|
||||
Some(selcx.infcx().resolve_type_vars_if_possible(&a_impl_header))
|
||||
}
|
||||
|
||||
pub fn trait_ref_is_knowable<'tcx>(tcx: &TyCtxt<'tcx>, trait_ref: &ty::TraitRef<'tcx>) -> bool
|
||||
|
|
@ -125,44 +119,6 @@ pub fn trait_ref_is_knowable<'tcx>(tcx: &TyCtxt<'tcx>, trait_ref: &ty::TraitRef<
|
|||
orphan_check_trait_ref(tcx, trait_ref, InferIsLocal(true)).is_err()
|
||||
}
|
||||
|
||||
type SubstsFn = for<'a,'tcx> fn(infcx: &InferCtxt<'a, 'tcx>,
|
||||
span: Span,
|
||||
impl_def_id: DefId)
|
||||
-> Substs<'tcx>;
|
||||
|
||||
/// Instantiate fresh variables for all bound parameters of the impl
|
||||
/// and return the impl trait ref with those variables substituted.
|
||||
fn impl_trait_ref_and_oblig<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
|
||||
impl_def_id: DefId,
|
||||
substs_fn: SubstsFn)
|
||||
-> (ty::TraitRef<'tcx>,
|
||||
Vec<PredicateObligation<'tcx>>)
|
||||
{
|
||||
let impl_substs =
|
||||
&substs_fn(selcx.infcx(), DUMMY_SP, impl_def_id);
|
||||
let impl_trait_ref =
|
||||
selcx.tcx().impl_trait_ref(impl_def_id).unwrap();
|
||||
let impl_trait_ref =
|
||||
impl_trait_ref.subst(selcx.tcx(), impl_substs);
|
||||
let Normalized { value: impl_trait_ref, obligations: normalization_obligations1 } =
|
||||
project::normalize(selcx, ObligationCause::dummy(), &impl_trait_ref);
|
||||
|
||||
let predicates = selcx.tcx().lookup_predicates(impl_def_id);
|
||||
let predicates = predicates.instantiate(selcx.tcx(), impl_substs);
|
||||
let Normalized { value: predicates, obligations: normalization_obligations2 } =
|
||||
project::normalize(selcx, ObligationCause::dummy(), &predicates);
|
||||
let impl_obligations =
|
||||
util::predicates_for_generics(ObligationCause::dummy(), 0, &predicates);
|
||||
|
||||
let impl_obligations: Vec<_> =
|
||||
impl_obligations.into_iter()
|
||||
.chain(normalization_obligations1)
|
||||
.chain(normalization_obligations2)
|
||||
.collect();
|
||||
|
||||
(impl_trait_ref, impl_obligations)
|
||||
}
|
||||
|
||||
pub enum OrphanCheckErr<'tcx> {
|
||||
NoLocalInputType,
|
||||
UncoveredTy(Ty<'tcx>),
|
||||
|
|
|
|||
|
|
@ -391,7 +391,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
// The result is "true" if the obligation *may* hold and "false" if
|
||||
// we can be sure it does not.
|
||||
|
||||
|
||||
/// Evaluates whether the obligation `obligation` can be satisfied (by any means).
|
||||
pub fn evaluate_obligation(&mut self,
|
||||
obligation: &PredicateObligation<'tcx>)
|
||||
|
|
|
|||
|
|
@ -146,6 +146,10 @@ pub trait TypeFolder<'tcx> : Sized {
|
|||
t.super_fold_with(self)
|
||||
}
|
||||
|
||||
fn fold_impl_header(&mut self, imp: &ty::ImplHeader<'tcx>) -> ty::ImplHeader<'tcx> {
|
||||
imp.super_fold_with(self)
|
||||
}
|
||||
|
||||
fn fold_substs(&mut self,
|
||||
substs: &subst::Substs<'tcx>)
|
||||
-> subst::Substs<'tcx> {
|
||||
|
|
|
|||
|
|
@ -152,6 +152,41 @@ impl ImplOrTraitItemContainer {
|
|||
}
|
||||
}
|
||||
|
||||
/// The "header" of an impl is everything outside the body: a Self type, a trait
|
||||
/// ref (in the case of a trait impl), and a set of predicates (from the
|
||||
/// bounds/where clauses).
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct ImplHeader<'tcx> {
|
||||
pub impl_def_id: DefId,
|
||||
pub self_ty: Ty<'tcx>,
|
||||
pub trait_ref: Option<TraitRef<'tcx>>,
|
||||
pub predicates: Vec<Predicate<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> ImplHeader<'tcx> {
|
||||
pub fn with_fresh_ty_vars<'a>(selcx: &mut traits::SelectionContext<'a, 'tcx>,
|
||||
impl_def_id: DefId)
|
||||
-> ImplHeader<'tcx>
|
||||
{
|
||||
let tcx = selcx.tcx();
|
||||
let impl_generics = tcx.lookup_item_type(impl_def_id).generics;
|
||||
let impl_substs = selcx.infcx().fresh_substs_for_generics(DUMMY_SP, &impl_generics);
|
||||
|
||||
let header = ImplHeader {
|
||||
impl_def_id: impl_def_id,
|
||||
self_ty: tcx.lookup_item_type(impl_def_id).ty,
|
||||
trait_ref: tcx.impl_trait_ref(impl_def_id),
|
||||
predicates: tcx.lookup_predicates(impl_def_id).predicates.into_vec(),
|
||||
}.subst(tcx, &impl_substs);
|
||||
|
||||
let traits::Normalized { value: mut header, obligations } =
|
||||
traits::normalize(selcx, traits::ObligationCause::dummy(), &header);
|
||||
|
||||
header.predicates.extend(obligations.into_iter().map(|o| o.predicate));
|
||||
header
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ImplOrTraitItem<'tcx> {
|
||||
ConstTraitItem(Rc<AssociatedConst<'tcx>>),
|
||||
|
|
|
|||
|
|
@ -446,6 +446,27 @@ impl<'tcx> TypeFoldable<'tcx> for ty::TraitRef<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeFoldable<'tcx> for ty::ImplHeader<'tcx> {
|
||||
fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
|
||||
ty::ImplHeader {
|
||||
impl_def_id: self.impl_def_id,
|
||||
self_ty: self.self_ty.fold_with(folder),
|
||||
trait_ref: self.trait_ref.map(|t| t.fold_with(folder)),
|
||||
predicates: self.predicates.iter().map(|p| p.fold_with(folder)).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
|
||||
folder.fold_impl_header(self)
|
||||
}
|
||||
|
||||
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
|
||||
self.self_ty.visit_with(visitor) ||
|
||||
self.trait_ref.map(|r| r.visit_with(visitor)).unwrap_or(false) ||
|
||||
self.predicates.iter().any(|p| p.visit_with(visitor))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeFoldable<'tcx> for ty::Region {
|
||||
fn super_fold_with<F: TypeFolder<'tcx>>(&self, _folder: &mut F) -> Self {
|
||||
*self
|
||||
|
|
|
|||
|
|
@ -35,7 +35,9 @@ use CrateCtxt;
|
|||
use middle::infer::{self, InferCtxt, TypeOrigin, new_infer_ctxt};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use syntax::ast;
|
||||
use syntax::codemap::Span;
|
||||
use syntax::errors::DiagnosticBuilder;
|
||||
use util::nodemap::{DefIdMap, FnvHashMap};
|
||||
use rustc::dep_graph::DepNode;
|
||||
use rustc::front::map as hir_map;
|
||||
|
|
@ -519,6 +521,13 @@ fn enforce_trait_manually_implementable(tcx: &TyCtxt, sp: Span, trait_def_id: De
|
|||
err.emit();
|
||||
}
|
||||
|
||||
// Factored out into helper because the error cannot be defined in multiple locations.
|
||||
pub fn report_duplicate_item<'tcx>(tcx: &TyCtxt<'tcx>, sp: Span, name: ast::Name)
|
||||
-> DiagnosticBuilder<'tcx>
|
||||
{
|
||||
struct_span_err!(tcx.sess, sp, E0201, "duplicate definitions with name `{}`:", name)
|
||||
}
|
||||
|
||||
pub fn check_coherence(crate_context: &CrateCtxt) {
|
||||
let _task = crate_context.tcx.dep_graph.in_task(DepNode::Coherence);
|
||||
let infcx = new_infer_ctxt(crate_context.tcx, &crate_context.tcx.tables, None);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@
|
|||
// except according to those terms.
|
||||
|
||||
//! Overlap: No two impls for the same trait are implemented for the
|
||||
//! same type.
|
||||
//! 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::def_id::DefId;
|
||||
|
|
@ -115,7 +116,6 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
fn check_if_impls_overlap(&self,
|
||||
impl1_def_id: DefId,
|
||||
impl2_def_id: DefId)
|
||||
|
|
@ -128,8 +128,8 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
|
|||
impl2_def_id);
|
||||
|
||||
let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None);
|
||||
if let Some(trait_ref) = traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id) {
|
||||
self.report_overlap_error(impl1_def_id, impl2_def_id, trait_ref);
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -150,13 +150,13 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
|
|||
}).unwrap_or(String::new())
|
||||
};
|
||||
|
||||
let mut err = struct_span_err!(self.tcx.sess, self.span_of_impl(impl1), E0119,
|
||||
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_impl(impl2),
|
||||
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);
|
||||
|
|
@ -165,10 +165,61 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
|
|||
err.emit();
|
||||
}
|
||||
|
||||
fn span_of_impl(&self, impl_did: DefId) -> Span {
|
||||
let node_id = self.tcx.map.as_local_node_id(impl_did).unwrap();
|
||||
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)
|
||||
}
|
||||
|
||||
fn check_for_common_items_in_impls(&self, impl1: DefId, impl2: DefId) {
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
enum Namespace { Type, Value }
|
||||
|
||||
fn name_and_namespace(tcx: &TyCtxt, item: &ty::ImplOrTraitItemId)
|
||||
-> (ast::Name, Namespace)
|
||||
{
|
||||
let name = tcx.impl_or_trait_item(item.def_id()).name();
|
||||
(name, match *item {
|
||||
ty::TypeTraitItemId(..) => Namespace::Type,
|
||||
ty::ConstTraitItemId(..) => Namespace::Value,
|
||||
ty::MethodTraitItemId(..) => Namespace::Value,
|
||||
})
|
||||
}
|
||||
|
||||
let impl_items = self.tcx.impl_items.borrow();
|
||||
|
||||
for item1 in &impl_items[&impl1] {
|
||||
let (name, namespace) = name_and_namespace(&self.tcx, item1);
|
||||
|
||||
for item2 in &impl_items[&impl2] {
|
||||
if (name, namespace) == name_and_namespace(&self.tcx, item2) {
|
||||
let mut err = super::report_duplicate_item(
|
||||
&self.tcx, self.span_of_def_id(item1.def_id()), name);
|
||||
span_note!(&mut err, self.span_of_def_id(item2.def_id()),
|
||||
"conflicting definition is here:");
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.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)..] {
|
||||
let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None);
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -180,6 +231,11 @@ impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> {
|
|||
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);
|
||||
}
|
||||
|
||||
hir::ItemDefaultImpl(..) => {
|
||||
// look for another default impl; note that due to the
|
||||
// general orphan/coherence rules, it must always be
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ use lint;
|
|||
use middle::def::Def;
|
||||
use middle::def_id::DefId;
|
||||
use constrained_type_params as ctp;
|
||||
use coherence;
|
||||
use middle::lang_items::SizedTraitLangItem;
|
||||
use middle::resolve_lifetime;
|
||||
use middle::const_eval::{self, ConstVal};
|
||||
|
|
@ -750,17 +751,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
|
|||
_ => &mut seen_value_items,
|
||||
};
|
||||
if !seen_items.insert(impl_item.name) {
|
||||
let desc = match impl_item.node {
|
||||
hir::ImplItemKind::Const(_, _) => "associated constant",
|
||||
hir::ImplItemKind::Type(_) => "associated type",
|
||||
hir::ImplItemKind::Method(ref sig, _) =>
|
||||
match sig.explicit_self.node {
|
||||
hir::SelfStatic => "associated function",
|
||||
_ => "method",
|
||||
},
|
||||
};
|
||||
|
||||
span_err!(tcx.sess, impl_item.span, E0201, "duplicate {}", desc);
|
||||
coherence::report_duplicate_item(tcx, impl_item.span, impl_item.name).emit();
|
||||
}
|
||||
|
||||
if let hir::ImplItemKind::Const(ref ty, _) = impl_item.node {
|
||||
|
|
|
|||
|
|
@ -2285,6 +2285,21 @@ impl Baz for Foo {
|
|||
type Quux = u32;
|
||||
}
|
||||
```
|
||||
|
||||
Note, however, that items with the same name are allowed for inherent `impl`
|
||||
blocks that don't overlap:
|
||||
|
||||
```
|
||||
struct Foo<T>(T);
|
||||
|
||||
impl Foo<u8> {
|
||||
fn bar(&self) -> bool { self.0 > 5 }
|
||||
}
|
||||
|
||||
impl Foo<bool> {
|
||||
fn bar(&self) -> bool { self.0 }
|
||||
}
|
||||
```
|
||||
"##,
|
||||
|
||||
E0202: r##"
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ struct Foo;
|
|||
|
||||
impl Foo {
|
||||
const bar: bool = true;
|
||||
fn bar() {} //~ ERROR duplicate associated function
|
||||
fn bar() {} //~ ERROR duplicate definitions
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ struct Baz;
|
|||
|
||||
impl Foo for Baz {
|
||||
type Bar = i16;
|
||||
type Bar = u16; //~ ERROR duplicate associated type
|
||||
type Bar = u16; //~ ERROR duplicate definitions
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ trait Foo {
|
|||
|
||||
impl Foo for () {
|
||||
type Ty = ();
|
||||
type Ty = usize; //~ ERROR duplicate associated type
|
||||
type Ty = usize; //~ ERROR duplicate definitions
|
||||
const BAR: u32 = 7;
|
||||
const BAR: u32 = 8; //~ ERROR duplicate associated constant
|
||||
const BAR: u32 = 8; //~ ERROR duplicate definitions
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
struct Foo;
|
||||
impl Foo {
|
||||
fn orange(&self){}
|
||||
fn orange(&self){} //~ ERROR duplicate method
|
||||
fn orange(&self){} //~ ERROR duplicate definitions
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
|||
44
src/test/compile-fail/inherent-overlap.rs
Normal file
44
src/test/compile-fail/inherent-overlap.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2016 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.
|
||||
|
||||
// Test that you cannot define items with the same name in overlapping inherent
|
||||
// impl blocks.
|
||||
|
||||
struct Foo;
|
||||
|
||||
impl Foo {
|
||||
fn id() {} //~ ERROR E0201
|
||||
}
|
||||
|
||||
impl Foo {
|
||||
fn id() {}
|
||||
}
|
||||
|
||||
struct Bar<T>(T);
|
||||
|
||||
impl<T> Bar<T> {
|
||||
fn bar(&self) {} //~ ERROR E0201
|
||||
}
|
||||
|
||||
impl Bar<u32> {
|
||||
fn bar(&self) {}
|
||||
}
|
||||
|
||||
struct Baz<T>(T);
|
||||
|
||||
impl<T: Copy> Baz<T> {
|
||||
fn baz(&self) {} //~ ERROR E0201
|
||||
}
|
||||
|
||||
impl<T> Baz<Vec<T>> {
|
||||
fn baz(&self) {}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -17,7 +17,7 @@ impl Foo {
|
|||
Foo { baz: 0 }.bar();
|
||||
}
|
||||
|
||||
fn bar() { //~ ERROR duplicate associated function
|
||||
fn bar() { //~ ERROR duplicate definitions
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ trait Bar {
|
|||
|
||||
impl Bar for Foo {
|
||||
fn bar(&self) -> isize {1}
|
||||
fn bar(&self) -> isize {2} //~ ERROR duplicate method
|
||||
fn bar(&self) -> isize {2} //~ ERROR duplicate definitions
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ impl S {
|
|||
|
||||
// Cause an error. It shouldn't have any macro backtrace frames.
|
||||
fn bar(&self) { }
|
||||
fn bar(&self) { } //~ ERROR duplicate method
|
||||
fn bar(&self) { } //~ ERROR duplicate definitions
|
||||
}
|
||||
|
||||
fn main() { }
|
||||
|
|
|
|||
|
|
@ -17,15 +17,15 @@ pub trait Bar {
|
|||
fn quux(self);
|
||||
}
|
||||
|
||||
impl<T> Foo<T> {
|
||||
impl Foo<u8> {
|
||||
// @has - '//*[@id="method.pass"]//code' 'fn pass()'
|
||||
pub fn pass() {}
|
||||
}
|
||||
impl<T> Foo<T> {
|
||||
impl Foo<u16> {
|
||||
// @has - '//*[@id="method.pass-1"]//code' 'fn pass() -> usize'
|
||||
pub fn pass() -> usize { 42 }
|
||||
}
|
||||
impl<T> Foo<T> {
|
||||
impl Foo<u32> {
|
||||
// @has - '//*[@id="method.pass-2"]//code' 'fn pass() -> isize'
|
||||
pub fn pass() -> isize { 42 }
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue