Skip to content

Commit 632ad19

Browse files
committed
Auto merge of #46192 - arielb1:locally-coherent, r=nikomatsakis
coherence: fix is_knowable logic A trait-ref that passes the orphan-check rules can still be implemented in a crate downstream from our crate (for example, `LocalType for LocalTrait<_>` might be matched by a `LocalType for LocalTrait<TypeFromDownstreamCrate>`), and this should be known by the `is_knowable` logic. Trait selection had a hackfix for this, but it's an hacky fix that does not handle all cases. This patch removes it. fixes #43355. r? @nikomatsakis Needs a crater run
2 parents a62910b + 425c2c3 commit 632ad19

File tree

10 files changed

+453
-141
lines changed

10 files changed

+453
-141
lines changed

src/librustc/lint/builtin.rs

+7
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,12 @@ declare_lint! {
204204
"detects generic lifetime arguments in path segments with late bound lifetime parameters"
205205
}
206206

207+
declare_lint! {
208+
pub INCOHERENT_FUNDAMENTAL_IMPLS,
209+
Warn,
210+
"potentially-conflicting impls were erroneously allowed"
211+
}
212+
207213
declare_lint! {
208214
pub DEPRECATED,
209215
Warn,
@@ -267,6 +273,7 @@ impl LintPass for HardwiredLints {
267273
MISSING_FRAGMENT_SPECIFIER,
268274
PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES,
269275
LATE_BOUND_LIFETIME_ARGUMENTS,
276+
INCOHERENT_FUNDAMENTAL_IMPLS,
270277
DEPRECATED,
271278
UNUSED_UNSAFE,
272279
UNUSED_MUT,

src/librustc/traits/coherence.rs

+192-57
Large diffs are not rendered by default.

src/librustc/traits/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ mod structural_impls;
6060
pub mod trans;
6161
mod util;
6262

63+
// Whether to enable bug compatibility with issue #43355
64+
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
65+
pub enum IntercrateMode {
66+
Issue43355,
67+
Fixed
68+
}
69+
6370
/// An `Obligation` represents some trait reference (e.g. `int:Eq`) for
6471
/// which the vtable must be found. The process of finding a vtable is
6572
/// called "resolving" the `Obligation`. This process consists of

src/librustc/traits/select.rs

+60-39
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@
1313
use self::SelectionCandidate::*;
1414
use self::EvaluationResult::*;
1515

16-
use super::coherence;
16+
use super::coherence::{self, Conflict};
1717
use super::DerivedObligationCause;
18+
use super::IntercrateMode;
1819
use super::project;
1920
use super::project::{normalize_with_depth, Normalized, ProjectionCacheKey};
2021
use super::{PredicateObligation, TraitObligation, ObligationCause};
@@ -87,7 +88,7 @@ pub struct SelectionContext<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> {
8788
/// other words, we consider `$0 : Bar` to be unimplemented if
8889
/// there is no type that the user could *actually name* that
8990
/// would satisfy it. This avoids crippling inference, basically.
90-
intercrate: bool,
91+
intercrate: Option<IntercrateMode>,
9192

9293
inferred_obligations: SnapshotVec<InferredObligationsSnapshotVecDelegate<'tcx>>,
9394

@@ -111,21 +112,24 @@ impl IntercrateAmbiguityCause {
111112
/// See #23980 for details.
112113
pub fn add_intercrate_ambiguity_hint<'a, 'tcx>(&self,
113114
err: &mut ::errors::DiagnosticBuilder) {
115+
err.note(&self.intercrate_ambiguity_hint());
116+
}
117+
118+
pub fn intercrate_ambiguity_hint(&self) -> String {
114119
match self {
115120
&IntercrateAmbiguityCause::DownstreamCrate { ref trait_desc, ref self_desc } => {
116121
let self_desc = if let &Some(ref ty) = self_desc {
117122
format!(" for type `{}`", ty)
118123
} else { "".to_string() };
119-
err.note(&format!("downstream crates may implement trait `{}`{}",
120-
trait_desc, self_desc));
124+
format!("downstream crates may implement trait `{}`{}", trait_desc, self_desc)
121125
}
122126
&IntercrateAmbiguityCause::UpstreamCrateUpdate { ref trait_desc, ref self_desc } => {
123127
let self_desc = if let &Some(ref ty) = self_desc {
124128
format!(" for type `{}`", ty)
125129
} else { "".to_string() };
126-
err.note(&format!("upstream crates may add new impl of trait `{}`{} \
127-
in future versions",
128-
trait_desc, self_desc));
130+
format!("upstream crates may add new impl of trait `{}`{} \
131+
in future versions",
132+
trait_desc, self_desc)
129133
}
130134
}
131135
}
@@ -417,17 +421,19 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
417421
SelectionContext {
418422
infcx,
419423
freshener: infcx.freshener(),
420-
intercrate: false,
424+
intercrate: None,
421425
inferred_obligations: SnapshotVec::new(),
422426
intercrate_ambiguity_causes: Vec::new(),
423427
}
424428
}
425429

426-
pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>) -> SelectionContext<'cx, 'gcx, 'tcx> {
430+
pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
431+
mode: IntercrateMode) -> SelectionContext<'cx, 'gcx, 'tcx> {
432+
debug!("intercrate({:?})", mode);
427433
SelectionContext {
428434
infcx,
429435
freshener: infcx.freshener(),
430-
intercrate: true,
436+
intercrate: Some(mode),
431437
inferred_obligations: SnapshotVec::new(),
432438
intercrate_ambiguity_causes: Vec::new(),
433439
}
@@ -758,7 +764,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
758764
debug!("evaluate_trait_predicate_recursively({:?})",
759765
obligation);
760766

761-
if !self.intercrate && obligation.is_global() {
767+
if !self.intercrate.is_some() && obligation.is_global() {
762768
// If a param env is consistent, global obligations do not depend on its particular
763769
// value in order to work, so we can clear out the param env and get better
764770
// caching. (If the current param env is inconsistent, we don't care what happens).
@@ -814,7 +820,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
814820
// terms of `Fn` etc, but we could probably make this more
815821
// precise still.
816822
let unbound_input_types = stack.fresh_trait_ref.input_types().any(|ty| ty.is_fresh());
817-
if unbound_input_types && self.intercrate {
823+
// this check was an imperfect workaround for a bug n the old
824+
// intercrate mode, it should be removed when that goes away.
825+
if unbound_input_types &&
826+
self.intercrate == Some(IntercrateMode::Issue43355)
827+
{
818828
debug!("evaluate_stack({:?}) --> unbound argument, intercrate --> ambiguous",
819829
stack.fresh_trait_ref);
820830
// Heuristics: show the diagnostics when there are no candidates in crate.
@@ -1077,28 +1087,32 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
10771087
return Ok(None);
10781088
}
10791089

1080-
if !self.is_knowable(stack) {
1081-
debug!("coherence stage: not knowable");
1082-
// Heuristics: show the diagnostics when there are no candidates in crate.
1083-
let candidate_set = self.assemble_candidates(stack)?;
1084-
if !candidate_set.ambiguous && candidate_set.vec.is_empty() {
1085-
let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
1086-
let self_ty = trait_ref.self_ty();
1087-
let trait_desc = trait_ref.to_string();
1088-
let self_desc = if self_ty.has_concrete_skeleton() {
1089-
Some(self_ty.to_string())
1090-
} else {
1091-
None
1092-
};
1093-
let cause = if !coherence::trait_ref_is_local_or_fundamental(self.tcx(),
1094-
trait_ref) {
1095-
IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc }
1096-
} else {
1097-
IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc }
1098-
};
1099-
self.intercrate_ambiguity_causes.push(cause);
1090+
match self.is_knowable(stack) {
1091+
None => {}
1092+
Some(conflict) => {
1093+
debug!("coherence stage: not knowable");
1094+
// Heuristics: show the diagnostics when there are no candidates in crate.
1095+
let candidate_set = self.assemble_candidates(stack)?;
1096+
if !candidate_set.ambiguous && candidate_set.vec.iter().all(|c| {
1097+
!self.evaluate_candidate(stack, &c).may_apply()
1098+
}) {
1099+
let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
1100+
let self_ty = trait_ref.self_ty();
1101+
let trait_desc = trait_ref.to_string();
1102+
let self_desc = if self_ty.has_concrete_skeleton() {
1103+
Some(self_ty.to_string())
1104+
} else {
1105+
None
1106+
};
1107+
let cause = if let Conflict::Upstream = conflict {
1108+
IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc }
1109+
} else {
1110+
IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc }
1111+
};
1112+
self.intercrate_ambiguity_causes.push(cause);
1113+
}
1114+
return Ok(None);
11001115
}
1101-
return Ok(None);
11021116
}
11031117

11041118
let candidate_set = self.assemble_candidates(stack)?;
@@ -1205,12 +1219,12 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
12051219

12061220
fn is_knowable<'o>(&mut self,
12071221
stack: &TraitObligationStack<'o, 'tcx>)
1208-
-> bool
1222+
-> Option<Conflict>
12091223
{
1210-
debug!("is_knowable(intercrate={})", self.intercrate);
1224+
debug!("is_knowable(intercrate={:?})", self.intercrate);
12111225

1212-
if !self.intercrate {
1213-
return true;
1226+
if !self.intercrate.is_some() {
1227+
return None;
12141228
}
12151229

12161230
let obligation = &stack.obligation;
@@ -1221,7 +1235,14 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
12211235
// bound regions
12221236
let trait_ref = predicate.skip_binder().trait_ref;
12231237

1224-
coherence::trait_ref_is_knowable(self.tcx(), trait_ref)
1238+
let result = coherence::trait_ref_is_knowable(self.tcx(), trait_ref);
1239+
if let (Some(Conflict::Downstream { used_to_be_broken: true }),
1240+
Some(IntercrateMode::Issue43355)) = (result, self.intercrate) {
1241+
debug!("is_knowable: IGNORING conflict to be bug-compatible with #43355");
1242+
None
1243+
} else {
1244+
result
1245+
}
12251246
}
12261247

12271248
/// Returns true if the global caches can be used.
@@ -1246,7 +1267,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
12461267
// the master cache. Since coherence executes pretty quickly,
12471268
// it's not worth going to more trouble to increase the
12481269
// hit-rate I don't think.
1249-
if self.intercrate {
1270+
if self.intercrate.is_some() {
12501271
return false;
12511272
}
12521273

src/librustc/traits/specialize/mod.rs

+29-10
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ use ty::{self, TyCtxt, TypeFoldable};
3030
use syntax_pos::DUMMY_SP;
3131
use std::rc::Rc;
3232

33+
use lint;
34+
3335
pub mod specialization_graph;
3436

3537
/// Information pertinent to an overlapping impl error.
@@ -325,16 +327,33 @@ pub(super) fn specialization_graph_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx
325327
// This is where impl overlap checking happens:
326328
let insert_result = sg.insert(tcx, impl_def_id);
327329
// Report error if there was one.
328-
if let Err(overlap) = insert_result {
329-
let mut err = struct_span_err!(tcx.sess,
330-
tcx.span_of_impl(impl_def_id).unwrap(),
331-
E0119,
332-
"conflicting implementations of trait `{}`{}:",
333-
overlap.trait_desc,
334-
overlap.self_desc.clone().map_or(String::new(),
335-
|ty| {
336-
format!(" for type `{}`", ty)
337-
}));
330+
let (overlap, used_to_be_allowed) = match insert_result {
331+
Err(overlap) => (Some(overlap), false),
332+
Ok(opt_overlap) => (opt_overlap, true)
333+
};
334+
335+
if let Some(overlap) = overlap {
336+
let msg = format!("conflicting implementations of trait `{}`{}:{}",
337+
overlap.trait_desc,
338+
overlap.self_desc.clone().map_or(
339+
String::new(), |ty| {
340+
format!(" for type `{}`", ty)
341+
}),
342+
if used_to_be_allowed { " (E0119)" } else { "" }
343+
);
344+
let mut err = if used_to_be_allowed {
345+
tcx.struct_span_lint_node(
346+
lint::builtin::INCOHERENT_FUNDAMENTAL_IMPLS,
347+
tcx.hir.as_local_node_id(impl_def_id).unwrap(),
348+
tcx.span_of_impl(impl_def_id).unwrap(),
349+
&msg)
350+
} else {
351+
struct_span_err!(tcx.sess,
352+
tcx.span_of_impl(impl_def_id).unwrap(),
353+
E0119,
354+
"{}",
355+
msg)
356+
};
338357

339358
match tcx.span_of_impl(overlap.with_impl) {
340359
Ok(span) => {

0 commit comments

Comments
 (0)