Skip to content

Commit 7f3c2c7

Browse files
Rework negative coherence
1 parent 66d7cfd commit 7f3c2c7

File tree

1 file changed

+152
-39
lines changed

1 file changed

+152
-39
lines changed

compiler/rustc_trait_selection/src/traits/coherence.rs

+152-39
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,10 @@ use crate::traits::outlives_bounds::InferCtxtExt as _;
1313
use crate::traits::query::evaluate_obligation::InferCtxtExt;
1414
use crate::traits::select::{IntercrateAmbiguityCause, TreatInductiveCycleAs};
1515
use crate::traits::structural_normalize::StructurallyNormalizeExt;
16-
use crate::traits::util::impl_subject_and_oblig;
1716
use crate::traits::NormalizeExt;
1817
use crate::traits::SkipLeakCheck;
1918
use crate::traits::{
20-
self, Obligation, ObligationCause, ObligationCtxt, PredicateObligation, PredicateObligations,
19+
Obligation, ObligationCause, ObligationCtxt, PredicateObligation, PredicateObligations,
2120
SelectionContext,
2221
};
2322
use rustc_data_structures::fx::FxIndexSet;
@@ -32,7 +31,7 @@ use rustc_middle::traits::DefiningAnchor;
3231
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
3332
use rustc_middle::ty::print::with_no_trimmed_paths;
3433
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
35-
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor};
34+
use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor};
3635
use rustc_session::lint::builtin::COINDUCTIVE_OVERLAP_IN_COHERENCE;
3736
use rustc_span::symbol::sym;
3837
use rustc_span::DUMMY_SP;
@@ -399,53 +398,167 @@ fn impl_intersection_has_negative_obligation(
399398
) -> bool {
400399
debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id);
401400

402-
// Create an infcx, taking the predicates of impl1 as assumptions:
403-
let infcx = tcx.infer_ctxt().build();
404-
// create a parameter environment corresponding to a (placeholder) instantiation of impl1
405-
let impl_env = tcx.param_env(impl1_def_id);
406-
let subject1 = match traits::fully_normalize(
407-
&infcx,
408-
ObligationCause::dummy(),
409-
impl_env,
410-
tcx.impl_subject(impl1_def_id).instantiate_identity(),
411-
) {
412-
Ok(s) => s,
413-
Err(err) => {
414-
tcx.sess.delay_span_bug(
415-
tcx.def_span(impl1_def_id),
416-
format!("failed to fully normalize {impl1_def_id:?}: {err:?}"),
417-
);
418-
return false;
419-
}
420-
};
401+
let ref infcx = tcx.infer_ctxt().intercrate(true).build();
402+
let universe = infcx.create_next_universe();
421403

422-
// Attempt to prove that impl2 applies, given all of the above.
423-
let selcx = &mut SelectionContext::new(&infcx);
424-
let impl2_args = infcx.fresh_args_for_item(DUMMY_SP, impl2_def_id);
425-
let (subject2, normalization_obligations) =
426-
impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_args, |_, _| {
427-
ObligationCause::dummy()
428-
});
429-
430-
// do the impls unify? If not, then it's not currently possible to prove any
431-
// obligations about their intersection.
432-
let Ok(InferOk { obligations: equate_obligations, .. }) =
433-
infcx.at(&ObligationCause::dummy(), impl_env).eq(DefineOpaqueTypes::No, subject1, subject2)
404+
let impl1_header = fresh_impl_header(infcx, impl1_def_id);
405+
let param_env =
406+
ty::EarlyBinder::bind(tcx.param_env(impl1_def_id)).instantiate(tcx, impl1_header.impl_args);
407+
408+
let impl2_header = fresh_impl_header(infcx, impl2_def_id);
409+
410+
// Equate the headers to find their intersection (the general type, with infer vars,
411+
// that may apply both impls).
412+
let Some(_equate_obligations) =
413+
equate_impl_headers(infcx, param_env, &impl1_header, &impl2_header)
434414
else {
435-
debug!("explicit_disjoint: {:?} does not unify with {:?}", subject1, subject2);
436415
return false;
437416
};
438417

439-
for obligation in normalization_obligations.into_iter().chain(equate_obligations) {
440-
if negative_impl_exists(&infcx, &obligation, impl1_def_id) {
441-
debug!("overlap: obligation unsatisfiable {:?}", obligation);
442-
return true;
418+
plug_infer_with_placeholders(infcx, universe, (impl1_header.impl_args, impl2_header.impl_args));
419+
420+
for (predicate, _) in util::elaborate(
421+
tcx,
422+
tcx.predicates_of(impl2_def_id).instantiate(tcx, impl2_header.impl_args),
423+
) {
424+
let Some(negative_predicate) = predicate.as_predicate().flip_polarity(tcx) else {
425+
continue;
426+
};
427+
428+
let ref infcx = infcx.fork();
429+
let ocx = ObligationCtxt::new(infcx);
430+
431+
ocx.register_obligation(Obligation::new(
432+
tcx,
433+
ObligationCause::dummy(),
434+
param_env,
435+
negative_predicate,
436+
));
437+
438+
if !ocx.select_all_or_error().is_empty() {
439+
continue;
443440
}
441+
442+
// FIXME: regions here too...
443+
444+
return true;
444445
}
445446

446447
false
447448
}
448449

450+
fn plug_infer_with_placeholders<'tcx>(
451+
infcx: &InferCtxt<'tcx>,
452+
universe: ty::UniverseIndex,
453+
value: impl TypeVisitable<TyCtxt<'tcx>>,
454+
) {
455+
struct PlugInferWithPlaceholder<'a, 'tcx> {
456+
infcx: &'a InferCtxt<'tcx>,
457+
universe: ty::UniverseIndex,
458+
var: ty::BoundVar,
459+
}
460+
461+
impl<'tcx> PlugInferWithPlaceholder<'_, 'tcx> {
462+
fn next_var(&mut self) -> ty::BoundVar {
463+
let var = self.var;
464+
self.var = self.var + 1;
465+
var
466+
}
467+
}
468+
469+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for PlugInferWithPlaceholder<'_, 'tcx> {
470+
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
471+
let ty = self.infcx.shallow_resolve(ty);
472+
if ty.is_ty_var() {
473+
let Ok(InferOk { value: (), obligations }) =
474+
self.infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq(
475+
DefineOpaqueTypes::No,
476+
ty,
477+
Ty::new_placeholder(
478+
self.infcx.tcx,
479+
ty::Placeholder {
480+
universe: self.universe,
481+
bound: ty::BoundTy {
482+
var: self.next_var(),
483+
kind: ty::BoundTyKind::Anon,
484+
},
485+
},
486+
),
487+
)
488+
else {
489+
bug!()
490+
};
491+
assert_eq!(obligations, &[]);
492+
ControlFlow::Continue(())
493+
} else {
494+
ty.super_visit_with(self)
495+
}
496+
}
497+
498+
fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
499+
let ct = self.infcx.shallow_resolve(ct);
500+
if ct.is_ct_infer() {
501+
let Ok(InferOk { value: (), obligations }) =
502+
self.infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq(
503+
DefineOpaqueTypes::No,
504+
ct,
505+
ty::Const::new_placeholder(
506+
self.infcx.tcx,
507+
ty::Placeholder { universe: self.universe, bound: self.next_var() },
508+
ct.ty(),
509+
),
510+
)
511+
else {
512+
bug!()
513+
};
514+
assert_eq!(obligations, &[]);
515+
ControlFlow::Continue(())
516+
} else {
517+
ct.super_visit_with(self)
518+
}
519+
}
520+
521+
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
522+
if let ty::ReVar(vid) = *r {
523+
let r = self
524+
.infcx
525+
.inner
526+
.borrow_mut()
527+
.unwrap_region_constraints()
528+
.opportunistic_resolve_var(self.infcx.tcx, vid);
529+
if r.is_var() {
530+
let Ok(InferOk { value: (), obligations }) =
531+
self.infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq(
532+
DefineOpaqueTypes::No,
533+
r,
534+
ty::Region::new_placeholder(
535+
self.infcx.tcx,
536+
ty::Placeholder {
537+
universe: self.universe,
538+
bound: ty::BoundRegion {
539+
var: self.next_var(),
540+
kind: ty::BoundRegionKind::BrAnon,
541+
},
542+
},
543+
),
544+
)
545+
else {
546+
bug!()
547+
};
548+
assert_eq!(obligations, &[]);
549+
}
550+
}
551+
ControlFlow::Continue(())
552+
}
553+
}
554+
555+
value.visit_with(&mut PlugInferWithPlaceholder {
556+
infcx,
557+
universe,
558+
var: ty::BoundVar::from_u32(0),
559+
});
560+
}
561+
449562
/// Try to prove that a negative impl exist for the obligation or its supertraits.
450563
///
451564
/// If such a negative impl exists, then the obligation definitely must not hold

0 commit comments

Comments
 (0)