Skip to main content

rustc_borrowck/
implied_bounds.rs

1use rustc_hir::def::DefKind;
2use rustc_hir::def_id::LocalDefId;
3use rustc_infer::infer::TyCtxtInferExt;
4use rustc_infer::traits::ObligationCause;
5use rustc_infer::traits::query::MirBorrowckImpliedOutlivesBounds;
6use rustc_middle::infer::canonical::{Canonical, QueryResponse};
7use rustc_middle::ty::{
8    self, CanonicalVarValues, GenericArg, Ty, TyCtxt, TypeVisitableExt, TypingEnv, fold_regions,
9};
10use rustc_span::DUMMY_SP;
11use rustc_trait_selection::solve::NoSolution;
12use rustc_trait_selection::traits::ObligationCtxt;
13use rustc_trait_selection::traits::implied_outlives_bounds::{
14    compute_implied_outlives_bounds_inner, consider_implied_bounds_hack_for_ty,
15};
16use smallvec::SmallVec;
17use tracing::instrument;
18
19use crate::universal_regions::DefiningTy;
20
21/// Computes the implied bounds for `body_def_id`. This is a separate query
22/// as it must not reveal the hidden type of opaques defined by `body_def_id`
23/// for typeck roots.
24///
25/// However, nested bodies are checked in the scope of their parent. This means
26/// we should actually normalize opaques when computing their implied bounds.
27pub(super) fn mir_borrowck_implied_outlives_bounds<'tcx>(
28    tcx: TyCtxt<'tcx>,
29    body_def_id: LocalDefId,
30) -> Result<
31    &'tcx Canonical<'tcx, QueryResponse<'tcx, MirBorrowckImpliedOutlivesBounds<'tcx>>>,
32    NoSolution,
33> {
34    // If we're in a typeck root we don't want to reveal any opaque types. We need to
35    // make sure the caller actually checks that all our implied bounds actually hold.
36    // This is not the case with the hidden types of opaque types if we're a defining-scope
37    // and the caller is not.
38    //
39    // However, for nested bodies, we always check that they are well-formed in their
40    // parent body, so for these we do want to define opaque types. Not doing so can result
41    // in incorrect errors when normalizing implied bounds.
42    let typing_env = if tcx.is_typeck_child(body_def_id.to_def_id()) {
43        TypingEnv::post_typeck_until_borrowck(tcx, body_def_id)
44    } else {
45        TypingEnv::non_body_analysis(tcx, body_def_id)
46    };
47
48    let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
49    let ocx = ObligationCtxt::new(&infcx);
50
51    let defining_ty = DefiningTy::new(tcx, body_def_id);
52
53    let inputs_and_output = defining_ty.inputs_and_output(tcx);
54    let inputs_and_output =
55        tcx.liberate_late_bound_regions(body_def_id.to_def_id(), inputs_and_output);
56    let inputs_and_output = replace_erased_regions_with_placeholders(tcx, inputs_and_output);
57
58    let mut outlives_bounds = ::alloc::vec::Vec::new()vec![];
59    // Need to return the normalized signature used to compute implied bounds back to borrowck
60    // to deal with unconstrained regions due to #136547.
61    let mut normalized_inputs_and_output = Vec::with_capacity(inputs_and_output.len());
62    for &ty in &inputs_and_output {
63        let num_registered_region_obligations = infcx.num_registered_region_obligations();
64        let normalized_ty = ocx
65            .deeply_normalize(&ObligationCause::dummy(), param_env, ty::Unnormalized::new_wip(ty))
66            .map_err(|_| NoSolution)?;
67
68        outlives_bounds.extend(compute_implied_outlives_bounds_inner(
69            &ocx,
70            param_env,
71            ty,
72            normalized_ty,
73            DUMMY_SP,
74        )?);
75
76        outlives_bounds.extend(consider_implied_bounds_hack_for_ty(&ocx, normalized_ty, || {
77            infcx.registered_region_obligations_since(num_registered_region_obligations)
78        }));
79
80        normalized_inputs_and_output.push(normalized_ty);
81    }
82
83    // Add implied bounds from impl header.
84    //
85    // We don't use `assumed_wf_types` to source the entire set of implied bounds for
86    // a few reasons:
87    // - `DefiningTy` for closure has the `&'env Self` type while `assumed_wf_types` doesn't
88    // - We compute implied bounds from the unnormalized types in the `DefiningTy` but do not
89    //   do so for types in impl headers
90    // - We must compute the normalized signature and then compute implied bounds from that
91    //   in order to connect any unconstrained region vars created during normalization to
92    //   the types of the locals corresponding to the inputs and outputs of the item. #136547
93    if #[allow(non_exhaustive_omitted_patterns)] match tcx.def_kind(body_def_id) {
    DefKind::AssocFn | DefKind::AssocConst { .. } => true,
    _ => false,
}matches!(tcx.def_kind(body_def_id), DefKind::AssocFn | DefKind::AssocConst { .. }) {
94        for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(body_def_id)) {
95            let normalized_ty = ocx
96                .deeply_normalize(
97                    &ObligationCause::dummy(),
98                    param_env,
99                    ty::Unnormalized::new_wip(ty),
100                )
101                .map_err(|_| NoSolution)?;
102
103            // We don't consider the constraints from normalizing the impl header
104            // for the bevy implied bounds hack.
105            let num_registered_region_obligations = infcx.num_registered_region_obligations();
106            outlives_bounds.extend(compute_implied_outlives_bounds_inner(
107                &ocx,
108                param_env,
109                normalized_ty,
110                normalized_ty,
111                DUMMY_SP,
112            )?);
113
114            outlives_bounds.extend(consider_implied_bounds_hack_for_ty(
115                &ocx,
116                normalized_ty,
117                || infcx.registered_region_obligations_since(num_registered_region_obligations),
118            ));
119        }
120    }
121
122    let var_values = implied_bounds_query_var_values(tcx, &inputs_and_output, |r| match r.kind() {
123        ty::RePlaceholder(_) => true,
124        ty::ReEarlyParam(_)
125        | ty::ReLateParam(_)
126        | ty::ReBound(..)
127        | ty::ReStatic
128        | ty::ReError(_) => false,
129        ty::ReVar(..) | ty::ReErased => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
130    });
131    let input_values = CanonicalVarValues { var_values: tcx.mk_args(&var_values) };
132
133    ocx.make_canonicalized_query_response(
134        input_values,
135        MirBorrowckImpliedOutlivesBounds { outlives_bounds, normalized_inputs_and_output },
136    )
137}
138
139/// This computes the `var_values` used by the `mir_borrowck_implied_outlives_bounds` query.
140/// The old solver canonicalization does not replace early and late bound parameters,
141/// so the only `var_values` we need are external regions from the signature of the nested
142/// body as we don't have a shared representation between this query and MIR borrowck.
143///
144/// These are not all external regions of the nested body, only the external regions
145/// which may get accessed by this query. We only need to add things to the `var_values`
146/// which can be referenced by both this query and MIR borrowck. MIR borrowck currently
147/// creates external regions for late-bound regions of parent items as these can be
148/// explicitly mentioned in user types. We do not encounter these in this query, as all
149/// free regions in the signature of nested bodies get replaced with `'erased` at the end
150/// of HIR typeck, so we don't care about them.
151x;#[instrument(level = "debug", skip(tcx, is_external_region), ret)]
152pub(crate) fn implied_bounds_query_var_values<'tcx>(
153    tcx: TyCtxt<'tcx>,
154    unnormalized_inputs_and_output: &[Ty<'tcx>],
155    mut is_external_region: impl FnMut(ty::Region<'tcx>) -> bool,
156) -> SmallVec<[GenericArg<'tcx>; 8]> {
157    let mut values: SmallVec<[GenericArg<'tcx>; 8]> = Default::default();
158
159    for ty in unnormalized_inputs_and_output {
160        tcx.for_each_free_region(ty, |region| {
161            if is_external_region(region) {
162                values.push(region.into());
163            }
164        });
165    }
166
167    values
168}
169
170/// This replaces all external regions in the signature of the current item with
171/// a unique placeholder to collect its implied bounds. This mirrors the way MIR
172/// borrowck replaces all of them with unique NLL vars.
173fn replace_erased_regions_with_placeholders<'tcx>(
174    tcx: TyCtxt<'tcx>,
175    inputs_and_output: &[Ty<'tcx>],
176) -> Vec<Ty<'tcx>> {
177    if true {
    if !!inputs_and_output.has_placeholders() {
        ::core::panicking::panic("assertion failed: !inputs_and_output.has_placeholders()")
    };
};debug_assert!(!inputs_and_output.has_placeholders());
178    let mut next_placeholder = 0;
179    inputs_and_output
180        .iter()
181        .map(|&ty| {
182            fold_regions(tcx, ty, |r, _| match r.kind() {
183                ty::ReErased => {
184                    let var = ty::BoundVar::from_usize(next_placeholder);
185                    next_placeholder += 1;
186                    ty::Region::new_placeholder(
187                        tcx,
188                        ty::PlaceholderRegion::new(
189                            ty::UniverseIndex::ROOT,
190                            ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon },
191                        ),
192                    )
193                }
194                ty::ReEarlyParam(_)
195                | ty::ReLateParam(_)
196                | ty::ReBound(..)
197                | ty::ReStatic
198                | ty::ReError(_) => r,
199                ty::ReVar(..) | ty::RePlaceholder(..) => {
200                    { ::core::panicking::panic_fmt(format_args!("unexpected region: {0:?}", r)); }panic!("unexpected region: {r:?}")
201                }
202            })
203        })
204        .collect()
205}