Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make negative coherence work when there's impl negative on super predicates #95039

Merged
merged 8 commits into from
Mar 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}

Expand Down
90 changes: 58 additions & 32 deletions compiler/rustc_trait_selection/src/traits/coherence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -353,49 +353,75 @@ 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>,
region_context: DefId,
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>(
Expand Down
12 changes: 12 additions & 0 deletions src/test/ui/coherence/coherence-overlap-double-negative.rs
Original file line number Diff line number Diff line change
@@ -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() {}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// check-pass

#![feature(negative_impls)]
#![feature(rustc_attrs)]
#![feature(trait_alias)]
Expand All @@ -13,7 +15,5 @@ impl !A for u32 {}
trait C {}
impl<T: AB> 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() {}
11 changes: 0 additions & 11 deletions src/test/ui/coherence/coherence-overlap-negate-alias-strict.stderr

This file was deleted.

18 changes: 18 additions & 0 deletions src/test/ui/coherence/coherence-overlap-super-negative.rs
Original file line number Diff line number Diff line change
@@ -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<T: Trait1> Foo for T {}
impl Foo for MyType {}

fn main() {}