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
25 changes: 24 additions & 1 deletion compiler/rustc_infer/src/infer/at.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
use super::*;

use rustc_middle::ty::relate::{Relate, TypeRelation};
use rustc_middle::ty::Const;
use rustc_middle::ty::{Const, ImplSubject};

pub struct At<'a, 'tcx> {
pub infcx: &'a InferCtxt<'a, 'tcx>,
Expand Down Expand Up @@ -272,6 +272,29 @@ impl<'a, 'tcx> Trace<'a, 'tcx> {
}
}

impl<'tcx> ToTrace<'tcx> for ImplSubject<'tcx> {
fn to_trace(
tcx: TyCtxt<'tcx>,
cause: &ObligationCause<'tcx>,
a_is_expected: bool,
a: Self,
b: Self,
) -> TypeTrace<'tcx> {
match (a, b) {
(ImplSubject::Trait(trait_ref_a), ImplSubject::Trait(trait_ref_b)) => {
ToTrace::to_trace(tcx, cause, a_is_expected, trait_ref_a, trait_ref_b)
}
(ImplSubject::Inherent(ty_a), ImplSubject::Inherent(ty_b)) => {
ToTrace::to_trace(tcx, cause, a_is_expected, ty_a, ty_b)
}
(ImplSubject::Trait(_), ImplSubject::Inherent(_))
| (ImplSubject::Inherent(_), ImplSubject::Trait(_)) => {
bug!("can not trace TraitRef and Ty");
}
}
}
}

impl<'tcx> ToTrace<'tcx> for Ty<'tcx> {
fn to_trace(
_: TyCtxt<'tcx>,
Expand Down
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_subject(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
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 @@ -44,6 +44,7 @@ use rustc_span::symbol::{kw, Ident, Symbol};
use rustc_span::Span;
use rustc_target::abi::Align;

use std::fmt::Debug;
use std::hash::Hash;
use std::ops::ControlFlow;
use std::{fmt, str};
Expand Down Expand Up @@ -172,6 +173,12 @@ pub struct ImplHeader<'tcx> {
pub predicates: Vec<Predicate<'tcx>>,
}

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

#[derive(
Copy,
Clone,
Expand Down
26 changes: 25 additions & 1 deletion compiler/rustc_middle/src/ty/relate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use crate::mir::interpret::{get_slice_bytes, ConstValue, GlobalAlloc, Scalar};
use crate::ty::error::{ExpectedFound, TypeError};
use crate::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef};
use crate::ty::{self, Term, Ty, TyCtxt, TypeFoldable};
use crate::ty::{self, ImplSubject, Term, Ty, TyCtxt, TypeFoldable};
use rustc_hir as ast;
use rustc_hir::def_id::DefId;
use rustc_span::DUMMY_SP;
Expand Down Expand Up @@ -356,6 +356,30 @@ impl<'tcx> Relate<'tcx> for GeneratorWitness<'tcx> {
}
}

impl<'tcx> Relate<'tcx> for ImplSubject<'tcx> {
#[inline]
fn relate<R: TypeRelation<'tcx>>(
relation: &mut R,
a: ImplSubject<'tcx>,
b: ImplSubject<'tcx>,
) -> RelateResult<'tcx, ImplSubject<'tcx>> {
match (a, b) {
(ImplSubject::Trait(trait_ref_a), ImplSubject::Trait(trait_ref_b)) => {
let trait_ref = ty::TraitRef::relate(relation, trait_ref_a, trait_ref_b)?;
Ok(ImplSubject::Trait(trait_ref))
}
(ImplSubject::Inherent(ty_a), ImplSubject::Inherent(ty_b)) => {
let ty = Ty::relate(relation, ty_a, ty_b)?;
Ok(ImplSubject::Inherent(ty))
}
(ImplSubject::Trait(_), ImplSubject::Inherent(_))
| (ImplSubject::Inherent(_), ImplSubject::Trait(_)) => {
bug!("can not relate TraitRef and Ty");
}
}
}
}

impl<'tcx> Relate<'tcx> for Ty<'tcx> {
#[inline]
fn relate<R: TypeRelation<'tcx>>(
Expand Down
87 changes: 45 additions & 42 deletions compiler/rustc_trait_selection/src/traits/coherence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use crate::infer::outlives::env::OutlivesEnvironment;
use crate::infer::{CombinedSnapshot, InferOk, RegionckMode};
use crate::traits::select::IntercrateAmbiguityCause;
use crate::traits::util::impl_trait_ref_and_oblig;
use crate::traits::util::impl_subject_and_oblig;
use crate::traits::SkipLeakCheck;
use crate::traits::{
self, FulfillmentContext, Normalized, Obligation, ObligationCause, PredicateObligation,
Expand All @@ -23,9 +23,10 @@ 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 +301,62 @@ 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(
// create a parameter environment corresponding to a (placeholder) instantiation of impl1
let impl_env = tcx.param_env(impl1_def_id);
let subject1 = match traits::fully_normalize(
&infcx,
FulfillmentContext::new(),
ObligationCause::dummy(),
impl1_env,
impl1_trait_ref,
impl_env,
tcx.impl_subject(impl1_def_id),
) {
Ok(impl1_trait_ref) => impl1_trait_ref,
Err(err) => {
bug!("failed to fully normalize {:?}: {:?}", impl1_trait_ref, err);
}
Ok(s) => s,
Err(err) => bug!("failed to fully normalize {:?}: {:?}", impl1_def_id, 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);

// 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 {
debug!(
"explicit_disjoint: {:?} does not unify with {:?}",
impl1_trait_ref, impl2_trait_ref
);
return false;
};
let (subject2, obligations) =
impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs);

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
}
!equate(&infcx, impl_env, impl1_def_id, subject1, subject2, obligations)
})
}

/// Try to prove that a negative impl exist for the given obligation and their super predicates.
fn equate<'cx, 'tcx>(
infcx: &InferCtxt<'cx, 'tcx>,
impl_env: ty::ParamEnv<'tcx>,
impl1_def_id: DefId,
subject1: ImplSubject<'tcx>,
subject2: ImplSubject<'tcx>,
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(), impl_env).eq(subject1, subject2)
else {
debug!("explicit_disjoint: {:?} does not unify with {:?}", subject1, subject2);
return true;
};

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

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 its super predicates.
#[instrument(level = "debug", skip(selcx))]
fn negative_impl_exists<'cx, 'tcx>(
selcx: &SelectionContext<'cx, 'tcx>,
Expand All @@ -367,7 +370,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
20 changes: 11 additions & 9 deletions compiler/rustc_trait_selection/src/traits/specialize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ use rustc_errors::{struct_span_err, EmissionGuarantee};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::lint::LintDiagnosticBuilder;
use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef};
use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::ty::{self, ImplSubject, TyCtxt};
use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK;
use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS;
use rustc_span::{Span, DUMMY_SP};

use super::util::impl_trait_ref_and_oblig;
use super::util;
use super::{FulfillmentContext, SelectionContext};

/// Information pertinent to an overlapping impl error.
Expand Down Expand Up @@ -186,18 +186,20 @@ fn fulfill_implication<'a, 'tcx>(
param_env, source_trait_ref, target_impl
);

let source_trait = ImplSubject::Trait(source_trait_ref);

let selcx = &mut SelectionContext::new(&infcx);
let target_substs = infcx.fresh_substs_for_item(DUMMY_SP, target_impl);
let (target_trait_ref, obligations) =
impl_trait_ref_and_oblig(selcx, param_env, target_impl, target_substs);
let (target_trait, obligations) =
util::impl_subject_and_oblig(selcx, param_env, target_impl, target_substs);

// do the impls unify? If not, no specialization.
let Ok(InferOk { obligations: more_obligations, .. }) =
infcx.at(&ObligationCause::dummy(), param_env).eq(source_trait_ref, target_trait_ref)
infcx.at(&ObligationCause::dummy(), param_env).eq(source_trait, target_trait)
else {
debug!(
"fulfill_implication: {:?} does not unify with {:?}",
source_trait_ref, target_trait_ref
source_trait, target_trait
);
return Err(());
};
Expand Down Expand Up @@ -225,7 +227,7 @@ fn fulfill_implication<'a, 'tcx>(
[] => {
debug!(
"fulfill_implication: an impl for {:?} specializes {:?}",
source_trait_ref, target_trait_ref
source_trait, target_trait
);

// Now resolve the *substitution* we built for the target earlier, replacing
Expand All @@ -237,8 +239,8 @@ fn fulfill_implication<'a, 'tcx>(
debug!(
"fulfill_implication: for impls on {:?} and {:?}, \
could not fulfill: {:?} given {:?}",
source_trait_ref,
target_trait_ref,
source_trait,
target_trait,
errors,
param_env.caller_bounds()
);
Expand Down
20 changes: 10 additions & 10 deletions compiler/rustc_trait_selection/src/traits/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use smallvec::SmallVec;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::subst::{GenericArg, Subst, SubstsRef};
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable};
use rustc_middle::ty::{self, ImplSubject, ToPredicate, Ty, TyCtxt, TypeFoldable};

use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext};
pub use rustc_infer::traits::{self, util::*};
Expand Down Expand Up @@ -190,19 +190,19 @@ impl Iterator for SupertraitDefIds<'_> {
// Other
///////////////////////////////////////////////////////////////////////////

/// Instantiate all bound parameters of the impl with the given substs,
/// returning the resulting trait ref and all obligations that arise.
/// Instantiate all bound parameters of the impl subject with the given substs,
/// returning the resulting subject and all obligations that arise.
/// The obligations are closed under normalization.
pub fn impl_trait_ref_and_oblig<'a, 'tcx>(
pub fn impl_subject_and_oblig<'a, 'tcx>(
selcx: &mut SelectionContext<'a, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
impl_def_id: DefId,
impl_substs: SubstsRef<'tcx>,
) -> (ty::TraitRef<'tcx>, impl Iterator<Item = PredicateObligation<'tcx>>) {
let impl_trait_ref = selcx.tcx().impl_trait_ref(impl_def_id).unwrap();
let impl_trait_ref = impl_trait_ref.subst(selcx.tcx(), impl_substs);
let Normalized { value: impl_trait_ref, obligations: normalization_obligations1 } =
super::normalize(selcx, param_env, ObligationCause::dummy(), impl_trait_ref);
) -> (ImplSubject<'tcx>, impl Iterator<Item = PredicateObligation<'tcx>>) {
let subject = selcx.tcx().impl_subject(impl_def_id);
let subject = subject.subst(selcx.tcx(), impl_substs);
let Normalized { value: subject, obligations: normalization_obligations1 } =
super::normalize(selcx, param_env, ObligationCause::dummy(), subject);

let predicates = selcx.tcx().predicates_of(impl_def_id);
let predicates = predicates.instantiate(selcx.tcx(), impl_substs);
Expand All @@ -215,7 +215,7 @@ pub fn impl_trait_ref_and_oblig<'a, 'tcx>(
.chain(normalization_obligations1.into_iter())
.chain(normalization_obligations2.into_iter());

(impl_trait_ref, impl_obligations)
(subject, impl_obligations)
}

pub fn predicates_for_generics<'tcx>(
Expand Down
25 changes: 25 additions & 0 deletions src/test/ui/coherence/coherence-negative-inherent-where-bounds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// check-pass

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

trait Foo {}

impl !Foo for u32 {}

#[rustc_strict_coherence]
struct MyStruct<T>(T);

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

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

fn main() {}
Loading