Skip to content

Commit 0c5e181

Browse files
committed
Auto merge of #46192 - arielb1:locally-coherent, r=<try>
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. cc #43355. FIXME: make this a soft error. I suppose we'll crater first. r? @nikomatsakis Needs a crater run
2 parents b9b82fd + 4c08ed5 commit 0c5e181

File tree

10 files changed

+362
-140
lines changed

10 files changed

+362
-140
lines changed

src/librustc/lint/builtin.rs

+7
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,12 @@ declare_lint! {
198198
"detects generic lifetime arguments in path segments with late bound lifetime parameters"
199199
}
200200

201+
declare_lint! {
202+
pub INCOHERENT_FUNDAMENTAL_IMPLS,
203+
Deny,
204+
"potentially-conflicting impls were erroneously allowed"
205+
}
206+
201207
declare_lint! {
202208
pub DEPRECATED,
203209
Warn,
@@ -254,6 +260,7 @@ impl LintPass for HardwiredLints {
254260
MISSING_FRAGMENT_SPECIFIER,
255261
PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES,
256262
LATE_BOUND_LIFETIME_ARGUMENTS,
263+
INCOHERENT_FUNDAMENTAL_IMPLS,
257264
DEPRECATED,
258265
UNUSED_UNSAFE,
259266
UNUSED_MUT

src/librustc/traits/coherence.rs

+100-57
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,26 @@
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::IntercrateMode;
1617
use traits::select::IntercrateAmbiguityCause;
1718
use ty::{self, Ty, TyCtxt};
1819
use ty::subst::Subst;
1920

2021
use infer::{InferCtxt, InferOk};
2122

22-
#[derive(Copy, Clone)]
23-
struct InferIsLocal(bool);
23+
#[derive(Copy, Clone, Debug)]
24+
/// Whether we do the orphan check relative to this crate or
25+
/// to some remote crate.
26+
enum InCrate {
27+
Local,
28+
Remote
29+
}
30+
31+
#[derive(Debug, Copy, Clone)]
32+
pub enum Conflict {
33+
Upstream,
34+
Downstream { used_to_be_broken: bool }
35+
}
2436

2537
pub struct OverlapResult<'tcx> {
2638
pub impl_header: ty::ImplHeader<'tcx>,
@@ -31,16 +43,19 @@ pub struct OverlapResult<'tcx> {
3143
/// `ImplHeader` with those types substituted
3244
pub fn overlapping_impls<'cx, 'gcx, 'tcx>(infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
3345
impl1_def_id: DefId,
34-
impl2_def_id: DefId)
46+
impl2_def_id: DefId,
47+
intercrate_mode: IntercrateMode)
3548
-> Option<OverlapResult<'tcx>>
3649
{
3750
debug!("impl_can_satisfy(\
3851
impl1_def_id={:?}, \
39-
impl2_def_id={:?})",
52+
impl2_def_id={:?},
53+
intercrate_mode={:?})",
4054
impl1_def_id,
41-
impl2_def_id);
55+
impl2_def_id,
56+
intercrate_mode);
4257

43-
let selcx = &mut SelectionContext::intercrate(infcx);
58+
let selcx = &mut SelectionContext::intercrate(infcx, intercrate_mode);
4459
overlap(selcx, impl1_def_id, impl2_def_id)
4560
}
4661

@@ -126,32 +141,49 @@ fn overlap<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
126141
}
127142

128143
pub fn trait_ref_is_knowable<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
129-
trait_ref: ty::TraitRef<'tcx>) -> bool
144+
trait_ref: ty::TraitRef<'tcx>)
145+
-> Option<Conflict>
130146
{
131147
debug!("trait_ref_is_knowable(trait_ref={:?})", trait_ref);
132-
133-
// if the orphan rules pass, that means that no ancestor crate can
134-
// impl this, so it's up to us.
135-
if orphan_check_trait_ref(tcx, trait_ref, InferIsLocal(false)).is_ok() {
136-
debug!("trait_ref_is_knowable: orphan check passed");
137-
return true;
148+
if orphan_check_trait_ref(tcx, trait_ref, InCrate::Remote).is_ok() {
149+
// A downstream or cousin crate is allowed to implement some
150+
// substitution of this trait-ref.
151+
152+
// A trait can be implementable for a trait ref by both the current
153+
// crate and crates downstream of it. Older versions of rustc
154+
// were not aware of this, causing incoherence (issue #43355).
155+
let used_to_be_broken =
156+
orphan_check_trait_ref(tcx, trait_ref, InCrate::Local).is_ok();
157+
if used_to_be_broken {
158+
debug!("trait_ref_is_knowable({:?}) - USED TO BE BROKEN", trait_ref);
159+
}
160+
return Some(Conflict::Downstream { used_to_be_broken });
138161
}
139162

140-
// if the trait is not marked fundamental, then it's always possible that
141-
// an ancestor crate will impl this in the future, if they haven't
142-
// already
143-
if !trait_ref_is_local_or_fundamental(tcx, trait_ref) {
144-
debug!("trait_ref_is_knowable: trait is neither local nor fundamental");
145-
return false;
163+
if trait_ref_is_local_or_fundamental(tcx, trait_ref) {
164+
// This is a local or fundamental trait, so future-compatibility
165+
// is no concern. We know that downstream/cousin crates are not
166+
// allowed to implement a substitution of this trait ref, which
167+
// means impls could only come from dependencies of this crate,
168+
// which we already know about.
169+
return None;
146170
}
147171

148-
// find out when some downstream (or cousin) crate could impl this
149-
// trait-ref, presuming that all the parameters were instantiated
150-
// with downstream types. If not, then it could only be
151-
// implemented by an upstream crate, which means that the impl
152-
// must be visible to us, and -- since the trait is fundamental
153-
// -- we can test.
154-
orphan_check_trait_ref(tcx, trait_ref, InferIsLocal(true)).is_err()
172+
// This is a remote non-fundamental trait, so if another crate
173+
// can be the "final owner" of a substitution of this trait-ref,
174+
// they are allowed to implement it future-compatibly.
175+
//
176+
// However, if we are a final owner, then nobody else can be,
177+
// and if we are an intermediate owner, then we don't care
178+
// about future-compatibility, which means that we're OK if
179+
// we are an owner.
180+
if orphan_check_trait_ref(tcx, trait_ref, InCrate::Local).is_ok() {
181+
debug!("trait_ref_is_knowable: orphan check passed");
182+
return None;
183+
} else {
184+
debug!("trait_ref_is_knowable: nonlocal, nonfundamental, unowned");
185+
return Some(Conflict::Upstream);
186+
}
155187
}
156188

157189
pub fn trait_ref_is_local_or_fundamental<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
@@ -189,30 +221,32 @@ pub fn orphan_check<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
189221
return Ok(());
190222
}
191223

192-
orphan_check_trait_ref(tcx, trait_ref, InferIsLocal(false))
224+
orphan_check_trait_ref(tcx, trait_ref, InCrate::Local)
193225
}
194226

195227
fn orphan_check_trait_ref<'tcx>(tcx: TyCtxt,
196228
trait_ref: ty::TraitRef<'tcx>,
197-
infer_is_local: InferIsLocal)
229+
in_crate: InCrate)
198230
-> Result<(), OrphanCheckErr<'tcx>>
199231
{
200-
debug!("orphan_check_trait_ref(trait_ref={:?}, infer_is_local={})",
201-
trait_ref, infer_is_local.0);
232+
debug!("orphan_check_trait_ref(trait_ref={:?}, in_crate={:?})",
233+
trait_ref, in_crate);
202234

203235
// First, create an ordered iterator over all the type parameters to the trait, with the self
204236
// type appearing first.
205237
// Find the first input type that either references a type parameter OR
206238
// some local type.
207239
for input_ty in trait_ref.input_types() {
208-
if ty_is_local(tcx, input_ty, infer_is_local) {
240+
if ty_is_local(tcx, input_ty, in_crate) {
209241
debug!("orphan_check_trait_ref: ty_is_local `{:?}`", input_ty);
210242

211243
// First local input type. Check that there are no
212244
// uncovered type parameters.
213-
let uncovered_tys = uncovered_tys(tcx, input_ty, infer_is_local);
245+
let uncovered_tys = uncovered_tys(tcx, input_ty, in_crate);
214246
for uncovered_ty in uncovered_tys {
215-
if let Some(param) = uncovered_ty.walk().find(|t| is_type_parameter(t)) {
247+
if let Some(param) = uncovered_ty.walk()
248+
.find(|t| is_possibly_remote_type(t, in_crate))
249+
{
216250
debug!("orphan_check_trait_ref: uncovered type `{:?}`", param);
217251
return Err(OrphanCheckErr::UncoveredTy(param));
218252
}
@@ -224,11 +258,11 @@ fn orphan_check_trait_ref<'tcx>(tcx: TyCtxt,
224258

225259
// Otherwise, enforce invariant that there are no type
226260
// parameters reachable.
227-
if !infer_is_local.0 {
228-
if let Some(param) = input_ty.walk().find(|t| is_type_parameter(t)) {
229-
debug!("orphan_check_trait_ref: uncovered type `{:?}`", param);
230-
return Err(OrphanCheckErr::UncoveredTy(param));
231-
}
261+
if let Some(param) = input_ty.walk()
262+
.find(|t| is_possibly_remote_type(t, in_crate))
263+
{
264+
debug!("orphan_check_trait_ref: uncovered type `{:?}`", param);
265+
return Err(OrphanCheckErr::UncoveredTy(param));
232266
}
233267
}
234268

@@ -237,29 +271,29 @@ fn orphan_check_trait_ref<'tcx>(tcx: TyCtxt,
237271
return Err(OrphanCheckErr::NoLocalInputType);
238272
}
239273

240-
fn uncovered_tys<'tcx>(tcx: TyCtxt, ty: Ty<'tcx>, infer_is_local: InferIsLocal)
274+
fn uncovered_tys<'tcx>(tcx: TyCtxt, ty: Ty<'tcx>, in_crate: InCrate)
241275
-> Vec<Ty<'tcx>> {
242-
if ty_is_local_constructor(ty, infer_is_local) {
276+
if ty_is_local_constructor(ty, in_crate) {
243277
vec![]
244278
} else if fundamental_ty(tcx, ty) {
245279
ty.walk_shallow()
246-
.flat_map(|t| uncovered_tys(tcx, t, infer_is_local))
280+
.flat_map(|t| uncovered_tys(tcx, t, in_crate))
247281
.collect()
248282
} else {
249283
vec![ty]
250284
}
251285
}
252286

253-
fn is_type_parameter(ty: Ty) -> bool {
287+
fn is_possibly_remote_type(ty: Ty, _in_crate: InCrate) -> bool {
254288
match ty.sty {
255289
ty::TyProjection(..) | ty::TyParam(..) => true,
256290
_ => false,
257291
}
258292
}
259293

260-
fn ty_is_local(tcx: TyCtxt, ty: Ty, infer_is_local: InferIsLocal) -> bool {
261-
ty_is_local_constructor(ty, infer_is_local) ||
262-
fundamental_ty(tcx, ty) && ty.walk_shallow().any(|t| ty_is_local(tcx, t, infer_is_local))
294+
fn ty_is_local(tcx: TyCtxt, ty: Ty, in_crate: InCrate) -> bool {
295+
ty_is_local_constructor(ty, in_crate) ||
296+
fundamental_ty(tcx, ty) && ty.walk_shallow().any(|t| ty_is_local(tcx, t, in_crate))
263297
}
264298

265299
fn fundamental_ty(tcx: TyCtxt, ty: Ty) -> bool {
@@ -273,7 +307,16 @@ fn fundamental_ty(tcx: TyCtxt, ty: Ty) -> bool {
273307
}
274308
}
275309

276-
fn ty_is_local_constructor(ty: Ty, infer_is_local: InferIsLocal)-> bool {
310+
fn def_id_is_local(def_id: DefId, in_crate: InCrate) -> bool {
311+
match in_crate {
312+
// The type is local to *this* crate - it will not be
313+
// local in any other crate.
314+
InCrate::Remote => false,
315+
InCrate::Local => def_id.is_local()
316+
}
317+
}
318+
319+
fn ty_is_local_constructor(ty: Ty, in_crate: InCrate) -> bool {
277320
debug!("ty_is_local_constructor({:?})", ty);
278321

279322
match ty.sty {
@@ -296,20 +339,20 @@ fn ty_is_local_constructor(ty: Ty, infer_is_local: InferIsLocal)-> bool {
296339
false
297340
}
298341

299-
ty::TyInfer(..) => {
300-
infer_is_local.0
301-
}
302-
303-
ty::TyAdt(def, _) => {
304-
def.did.is_local()
305-
}
342+
ty::TyInfer(..) => match in_crate {
343+
InCrate::Local => false,
344+
// The inference variable might be unified with a local
345+
// type in that remote crate.
346+
InCrate::Remote => true,
347+
},
306348

307-
ty::TyForeign(did) => {
308-
did.is_local()
309-
}
349+
ty::TyAdt(def, _) => def_id_is_local(def.did, in_crate),
350+
ty::TyForeign(did) => def_id_is_local(did, in_crate),
310351

311352
ty::TyDynamic(ref tt, ..) => {
312-
tt.principal().map_or(false, |p| p.def_id().is_local())
353+
tt.principal().map_or(false, |p| {
354+
def_id_is_local(p.def_id(), in_crate)
355+
})
313356
}
314357

315358
ty::TyError => {

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

0 commit comments

Comments
 (0)