diff options
Diffstat (limited to '')
-rw-r--r-- | compiler/rustc_trait_selection/src/traits/coherence.rs | 172 |
1 files changed, 94 insertions, 78 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 1c8cdf4ca..8aab75490 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -6,21 +6,22 @@ use crate::infer::outlives::env::OutlivesEnvironment; use crate::infer::{CombinedSnapshot, InferOk}; +use crate::traits::outlives_bounds::InferCtxtExt as _; use crate::traits::select::IntercrateAmbiguityCause; use crate::traits::util::impl_subject_and_oblig; use crate::traits::SkipLeakCheck; use crate::traits::{ - self, FulfillmentContext, Normalized, Obligation, ObligationCause, PredicateObligation, - PredicateObligations, SelectionContext, TraitEngineExt, + self, Normalized, Obligation, ObligationCause, ObligationCtxt, PredicateObligation, + PredicateObligations, SelectionContext, }; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::Diagnostic; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE}; +use rustc_hir::CRATE_HIR_ID; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; -use rustc_infer::traits::{util, TraitEngine}; +use rustc_infer::traits::util; use rustc_middle::traits::specialization_graph::OverlapMode; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; -use rustc_middle::ty::subst::Subst; use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitor}; use rustc_span::symbol::sym; @@ -59,23 +60,17 @@ pub fn add_placeholder_note(err: &mut Diagnostic) { ); } -/// If there are types that satisfy both impls, invokes `on_overlap` +/// If there are types that satisfy both impls, returns `Some` /// with a suitably-freshened `ImplHeader` with those types -/// substituted. Otherwise, invokes `no_overlap`. -#[instrument(skip(tcx, skip_leak_check, on_overlap, no_overlap), level = "debug")] -pub fn overlapping_impls<F1, F2, R>( +/// substituted. Otherwise, returns `None`. +#[instrument(skip(tcx, skip_leak_check), level = "debug")] +pub fn overlapping_impls( tcx: TyCtxt<'_>, impl1_def_id: DefId, impl2_def_id: DefId, skip_leak_check: SkipLeakCheck, overlap_mode: OverlapMode, - on_overlap: F1, - no_overlap: F2, -) -> R -where - F1: FnOnce(OverlapResult<'_>) -> R, - F2: FnOnce() -> R, -{ +) -> 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. @@ -96,28 +91,24 @@ where if !may_overlap { // Some types involved are definitely different, so the impls couldn't possibly overlap. debug!("overlapping_impls: fast_reject early-exit"); - return no_overlap(); + return None; } - let overlaps = tcx.infer_ctxt().enter(|infcx| { - let selcx = &mut SelectionContext::intercrate(&infcx); - overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).is_some() - }); - + let infcx = tcx.infer_ctxt().build(); + let selcx = &mut SelectionContext::intercrate(&infcx); + let overlaps = + overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).is_some(); if !overlaps { - return no_overlap(); + return None; } // 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.) - tcx.infer_ctxt().enter(|infcx| { - let selcx = &mut SelectionContext::intercrate(&infcx); - selcx.enable_tracking_intercrate_ambiguity_causes(); - on_overlap( - overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).unwrap(), - ) - }) + let infcx = tcx.infer_ctxt().build(); + let selcx = &mut SelectionContext::intercrate(&infcx); + selcx.enable_tracking_intercrate_ambiguity_causes(); + Some(overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).unwrap()) } fn with_fresh_ty_vars<'cx, 'tcx>( @@ -166,7 +157,7 @@ fn overlap_within_probe<'cx, 'tcx>( impl1_def_id: DefId, impl2_def_id: DefId, overlap_mode: OverlapMode, - snapshot: &CombinedSnapshot<'_, 'tcx>, + snapshot: &CombinedSnapshot<'tcx>, ) -> Option<OverlapResult<'tcx>> { let infcx = selcx.infcx(); @@ -196,7 +187,7 @@ fn overlap_within_probe<'cx, 'tcx>( } } - // We disable the leak when when creating the `snapshot` by using + // We disable the leak when creating the `snapshot` by using // `infcx.probe_maybe_disable_leak_check`. if infcx.leak_check(true, snapshot).is_err() { debug!("overlap: leak check failed"); @@ -297,36 +288,41 @@ fn negative_impl<'cx, 'tcx>( let tcx = selcx.infcx().tcx; // Create an infcx, taking the predicates of impl1 as assumptions: - tcx.infer_ctxt().enter(|infcx| { - // 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(), - impl_env, - tcx.impl_subject(impl1_def_id), - ) { - Ok(s) => s, - Err(err) => bug!("failed to fully normalize {:?}: {:?}", impl1_def_id, err), - }; + let infcx = tcx.infer_ctxt().build(); + // 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), + ) { + 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 selcx = &mut SelectionContext::new(&infcx); - let impl2_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl2_def_id); - let (subject2, obligations) = - impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs); + // 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 (subject2, obligations) = + impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs); - !equate(&infcx, impl_env, subject1, subject2, obligations) - }) + !equate(&infcx, impl_env, subject1, subject2, obligations, impl1_def_id) } -fn equate<'cx, 'tcx>( - infcx: &InferCtxt<'cx, 'tcx>, +fn equate<'tcx>( + infcx: &InferCtxt<'tcx>, impl_env: ty::ParamEnv<'tcx>, subject1: ImplSubject<'tcx>, subject2: ImplSubject<'tcx>, obligations: impl Iterator<Item = PredicateObligation<'tcx>>, + body_def_id: DefId, ) -> bool { // do the impls unify? If not, not disjoint. let Ok(InferOk { obligations: more_obligations, .. }) = @@ -340,7 +336,7 @@ fn equate<'cx, 'tcx>( let opt_failing_obligation = obligations .into_iter() .chain(more_obligations) - .find(|o| negative_impl_exists(selcx, impl_env, o)); + .find(|o| negative_impl_exists(selcx, o, body_def_id)); if let Some(failing_obligation) = opt_failing_obligation { debug!("overlap: obligation unsatisfiable {:?}", failing_obligation); @@ -354,18 +350,16 @@ fn equate<'cx, 'tcx>( #[instrument(level = "debug", skip(selcx))] fn negative_impl_exists<'cx, 'tcx>( selcx: &SelectionContext<'cx, 'tcx>, - param_env: ty::ParamEnv<'tcx>, o: &PredicateObligation<'tcx>, + body_def_id: DefId, ) -> bool { - let infcx = &selcx.infcx().fork(); - - if resolve_negative_obligation(infcx, param_env, o) { + if resolve_negative_obligation(selcx.infcx().fork(), o, body_def_id) { return true; } // 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, &o) { + for o in util::elaborate_predicates(selcx.tcx(), iter::once(o.predicate)) { + if resolve_negative_obligation(selcx.infcx().fork(), &o, body_def_id) { return true; } } @@ -374,10 +368,10 @@ fn negative_impl_exists<'cx, 'tcx>( } #[instrument(level = "debug", skip(infcx))] -fn resolve_negative_obligation<'cx, 'tcx>( - infcx: &InferCtxt<'cx, 'tcx>, - param_env: ty::ParamEnv<'tcx>, +fn resolve_negative_obligation<'tcx>( + infcx: InferCtxt<'tcx>, o: &PredicateObligation<'tcx>, + body_def_id: DefId, ) -> bool { let tcx = infcx.tcx; @@ -385,17 +379,25 @@ fn resolve_negative_obligation<'cx, 'tcx>( return false; }; - let mut fulfillment_cx = <dyn TraitEngine<'tcx>>::new(infcx.tcx); - fulfillment_cx.register_predicate_obligation(infcx, o); - - let errors = fulfillment_cx.select_all_or_error(infcx); - - if !errors.is_empty() { + let param_env = o.param_env; + if !super::fully_solve_obligation(&infcx, o).is_empty() { return false; } - // FIXME -- also add "assumed to be well formed" types into the `outlives_env` - let outlives_env = OutlivesEnvironment::new(param_env); + let (body_id, body_def_id) = if let Some(body_def_id) = body_def_id.as_local() { + (tcx.hir().local_def_id_to_hir_id(body_def_id), body_def_id) + } else { + (CRATE_HIR_ID, CRATE_DEF_ID) + }; + + let ocx = ObligationCtxt::new(&infcx); + 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_id, wf_tys), + ); + infcx.process_registered_region_obligations(outlives_env.region_bound_pairs(), param_env); infcx.resolve_regions(&outlives_env).is_empty() @@ -404,12 +406,12 @@ fn resolve_negative_obligation<'cx, 'tcx>( pub fn trait_ref_is_knowable<'tcx>( tcx: TyCtxt<'tcx>, trait_ref: ty::TraitRef<'tcx>, -) -> Option<Conflict> { +) -> Result<(), Conflict> { debug!("trait_ref_is_knowable(trait_ref={:?})", trait_ref); if orphan_check_trait_ref(tcx, trait_ref, InCrate::Remote).is_ok() { // A downstream or cousin crate is allowed to implement some // substitution of this trait-ref. - return Some(Conflict::Downstream); + return Err(Conflict::Downstream); } if trait_ref_is_local_or_fundamental(tcx, trait_ref) { @@ -418,7 +420,7 @@ pub fn trait_ref_is_knowable<'tcx>( // allowed to implement a substitution of this trait ref, which // means impls could only come from dependencies of this crate, // which we already know about. - return None; + return Ok(()); } // This is a remote non-fundamental trait, so if another crate @@ -431,10 +433,10 @@ pub fn trait_ref_is_knowable<'tcx>( // we are an owner. if orphan_check_trait_ref(tcx, trait_ref, InCrate::Local).is_ok() { debug!("trait_ref_is_knowable: orphan check passed"); - None + Ok(()) } else { debug!("trait_ref_is_knowable: nonlocal, nonfundamental, unowned"); - Some(Conflict::Upstream) + Err(Conflict::Upstream) } } @@ -740,7 +742,21 @@ impl<'tcx> TypeVisitor<'tcx> for OrphanChecker<'tcx> { result } - // FIXME: Constants should participate in orphan checking. + /// All possible values for a constant parameter already exist + /// in the crate defining the trait, so they are always non-local[^1]. + /// + /// Because there's no way to have an impl where the first local + /// generic argument is a constant, we also don't have to fail + /// the orphan check when encountering a parameter or a generic constant. + /// + /// This means that we can completely ignore constants during the orphan check. + /// + /// See `src/test/ui/coherence/const-generics-orphan-check-ok.rs` for examples. + /// + /// [^1]: This might not hold for function pointers or trait objects in the future. + /// As these should be quite rare as const arguments and especially rare as impl + /// parameters, allowing uncovered const parameters in impls seems more useful + /// than allowing `impl<T> Trait<local_fn_ptr, T> for i32` to compile. fn visit_const(&mut self, _c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { ControlFlow::CONTINUE } |