Skip to content

Commit 21df87f

Browse files
committed
Forbid items with the same name being defined 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. Closes #22889
1 parent 9cc3bfc commit 21df87f

File tree

9 files changed

+113
-45
lines changed

9 files changed

+113
-45
lines changed

src/librustc/dep_graph/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ pub enum DepNode {
5858
CoherenceCheckImpl(DefId),
5959
CoherenceOverlapCheck(DefId),
6060
CoherenceOverlapCheckSpecial(DefId),
61+
CoherenceOverlapInherentCheck(DefId),
6162
CoherenceOrphanCheck(DefId),
6263
Variance,
6364
WfCheck(DefId),

src/librustc/middle/infer/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -482,14 +482,14 @@ pub fn mk_sub_poly_trait_refs<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
482482
pub fn mk_eq_impl_headers<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
483483
a_is_expected: bool,
484484
origin: TypeOrigin,
485-
a: ty::ImplHeader<'tcx>,
486-
b: ty::ImplHeader<'tcx>)
485+
a: &ty::ImplHeader<'tcx>,
486+
b: &ty::ImplHeader<'tcx>)
487487
-> UnitResult<'tcx>
488488
{
489489
debug!("mk_eq_impl_header({:?} = {:?})", a, b);
490490
match (a.trait_ref, b.trait_ref) {
491-
(Some(a_ref), Some(b_ref)) => mk_eq_trait_refs(cx, a_is_expected, a_ref, b_ref),
492-
(None, None) => mk_eqty(cx, a_is_expected, a.self_ty, b.self_ty),
491+
(Some(a_ref), Some(b_ref)) => mk_eq_trait_refs(cx, a_is_expected, origin, a_ref, b_ref),
492+
(None, None) => mk_eqty(cx, a_is_expected, origin, a.self_ty, b.self_ty),
493493
_ => cx.tcx.sess.bug("mk_eq_impl_headers given mismatched impl kinds"),
494494
}
495495
}

src/librustc/middle/traits/coherence.rs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,15 @@
1010

1111
//! See `README.md` for high-level documentation
1212
13-
use super::{Normalized, SelectionContext};
14-
use super::{Obligation, ObligationCause, PredicateObligation};
15-
use super::project;
16-
use super::util;
13+
use super::{SelectionContext};
14+
use super::{Obligation, ObligationCause};
1715

1816
use middle::cstore::LOCAL_CRATE;
1917
use middle::def_id::DefId;
20-
use middle::subst::{Subst, Substs, TypeSpace};
18+
use middle::subst::TypeSpace;
2119
use middle::ty::{self, Ty, TyCtxt};
22-
use middle::ty::error::TypeError;
2320
use middle::infer::{self, InferCtxt, TypeOrigin};
24-
use syntax::codemap::{DUMMY_SP, Span};
21+
use syntax::codemap::DUMMY_SP;
2522

2623
#[derive(Copy, Clone)]
2724
struct InferIsLocal(bool);
@@ -31,7 +28,7 @@ struct InferIsLocal(bool);
3128
pub fn overlapping_impls<'cx, 'tcx>(infcx: &InferCtxt<'cx, 'tcx>,
3229
impl1_def_id: DefId,
3330
impl2_def_id: DefId)
34-
-> Option<ImplTy<'tcx>>
31+
-> Option<ty::ImplHeader<'tcx>>
3532
{
3633
debug!("impl_can_satisfy(\
3734
impl1_def_id={:?}, \
@@ -48,7 +45,7 @@ pub fn overlapping_impls<'cx, 'tcx>(infcx: &InferCtxt<'cx, 'tcx>,
4845
fn overlap<'cx, 'tcx>(selcx: &mut SelectionContext<'cx, 'tcx>,
4946
a_def_id: DefId,
5047
b_def_id: DefId)
51-
-> Option<ImplHeader<'tcx>>
48+
-> Option<ty::ImplHeader<'tcx>>
5249
{
5350
debug!("overlap(a_def_id={:?}, b_def_id={:?})",
5451
a_def_id,
@@ -64,8 +61,8 @@ fn overlap<'cx, 'tcx>(selcx: &mut SelectionContext<'cx, 'tcx>,
6461
if let Err(_) = infer::mk_eq_impl_headers(selcx.infcx(),
6562
true,
6663
TypeOrigin::Misc(DUMMY_SP),
67-
a_impl_header,
68-
b_impl_header) {
64+
&a_impl_header,
65+
&b_impl_header) {
6966
return None;
7067
}
7168

@@ -74,7 +71,7 @@ fn overlap<'cx, 'tcx>(selcx: &mut SelectionContext<'cx, 'tcx>,
7471
// Are any of the obligations unsatisfiable? If so, no overlap.
7572
let infcx = selcx.infcx();
7673
let opt_failing_obligation =
77-
a_impl_header.prediates
74+
a_impl_header.predicates
7875
.iter()
7976
.chain(&b_impl_header.predicates)
8077
.map(|p| infcx.resolve_type_vars_if_possible(p))

src/librustc/middle/ty/mod.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -164,23 +164,23 @@ pub struct ImplHeader<'tcx> {
164164
}
165165

166166
impl<'tcx> ImplHeader<'tcx> {
167-
pub fn with_fresh_ty_vars<'a,'tcx>(selcx: &mut traits::SelectionContext<'a,'tcx>,
168-
impl_def_id: DefId)
169-
-> ImplHeader<'tcx>
167+
pub fn with_fresh_ty_vars<'a>(selcx: &mut traits::SelectionContext<'a, 'tcx>,
168+
impl_def_id: DefId)
169+
-> ImplHeader<'tcx>
170170
{
171171
let tcx = selcx.tcx();
172172
let impl_generics = tcx.lookup_item_type(impl_def_id).generics;
173173
let impl_substs = selcx.infcx().fresh_substs_for_generics(DUMMY_SP, &impl_generics);
174174

175175
let header = ImplHeader {
176176
impl_def_id: impl_def_id,
177-
self_ty: tcx.lookup_item_type(impl_def_id),
177+
self_ty: tcx.lookup_item_type(impl_def_id).ty,
178178
trait_ref: tcx.impl_trait_ref(impl_def_id),
179-
predicates: tcx.lookup_predicates(impl_def_id),
180-
}.subst(tcx, impl_substs);
179+
predicates: tcx.lookup_predicates(impl_def_id).predicates.into_vec(),
180+
}.subst(tcx, &impl_substs);
181181

182-
let Normalized { value: mut header, obligations: obligations } =
183-
proect::normalize(selcx, ObligationCause::dummy(), &header);
182+
let traits::Normalized { value: mut header, obligations } =
183+
traits::normalize(selcx, traits::ObligationCause::dummy(), &header);
184184

185185
header.predicates.extend(obligations.into_iter().map(|o| o.predicate));
186186
header

src/librustc/middle/ty/structural_impls.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -452,8 +452,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::ImplHeader<'tcx> {
452452
impl_def_id: self.impl_def_id,
453453
self_ty: self.self_ty.fold_with(folder),
454454
trait_ref: self.trait_ref.map(|t| t.fold_with(folder)),
455-
predicates: self.predicates.into_iter().map(|p| p.fold_with(folder)).collect(),
456-
polarity: self.polarity,
455+
predicates: self.predicates.iter().map(|p| p.fold_with(folder)).collect(),
457456
}
458457
}
459458

src/librustc_typeck/coherence/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ use CrateCtxt;
3535
use middle::infer::{self, InferCtxt, TypeOrigin, new_infer_ctxt};
3636
use std::cell::RefCell;
3737
use std::rc::Rc;
38+
use syntax::ast;
3839
use syntax::codemap::Span;
40+
use syntax::errors::DiagnosticBuilder;
3941
use util::nodemap::{DefIdMap, FnvHashMap};
4042
use rustc::dep_graph::DepNode;
4143
use rustc::front::map as hir_map;
@@ -519,6 +521,13 @@ fn enforce_trait_manually_implementable(tcx: &TyCtxt, sp: Span, trait_def_id: De
519521
err.emit();
520522
}
521523

524+
// Factored out into helper because the error cannot be defined in multiple locations.
525+
pub fn report_duplicate_item<'tcx>(tcx: &TyCtxt<'tcx>, sp: Span, name: ast::Name)
526+
-> DiagnosticBuilder<'tcx>
527+
{
528+
struct_span_err!(tcx.sess, sp, E0201, "duplicate definitions with name `{}`:", name)
529+
}
530+
522531
pub fn check_coherence(crate_context: &CrateCtxt) {
523532
let _task = crate_context.tcx.dep_graph.in_task(DepNode::Coherence);
524533
let infcx = new_infer_ctxt(crate_context.tcx, &crate_context.tcx.tables, None);

src/librustc_typeck/coherence/overlap.rs

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
// except according to those terms.
1010

1111
//! Overlap: No two impls for the same trait are implemented for the
12-
//! same type.
12+
//! same type. Likewise, no two inherent impls for a given type
13+
//! constructor provide a method with the same name.
1314
1415
use middle::cstore::{CrateStore, LOCAL_CRATE};
1516
use middle::def_id::DefId;
@@ -115,7 +116,6 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
115116
}
116117
}
117118

118-
119119
fn check_if_impls_overlap(&self,
120120
impl1_def_id: DefId,
121121
impl2_def_id: DefId)
@@ -128,8 +128,8 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
128128
impl2_def_id);
129129

130130
let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None);
131-
if let Some(trait_ref) = traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id) {
132-
self.report_overlap_error(impl1_def_id, impl2_def_id, trait_ref);
131+
if let Some(header) = traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id) {
132+
self.report_overlap_error(impl1_def_id, impl2_def_id, header.trait_ref.unwrap());
133133
}
134134
}
135135
}
@@ -150,13 +150,13 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
150150
}).unwrap_or(String::new())
151151
};
152152

153-
let mut err = struct_span_err!(self.tcx.sess, self.span_of_impl(impl1), E0119,
153+
let mut err = struct_span_err!(self.tcx.sess, self.span_of_def_id(impl1), E0119,
154154
"conflicting implementations of trait `{}`{}:",
155155
trait_ref,
156156
self_type);
157157

158158
if impl2.is_local() {
159-
span_note!(&mut err, self.span_of_impl(impl2),
159+
span_note!(&mut err, self.span_of_def_id(impl2),
160160
"conflicting implementation is here:");
161161
} else {
162162
let cname = self.tcx.sess.cstore.crate_name(impl2.krate);
@@ -165,10 +165,61 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
165165
err.emit();
166166
}
167167

168-
fn span_of_impl(&self, impl_did: DefId) -> Span {
169-
let node_id = self.tcx.map.as_local_node_id(impl_did).unwrap();
168+
fn span_of_def_id(&self, did: DefId) -> Span {
169+
let node_id = self.tcx.map.as_local_node_id(did).unwrap();
170170
self.tcx.map.span(node_id)
171171
}
172+
173+
fn check_for_common_items_in_impls(&self, impl1: DefId, impl2: DefId) {
174+
#[derive(Copy, Clone, PartialEq)]
175+
enum Namespace { Type, Value }
176+
177+
fn name_and_namespace(tcx: &TyCtxt, item: &ty::ImplOrTraitItemId)
178+
-> (ast::Name, Namespace)
179+
{
180+
let name = tcx.impl_or_trait_item(item.def_id()).name();
181+
(name, match *item {
182+
ty::TypeTraitItemId(..) => Namespace::Type,
183+
ty::ConstTraitItemId(..) => Namespace::Value,
184+
ty::MethodTraitItemId(..) => Namespace::Value,
185+
})
186+
}
187+
188+
let impl_items = self.tcx.impl_items.borrow();
189+
190+
for item1 in &impl_items[&impl1] {
191+
let (name, namespace) = name_and_namespace(&self.tcx, item1);
192+
193+
for item2 in &impl_items[&impl2] {
194+
if (name, namespace) == name_and_namespace(&self.tcx, item2) {
195+
let mut err = super::report_duplicate_item(
196+
&self.tcx, self.span_of_def_id(item1.def_id()), name);
197+
span_note!(&mut err, self.span_of_def_id(item2.def_id()),
198+
"conflicting definition is here:");
199+
err.emit();
200+
}
201+
}
202+
}
203+
}
204+
205+
fn check_for_overlapping_inherent_impls(&self, ty_def_id: DefId) {
206+
let _task = self.tcx.dep_graph.in_task(DepNode::CoherenceOverlapInherentCheck(ty_def_id));
207+
208+
let inherent_impls = self.tcx.inherent_impls.borrow();
209+
let impls = match inherent_impls.get(&ty_def_id) {
210+
Some(impls) => impls,
211+
None => return
212+
};
213+
214+
for (i, &impl1_def_id) in impls.iter().enumerate() {
215+
for &impl2_def_id in &impls[(i+1)..] {
216+
let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None);
217+
if traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id).is_some() {
218+
self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id)
219+
}
220+
}
221+
}
222+
}
172223
}
173224

174225

@@ -180,6 +231,11 @@ impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> {
180231
self.check_for_overlapping_impls_of_trait(trait_def_id);
181232
}
182233

234+
hir::ItemEnum(..) | hir::ItemStruct(..) => {
235+
let type_def_id = self.tcx.map.local_def_id(item.id);
236+
self.check_for_overlapping_inherent_impls(type_def_id);
237+
}
238+
183239
hir::ItemDefaultImpl(..) => {
184240
// look for another default impl; note that due to the
185241
// general orphan/coherence rules, it must always be

src/librustc_typeck/collect.rs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ use lint;
6363
use middle::def::Def;
6464
use middle::def_id::DefId;
6565
use constrained_type_params as ctp;
66+
use coherence;
6667
use middle::lang_items::SizedTraitLangItem;
6768
use middle::resolve_lifetime;
6869
use middle::const_eval::{self, ConstVal};
@@ -750,17 +751,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
750751
_ => &mut seen_value_items,
751752
};
752753
if !seen_items.insert(impl_item.name) {
753-
let desc = match impl_item.node {
754-
hir::ImplItemKind::Const(_, _) => "associated constant",
755-
hir::ImplItemKind::Type(_) => "associated type",
756-
hir::ImplItemKind::Method(ref sig, _) =>
757-
match sig.explicit_self.node {
758-
hir::SelfStatic => "associated function",
759-
_ => "method",
760-
},
761-
};
762-
763-
span_err!(tcx.sess, impl_item.span, E0201, "duplicate {}", desc);
754+
coherence::report_duplicate_item(tcx, impl_item.span, impl_item.name).emit();
764755
}
765756

766757
if let hir::ImplItemKind::Const(ref ty, _) = impl_item.node {

src/librustc_typeck/diagnostics.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2285,6 +2285,21 @@ impl Baz for Foo {
22852285
type Quux = u32;
22862286
}
22872287
```
2288+
2289+
Note, however, that items with the same name are allowed for inherent `impl`
2290+
blocks that don't overlap:
2291+
2292+
```
2293+
struct Foo<T>(T);
2294+
2295+
impl Foo<u8> {
2296+
fn bar(&self) -> bool { self.0 > 5 }
2297+
}
2298+
2299+
impl Foo<bool> {
2300+
fn bar(&self) -> bool { self.0 }
2301+
}
2302+
```
22882303
"##,
22892304

22902305
E0202: r##"

0 commit comments

Comments
 (0)