Skip to content

Commit 45ac61a

Browse files
committed
Fix coinductive coherence overlap checks
1 parent 408c732 commit 45ac61a

File tree

5 files changed

+90
-1
lines changed

5 files changed

+90
-1
lines changed

compiler/rustc_trait_selection/src/traits/coherence.rs

+42
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,48 @@ pub fn overlapping_impls(
120120
Some(overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).unwrap())
121121
}
122122

123+
/// Given an impl_def_id that "positively" implement a trait, check if the "negative" holds.
124+
pub fn negative_impl_holds(tcx: TyCtxt<'_>, impl_def_id: DefId, overlap_mode: OverlapMode) -> bool {
125+
debug!("negative_impl_holds(impl1_header={:?}, overlap_mode={:?})", impl_def_id, overlap_mode);
126+
// `for<T> (Vec<u32>, T): Trait`
127+
let header = tcx.impl_trait_ref(impl_def_id).unwrap();
128+
129+
let infcx = tcx
130+
.infer_ctxt()
131+
.with_opaque_type_inference(DefiningAnchor::Bubble)
132+
.intercrate(true)
133+
.build();
134+
135+
// `[?t]`
136+
let infer_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
137+
138+
// `(Vec<u32>, ?t): Trait`
139+
let trait_ref = header.subst(tcx, infer_substs);
140+
141+
// `(Vec<u32>, ?t): !Trait`
142+
let trait_pred = tcx.mk_predicate(ty::Binder::dummy(ty::PredicateKind::Clause(
143+
ty::Clause::Trait(ty::TraitPredicate {
144+
trait_ref,
145+
constness: ty::BoundConstness::NotConst,
146+
polarity: ty::ImplPolarity::Negative,
147+
}),
148+
)));
149+
150+
// Ideally we would use param_env(impl_def_id) but that's unsound today.
151+
let param_env = ty::ParamEnv::empty();
152+
153+
let selcx = &mut SelectionContext::new(&infcx);
154+
selcx
155+
.evaluate_root_obligation(&Obligation::new(
156+
tcx,
157+
ObligationCause::dummy(),
158+
param_env,
159+
trait_pred,
160+
))
161+
.expect("Overflow should be caught earlier in standard query mode")
162+
.must_apply_modulo_regions()
163+
}
164+
123165
fn with_fresh_ty_vars<'cx, 'tcx>(
124166
selcx: &mut SelectionContext<'cx, 'tcx>,
125167
param_env: ty::ParamEnv<'tcx>,

compiler/rustc_trait_selection/src/traits/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ pub use self::ImplSource::*;
4141
pub use self::ObligationCauseCode::*;
4242
pub use self::SelectionError::*;
4343

44-
pub use self::coherence::{add_placeholder_note, orphan_check, overlapping_impls};
44+
pub use self::coherence::{
45+
add_placeholder_note, negative_impl_holds, orphan_check, overlapping_impls,
46+
};
4547
pub use self::coherence::{OrphanCheckErr, OverlapResult};
4648
pub use self::engine::{ObligationCtxt, TraitEngineExt};
4749
pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation};

compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs

+16
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,22 @@ fn overlap_check_considering_specialization<'tcx>(
226226
return Ok(OverlapResult::SpecializeAll(replace_children));
227227
}
228228

229+
if overlap_mode.use_negative_impl()
230+
&& tcx.impl_polarity(impl_def_id) == ty::ImplPolarity::Positive
231+
&& traits::negative_impl_holds(tcx, impl_def_id, overlap_mode)
232+
{
233+
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().skip_binder();
234+
let self_ty = trait_ref.self_ty();
235+
236+
return Err(OverlapError {
237+
with_impl: impl_def_id,
238+
trait_ref,
239+
self_ty: self_ty.has_concrete_skeleton().then_some(self_ty),
240+
intercrate_ambiguity_causes: Default::default(),
241+
involves_placeholder: false,
242+
});
243+
}
244+
229245
Ok(OverlapResult::NoOverlap(last_lint))
230246
}
231247

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#![feature(trivial_bounds)]
2+
#![feature(negative_impls)]
3+
#![feature(rustc_attrs)]
4+
#![feature(with_negative_coherence)]
5+
#![allow(trivial_bounds)]
6+
7+
#[rustc_strict_coherence]
8+
trait MyTrait {}
9+
10+
struct Foo {}
11+
12+
impl !MyTrait for Foo {}
13+
14+
impl MyTrait for Foo where Foo: MyTrait {}
15+
//~^ ERROR: conflicting implementations of trait `MyTrait`
16+
17+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0119]: conflicting implementations of trait `MyTrait` for type `Foo`
2+
--> $DIR/coherence-overlap-negative-cycles.rs:14:1
3+
|
4+
LL | impl MyTrait for Foo where Foo: MyTrait {}
5+
| ^^^^^^^^^^^^^^^^^^^^
6+
| |
7+
| first implementation here
8+
| conflicting implementation for `Foo`
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0119`.

0 commit comments

Comments
 (0)