diff options
| author | Matthias Krüger <matthias.krueger@famsik.de> | 2022-08-24 18:20:11 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-08-24 18:20:11 +0200 |
| commit | 0fcabec6204065a66b35e3a4aed1b1d9fada409c (patch) | |
| tree | 7b2eea4e8fe4da11d52920851acb570eb99ac943 /compiler/rustc_trait_selection/src | |
| parent | e802df9e8b2232b7a105eba75852c55f33019c1b (diff) | |
| parent | 4da14ef50eb697298f36d41f91cb02dc800275bf (diff) | |
| download | rust-0fcabec6204065a66b35e3a4aed1b1d9fada409c.tar.gz rust-0fcabec6204065a66b35e3a4aed1b1d9fada409c.zip | |
Rollup merge of #100888 - spastorino:coherence-negative-impls-implied-bounds, r=lcnr
Coherence negative impls implied bounds Fixes #93875 This PR is rebased on top of #100789 and it would need to include that one which is already r+ed. r? ``@nikomatsakis`` cc ``@lcnr`` (which I've talked about https://github.com/rust-lang/rust/commit/3222f420d9d2312efe0735eb48160c7b070adc54, I guess after you finish your reordering of modules and work with OutlivesEnvironmentEnv this commit can just be reverted).
Diffstat (limited to 'compiler/rustc_trait_selection/src')
4 files changed, 146 insertions, 11 deletions
diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 9c252fcfe1c..ce48d4c99e9 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -20,6 +20,7 @@ #![feature(let_else)] #![feature(if_let_guard)] #![feature(never_type)] +#![feature(type_alias_impl_trait)] #![recursion_limit = "512"] // For rustdoc #[macro_use] diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 3bc08fba91a..292787d4dbb 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -6,16 +6,18 @@ 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, Normalized, Obligation, ObligationCause, PredicateObligation, PredicateObligations, - SelectionContext, + 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; use rustc_middle::traits::specialization_graph::OverlapMode; @@ -322,7 +324,7 @@ fn negative_impl<'cx, 'tcx>( 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) }) } @@ -332,6 +334,7 @@ fn equate<'cx, '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, .. }) = @@ -342,8 +345,10 @@ fn equate<'cx, 'tcx>( }; let selcx = &mut SelectionContext::new(&infcx); - let opt_failing_obligation = - obligations.into_iter().chain(more_obligations).find(|o| negative_impl_exists(selcx, o)); + let opt_failing_obligation = obligations + .into_iter() + .chain(more_obligations) + .find(|o| negative_impl_exists(selcx, o, body_def_id)); if let Some(failing_obligation) = opt_failing_obligation { debug!("overlap: obligation unsatisfiable {:?}", failing_obligation); @@ -358,14 +363,15 @@ fn equate<'cx, 'tcx>( fn negative_impl_exists<'cx, 'tcx>( selcx: &SelectionContext<'cx, 'tcx>, o: &PredicateObligation<'tcx>, + body_def_id: DefId, ) -> bool { - if resolve_negative_obligation(selcx.infcx().fork(), 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(selcx.tcx(), iter::once(o.predicate)) { - if resolve_negative_obligation(selcx.infcx().fork(), &o) { + if resolve_negative_obligation(selcx.infcx().fork(), &o, body_def_id) { return true; } } @@ -377,6 +383,7 @@ fn negative_impl_exists<'cx, 'tcx>( fn resolve_negative_obligation<'cx, 'tcx>( infcx: InferCtxt<'cx, 'tcx>, o: &PredicateObligation<'tcx>, + body_def_id: DefId, ) -> bool { let tcx = infcx.tcx; @@ -385,12 +392,24 @@ fn resolve_negative_obligation<'cx, 'tcx>( }; let param_env = o.param_env; - let errors = super::fully_solve_obligation(&infcx, o); - if !errors.is_empty() { + if !super::fully_solve_obligation(&infcx, o).is_empty() { return false; } - 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() diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index d922f893321..904c81f9215 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -13,6 +13,7 @@ mod fulfill; pub mod misc; mod object_safety; mod on_unimplemented; +pub mod outlives_bounds; mod project; pub mod query; pub(crate) mod relationships; diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs new file mode 100644 index 00000000000..a4b54018228 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -0,0 +1,114 @@ +use crate::infer::InferCtxt; +use crate::traits::query::type_op::{self, TypeOp, TypeOpOutput}; +use crate::traits::query::NoSolution; +use crate::traits::{ObligationCause, TraitEngine, TraitEngineExt}; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir as hir; +use rustc_hir::HirId; +use rustc_middle::ty::{self, ParamEnv, Ty}; + +pub use rustc_middle::traits::query::OutlivesBound; + +type Bounds<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a; +pub trait InferCtxtExt<'a, 'tcx> { + fn implied_outlives_bounds( + &self, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, + ty: Ty<'tcx>, + ) -> Vec<OutlivesBound<'tcx>>; + + fn implied_bounds_tys( + &'a self, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, + tys: FxHashSet<Ty<'tcx>>, + ) -> Bounds<'a, 'tcx>; +} + +impl<'a, 'cx, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'cx, 'tcx> { + /// Implied bounds are region relationships that we deduce + /// automatically. The idea is that (e.g.) a caller must check that a + /// function's argument types are well-formed immediately before + /// calling that fn, and hence the *callee* can assume that its + /// argument types are well-formed. This may imply certain relationships + /// between generic parameters. For example: + /// ``` + /// fn foo<'a,T>(x: &'a T) {} + /// ``` + /// can only be called with a `'a` and `T` such that `&'a T` is WF. + /// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. + /// + /// # Parameters + /// + /// - `param_env`, the where-clauses in scope + /// - `body_id`, the body-id to use when normalizing assoc types. + /// Note that this may cause outlives obligations to be injected + /// into the inference context with this body-id. + /// - `ty`, the type that we are supposed to assume is WF. + #[instrument(level = "debug", skip(self, param_env, body_id))] + fn implied_outlives_bounds( + &self, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, + ty: Ty<'tcx>, + ) -> Vec<OutlivesBound<'tcx>> { + let span = self.tcx.hir().span(body_id); + let result = param_env + .and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty }) + .fully_perform(self); + let result = match result { + Ok(r) => r, + Err(NoSolution) => { + self.tcx.sess.delay_span_bug( + span, + "implied_outlives_bounds failed to solve all obligations", + ); + return vec![]; + } + }; + + let TypeOpOutput { output, constraints, .. } = result; + + if let Some(constraints) = constraints { + // Instantiation may have produced new inference variables and constraints on those + // variables. Process these constraints. + let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(self.tcx); + let cause = ObligationCause::misc(span, body_id); + for &constraint in &constraints.outlives { + let obligation = self.query_outlives_constraint_to_obligation( + constraint, + cause.clone(), + param_env, + ); + fulfill_cx.register_predicate_obligation(self, obligation); + } + if !constraints.member_constraints.is_empty() { + span_bug!(span, "{:#?}", constraints.member_constraints); + } + let errors = fulfill_cx.select_all_or_error(self); + if !errors.is_empty() { + self.tcx.sess.delay_span_bug( + span, + "implied_outlives_bounds failed to solve obligations from instantiation", + ); + } + }; + + output + } + + fn implied_bounds_tys( + &'a self, + param_env: ParamEnv<'tcx>, + body_id: HirId, + tys: FxHashSet<Ty<'tcx>>, + ) -> Bounds<'a, 'tcx> { + tys.into_iter() + .map(move |ty| { + let ty = self.resolve_vars_if_possible(ty); + self.implied_outlives_bounds(param_env, body_id, ty) + }) + .flatten() + } +} |
