diff options
Diffstat (limited to 'compiler/rustc_trait_selection/src/traits/coherence.rs')
-rw-r--r-- | compiler/rustc_trait_selection/src/traits/coherence.rs | 86 |
1 files changed, 55 insertions, 31 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 6b688c322..20c2605f2 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -17,7 +17,7 @@ use crate::traits::{ use rustc_data_structures::fx::FxIndexSet; use rustc_errors::Diagnostic; use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE}; -use rustc_infer::infer::{DefiningAnchor, InferCtxt, TyCtxtInferExt}; +use rustc_infer::infer::{DefineOpaqueTypes, DefiningAnchor, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::util; use rustc_middle::traits::specialization_graph::OverlapMode; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; @@ -75,12 +75,13 @@ 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 { treat_obligation_params: TreatParams::AsInfer }; + let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsCandidateKey }; 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)) => iter::zip(a.skip_binder().substs, b.skip_binder().substs) - .all(|(arg1, arg2)| drcx.generic_args_may_unify(arg1, arg2)), + (Some(a), Some(b)) => { + drcx.substs_refs_may_unify(a.skip_binder().substs, b.skip_binder().substs) + } (None, None) => { let self_ty1 = tcx.type_of(impl1_def_id).skip_binder(); let self_ty2 = tcx.type_of(impl2_def_id).skip_binder(); @@ -95,8 +96,11 @@ pub fn overlapping_impls( return None; } - let infcx = - tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).intercrate().build(); + let infcx = tcx + .infer_ctxt() + .with_opaque_type_inference(DefiningAnchor::Bubble) + .intercrate(true) + .build(); let selcx = &mut SelectionContext::new(&infcx); let overlaps = overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).is_some(); @@ -107,8 +111,11 @@ pub fn overlapping_impls( // In the case where we detect an error, run the check again, but // this time tracking intercrate ambiguity causes for better // diagnostics. (These take time and can lead to false errors.) - let infcx = - tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).intercrate().build(); + let infcx = tcx + .infer_ctxt() + .with_opaque_type_inference(DefiningAnchor::Bubble) + .intercrate(true) + .build(); let selcx = &mut SelectionContext::new(&infcx); selcx.enable_tracking_intercrate_ambiguity_causes(); Some(overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).unwrap()) @@ -181,7 +188,7 @@ fn overlap_within_probe<'cx, 'tcx>( let impl1_header = with_fresh_ty_vars(selcx, param_env, impl1_def_id); let impl2_header = with_fresh_ty_vars(selcx, param_env, impl2_def_id); - let obligations = equate_impl_headers(selcx, &impl1_header, &impl2_header)?; + let obligations = equate_impl_headers(selcx.infcx, &impl1_header, &impl2_header)?; debug!("overlap: unification check succeeded"); if overlap_mode.use_implicit_negative() { @@ -207,20 +214,25 @@ fn overlap_within_probe<'cx, 'tcx>( Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder }) } -fn equate_impl_headers<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - impl1_header: &ty::ImplHeader<'tcx>, - impl2_header: &ty::ImplHeader<'tcx>, +#[instrument(level = "debug", skip(infcx), ret)] +fn equate_impl_headers<'tcx>( + infcx: &InferCtxt<'tcx>, + impl1: &ty::ImplHeader<'tcx>, + impl2: &ty::ImplHeader<'tcx>, ) -> Option<PredicateObligations<'tcx>> { - // Do `a` and `b` unify? If not, no overlap. - debug!("equate_impl_headers(impl1_header={:?}, impl2_header={:?}", impl1_header, impl2_header); - selcx - .infcx - .at(&ObligationCause::dummy(), ty::ParamEnv::empty()) - .define_opaque_types(true) - .eq_impl_headers(impl1_header, impl2_header) - .map(|infer_ok| infer_ok.obligations) - .ok() + let result = match (impl1.trait_ref, impl2.trait_ref) { + (Some(impl1_ref), Some(impl2_ref)) => infcx + .at(&ObligationCause::dummy(), ty::ParamEnv::empty()) + .eq(DefineOpaqueTypes::Yes, impl1_ref, impl2_ref), + (None, None) => infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq( + DefineOpaqueTypes::Yes, + impl1.self_ty, + impl2.self_ty, + ), + _ => bug!("mk_eq_impl_headers given mismatched impl kinds"), + }; + + result.map(|infer_ok| infer_ok.obligations).ok() } /// Given impl1 and impl2 check if both impls can be satisfied by a common type (including @@ -294,7 +306,7 @@ fn negative_impl(tcx: TyCtxt<'_>, impl1_def_id: DefId, impl2_def_id: DefId) -> b &infcx, ObligationCause::dummy(), impl_env, - tcx.impl_subject(impl1_def_id), + tcx.impl_subject(impl1_def_id).subst_identity(), ) { Ok(s) => s, Err(err) => { @@ -325,7 +337,7 @@ fn equate<'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) + infcx.at(&ObligationCause::dummy(), impl_env).eq(DefineOpaqueTypes::No,subject1, subject2) else { debug!("explicit_disjoint: {:?} does not unify with {:?}", subject1, subject2); return true; @@ -356,8 +368,8 @@ fn negative_impl_exists<'tcx>( } // 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.fork(), &o, body_def_id) { + for pred in util::elaborate(infcx.tcx, iter::once(o.predicate)) { + if resolve_negative_obligation(infcx.fork(), &o.with(infcx.tcx, pred), body_def_id) { return true; } } @@ -378,7 +390,10 @@ fn resolve_negative_obligation<'tcx>( }; let param_env = o.param_env; - if !super::fully_solve_obligation(&infcx, o).is_empty() { + let ocx = ObligationCtxt::new(&infcx); + ocx.register_obligation(o); + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { return false; } @@ -388,20 +403,29 @@ fn resolve_negative_obligation<'tcx>( let wf_tys = ocx.assumed_wf_types(param_env, DUMMY_SP, body_def_id); let outlives_env = OutlivesEnvironment::with_bounds( param_env, - Some(&infcx), infcx.implied_bounds_tys(param_env, body_def_id, wf_tys), ); - - infcx.process_registered_region_obligations(outlives_env.region_bound_pairs(), param_env); - infcx.resolve_regions(&outlives_env).is_empty() } +/// Returns whether all impls which would apply to the `trait_ref` +/// e.g. `Ty: Trait<Arg>` are already known in the local crate. +/// +/// This both checks whether any downstream or sibling crates could +/// implement it and whether an upstream crate can add this impl +/// without breaking backwards compatibility. #[instrument(level = "debug", skip(tcx), ret)] pub fn trait_ref_is_knowable<'tcx>( tcx: TyCtxt<'tcx>, trait_ref: ty::TraitRef<'tcx>, ) -> Result<(), Conflict> { + if Some(trait_ref.def_id) == tcx.lang_items().fn_ptr_trait() { + // The only types implementing `FnPtr` are function pointers, + // so if there's no impl of `FnPtr` in the current crate, + // then such an impl will never be added in the future. + return Ok(()); + } + if orphan_check_trait_ref(trait_ref, InCrate::Remote).is_ok() { // A downstream or cousin crate is allowed to implement some // substitution of this trait-ref. |