Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ impl<'tcx> InherentOverlapChecker<'tcx> {
impl1_def_id: DefId,
impl2_def_id: DefId,
) -> Result<(), ErrorGuaranteed> {
let maybe_overlap = traits::overlapping_impls(
let maybe_overlap = traits::overlapping_inherent_impls(
self.tcx,
impl1_def_id,
impl2_def_id,
Expand Down
92 changes: 70 additions & 22 deletions compiler/rustc_trait_selection/src/traits/coherence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ impl TrackAmbiguityCauses {
/// with a suitably-freshened `ImplHeader` with those types
/// instantiated. Otherwise, returns `None`.
#[instrument(skip(tcx, skip_leak_check), level = "debug")]
pub fn overlapping_impls(
pub fn overlapping_inherent_impls(
tcx: TyCtxt<'_>,
impl1_def_id: DefId,
impl2_def_id: DefId,
Expand All @@ -110,25 +110,55 @@ pub fn overlapping_impls(
// Before doing expensive operations like entering an inference context, do
// a quick check via fast_reject to tell if the impl headers could possibly
// unify.
let drcx = DeepRejectCtxt::relate_infer_infer(tcx);
let impl1_ref = tcx.impl_trait_ref(impl1_def_id);
let impl2_ref = tcx.impl_trait_ref(impl2_def_id);
let may_overlap = match (impl1_ref, impl2_ref) {
(Some(a), Some(b)) => drcx.args_may_unify(a.skip_binder().args, b.skip_binder().args),
(None, None) => {
let self_ty1 = tcx.type_of(impl1_def_id).skip_binder();
let self_ty2 = tcx.type_of(impl2_def_id).skip_binder();
drcx.types_may_unify(self_ty1, self_ty2)
}
_ => bug!("unexpected impls: {impl1_def_id:?} {impl2_def_id:?}"),
};
let self_ty1 = tcx.type_of(impl1_def_id).skip_binder();
let self_ty2 = tcx.type_of(impl2_def_id).skip_binder();
let may_overlap = DeepRejectCtxt::relate_infer_infer(tcx).types_may_unify(self_ty1, self_ty2);

if !may_overlap {
// Some types involved are definitely different, so the impls couldn't possibly overlap.
debug!("overlapping_inherent_impls: fast_reject early-exit");
return None;
}

overlapping_impls(tcx, impl1_def_id, impl2_def_id, skip_leak_check, overlap_mode, false)
}

/// If there are types that satisfy both impls, returns `Some`
/// with a suitably-freshened `ImplHeader` with those types
/// instantiated. Otherwise, returns `None`.
#[instrument(skip(tcx, skip_leak_check), level = "debug")]
pub fn overlapping_trait_impls(
tcx: TyCtxt<'_>,
impl1_def_id: DefId,
impl2_def_id: DefId,
skip_leak_check: SkipLeakCheck,
overlap_mode: OverlapMode,
) -> Option<OverlapResult<'_>> {
// Before doing expensive operations like entering an inference context, do
// a quick check via fast_reject to tell if the impl headers could possibly
// unify.
let impl1_args = tcx.impl_trait_ref(impl1_def_id).unwrap().skip_binder().args;
let impl2_args = tcx.impl_trait_ref(impl2_def_id).unwrap().skip_binder().args;
let may_overlap =
DeepRejectCtxt::relate_infer_infer(tcx).args_may_unify(impl1_args, impl2_args);

if !may_overlap {
// Some types involved are definitely different, so the impls couldn't possibly overlap.
debug!("overlapping_impls: fast_reject early-exit");
return None;
}

overlapping_impls(tcx, impl1_def_id, impl2_def_id, skip_leak_check, overlap_mode, true)
}

fn overlapping_impls(
tcx: TyCtxt<'_>,
impl1_def_id: DefId,
impl2_def_id: DefId,
skip_leak_check: SkipLeakCheck,
overlap_mode: OverlapMode,
is_of_trait: bool,
) -> Option<OverlapResult<'_>> {
if tcx.next_trait_solver_in_coherence() {
overlap(
tcx,
Expand All @@ -137,6 +167,7 @@ pub fn overlapping_impls(
impl1_def_id,
impl2_def_id,
overlap_mode,
is_of_trait,
)
} else {
let _overlap_with_bad_diagnostics = overlap(
Expand All @@ -146,6 +177,7 @@ pub fn overlapping_impls(
impl1_def_id,
impl2_def_id,
overlap_mode,
is_of_trait,
)?;

// In the case where we detect an error, run the check again, but
Expand All @@ -158,21 +190,27 @@ pub fn overlapping_impls(
impl1_def_id,
impl2_def_id,
overlap_mode,
is_of_trait,
)
.unwrap();
Some(overlap)
}
}

fn fresh_impl_header<'tcx>(infcx: &InferCtxt<'tcx>, impl_def_id: DefId) -> ImplHeader<'tcx> {
fn fresh_impl_header<'tcx>(
infcx: &InferCtxt<'tcx>,
impl_def_id: DefId,
is_of_trait: bool,
) -> ImplHeader<'tcx> {
let tcx = infcx.tcx;
let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id);

ImplHeader {
impl_def_id,
impl_args,
self_ty: tcx.type_of(impl_def_id).instantiate(tcx, impl_args),
trait_ref: tcx.impl_trait_ref(impl_def_id).map(|i| i.instantiate(tcx, impl_args)),
trait_ref: is_of_trait
.then(|| tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(tcx, impl_args)),
predicates: tcx
.predicates_of(impl_def_id)
.instantiate(tcx, impl_args)
Expand All @@ -186,8 +224,9 @@ fn fresh_impl_header_normalized<'tcx>(
infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
impl_def_id: DefId,
is_of_trait: bool,
) -> ImplHeader<'tcx> {
let header = fresh_impl_header(infcx, impl_def_id);
let header = fresh_impl_header(infcx, impl_def_id, is_of_trait);

let InferOk { value: mut header, obligations } =
infcx.at(&ObligationCause::dummy(), param_env).normalize(header);
Expand All @@ -206,10 +245,16 @@ fn overlap<'tcx>(
impl1_def_id: DefId,
impl2_def_id: DefId,
overlap_mode: OverlapMode,
is_of_trait: bool,
) -> Option<OverlapResult<'tcx>> {
if overlap_mode.use_negative_impl() {
if impl_intersection_has_negative_obligation(tcx, impl1_def_id, impl2_def_id)
|| impl_intersection_has_negative_obligation(tcx, impl2_def_id, impl1_def_id)
if impl_intersection_has_negative_obligation(tcx, impl1_def_id, impl2_def_id, is_of_trait)
|| impl_intersection_has_negative_obligation(
tcx,
impl2_def_id,
impl1_def_id,
is_of_trait,
)
{
return None;
}
Expand All @@ -231,8 +276,10 @@ fn overlap<'tcx>(
// empty environment.
let param_env = ty::ParamEnv::empty();

let impl1_header = fresh_impl_header_normalized(selcx.infcx, param_env, impl1_def_id);
let impl2_header = fresh_impl_header_normalized(selcx.infcx, param_env, impl2_def_id);
let impl1_header =
fresh_impl_header_normalized(selcx.infcx, param_env, impl1_def_id, is_of_trait);
let impl2_header =
fresh_impl_header_normalized(selcx.infcx, param_env, impl2_def_id, is_of_trait);

// Equate the headers to find their intersection (the general type, with infer vars,
// that may apply both impls).
Expand Down Expand Up @@ -446,6 +493,7 @@ fn impl_intersection_has_negative_obligation(
tcx: TyCtxt<'_>,
impl1_def_id: DefId,
impl2_def_id: DefId,
is_of_trait: bool,
) -> bool {
debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id);

Expand All @@ -455,11 +503,11 @@ fn impl_intersection_has_negative_obligation(
let root_universe = infcx.universe();
assert_eq!(root_universe, ty::UniverseIndex::ROOT);

let impl1_header = fresh_impl_header(infcx, impl1_def_id);
let impl1_header = fresh_impl_header(infcx, impl1_def_id, is_of_trait);
let param_env =
ty::EarlyBinder::bind(tcx.param_env(impl1_def_id)).instantiate(tcx, impl1_header.impl_args);

let impl2_header = fresh_impl_header(infcx, impl2_def_id);
let impl2_header = fresh_impl_header(infcx, impl2_def_id, is_of_trait);

// Equate the headers to find their intersection (the general type, with infer vars,
// that may apply both impls).
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_trait_selection/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ use tracing::{debug, instrument};

pub use self::coherence::{
InCrate, IsFirstInputType, OrphanCheckErr, OrphanCheckMode, OverlapResult, UncoveredTyParams,
add_placeholder_note, orphan_check_trait_ref, overlapping_impls,
add_placeholder_note, orphan_check_trait_ref, overlapping_inherent_impls,
overlapping_trait_impls,
};
pub use self::dyn_compatibility::{
DynCompatibilityViolation, dyn_compatibility_violations_for_assoc_item,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ impl<'tcx> Children {
// Found overlap, but no specialization; error out or report future-compat warning.

// Do we *still* get overlap if we disable the future-incompatible modes?
let should_err = traits::overlapping_impls(
let should_err = traits::overlapping_trait_impls(
tcx,
possible_sibling,
impl_def_id,
Expand All @@ -137,7 +137,7 @@ impl<'tcx> Children {
};

let last_lint_mut = &mut last_lint;
let (le, ge) = traits::overlapping_impls(
let (le, ge) = traits::overlapping_trait_impls(
tcx,
possible_sibling,
impl_def_id,
Expand Down
Loading