Skip to content

Commit fdc212d

Browse files
committed
Auto merge of #160491 - lcnr:implied-bounds-opaque, r=<try>
move implied bounds computation out of borrowck
2 parents c9ff496 + 1dd6c27 commit fdc212d

41 files changed

Lines changed: 1412 additions & 527 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
use rustc_hir::def::DefKind;
2+
use rustc_hir::def_id::LocalDefId;
3+
use rustc_infer::infer::TyCtxtInferExt;
4+
use rustc_infer::traits::ObligationCause;
5+
use rustc_infer::traits::query::MirBorrowckImpliedOutlivesBounds;
6+
use rustc_middle::infer::canonical::{Canonical, QueryResponse};
7+
use rustc_middle::ty::{
8+
self, CanonicalVarValues, GenericArg, Ty, TyCtxt, TypeVisitableExt, TypingEnv, fold_regions,
9+
};
10+
use rustc_span::DUMMY_SP;
11+
use rustc_trait_selection::solve::NoSolution;
12+
use rustc_trait_selection::traits::ObligationCtxt;
13+
use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::{
14+
compute_implied_outlives_bounds_inner, consider_implied_bounds_hack_for_ty,
15+
};
16+
use smallvec::SmallVec;
17+
use tracing::instrument;
18+
19+
use 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+
pub(super) fn mir_borrowck_implied_outlives_bounds<'tcx>(
24+
tcx: TyCtxt<'tcx>,
25+
body_def_id: LocalDefId,
26+
) -> Result<
27+
&'tcx Canonical<'tcx, QueryResponse<'tcx, MirBorrowckImpliedOutlivesBounds<'tcx>>>,
28+
NoSolution,
29+
> {
30+
// We do not want to reveal the hidden types of any opaque types in this function.
31+
let typing_env = TypingEnv::non_body_analysis(tcx, body_def_id);
32+
let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
33+
let ocx = ObligationCtxt::new(&infcx);
34+
35+
let defining_ty = DefiningTy::new(tcx, body_def_id);
36+
37+
let inputs_and_output = defining_ty.inputs_and_output(tcx);
38+
let inputs_and_output =
39+
tcx.liberate_late_bound_regions(body_def_id.to_def_id(), inputs_and_output);
40+
let inputs_and_output = replace_erased_regions_with_placeholders(tcx, inputs_and_output);
41+
42+
let mut outlives_bounds = vec![];
43+
// Need to return the normalized signature used to compute implied bounds back to borrowck
44+
// to deal with unconstrained regions due to #136547.
45+
let mut normalized_inputs_and_output = Vec::with_capacity(inputs_and_output.len());
46+
for &ty in &inputs_and_output {
47+
let num_registered_region_obligations = infcx.num_registered_region_obligations();
48+
let normalized_ty = ocx
49+
.deeply_normalize(&ObligationCause::dummy(), param_env, ty::Unnormalized::new_wip(ty))
50+
.map_err(|_| NoSolution)?;
51+
52+
outlives_bounds.extend(compute_implied_outlives_bounds_inner(
53+
&ocx,
54+
param_env,
55+
ty,
56+
normalized_ty,
57+
DUMMY_SP,
58+
)?);
59+
60+
outlives_bounds.extend(consider_implied_bounds_hack_for_ty(&ocx, normalized_ty, || {
61+
infcx.registered_region_obligations_since(num_registered_region_obligations)
62+
}));
63+
64+
normalized_inputs_and_output.push(normalized_ty);
65+
}
66+
67+
// Add implied bounds from impl header.
68+
//
69+
// We don't use `assumed_wf_types` to source the entire set of implied bounds for
70+
// a few reasons:
71+
// - `DefiningTy` for closure has the `&'env Self` type while `assumed_wf_types` doesn't
72+
// - We compute implied bounds from the unnormalized types in the `DefiningTy` but do not
73+
// do so for types in impl headers
74+
// - We must compute the normalized signature and then compute implied bounds from that
75+
// in order to connect any unconstrained region vars created during normalization to
76+
// the types of the locals corresponding to the inputs and outputs of the item. #136547
77+
if matches!(tcx.def_kind(body_def_id), DefKind::AssocFn | DefKind::AssocConst { .. }) {
78+
for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(body_def_id)) {
79+
let normalized_ty = ocx
80+
.deeply_normalize(
81+
&ObligationCause::dummy(),
82+
param_env,
83+
ty::Unnormalized::new_wip(ty),
84+
)
85+
.map_err(|_| NoSolution)?;
86+
87+
// We don't consider the constraints from normalizing the impl header
88+
// for the bevy implied bounds hack.
89+
let num_registered_region_obligations = infcx.num_registered_region_obligations();
90+
outlives_bounds.extend(compute_implied_outlives_bounds_inner(
91+
&ocx,
92+
param_env,
93+
normalized_ty,
94+
normalized_ty,
95+
DUMMY_SP,
96+
)?);
97+
98+
outlives_bounds.extend(consider_implied_bounds_hack_for_ty(
99+
&ocx,
100+
normalized_ty,
101+
|| infcx.registered_region_obligations_since(num_registered_region_obligations),
102+
));
103+
}
104+
}
105+
106+
let var_values = implied_bounds_query_var_values(tcx, &inputs_and_output, |r| match r.kind() {
107+
ty::RePlaceholder(_) => true,
108+
ty::ReEarlyParam(_)
109+
| ty::ReLateParam(_)
110+
| ty::ReBound(..)
111+
| ty::ReStatic
112+
| ty::ReError(_) => false,
113+
ty::ReVar(..) | ty::ReErased => unreachable!(),
114+
});
115+
let input_values = CanonicalVarValues { var_values: tcx.mk_args(&var_values) };
116+
117+
ocx.make_canonicalized_query_response(
118+
input_values,
119+
MirBorrowckImpliedOutlivesBounds { outlives_bounds, normalized_inputs_and_output },
120+
)
121+
}
122+
123+
/// This computes the `var_values` used by the `mir_borrowck_implied_outlives_bounds` query.
124+
/// The old solver canonicalization does not replace early and late bound parameters,
125+
/// so the only `var_values` we need are external regions as we don't have a shared unified
126+
/// representation between this query and MIR borrowck.
127+
///
128+
/// We never late bound regions from a parent while computing implied bounds for the current item.
129+
/// Any free region in the signature of nested body gets replaced with `'erased` at the end of HIR typeck,
130+
/// so even if a late bound region of a parent is mentioned in our signature, it will have been erased
131+
/// and will get represented as an external region instead.
132+
#[instrument(level = "debug", skip(tcx, is_external_region), ret)]
133+
pub(crate) fn implied_bounds_query_var_values<'tcx>(
134+
tcx: TyCtxt<'tcx>,
135+
unnormalized_inputs_and_output: &[Ty<'tcx>],
136+
mut is_external_region: impl FnMut(ty::Region<'tcx>) -> bool,
137+
) -> SmallVec<[GenericArg<'tcx>; 8]> {
138+
let mut values: SmallVec<[GenericArg<'tcx>; 8]> = Default::default();
139+
140+
for ty in unnormalized_inputs_and_output {
141+
tcx.for_each_free_region(ty, |region| {
142+
if is_external_region(region) {
143+
values.push(region.into());
144+
}
145+
});
146+
}
147+
148+
values
149+
}
150+
151+
/// This replaces all external regions in the signature of the current item with
152+
/// a unique placeholder to collect its implied bounds. This mirrors the way MIR
153+
/// borrowck replaces all of them with unique NLL vars.
154+
fn replace_erased_regions_with_placeholders<'tcx>(
155+
tcx: TyCtxt<'tcx>,
156+
inputs_and_output: &[Ty<'tcx>],
157+
) -> Vec<Ty<'tcx>> {
158+
debug_assert!(!inputs_and_output.has_placeholders());
159+
let mut next_placeholder = 0;
160+
inputs_and_output
161+
.iter()
162+
.map(|&ty| {
163+
fold_regions(tcx, ty, |r, _| match r.kind() {
164+
ty::ReErased => {
165+
let var = ty::BoundVar::from_usize(next_placeholder);
166+
next_placeholder += 1;
167+
ty::Region::new_placeholder(
168+
tcx,
169+
ty::PlaceholderRegion::new(
170+
ty::UniverseIndex::ROOT,
171+
ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon },
172+
),
173+
)
174+
}
175+
ty::ReEarlyParam(_)
176+
| ty::ReLateParam(_)
177+
| ty::ReBound(..)
178+
| ty::ReStatic
179+
| ty::ReError(_) => r,
180+
ty::ReVar(..) | ty::RePlaceholder(..) => {
181+
panic!("unexpected region: {r:?}")
182+
}
183+
})
184+
})
185+
.collect()
186+
}

compiler/rustc_borrowck/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ use crate::dataflow::{BorrowIndex, Borrowck, BorrowckDomain, Borrows};
5959
use crate::diagnostics::{
6060
AccessKind, BorrowckDiagnosticsBuffer, IllegalMoveOriginKind, MoveError, RegionName,
6161
};
62+
use crate::implied_bounds::mir_borrowck_implied_outlives_bounds;
6263
use crate::path_utils::*;
6364
use crate::place_ext::PlaceExt;
6465
use crate::places_conflict::{PlaceConflictBias, places_conflict};
@@ -81,6 +82,7 @@ mod dataflow;
8182
mod def_use;
8283
mod diagnostics;
8384
mod handle_placeholders;
85+
mod implied_bounds;
8486
mod nll;
8587
mod path_utils;
8688
mod place_ext;
@@ -106,7 +108,7 @@ impl<'tcx> TyCtxtConsts<'tcx> {
106108
}
107109

108110
pub fn provide(providers: &mut Providers) {
109-
*providers = Providers { mir_borrowck, ..*providers };
111+
*providers = Providers { mir_borrowck, mir_borrowck_implied_outlives_bounds, ..*providers };
110112
}
111113

112114
/// Provider for `query mir_borrowck`. Unlike `typeck`, this must

0 commit comments

Comments
 (0)