blob: c63b7773739d0f8fcc3842434e56e08d9cd37ab1 [file]
use rustc_data_structures::fx::FxIndexSet;
use rustc_hir::def_id::LocalDefId;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{
InferCtxt, RegionResolutionError, SubregionOrigin, TyCtxtInferExt, TypeOutlivesConstraint,
};
use rustc_macros::extension;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypingMode, elaborate};
use rustc_span::DUMMY_SP;
use crate::traits::outlives_bounds::InferCtxtExt;
#[extension(pub trait OutlivesEnvironmentBuildExt<'tcx>)]
impl<'tcx> OutlivesEnvironment<'tcx> {
fn new(
infcx: &InferCtxt<'tcx>,
body_def_id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>,
) -> Self {
Self::new_with_implied_bounds_compat(infcx, body_def_id, param_env, assumed_wf_tys, false)
}
fn new_with_implied_bounds_compat(
infcx: &InferCtxt<'tcx>,
body_def_id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>,
disable_implied_bounds_hack: bool,
) -> Self {
let mut bounds = vec![];
for bound in param_env.caller_bounds() {
if let Some(type_outlives) = bound.as_type_outlives_clause() {
debug_assert!(!infcx.next_trait_solver() || !type_outlives.has_non_rigid_aliases());
bounds.push(type_outlives);
}
}
// FIXME(-Znext-trait-solver): Normalize these.
let higher_ranked_assumptions = infcx.take_registered_region_assumptions();
let higher_ranked_assumptions =
elaborate::elaborate_outlives_assumptions(infcx.tcx, higher_ranked_assumptions);
// FIXME: This needs to be modified so that we normalize the known type
// outlives obligations then elaborate them into their region/type components.
// Otherwise, `<W<'a> as Mirror>::Assoc: 'b` will not imply `'a: 'b` even
// if we can normalize `'a`.
OutlivesEnvironment::from_normalized_bounds(
param_env,
bounds,
infcx.implied_bounds_tys(
body_def_id,
param_env,
assumed_wf_tys,
disable_implied_bounds_hack,
),
higher_ranked_assumptions,
)
}
}
#[extension(pub trait InferCtxtRegionExt<'tcx>)]
impl<'tcx> InferCtxt<'tcx> {
/// Resolve regions lexically.
///
/// This function assumes that all infer variables are already constrained.
///
/// FIXME(#155345): this can probably be moved back to `rustc_infer` now that normalization is
/// no longer required. These two extension traits won't be needed then.
fn resolve_regions(
&self,
body_def_id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>,
) -> Vec<RegionResolutionError<'tcx>> {
self.resolve_regions_with_outlives_env(
&OutlivesEnvironment::new(self, body_def_id, param_env, assumed_wf_tys),
self.tcx.def_span(body_def_id),
)
}
}
/// Given a known `param_env` and a set of well formed types, can we prove that
/// `ty` outlives `region`.
pub fn ty_known_to_outlive<'tcx>(
tcx: TyCtxt<'tcx>,
id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
wf_tys: &FxIndexSet<Ty<'tcx>>,
ty: Ty<'tcx>,
region: ty::Region<'tcx>,
) -> bool {
test_region_obligations(tcx, id, param_env, wf_tys, |infcx| {
infcx.register_type_outlives_constraint_inner(TypeOutlivesConstraint {
sub_region: region,
sup_type: ty,
origin: SubregionOrigin::RelateParamBound(DUMMY_SP, ty, None),
});
})
}
/// Given a known `param_env` and a set of well formed types, can we prove that
/// `region_a` outlives `region_b`
pub fn region_known_to_outlive<'tcx>(
tcx: TyCtxt<'tcx>,
id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
wf_tys: &FxIndexSet<Ty<'tcx>>,
region_a: ty::Region<'tcx>,
region_b: ty::Region<'tcx>,
) -> bool {
test_region_obligations(tcx, id, param_env, wf_tys, |infcx| {
infcx.sub_regions(
SubregionOrigin::RelateRegionParamBound(DUMMY_SP, None),
region_b,
region_a,
ty::VisibleForLeakCheck::Unreachable,
);
})
}
/// Given a known `param_env` and a set of well formed types, set up an
/// `InferCtxt`, call the passed function (to e.g. set up region constraints
/// to be tested), then resolve region and return errors
pub fn test_region_obligations<'tcx>(
tcx: TyCtxt<'tcx>,
id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
wf_tys: &FxIndexSet<Ty<'tcx>>,
add_constraints: impl FnOnce(&InferCtxt<'tcx>),
) -> bool {
// Unfortunately, we have to use a new `InferCtxt` each call, because
// region constraints get added and solved there and we need to test each
// call individually.
let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
add_constraints(&infcx);
let errors = infcx.resolve_regions(id, param_env, wf_tys.iter().copied());
tracing::debug!(?errors, "errors");
// If we were able to prove that the type outlives the region without
// an error, it must be because of the implied or explicit bounds...
errors.is_empty()
}