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