Skip to content

Commit d49f278

Browse files
qnighynikomatsakis
authored andcommitted
Add hints when intercrate ambiguity causes overlap.
1 parent 2f1ef9e commit d49f278

File tree

6 files changed

+95
-22
lines changed

6 files changed

+95
-22
lines changed

src/librustc/traits/coherence.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use hir::def_id::{DefId, LOCAL_CRATE};
1414
use syntax_pos::DUMMY_SP;
1515
use traits::{self, Normalized, SelectionContext, Obligation, ObligationCause, Reveal};
16+
use traits::select::IntercrateAmbiguityCause;
1617
use ty::{self, Ty, TyCtxt};
1718
use ty::subst::Subst;
1819

@@ -21,12 +22,17 @@ use infer::{InferCtxt, InferOk};
2122
#[derive(Copy, Clone)]
2223
struct InferIsLocal(bool);
2324

25+
pub struct OverlapResult<'tcx> {
26+
pub impl_header: ty::ImplHeader<'tcx>,
27+
pub intercrate_ambiguity_causes: Vec<IntercrateAmbiguityCause>,
28+
}
29+
2430
/// If there are types that satisfy both impls, returns a suitably-freshened
2531
/// `ImplHeader` with those types substituted
2632
pub fn overlapping_impls<'cx, 'gcx, 'tcx>(infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
2733
impl1_def_id: DefId,
2834
impl2_def_id: DefId)
29-
-> Option<ty::ImplHeader<'tcx>>
35+
-> Option<OverlapResult<'tcx>>
3036
{
3137
debug!("impl_can_satisfy(\
3238
impl1_def_id={:?}, \
@@ -65,7 +71,7 @@ fn with_fresh_ty_vars<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, '
6571
fn overlap<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
6672
a_def_id: DefId,
6773
b_def_id: DefId)
68-
-> Option<ty::ImplHeader<'tcx>>
74+
-> Option<OverlapResult<'tcx>>
6975
{
7076
debug!("overlap(a_def_id={:?}, b_def_id={:?})",
7177
a_def_id,
@@ -113,7 +119,10 @@ fn overlap<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
113119
return None
114120
}
115121

116-
Some(selcx.infcx().resolve_type_vars_if_possible(&a_impl_header))
122+
Some(OverlapResult {
123+
impl_header: selcx.infcx().resolve_type_vars_if_possible(&a_impl_header),
124+
intercrate_ambiguity_causes: selcx.intercrate_ambiguity_causes().to_vec(),
125+
})
117126
}
118127

119128
pub fn trait_ref_is_knowable<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,

src/librustc/traits/mod.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,7 @@ use std::rc::Rc;
2828
use syntax::ast;
2929
use syntax_pos::{Span, DUMMY_SP};
3030

31-
pub use self::coherence::orphan_check;
32-
pub use self::coherence::overlapping_impls;
33-
pub use self::coherence::OrphanCheckErr;
31+
pub use self::coherence::{orphan_check, overlapping_impls, OrphanCheckErr, OverlapResult};
3432
pub use self::fulfill::{FulfillmentContext, RegionObligation};
3533
pub use self::project::MismatchedProjectionTypes;
3634
pub use self::project::{normalize, normalize_projection_type, Normalized};
@@ -39,6 +37,7 @@ pub use self::object_safety::ObjectSafetyViolation;
3937
pub use self::object_safety::MethodViolationCode;
4038
pub use self::on_unimplemented::{OnUnimplementedDirective, OnUnimplementedNote};
4139
pub use self::select::{EvaluationCache, SelectionContext, SelectionCache};
40+
pub use self::select::IntercrateAmbiguityCause;
4241
pub use self::specialize::{OverlapError, specialization_graph, translate_substs};
4342
pub use self::specialize::{SpecializesCache, find_associated_item};
4443
pub use self::util::elaborate_predicates;

src/librustc/traits/select.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ pub struct SelectionContext<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> {
9090
intercrate: bool,
9191

9292
inferred_obligations: SnapshotVec<InferredObligationsSnapshotVecDelegate<'tcx>>,
93+
94+
intercrate_ambiguity_causes: Vec<IntercrateAmbiguityCause>,
95+
}
96+
97+
#[derive(Clone)]
98+
pub enum IntercrateAmbiguityCause {
99+
DownstreamCrate(DefId),
100+
UpstreamCrateUpdate(DefId),
93101
}
94102

95103
// A stack that walks back up the stack frame.
@@ -380,6 +388,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
380388
freshener: infcx.freshener(),
381389
intercrate: false,
382390
inferred_obligations: SnapshotVec::new(),
391+
intercrate_ambiguity_causes: Vec::new(),
383392
}
384393
}
385394

@@ -389,6 +398,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
389398
freshener: infcx.freshener(),
390399
intercrate: true,
391400
inferred_obligations: SnapshotVec::new(),
401+
intercrate_ambiguity_causes: Vec::new(),
392402
}
393403
}
394404

@@ -404,6 +414,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
404414
self.infcx
405415
}
406416

417+
pub fn intercrate_ambiguity_causes(&self) -> &[IntercrateAmbiguityCause] {
418+
&self.intercrate_ambiguity_causes
419+
}
420+
407421
/// Wraps the inference context's in_snapshot s.t. snapshot handling is only from the selection
408422
/// context's self.
409423
fn in_snapshot<R, F>(&mut self, f: F) -> R
@@ -757,6 +771,14 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
757771
if unbound_input_types && self.intercrate {
758772
debug!("evaluate_stack({:?}) --> unbound argument, intercrate --> ambiguous",
759773
stack.fresh_trait_ref);
774+
// Heuristics: show the diagnostics when there are no candidates in crate.
775+
if let Ok(candidate_set) = self.assemble_candidates(stack) {
776+
if !candidate_set.ambiguous && candidate_set.vec.is_empty() {
777+
let did = stack.fresh_trait_ref.def_id();
778+
self.intercrate_ambiguity_causes.push(
779+
IntercrateAmbiguityCause::DownstreamCrate(did));
780+
}
781+
}
760782
return EvaluatedToAmbig;
761783
}
762784
if unbound_input_types &&
@@ -1003,6 +1025,13 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
10031025

10041026
if !self.is_knowable(stack) {
10051027
debug!("coherence stage: not knowable");
1028+
// Heuristics: show the diagnostics when there are no candidates in crate.
1029+
let candidate_set = self.assemble_candidates(stack)?;
1030+
if !candidate_set.ambiguous && candidate_set.vec.is_empty() {
1031+
let did = stack.obligation.predicate.def_id();
1032+
self.intercrate_ambiguity_causes.push(
1033+
IntercrateAmbiguityCause::UpstreamCrateUpdate(did));
1034+
}
10061035
return Ok(None);
10071036
}
10081037

src/librustc/traits/specialize/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use hir::def_id::DefId;
2525
use infer::{InferCtxt, InferOk};
2626
use ty::subst::{Subst, Substs};
2727
use traits::{self, Reveal, ObligationCause};
28+
use traits::select::IntercrateAmbiguityCause;
2829
use ty::{self, TyCtxt, TypeFoldable};
2930
use syntax_pos::DUMMY_SP;
3031
use std::rc::Rc;
@@ -36,6 +37,7 @@ pub struct OverlapError {
3637
pub with_impl: DefId,
3738
pub trait_desc: String,
3839
pub self_desc: Option<String>,
40+
pub intercrate_ambiguity_causes: Vec<IntercrateAmbiguityCause>,
3941
}
4042

4143
/// Given a subst for the requested impl, translate it to a subst
@@ -337,6 +339,20 @@ pub(super) fn specialization_graph_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx
337339
}
338340
}
339341

342+
for cause in &overlap.intercrate_ambiguity_causes {
343+
match cause {
344+
&IntercrateAmbiguityCause::DownstreamCrate(def_id) => {
345+
err.note(&format!("downstream crates may implement {}",
346+
tcx.item_path_str(def_id)));
347+
}
348+
&IntercrateAmbiguityCause::UpstreamCrateUpdate(def_id) => {
349+
err.note(&format!("upstream crates may add new impl for {} \
350+
in future versions",
351+
tcx.item_path_str(def_id)));
352+
}
353+
}
354+
}
355+
340356
err.emit();
341357
}
342358
} else {

src/librustc/traits/specialize/specialization_graph.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ impl<'a, 'gcx, 'tcx> Children {
113113
let overlap = traits::overlapping_impls(&infcx,
114114
possible_sibling,
115115
impl_def_id);
116-
if let Some(impl_header) = overlap {
116+
if let Some(overlap) = overlap {
117117
if tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling) {
118118
return Ok((false, false));
119119
}
@@ -123,7 +123,7 @@ impl<'a, 'gcx, 'tcx> Children {
123123

124124
if le == ge {
125125
// overlap, but no specialization; error out
126-
let trait_ref = impl_header.trait_ref.unwrap();
126+
let trait_ref = overlap.impl_header.trait_ref.unwrap();
127127
let self_ty = trait_ref.self_ty();
128128
Err(OverlapError {
129129
with_impl: possible_sibling,
@@ -135,7 +135,8 @@ impl<'a, 'gcx, 'tcx> Children {
135135
Some(self_ty.to_string())
136136
} else {
137137
None
138-
}
138+
},
139+
intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes,
139140
})
140141
} else {
141142
Ok((le, ge))

src/librustc_typeck/coherence/inherent_impls_overlap.rs

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
1212
use rustc::hir;
1313
use rustc::hir::itemlikevisit::ItemLikeVisitor;
1414
use rustc::traits;
15+
use rustc::traits::IntercrateAmbiguityCause;
1516
use rustc::ty::{self, TyCtxt};
1617

1718
pub fn crate_inherent_impls_overlap_check<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
@@ -26,7 +27,8 @@ struct InherentOverlapChecker<'a, 'tcx: 'a> {
2627
}
2728

2829
impl<'a, 'tcx> InherentOverlapChecker<'a, 'tcx> {
29-
fn check_for_common_items_in_impls(&self, impl1: DefId, impl2: DefId) {
30+
fn check_for_common_items_in_impls(&self, impl1: DefId, impl2: DefId,
31+
overlap: traits::OverlapResult) {
3032
#[derive(Copy, Clone, PartialEq)]
3133
enum Namespace {
3234
Type,
@@ -50,16 +52,32 @@ impl<'a, 'tcx> InherentOverlapChecker<'a, 'tcx> {
5052

5153
for &item2 in &impl_items2[..] {
5254
if (name, namespace) == name_and_namespace(item2) {
53-
struct_span_err!(self.tcx.sess,
54-
self.tcx.span_of_impl(item1).unwrap(),
55-
E0592,
56-
"duplicate definitions with name `{}`",
57-
name)
58-
.span_label(self.tcx.span_of_impl(item1).unwrap(),
59-
format!("duplicate definitions for `{}`", name))
60-
.span_label(self.tcx.span_of_impl(item2).unwrap(),
61-
format!("other definition for `{}`", name))
62-
.emit();
55+
let mut err = struct_span_err!(self.tcx.sess,
56+
self.tcx.span_of_impl(item1).unwrap(),
57+
E0592,
58+
"duplicate definitions with name `{}`",
59+
name);
60+
61+
err.span_label(self.tcx.span_of_impl(item1).unwrap(),
62+
format!("duplicate definitions for `{}`", name));
63+
err.span_label(self.tcx.span_of_impl(item2).unwrap(),
64+
format!("other definition for `{}`", name));
65+
66+
for cause in &overlap.intercrate_ambiguity_causes {
67+
match cause {
68+
&IntercrateAmbiguityCause::DownstreamCrate(def_id) => {
69+
err.note(&format!("downstream crates may implement {}",
70+
self.tcx.item_path_str(def_id)));
71+
}
72+
&IntercrateAmbiguityCause::UpstreamCrateUpdate(def_id) => {
73+
err.note(&format!("upstream crates may add new impl for {} \
74+
in future versions",
75+
self.tcx.item_path_str(def_id)));
76+
}
77+
}
78+
}
79+
80+
err.emit();
6381
}
6482
}
6583
}
@@ -71,8 +89,9 @@ impl<'a, 'tcx> InherentOverlapChecker<'a, 'tcx> {
7189
for (i, &impl1_def_id) in impls.iter().enumerate() {
7290
for &impl2_def_id in &impls[(i + 1)..] {
7391
self.tcx.infer_ctxt().enter(|infcx| {
74-
if traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id).is_some() {
75-
self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id)
92+
if let Some(overlap) =
93+
traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id) {
94+
self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap)
7695
}
7796
});
7897
}

0 commit comments

Comments
 (0)