Rework negative coherence
This commit is contained in:
parent
66d7cfd3b5
commit
7f3c2c7e2c
1 changed files with 151 additions and 38 deletions
|
@ -13,11 +13,10 @@ use crate::traits::outlives_bounds::InferCtxtExt as _;
|
||||||
use crate::traits::query::evaluate_obligation::InferCtxtExt;
|
use crate::traits::query::evaluate_obligation::InferCtxtExt;
|
||||||
use crate::traits::select::{IntercrateAmbiguityCause, TreatInductiveCycleAs};
|
use crate::traits::select::{IntercrateAmbiguityCause, TreatInductiveCycleAs};
|
||||||
use crate::traits::structural_normalize::StructurallyNormalizeExt;
|
use crate::traits::structural_normalize::StructurallyNormalizeExt;
|
||||||
use crate::traits::util::impl_subject_and_oblig;
|
|
||||||
use crate::traits::NormalizeExt;
|
use crate::traits::NormalizeExt;
|
||||||
use crate::traits::SkipLeakCheck;
|
use crate::traits::SkipLeakCheck;
|
||||||
use crate::traits::{
|
use crate::traits::{
|
||||||
self, Obligation, ObligationCause, ObligationCtxt, PredicateObligation, PredicateObligations,
|
Obligation, ObligationCause, ObligationCtxt, PredicateObligation, PredicateObligations,
|
||||||
SelectionContext,
|
SelectionContext,
|
||||||
};
|
};
|
||||||
use rustc_data_structures::fx::FxIndexSet;
|
use rustc_data_structures::fx::FxIndexSet;
|
||||||
|
@ -32,7 +31,7 @@ use rustc_middle::traits::DefiningAnchor;
|
||||||
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
|
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
|
||||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||||
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
|
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
|
||||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor};
|
use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor};
|
||||||
use rustc_session::lint::builtin::COINDUCTIVE_OVERLAP_IN_COHERENCE;
|
use rustc_session::lint::builtin::COINDUCTIVE_OVERLAP_IN_COHERENCE;
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
use rustc_span::DUMMY_SP;
|
use rustc_span::DUMMY_SP;
|
||||||
|
@ -399,53 +398,167 @@ fn impl_intersection_has_negative_obligation(
|
||||||
) -> bool {
|
) -> bool {
|
||||||
debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id);
|
debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id);
|
||||||
|
|
||||||
// Create an infcx, taking the predicates of impl1 as assumptions:
|
let ref infcx = tcx.infer_ctxt().intercrate(true).build();
|
||||||
let infcx = tcx.infer_ctxt().build();
|
let universe = infcx.create_next_universe();
|
||||||
// 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,
|
|
||||||
ObligationCause::dummy(),
|
|
||||||
impl_env,
|
|
||||||
tcx.impl_subject(impl1_def_id).instantiate_identity(),
|
|
||||||
) {
|
|
||||||
Ok(s) => s,
|
|
||||||
Err(err) => {
|
|
||||||
tcx.sess.delay_span_bug(
|
|
||||||
tcx.def_span(impl1_def_id),
|
|
||||||
format!("failed to fully normalize {impl1_def_id:?}: {err:?}"),
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Attempt to prove that impl2 applies, given all of the above.
|
let impl1_header = fresh_impl_header(infcx, impl1_def_id);
|
||||||
let selcx = &mut SelectionContext::new(&infcx);
|
let param_env =
|
||||||
let impl2_args = infcx.fresh_args_for_item(DUMMY_SP, impl2_def_id);
|
ty::EarlyBinder::bind(tcx.param_env(impl1_def_id)).instantiate(tcx, impl1_header.impl_args);
|
||||||
let (subject2, normalization_obligations) =
|
|
||||||
impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_args, |_, _| {
|
|
||||||
ObligationCause::dummy()
|
|
||||||
});
|
|
||||||
|
|
||||||
// do the impls unify? If not, then it's not currently possible to prove any
|
let impl2_header = fresh_impl_header(infcx, impl2_def_id);
|
||||||
// obligations about their intersection.
|
|
||||||
let Ok(InferOk { obligations: equate_obligations, .. }) =
|
// Equate the headers to find their intersection (the general type, with infer vars,
|
||||||
infcx.at(&ObligationCause::dummy(), impl_env).eq(DefineOpaqueTypes::No, subject1, subject2)
|
// that may apply both impls).
|
||||||
|
let Some(_equate_obligations) =
|
||||||
|
equate_impl_headers(infcx, param_env, &impl1_header, &impl2_header)
|
||||||
else {
|
else {
|
||||||
debug!("explicit_disjoint: {:?} does not unify with {:?}", subject1, subject2);
|
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
for obligation in normalization_obligations.into_iter().chain(equate_obligations) {
|
plug_infer_with_placeholders(infcx, universe, (impl1_header.impl_args, impl2_header.impl_args));
|
||||||
if negative_impl_exists(&infcx, &obligation, impl1_def_id) {
|
|
||||||
debug!("overlap: obligation unsatisfiable {:?}", obligation);
|
for (predicate, _) in util::elaborate(
|
||||||
return true;
|
tcx,
|
||||||
|
tcx.predicates_of(impl2_def_id).instantiate(tcx, impl2_header.impl_args),
|
||||||
|
) {
|
||||||
|
let Some(negative_predicate) = predicate.as_predicate().flip_polarity(tcx) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let ref infcx = infcx.fork();
|
||||||
|
let ocx = ObligationCtxt::new(infcx);
|
||||||
|
|
||||||
|
ocx.register_obligation(Obligation::new(
|
||||||
|
tcx,
|
||||||
|
ObligationCause::dummy(),
|
||||||
|
param_env,
|
||||||
|
negative_predicate,
|
||||||
|
));
|
||||||
|
|
||||||
|
if !ocx.select_all_or_error().is_empty() {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: regions here too...
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn plug_infer_with_placeholders<'tcx>(
|
||||||
|
infcx: &InferCtxt<'tcx>,
|
||||||
|
universe: ty::UniverseIndex,
|
||||||
|
value: impl TypeVisitable<TyCtxt<'tcx>>,
|
||||||
|
) {
|
||||||
|
struct PlugInferWithPlaceholder<'a, 'tcx> {
|
||||||
|
infcx: &'a InferCtxt<'tcx>,
|
||||||
|
universe: ty::UniverseIndex,
|
||||||
|
var: ty::BoundVar,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> PlugInferWithPlaceholder<'_, 'tcx> {
|
||||||
|
fn next_var(&mut self) -> ty::BoundVar {
|
||||||
|
let var = self.var;
|
||||||
|
self.var = self.var + 1;
|
||||||
|
var
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for PlugInferWithPlaceholder<'_, 'tcx> {
|
||||||
|
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||||
|
let ty = self.infcx.shallow_resolve(ty);
|
||||||
|
if ty.is_ty_var() {
|
||||||
|
let Ok(InferOk { value: (), obligations }) =
|
||||||
|
self.infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq(
|
||||||
|
DefineOpaqueTypes::No,
|
||||||
|
ty,
|
||||||
|
Ty::new_placeholder(
|
||||||
|
self.infcx.tcx,
|
||||||
|
ty::Placeholder {
|
||||||
|
universe: self.universe,
|
||||||
|
bound: ty::BoundTy {
|
||||||
|
var: self.next_var(),
|
||||||
|
kind: ty::BoundTyKind::Anon,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else {
|
||||||
|
bug!()
|
||||||
|
};
|
||||||
|
assert_eq!(obligations, &[]);
|
||||||
|
ControlFlow::Continue(())
|
||||||
|
} else {
|
||||||
|
ty.super_visit_with(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||||
|
let ct = self.infcx.shallow_resolve(ct);
|
||||||
|
if ct.is_ct_infer() {
|
||||||
|
let Ok(InferOk { value: (), obligations }) =
|
||||||
|
self.infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq(
|
||||||
|
DefineOpaqueTypes::No,
|
||||||
|
ct,
|
||||||
|
ty::Const::new_placeholder(
|
||||||
|
self.infcx.tcx,
|
||||||
|
ty::Placeholder { universe: self.universe, bound: self.next_var() },
|
||||||
|
ct.ty(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else {
|
||||||
|
bug!()
|
||||||
|
};
|
||||||
|
assert_eq!(obligations, &[]);
|
||||||
|
ControlFlow::Continue(())
|
||||||
|
} else {
|
||||||
|
ct.super_visit_with(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||||
|
if let ty::ReVar(vid) = *r {
|
||||||
|
let r = self
|
||||||
|
.infcx
|
||||||
|
.inner
|
||||||
|
.borrow_mut()
|
||||||
|
.unwrap_region_constraints()
|
||||||
|
.opportunistic_resolve_var(self.infcx.tcx, vid);
|
||||||
|
if r.is_var() {
|
||||||
|
let Ok(InferOk { value: (), obligations }) =
|
||||||
|
self.infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq(
|
||||||
|
DefineOpaqueTypes::No,
|
||||||
|
r,
|
||||||
|
ty::Region::new_placeholder(
|
||||||
|
self.infcx.tcx,
|
||||||
|
ty::Placeholder {
|
||||||
|
universe: self.universe,
|
||||||
|
bound: ty::BoundRegion {
|
||||||
|
var: self.next_var(),
|
||||||
|
kind: ty::BoundRegionKind::BrAnon,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else {
|
||||||
|
bug!()
|
||||||
|
};
|
||||||
|
assert_eq!(obligations, &[]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ControlFlow::Continue(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value.visit_with(&mut PlugInferWithPlaceholder {
|
||||||
|
infcx,
|
||||||
|
universe,
|
||||||
|
var: ty::BoundVar::from_u32(0),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Try to prove that a negative impl exist for the obligation or its supertraits.
|
/// Try to prove that a negative impl exist for the obligation or its supertraits.
|
||||||
///
|
///
|
||||||
/// If such a negative impl exists, then the obligation definitely must not hold
|
/// If such a negative impl exists, then the obligation definitely must not hold
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue