Skip to content

Commit 1e3ed61

Browse files
committed
Coherence for default trait implementations
- Don't allow multiple default trait implementations - Allow positive trait implementations just for structs and enums when there's a default implementation for such trait.
1 parent f0e9bd9 commit 1e3ed61

File tree

5 files changed

+95
-32
lines changed

5 files changed

+95
-32
lines changed

src/librustc/middle/traits/select.rs

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1626,6 +1626,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
16261626
Vec::new()
16271627
}
16281628

1629+
ty::ty_trait(..) |
16291630
ty::ty_projection(..) |
16301631
ty::ty_param(..) |
16311632
ty::ty_infer(..) => {
@@ -1639,18 +1640,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
16391640
vec![referent_ty]
16401641
}
16411642

1642-
1643-
ty::ty_trait(ref data) => {
1644-
// Recursively check all supertraits to find out if any further
1645-
// bounds are required and thus we must fulfill.
1646-
let principal =
1647-
data.principal_trait_ref_with_self_ty(self.tcx(),
1648-
self.tcx().types.err);
1649-
1650-
1651-
util::supertraits(self.tcx(), principal).map(|tr| tr.self_ty()).collect()
1652-
}
1653-
16541643
ty::ty_open(element_ty) => {vec![element_ty]},
16551644

16561645
ty::ty_ptr(ty::mt { ty: element_ty, ..}) |

src/librustc/middle/ty.rs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -758,7 +758,7 @@ pub struct ctxt<'tcx> {
758758
pub trait_impls: RefCell<DefIdMap<Rc<RefCell<Vec<ast::DefId>>>>>,
759759

760760
/// Maps a trait onto a list of *default* trait implementations
761-
default_trait_impls: RefCell<DefIdSet>,
761+
default_trait_impls: RefCell<DefIdMap<ast::DefId>>,
762762

763763
/// Maps a DefId of a type to a list of its inherent impls.
764764
/// Contains implementations of methods that are inherent to a type.
@@ -2496,7 +2496,7 @@ pub fn mk_ctxt<'tcx>(s: Session,
24962496
destructor_for_type: RefCell::new(DefIdMap()),
24972497
destructors: RefCell::new(DefIdSet()),
24982498
trait_impls: RefCell::new(DefIdMap()),
2499-
default_trait_impls: RefCell::new(DefIdSet()),
2499+
default_trait_impls: RefCell::new(DefIdMap()),
25002500
inherent_impls: RefCell::new(DefIdMap()),
25012501
impl_items: RefCell::new(DefIdMap()),
25022502
used_unsafe: RefCell::new(NodeSet()),
@@ -5999,18 +5999,26 @@ pub fn item_variances(tcx: &ctxt, item_id: ast::DefId) -> Rc<ItemVariances> {
59995999
|| Rc::new(csearch::get_item_variances(&tcx.sess.cstore, item_id)))
60006000
}
60016001

6002+
pub fn trait_default_impl(tcx: &ctxt, trait_def_id: DefId) -> Option<ast::DefId> {
6003+
match tcx.default_trait_impls.borrow().get(&trait_def_id) {
6004+
Some(id) => Some(*id),
6005+
None => None
6006+
}
6007+
}
6008+
60026009
pub fn trait_has_default_impl(tcx: &ctxt, trait_def_id: DefId) -> bool {
6003-
tcx.default_trait_impls.borrow().contains(&trait_def_id)
6010+
tcx.default_trait_impls.borrow().contains_key(&trait_def_id)
60046011
}
60056012

60066013
/// Records a trait-to-implementation mapping.
6007-
pub fn record_default_trait_implementation(tcx: &ctxt, trait_def_id: DefId) {
6008-
6009-
if tcx.default_trait_impls.borrow().contains(&trait_def_id) {
6010-
return;
6011-
}
6014+
pub fn record_default_trait_implementation(tcx: &ctxt,
6015+
trait_def_id: DefId,
6016+
impl_def_id: DefId) {
60126017

6013-
tcx.default_trait_impls.borrow_mut().insert(trait_def_id);
6018+
// We're using the latest implementation found as the reference one.
6019+
// Duplicated implementations are caught and reported in the coherence
6020+
// step.
6021+
tcx.default_trait_impls.borrow_mut().insert(trait_def_id, impl_def_id);
60146022
}
60156023

60166024

@@ -6100,7 +6108,8 @@ pub fn populate_implementations_for_trait_if_necessary(
61006108
let impl_items = csearch::get_impl_items(&tcx.sess.cstore, implementation_def_id);
61016109

61026110
if csearch::is_default_trait(&tcx.sess.cstore, implementation_def_id) {
6103-
record_default_trait_implementation(tcx, trait_id);
6111+
record_default_trait_implementation(tcx, trait_id,
6112+
implementation_def_id);
61046113
tcx.populated_external_traits.borrow_mut().insert(trait_id);
61056114

61066115
// Nothing else to do for default trait implementations since

src/librustc_typeck/coherence/overlap.rs

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,19 @@ use middle::infer::{self, new_infer_ctxt};
1717
use syntax::ast::{DefId};
1818
use syntax::ast::{LOCAL_CRATE};
1919
use syntax::ast;
20-
use syntax::codemap::{Span};
20+
use syntax::ast_util;
21+
use syntax::visit;
22+
use syntax::codemap::Span;
2123
use util::ppaux::Repr;
2224

2325
pub fn check(tcx: &ty::ctxt) {
24-
let overlap = OverlapChecker { tcx: tcx };
26+
let mut overlap = OverlapChecker { tcx: tcx };
2527
overlap.check_for_overlapping_impls();
28+
visit::walk_crate(&mut overlap, tcx.map.krate());
2629
}
2730

2831
struct OverlapChecker<'cx, 'tcx:'cx> {
29-
tcx: &'cx ty::ctxt<'tcx>
32+
tcx: &'cx ty::ctxt<'tcx>,
3033
}
3134

3235
impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
@@ -90,17 +93,28 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
9093
return;
9194
}
9295

93-
span_err!(self.tcx.sess, self.span_of_impl(impl1_def_id), E0119,
96+
self.report_overlap_error(trait_def_id, impl1_def_id, impl2_def_id);
97+
}
98+
99+
fn report_overlap_error(&self, trait_def_id: ast::DefId,
100+
impl1: ast::DefId, impl2: ast::DefId) {
101+
102+
span_err!(self.tcx.sess, self.span_of_impl(impl1), E0119,
94103
"conflicting implementations for trait `{}`",
95104
ty::item_path_str(self.tcx, trait_def_id));
96105

97-
if impl2_def_id.krate == ast::LOCAL_CRATE {
98-
span_note!(self.tcx.sess, self.span_of_impl(impl2_def_id),
106+
self.report_overlap_note(impl1, impl2);
107+
}
108+
109+
fn report_overlap_note(&self, impl1: ast::DefId, impl2: ast::DefId) {
110+
111+
if impl2.krate == ast::LOCAL_CRATE {
112+
span_note!(self.tcx.sess, self.span_of_impl(impl2),
99113
"note conflicting implementation here");
100114
} else {
101115
let crate_store = &self.tcx.sess.cstore;
102-
let cdata = crate_store.get_crate_data(impl2_def_id.krate);
103-
span_note!(self.tcx.sess, self.span_of_impl(impl1_def_id),
116+
let cdata = crate_store.get_crate_data(impl2.krate);
117+
span_note!(self.tcx.sess, self.span_of_impl(impl1),
104118
"conflicting implementation in crate `{}`",
105119
cdata.name);
106120
}
@@ -111,3 +125,52 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
111125
self.tcx.map.span(impl_did.node)
112126
}
113127
}
128+
129+
130+
impl<'cx, 'tcx,'v> visit::Visitor<'v> for OverlapChecker<'cx, 'tcx> {
131+
fn visit_item(&mut self, item: &'v ast::Item) {
132+
match item.node {
133+
ast::ItemImpl(_, ast::ImplPolarity::Positive, _, Some(ref ast_trait_ref), _, _) => {
134+
let trait_ref = ty::node_id_to_trait_ref(self.tcx, ast_trait_ref.ref_id);
135+
match ty::trait_default_impl(self.tcx, trait_ref.def_id) {
136+
Some(default_impl) => {
137+
match trait_ref.self_ty().sty {
138+
ty::ty_struct(..) | ty::ty_enum(..) => {},
139+
_ => {
140+
let impl_def_id = ast_util::local_def(item.id);
141+
span_err!(self.tcx.sess, self.span_of_impl(impl_def_id), E0317,
142+
"implementations for traits providing default \
143+
implementations are only allowed on structs and enums");
144+
145+
self.report_overlap_note(impl_def_id, default_impl);
146+
}
147+
}
148+
}
149+
None => {}
150+
}
151+
}
152+
ast::ItemDefaultImpl(_, _) => {
153+
let impl_def_id = ast_util::local_def(item.id);
154+
match ty::impl_trait_ref(self.tcx, impl_def_id) {
155+
Some(ref trait_ref) => {
156+
match ty::trait_default_impl(self.tcx, trait_ref.def_id) {
157+
Some(other_impl) if other_impl != impl_def_id => {
158+
self.report_overlap_error(trait_ref.def_id,
159+
other_impl,
160+
impl_def_id);
161+
}
162+
Some(_) => {}
163+
None => {
164+
self.tcx.sess.bug(
165+
&format!("no default implementation recorded for `{:?}`",
166+
item)[]);
167+
}
168+
}
169+
}
170+
_ => {}
171+
}
172+
}
173+
_ => {}
174+
}
175+
}
176+
}

src/librustc_typeck/collect.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,7 @@ fn convert_item(ccx: &CollectCtxt, it: &ast::Item) {
652652
let trait_ref = astconv::instantiate_trait_ref(ccx, &ExplicitRscope,
653653
ast_trait_ref, None, None);
654654

655-
ty::record_default_trait_implementation(tcx, trait_ref.def_id)
655+
ty::record_default_trait_implementation(tcx, trait_ref.def_id, local_def(it.id))
656656
}
657657
ast::ItemImpl(_, _,
658658
ref generics,
@@ -1190,6 +1190,7 @@ fn convert_typed_item<'a, 'tcx>(ccx: &CollectCtxt<'a, 'tcx>,
11901190
ast::ItemStruct(_, ref generics) => {
11911191
ty_generic_bounds_for_type_or_impl(ccx, &scheme.generics, generics)
11921192
}
1193+
ast::ItemDefaultImpl(..) |
11931194
ast::ItemTrait(..) |
11941195
ast::ItemExternCrate(..) |
11951196
ast::ItemUse(..) |

src/librustc_typeck/diagnostics.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@ register_diagnostics! {
172172
E0248, // found value name used as a type
173173
E0249, // expected constant expr for array length
174174
E0250, // expected constant expr for array length
175-
E0316 // can't create default impls for traits outside their crates
175+
E0316, // can't create default impls for traits outside their crates
176+
E0317
176177
}
177178

178179
__build_diagnostic_array! { DIAGNOSTICS }

0 commit comments

Comments
 (0)