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 3222f420d9, I guess after you finish your reordering of modules and work with OutlivesEnvironmentEnv this commit can just be reverted).
This commit is contained in:
Matthias Krüger 2022-08-24 18:20:11 +02:00 committed by GitHub
commit 0fcabec620
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 42 additions and 33 deletions

View file

@ -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]

View file

@ -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()

View file

@ -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;

View file

@ -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()
}
}