diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index e7cef9e198b6f..83ab761aa55a4 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -748,6 +748,13 @@ pub struct TraitPredicate<'tcx> { pub constness: BoundConstness, + /// If polarity is Positive: we are proving that the trait is implemented. + /// + /// If polarity is Negative: we are proving that a negative impl of this trait + /// exists. (Note that coherence also checks whether negative impls of supertraits + /// exist via a series of predicates.) + /// + /// If polarity is Reserved: that's a bug. pub polarity: ImplPolarity, } diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index a3d32acc6fb82..94a4001bbb91a 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -17,8 +17,8 @@ use crate::traits::{ use rustc_errors::Diagnostic; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::CRATE_HIR_ID; -use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::traits::TraitEngine; +use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_infer::traits::{util, TraitEngine}; use rustc_middle::traits::specialization_graph::OverlapMode; use rustc_middle::ty::fast_reject::{self, TreatParams}; use rustc_middle::ty::fold::TypeFoldable; @@ -353,6 +353,8 @@ fn negative_impl<'cx, 'tcx>( }) } +/// Try to prove that a negative impl exist for the given obligation and their super predicates. +#[instrument(level = "debug", skip(selcx))] fn negative_impl_exists<'cx, 'tcx>( selcx: &SelectionContext<'cx, 'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -360,42 +362,66 @@ fn negative_impl_exists<'cx, 'tcx>( o: &PredicateObligation<'tcx>, ) -> bool { let infcx = &selcx.infcx().fork(); + + if resolve_negative_obligation(infcx, param_env, region_context, o) { + return true; + } + + // Try to prove a negative obligation exist for super predicates + for o in util::elaborate_predicates(infcx.tcx, iter::once(o.predicate)) { + if resolve_negative_obligation(infcx, param_env, region_context, &o) { + return true; + } + } + + false +} + +#[instrument(level = "debug", skip(infcx))] +fn resolve_negative_obligation<'cx, 'tcx>( + infcx: &InferCtxt<'cx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + region_context: DefId, + o: &PredicateObligation<'tcx>, +) -> bool { let tcx = infcx.tcx; - o.flip_polarity(tcx) - .map(|o| { - let mut fulfillment_cx = FulfillmentContext::new(); - fulfillment_cx.register_predicate_obligation(infcx, o); - - let errors = fulfillment_cx.select_all_or_error(infcx); - if !errors.is_empty() { - return false; - } - let mut outlives_env = OutlivesEnvironment::new(param_env); - // FIXME -- add "assumed to be well formed" types into the `outlives_env` + let Some(o) = o.flip_polarity(tcx) else { + return false; + }; - // "Save" the accumulated implied bounds into the outlives environment - // (due to the FIXME above, there aren't any, but this step is still needed). - // The "body id" is given as `CRATE_HIR_ID`, which is the same body-id used - // by the "dummy" causes elsewhere (body-id is only relevant when checking - // function bodies with closures). - outlives_env.save_implied_bounds(CRATE_HIR_ID); + let mut fulfillment_cx = FulfillmentContext::new(); + fulfillment_cx.register_predicate_obligation(infcx, o); - infcx.process_registered_region_obligations( - outlives_env.region_bound_pairs_map(), - Some(tcx.lifetimes.re_root_empty), - param_env, - ); + let errors = fulfillment_cx.select_all_or_error(infcx); - let errors = - infcx.resolve_regions(region_context, &outlives_env, RegionckMode::default()); - if !errors.is_empty() { - return false; - } + if !errors.is_empty() { + return false; + } - true - }) - .unwrap_or(false) + let mut outlives_env = OutlivesEnvironment::new(param_env); + // FIXME -- add "assumed to be well formed" types into the `outlives_env` + + // "Save" the accumulated implied bounds into the outlives environment + // (due to the FIXME above, there aren't any, but this step is still needed). + // The "body id" is given as `CRATE_HIR_ID`, which is the same body-id used + // by the "dummy" causes elsewhere (body-id is only relevant when checking + // function bodies with closures). + outlives_env.save_implied_bounds(CRATE_HIR_ID); + + infcx.process_registered_region_obligations( + outlives_env.region_bound_pairs_map(), + Some(tcx.lifetimes.re_root_empty), + param_env, + ); + + let errors = infcx.resolve_regions(region_context, &outlives_env, RegionckMode::default()); + + if !errors.is_empty() { + return false; + } + + true } pub fn trait_ref_is_knowable<'tcx>( diff --git a/src/test/ui/coherence/coherence-overlap-double-negative.rs b/src/test/ui/coherence/coherence-overlap-double-negative.rs new file mode 100644 index 0000000000000..1ea0ddc7430e1 --- /dev/null +++ b/src/test/ui/coherence/coherence-overlap-double-negative.rs @@ -0,0 +1,12 @@ +// check-pass + +#![feature(negative_impls)] +#![feature(with_negative_coherence)] + +trait A {} +trait B: A {} + +impl !A for u32 {} +impl !B for u32 {} + +fn main() {} diff --git a/src/test/ui/coherence/coherence-overlap-negate-alias-strict.rs b/src/test/ui/coherence/coherence-overlap-negate-alias-strict.rs index c240a18398278..48dffc921a31b 100644 --- a/src/test/ui/coherence/coherence-overlap-negate-alias-strict.rs +++ b/src/test/ui/coherence/coherence-overlap-negate-alias-strict.rs @@ -1,3 +1,5 @@ +// check-pass + #![feature(negative_impls)] #![feature(rustc_attrs)] #![feature(trait_alias)] @@ -13,7 +15,5 @@ impl !A for u32 {} trait C {} impl C for T {} impl C for u32 {} -//~^ ERROR: conflicting implementations of trait `C` for type `u32` [E0119] -// FIXME this should work, we should implement an `assemble_neg_candidates` fn fn main() {} diff --git a/src/test/ui/coherence/coherence-overlap-negate-alias-strict.stderr b/src/test/ui/coherence/coherence-overlap-negate-alias-strict.stderr deleted file mode 100644 index 30d837a5c5019..0000000000000 --- a/src/test/ui/coherence/coherence-overlap-negate-alias-strict.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0119]: conflicting implementations of trait `C` for type `u32` - --> $DIR/coherence-overlap-negate-alias-strict.rs:15:1 - | -LL | impl C for T {} - | ------------------- first implementation here -LL | impl C for u32 {} - | ^^^^^^^^^^^^^^ conflicting implementation for `u32` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0119`. diff --git a/src/test/ui/coherence/coherence-overlap-super-negative.rs b/src/test/ui/coherence/coherence-overlap-super-negative.rs new file mode 100644 index 0000000000000..d296a094a3704 --- /dev/null +++ b/src/test/ui/coherence/coherence-overlap-super-negative.rs @@ -0,0 +1,18 @@ +// check-pass + +#![feature(negative_impls)] +#![feature(rustc_attrs)] +#![feature(with_negative_coherence)] + +trait Trait1: Trait2 {} +trait Trait2 {} + +struct MyType {} +impl !Trait2 for MyType {} + +#[rustc_strict_coherence] +trait Foo {} +impl Foo for T {} +impl Foo for MyType {} + +fn main() {}