blob: bb1d09698a0745c41e1e181260c8c3dfa4b39b84 [file] [edit]
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::ObligationCause;
use rustc_infer::traits::query::MirBorrowckImpliedOutlivesBounds;
use rustc_middle::infer::canonical::{Canonical, QueryResponse};
use rustc_middle::ty::{
self, CanonicalVarValues, GenericArg, Ty, TyCtxt, TypeVisitableExt, TypingEnv, fold_regions,
};
use rustc_span::DUMMY_SP;
use rustc_trait_selection::solve::NoSolution;
use rustc_trait_selection::traits::ObligationCtxt;
use rustc_trait_selection::traits::implied_outlives_bounds::{
compute_implied_outlives_bounds_inner, consider_implied_bounds_hack_for_ty,
};
use smallvec::SmallVec;
use tracing::instrument;
use crate::universal_regions::DefiningTy;
/// Computes the implied bounds for `body_def_id`. This is a separate query
/// as it must not reveal the hidden type of opaques defined by `body_def_id`
/// for typeck roots.
///
/// However, nested bodies are checked in the scope of their parent. This means
/// we should actually normalize opaques when computing their implied bounds.
pub(super) fn mir_borrowck_implied_outlives_bounds<'tcx>(
tcx: TyCtxt<'tcx>,
body_def_id: LocalDefId,
) -> Result<
&'tcx Canonical<'tcx, QueryResponse<'tcx, MirBorrowckImpliedOutlivesBounds<'tcx>>>,
NoSolution,
> {
// If we're in a typeck root we don't want to reveal any opaque types. We need to
// make sure the caller actually checks that all our implied bounds actually hold.
// This is not the case with the hidden types of opaque types if we're a defining-scope
// and the caller is not.
//
// However, for nested bodies, we always check that they are well-formed in their
// parent body, so for these we do want to define opaque types. Not doing so can result
// in incorrect errors when normalizing implied bounds.
let typing_env = if tcx.is_typeck_child(body_def_id.to_def_id()) {
TypingEnv::post_typeck_until_borrowck(tcx, body_def_id)
} else {
TypingEnv::non_body_analysis(tcx, body_def_id)
};
let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
let ocx = ObligationCtxt::new(&infcx);
let defining_ty = DefiningTy::new(tcx, body_def_id);
let inputs_and_output = defining_ty.inputs_and_output(tcx);
let inputs_and_output =
tcx.liberate_late_bound_regions(body_def_id.to_def_id(), inputs_and_output);
let inputs_and_output = replace_erased_regions_with_placeholders(tcx, inputs_and_output);
let mut outlives_bounds = vec![];
// Need to return the normalized signature used to compute implied bounds back to borrowck
// to deal with unconstrained regions due to #136547.
let mut normalized_inputs_and_output = Vec::with_capacity(inputs_and_output.len());
for &ty in &inputs_and_output {
let num_registered_region_obligations = infcx.num_registered_region_obligations();
let normalized_ty = ocx
.deeply_normalize(&ObligationCause::dummy(), param_env, ty::Unnormalized::new_wip(ty))
.map_err(|_| NoSolution)?;
outlives_bounds.extend(compute_implied_outlives_bounds_inner(
&ocx,
param_env,
ty,
normalized_ty,
DUMMY_SP,
)?);
outlives_bounds.extend(consider_implied_bounds_hack_for_ty(&ocx, normalized_ty, || {
infcx.registered_region_obligations_since(num_registered_region_obligations)
}));
normalized_inputs_and_output.push(normalized_ty);
}
// Add implied bounds from impl header.
//
// We don't use `assumed_wf_types` to source the entire set of implied bounds for
// a few reasons:
// - `DefiningTy` for closure has the `&'env Self` type while `assumed_wf_types` doesn't
// - We compute implied bounds from the unnormalized types in the `DefiningTy` but do not
// do so for types in impl headers
// - We must compute the normalized signature and then compute implied bounds from that
// in order to connect any unconstrained region vars created during normalization to
// the types of the locals corresponding to the inputs and outputs of the item. #136547
if matches!(tcx.def_kind(body_def_id), DefKind::AssocFn | DefKind::AssocConst { .. }) {
for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(body_def_id)) {
let normalized_ty = ocx
.deeply_normalize(
&ObligationCause::dummy(),
param_env,
ty::Unnormalized::new_wip(ty),
)
.map_err(|_| NoSolution)?;
// We don't consider the constraints from normalizing the impl header
// for the bevy implied bounds hack.
let num_registered_region_obligations = infcx.num_registered_region_obligations();
outlives_bounds.extend(compute_implied_outlives_bounds_inner(
&ocx,
param_env,
normalized_ty,
normalized_ty,
DUMMY_SP,
)?);
outlives_bounds.extend(consider_implied_bounds_hack_for_ty(
&ocx,
normalized_ty,
|| infcx.registered_region_obligations_since(num_registered_region_obligations),
));
}
}
let var_values = implied_bounds_query_var_values(tcx, &inputs_and_output, |r| match r.kind() {
ty::RePlaceholder(_) => true,
ty::ReEarlyParam(_)
| ty::ReLateParam(_)
| ty::ReBound(..)
| ty::ReStatic
| ty::ReError(_) => false,
ty::ReVar(..) | ty::ReErased => unreachable!(),
});
let input_values = CanonicalVarValues { var_values: tcx.mk_args(&var_values) };
ocx.make_canonicalized_query_response(
input_values,
MirBorrowckImpliedOutlivesBounds { outlives_bounds, normalized_inputs_and_output },
)
}
/// This computes the `var_values` used by the `mir_borrowck_implied_outlives_bounds` query.
/// The old solver canonicalization does not replace early and late bound parameters,
/// so the only `var_values` we need are external regions from the signature of the nested
/// body as we don't have a shared representation between this query and MIR borrowck.
///
/// These are not all external regions of the nested body, only the external regions
/// which may get accessed by this query. We only need to add things to the `var_values`
/// which can be referenced by both this query and MIR borrowck. MIR borrowck currently
/// creates external regions for late-bound regions of parent items as these can be
/// explicitly mentioned in user types. We do not encounter these in this query, as all
/// free regions in the signature of nested bodies get replaced with `'erased` at the end
/// of HIR typeck, so we don't care about them.
#[instrument(level = "debug", skip(tcx, is_external_region), ret)]
pub(crate) fn implied_bounds_query_var_values<'tcx>(
tcx: TyCtxt<'tcx>,
unnormalized_inputs_and_output: &[Ty<'tcx>],
mut is_external_region: impl FnMut(ty::Region<'tcx>) -> bool,
) -> SmallVec<[GenericArg<'tcx>; 8]> {
let mut values: SmallVec<[GenericArg<'tcx>; 8]> = Default::default();
for ty in unnormalized_inputs_and_output {
tcx.for_each_free_region(ty, |region| {
if is_external_region(region) {
values.push(region.into());
}
});
}
values
}
/// This replaces all external regions in the signature of the current item with
/// a unique placeholder to collect its implied bounds. This mirrors the way MIR
/// borrowck replaces all of them with unique NLL vars.
fn replace_erased_regions_with_placeholders<'tcx>(
tcx: TyCtxt<'tcx>,
inputs_and_output: &[Ty<'tcx>],
) -> Vec<Ty<'tcx>> {
debug_assert!(!inputs_and_output.has_placeholders());
let mut next_placeholder = 0;
inputs_and_output
.iter()
.map(|&ty| {
fold_regions(tcx, ty, |r, _| match r.kind() {
ty::ReErased => {
let var = ty::BoundVar::from_usize(next_placeholder);
next_placeholder += 1;
ty::Region::new_placeholder(
tcx,
ty::PlaceholderRegion::new(
ty::UniverseIndex::ROOT,
ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon },
),
)
}
ty::ReEarlyParam(_)
| ty::ReLateParam(_)
| ty::ReBound(..)
| ty::ReStatic
| ty::ReError(_) => r,
ty::ReVar(..) | ty::RePlaceholder(..) => {
panic!("unexpected region: {r:?}")
}
})
})
.collect()
}