Auto merge of #95082 - spastorino:overlap-inherent-impls, r=nikomatsakis

Overlap inherent impls

r? `@nikomatsakis`

Closes #94526
This commit is contained in:
bors 2022-03-25 09:09:48 +00:00
commit e70e211e99
9 changed files with 177 additions and 65 deletions

View File

@ -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>,
@ -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>,

View File

@ -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;
@ -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) {

View File

@ -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};
@ -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,

View File

@ -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;
@ -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>>(

View File

@ -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,
@ -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
@ -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);
let (subject2, obligations) =
impl_subject_and_oblig(selcx, impl_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 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>,
@ -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;

View File

@ -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.
@ -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(());
};
@ -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
@ -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()
);

View File

@ -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::*};
@ -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);
@ -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>(

View File

@ -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() {}

View File

@ -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() {}