Skip to content

Overlap inherent impls #95082

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

Merged
merged 9 commits into from
Mar 25, 2022
Merged
10 changes: 8 additions & 2 deletions compiler/rustc_middle/src/hir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ pub mod nested_filter;
pub mod place;

use crate::ty::query::Providers;
use crate::ty::TyCtxt;
use crate::ty::{ImplSubject, TyCtxt};
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_hir::def_id::LocalDefId;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::*;
use rustc_query_system::ich::StableHashingContext;
use rustc_span::DUMMY_SP;
Expand Down Expand Up @@ -54,6 +54,12 @@ impl<'tcx> TyCtxt<'tcx> {
pub fn parent_module(self, id: HirId) -> LocalDefId {
self.parent_module_from_def_id(id.owner)
}

pub fn impl_header(self, def_id: DefId) -> ImplSubject<'tcx> {
self.impl_trait_ref(def_id)
.map(ImplSubject::Trait)
.unwrap_or_else(|| ImplSubject::Inherent(self.type_of(def_id)))
}
}

pub fn provide(providers: &mut Providers) {
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,12 @@ pub struct ImplHeader<'tcx> {
pub predicates: Vec<Predicate<'tcx>>,
}

#[derive(Debug)]
pub enum ImplSubject<'tcx> {
Trait(TraitRef<'tcx>),
Inherent(Ty<'tcx>),
}

#[derive(
Copy,
Clone,
Expand Down
115 changes: 71 additions & 44 deletions compiler/rustc_trait_selection/src/traits/coherence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,17 @@ 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::at::ToTrace;
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;
use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt};
use rustc_span::symbol::sym;
use rustc_span::DUMMY_SP;
use std::fmt::Debug;
use std::iter;

/// Whether we do the orphan check relative to this crate or
Expand Down Expand Up @@ -300,60 +302,85 @@ fn negative_impl<'cx, 'tcx>(
debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id);
let tcx = selcx.infcx().tcx;

// create a parameter environment corresponding to a (placeholder) instantiation of impl1
let impl1_env = tcx.param_env(impl1_def_id);
let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap();

// Create an infcx, taking the predicates of impl1 as assumptions:
tcx.infer_ctxt().enter(|infcx| {
// Normalize the trait reference. The WF rules ought to ensure
// that this always succeeds.
let impl1_trait_ref = match traits::fully_normalize(
&infcx,
FulfillmentContext::new(),
ObligationCause::dummy(),
impl1_env,
impl1_trait_ref,
) {
Ok(impl1_trait_ref) => impl1_trait_ref,
Err(err) => {
bug!("failed to fully normalize {:?}: {:?}", impl1_trait_ref, err);
// create a parameter environment corresponding to a (placeholder) instantiation of impl1
let impl1_env = tcx.param_env(impl1_def_id);

match tcx.impl_header(impl1_def_id) {
ImplSubject::Trait(impl1_trait_ref) => {
// Normalize the trait reference. The WF rules ought to ensure
// that this always succeeds.
let impl1_trait_ref = match traits::fully_normalize(
&infcx,
FulfillmentContext::new(),
ObligationCause::dummy(),
impl1_env,
impl1_trait_ref,
) {
Ok(impl1_trait_ref) => impl1_trait_ref,
Err(err) => {
bug!("failed to fully normalize {:?}: {:?}", impl1_trait_ref, err);
}
};

// Attempt to prove that impl2 applies, given all of the above.
let selcx = &mut SelectionContext::new(&infcx);
let impl2_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl2_def_id);
let (impl2_trait_ref, obligations) =
impl_trait_ref_and_oblig(selcx, impl1_env, impl2_def_id, impl2_substs);

!equate(
&infcx,
impl1_env,
impl1_def_id,
impl1_trait_ref,
impl2_trait_ref,
obligations,
)
}
};
ImplSubject::Inherent(ty1) => {
let ty2 = tcx.type_of(impl2_def_id);
!equate(&infcx, impl1_env, impl1_def_id, ty1, ty2, iter::empty())
}
}
})
}

// Attempt to prove that impl2 applies, given all of the above.
let selcx = &mut SelectionContext::new(&infcx);
let impl2_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl2_def_id);
let (impl2_trait_ref, obligations) =
impl_trait_ref_and_oblig(selcx, impl1_env, impl2_def_id, impl2_substs);

// do the impls unify? If not, not disjoint.
let Ok(InferOk { obligations: more_obligations, .. }) = infcx
.at(&ObligationCause::dummy(), impl1_env)
.eq(impl1_trait_ref, impl2_trait_ref)
else {
fn equate<'cx, 'tcx, T: Debug + ToTrace<'tcx>>(
infcx: &InferCtxt<'cx, 'tcx>,
impl1_env: ty::ParamEnv<'tcx>,
impl1_def_id: DefId,
impl1: T,
impl2: T,
obligations: impl Iterator<Item = PredicateObligation<'tcx>>,
) -> bool {
// do the impls unify? If not, not disjoint.
let Ok(InferOk { obligations: more_obligations, .. }) = infcx
.at(&ObligationCause::dummy(), impl1_env)
.eq(impl1, impl2) else {
debug!(
"explicit_disjoint: {:?} does not unify with {:?}",
impl1_trait_ref, impl2_trait_ref
impl1, impl2
);
return false;
return true;
};

let opt_failing_obligation = obligations
.into_iter()
.chain(more_obligations)
.find(|o| negative_impl_exists(selcx, impl1_env, impl1_def_id, o));
let selcx = &mut SelectionContext::new(&infcx);
let opt_failing_obligation = obligations
.into_iter()
.chain(more_obligations)
.find(|o| negative_impl_exists(selcx, impl1_env, impl1_def_id, o));

if let Some(failing_obligation) = opt_failing_obligation {
debug!("overlap: obligation unsatisfiable {:?}", failing_obligation);
true
} else {
false
}
})
if let Some(failing_obligation) = opt_failing_obligation {
debug!("overlap: obligation unsatisfiable {:?}", failing_obligation);
false
} else {
true
}
}

/// Try to prove that a negative impl exist for the given obligation and their super predicates.
/// Try to prove that a negative impl exist for the given obligation and its super predicates.
#[instrument(level = "debug", skip(selcx))]
fn negative_impl_exists<'cx, 'tcx>(
selcx: &SelectionContext<'cx, 'tcx>,
Expand All @@ -367,7 +394,7 @@ fn negative_impl_exists<'cx, 'tcx>(
return true;
}

// Try to prove a negative obligation exist for super predicates
// Try to prove a negative obligation exists 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;
Expand Down
22 changes: 22 additions & 0 deletions src/test/ui/coherence/coherence-negative-inherent.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// check-pass

#![feature(negative_impls)]
#![feature(rustc_attrs)]
#![feature(with_negative_coherence)]

#[rustc_strict_coherence]
trait Foo {}

impl !Foo for u32 {}

struct MyStruct<T>(T);

impl<T: Foo> MyStruct<T> {
fn method(&self) {}
}

impl MyStruct<u32> {
fn method(&self) {}
}

fn main() {}