Skip to content

Commit 4eed808

Browse files
Merge pull request #21344 from flodiebold/push-prxvkyqorowx
internal: Don't use MIR ProjectionElem in closure analysis
2 parents 3364710 + 80e6945 commit 4eed808

3 files changed

Lines changed: 152 additions & 99 deletions

File tree

crates/hir-ty/src/infer/closure/analysis.rs

Lines changed: 109 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
//! Post-inference closure analysis: captures and closure kind.
22
3-
use std::{cmp, convert::Infallible, mem};
3+
use std::{cmp, mem};
44

5-
use either::Either;
5+
use base_db::Crate;
66
use hir_def::{
7-
DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId,
7+
DefWithBodyId, FieldId, HasModule, VariantId,
88
expr_store::path::Path,
99
hir::{
1010
Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, ExprOrPatId, Pat, PatId,
@@ -23,33 +23,97 @@ use syntax::utils::is_raw_identifier;
2323
use crate::{
2424
Adjust, Adjustment, BindingMode,
2525
db::{HirDatabase, InternedClosure, InternedClosureId},
26+
display::{DisplayTarget, HirDisplay as _},
2627
infer::InferenceContext,
27-
mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem},
28-
next_solver::{DbInterner, GenericArgs, StoredEarlyBinder, StoredTy, Ty, TyKind},
28+
mir::{BorrowKind, MirSpan, MutBorrowKind},
29+
next_solver::{
30+
DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, StoredEarlyBinder, StoredTy, Ty,
31+
TyKind,
32+
infer::{InferCtxt, traits::ObligationCause},
33+
obligation_ctxt::ObligationCtxt,
34+
},
2935
traits::FnTrait,
3036
};
3137

3238
// The below functions handle capture and closure kind (Fn, FnMut, ..)
3339

40+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
41+
pub(crate) enum HirPlaceProjection {
42+
Deref,
43+
Field(FieldId),
44+
TupleField(u32),
45+
}
46+
47+
impl HirPlaceProjection {
48+
fn projected_ty<'db>(
49+
self,
50+
infcx: &InferCtxt<'db>,
51+
env: ParamEnv<'db>,
52+
mut base: Ty<'db>,
53+
krate: Crate,
54+
) -> Ty<'db> {
55+
let interner = infcx.interner;
56+
let db = interner.db;
57+
if base.is_ty_error() {
58+
return Ty::new_error(interner, ErrorGuaranteed);
59+
}
60+
61+
if matches!(base.kind(), TyKind::Alias(..)) {
62+
let mut ocx = ObligationCtxt::new(infcx);
63+
match ocx.structurally_normalize_ty(&ObligationCause::dummy(), env, base) {
64+
Ok(it) => base = it,
65+
Err(_) => return Ty::new_error(interner, ErrorGuaranteed),
66+
}
67+
}
68+
match self {
69+
HirPlaceProjection::Deref => match base.kind() {
70+
TyKind::RawPtr(inner, _) | TyKind::Ref(_, inner, _) => inner,
71+
TyKind::Adt(adt_def, subst) if adt_def.is_box() => subst.type_at(0),
72+
_ => {
73+
never!(
74+
"Overloaded deref on type {} is not a projection",
75+
base.display(db, DisplayTarget::from_crate(db, krate))
76+
);
77+
Ty::new_error(interner, ErrorGuaranteed)
78+
}
79+
},
80+
HirPlaceProjection::Field(f) => match base.kind() {
81+
TyKind::Adt(_, subst) => {
82+
db.field_types(f.parent)[f.local_id].get().instantiate(interner, subst)
83+
}
84+
ty => {
85+
never!("Only adt has field, found {:?}", ty);
86+
Ty::new_error(interner, ErrorGuaranteed)
87+
}
88+
},
89+
HirPlaceProjection::TupleField(idx) => match base.kind() {
90+
TyKind::Tuple(subst) => {
91+
subst.as_slice().get(idx as usize).copied().unwrap_or_else(|| {
92+
never!("Out of bound tuple field");
93+
Ty::new_error(interner, ErrorGuaranteed)
94+
})
95+
}
96+
ty => {
97+
never!("Only tuple has tuple field: {:?}", ty);
98+
Ty::new_error(interner, ErrorGuaranteed)
99+
}
100+
},
101+
}
102+
}
103+
}
104+
34105
#[derive(Debug, Clone, PartialEq, Eq, Hash, salsa::Update)]
35106
pub(crate) struct HirPlace {
36107
pub(crate) local: BindingId,
37-
pub(crate) projections: Vec<ProjectionElem<Infallible>>,
108+
pub(crate) projections: Vec<HirPlaceProjection>,
38109
}
39110

40111
impl HirPlace {
41112
fn ty<'db>(&self, ctx: &mut InferenceContext<'_, 'db>) -> Ty<'db> {
113+
let krate = ctx.krate();
42114
let mut ty = ctx.table.resolve_completely(ctx.result.binding_ty(self.local));
43115
for p in &self.projections {
44-
ty = p.projected_ty(
45-
&ctx.table.infer_ctxt,
46-
ctx.table.param_env,
47-
ty,
48-
|_, _, _| {
49-
unreachable!("Closure field only happens in MIR");
50-
},
51-
ctx.owner.module(ctx.db).krate(ctx.db),
52-
);
116+
ty = p.projected_ty(ctx.infcx(), ctx.table.param_env, ty, krate);
53117
}
54118
ty
55119
}
@@ -62,7 +126,7 @@ impl HirPlace {
62126
if let CaptureKind::ByRef(BorrowKind::Mut {
63127
kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow,
64128
}) = current_capture
65-
&& self.projections[len..].contains(&ProjectionElem::Deref)
129+
&& self.projections[len..].contains(&HirPlaceProjection::Deref)
66130
{
67131
current_capture =
68132
CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture });
@@ -98,7 +162,7 @@ impl CapturedItem {
98162

99163
/// Returns whether this place has any field (aka. non-deref) projections.
100164
pub fn has_field_projections(&self) -> bool {
101-
self.place.projections.iter().any(|it| !matches!(it, ProjectionElem::Deref))
165+
self.place.projections.iter().any(|it| !matches!(it, HirPlaceProjection::Deref))
102166
}
103167

104168
pub fn ty<'db>(&self, db: &'db dyn HirDatabase, subst: GenericArgs<'db>) -> Ty<'db> {
@@ -120,8 +184,8 @@ impl CapturedItem {
120184
let mut result = body[self.place.local].name.as_str().to_owned();
121185
for proj in &self.place.projections {
122186
match proj {
123-
ProjectionElem::Deref => {}
124-
ProjectionElem::Field(Either::Left(f)) => {
187+
HirPlaceProjection::Deref => {}
188+
HirPlaceProjection::Field(f) => {
125189
let variant_data = f.parent.fields(db);
126190
match variant_data.shape {
127191
FieldsShape::Record => {
@@ -138,14 +202,8 @@ impl CapturedItem {
138202
FieldsShape::Unit => {}
139203
}
140204
}
141-
ProjectionElem::Field(Either::Right(f)) => format_to!(result, "_{}", f.index),
142-
&ProjectionElem::ClosureField(field) => format_to!(result, "_{field}"),
143-
ProjectionElem::Index(_)
144-
| ProjectionElem::ConstantIndex { .. }
145-
| ProjectionElem::Subslice { .. }
146-
| ProjectionElem::OpaqueCast(_) => {
147-
never!("Not happen in closure capture");
148-
continue;
205+
HirPlaceProjection::TupleField(idx) => {
206+
format_to!(result, "_{idx}")
149207
}
150208
}
151209
}
@@ -163,8 +221,8 @@ impl CapturedItem {
163221
for proj in &self.place.projections {
164222
match proj {
165223
// In source code autoderef kicks in.
166-
ProjectionElem::Deref => {}
167-
ProjectionElem::Field(Either::Left(f)) => {
224+
HirPlaceProjection::Deref => {}
225+
HirPlaceProjection::Field(f) => {
168226
let variant_data = f.parent.fields(db);
169227
match variant_data.shape {
170228
FieldsShape::Record => format_to!(
@@ -184,19 +242,8 @@ impl CapturedItem {
184242
FieldsShape::Unit => {}
185243
}
186244
}
187-
ProjectionElem::Field(Either::Right(f)) => {
188-
let field = f.index;
189-
format_to!(result, ".{field}");
190-
}
191-
&ProjectionElem::ClosureField(field) => {
192-
format_to!(result, ".{field}");
193-
}
194-
ProjectionElem::Index(_)
195-
| ProjectionElem::ConstantIndex { .. }
196-
| ProjectionElem::Subslice { .. }
197-
| ProjectionElem::OpaqueCast(_) => {
198-
never!("Not happen in closure capture");
199-
continue;
245+
HirPlaceProjection::TupleField(idx) => {
246+
format_to!(result, ".{idx}")
200247
}
201248
}
202249
}
@@ -205,7 +252,7 @@ impl CapturedItem {
205252
.projections
206253
.iter()
207254
.rev()
208-
.take_while(|proj| matches!(proj, ProjectionElem::Deref))
255+
.take_while(|proj| matches!(proj, HirPlaceProjection::Deref))
209256
.count();
210257
result.insert_str(0, &"*".repeat(final_derefs_count));
211258
result
@@ -219,11 +266,11 @@ impl CapturedItem {
219266
let mut field_need_paren = false;
220267
for proj in &self.place.projections {
221268
match proj {
222-
ProjectionElem::Deref => {
269+
HirPlaceProjection::Deref => {
223270
result = format!("*{result}");
224271
field_need_paren = true;
225272
}
226-
ProjectionElem::Field(Either::Left(f)) => {
273+
HirPlaceProjection::Field(f) => {
227274
if field_need_paren {
228275
result = format!("({result})");
229276
}
@@ -243,28 +290,13 @@ impl CapturedItem {
243290
result = format!("{result}.{field}");
244291
field_need_paren = false;
245292
}
246-
ProjectionElem::Field(Either::Right(f)) => {
247-
let field = f.index;
248-
if field_need_paren {
249-
result = format!("({result})");
250-
}
251-
result = format!("{result}.{field}");
252-
field_need_paren = false;
253-
}
254-
&ProjectionElem::ClosureField(field) => {
293+
HirPlaceProjection::TupleField(idx) => {
255294
if field_need_paren {
256295
result = format!("({result})");
257296
}
258-
result = format!("{result}.{field}");
297+
result = format!("{result}.{idx}");
259298
field_need_paren = false;
260299
}
261-
ProjectionElem::Index(_)
262-
| ProjectionElem::ConstantIndex { .. }
263-
| ProjectionElem::Subslice { .. }
264-
| ProjectionElem::OpaqueCast(_) => {
265-
never!("Not happen in closure capture");
266-
continue;
267-
}
268300
}
269301
}
270302
result
@@ -345,7 +377,9 @@ impl<'db> InferenceContext<'_, 'db> {
345377
let mut place = self.place_of_expr(*expr)?;
346378
let field = self.result.field_resolution(tgt_expr)?;
347379
self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr));
348-
place.projections.push(ProjectionElem::Field(field));
380+
place.projections.push(field.either(HirPlaceProjection::Field, |f| {
381+
HirPlaceProjection::TupleField(f.index)
382+
}));
349383
return Some(place);
350384
}
351385
Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
@@ -357,7 +391,7 @@ impl<'db> InferenceContext<'_, 'db> {
357391
if is_builtin_deref {
358392
let mut place = self.place_of_expr(*expr)?;
359393
self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr));
360-
place.projections.push(ProjectionElem::Deref);
394+
place.projections.push(HirPlaceProjection::Deref);
361395
return Some(place);
362396
}
363397
}
@@ -832,9 +866,6 @@ impl<'db> InferenceContext<'_, 'db> {
832866
&self.table.infer_ctxt,
833867
self.table.param_env,
834868
ty,
835-
|_, _, _| {
836-
unreachable!("Closure field only happens in MIR");
837-
},
838869
self.owner.module(self.db).krate(self.db),
839870
);
840871
if ty.is_raw_ptr() || ty.is_union() {
@@ -853,7 +884,7 @@ impl<'db> InferenceContext<'_, 'db> {
853884
let mut current_captures = std::mem::take(&mut self.current_captures);
854885
for capture in &mut current_captures {
855886
if let Some(first_deref) =
856-
capture.place.projections.iter().position(|proj| *proj == ProjectionElem::Deref)
887+
capture.place.projections.iter().position(|proj| *proj == HirPlaceProjection::Deref)
857888
{
858889
self.truncate_capture_spans(capture, first_deref);
859890
capture.place.projections.truncate(first_deref);
@@ -876,7 +907,7 @@ impl<'db> InferenceContext<'_, 'db> {
876907
}
877908
match it.next() {
878909
Some(it) => {
879-
lookup_place.projections.push(it.clone());
910+
lookup_place.projections.push(*it);
880911
}
881912
None => break None,
882913
}
@@ -903,7 +934,7 @@ impl<'db> InferenceContext<'_, 'db> {
903934
fn consume_with_pat(&mut self, mut place: HirPlace, tgt_pat: PatId) {
904935
let adjustments_count =
905936
self.result.pat_adjustments.get(&tgt_pat).map(|it| it.len()).unwrap_or_default();
906-
place.projections.extend((0..adjustments_count).map(|_| ProjectionElem::Deref));
937+
place.projections.extend((0..adjustments_count).map(|_| HirPlaceProjection::Deref));
907938
self.current_capture_span_stack
908939
.extend((0..adjustments_count).map(|_| MirSpan::PatId(tgt_pat)));
909940
'reset_span_stack: {
@@ -920,10 +951,7 @@ impl<'db> InferenceContext<'_, 'db> {
920951
for (&arg, i) in it {
921952
let mut p = place.clone();
922953
self.current_capture_span_stack.push(MirSpan::PatId(arg));
923-
p.projections.push(ProjectionElem::Field(Either::Right(TupleFieldId {
924-
tuple: TupleId(!0), // dummy this, as its unused anyways
925-
index: i as u32,
926-
})));
954+
p.projections.push(HirPlaceProjection::TupleField(i as u32));
927955
self.consume_with_pat(p, arg);
928956
self.current_capture_span_stack.pop();
929957
}
@@ -950,10 +978,10 @@ impl<'db> InferenceContext<'_, 'db> {
950978
};
951979
let mut p = place.clone();
952980
self.current_capture_span_stack.push(MirSpan::PatId(arg));
953-
p.projections.push(ProjectionElem::Field(Either::Left(FieldId {
981+
p.projections.push(HirPlaceProjection::Field(FieldId {
954982
parent: variant,
955983
local_id,
956-
})));
984+
}));
957985
self.consume_with_pat(p, arg);
958986
self.current_capture_span_stack.pop();
959987
}
@@ -1005,10 +1033,10 @@ impl<'db> InferenceContext<'_, 'db> {
10051033
for (&arg, (i, _)) in it {
10061034
let mut p = place.clone();
10071035
self.current_capture_span_stack.push(MirSpan::PatId(arg));
1008-
p.projections.push(ProjectionElem::Field(Either::Left(FieldId {
1036+
p.projections.push(HirPlaceProjection::Field(FieldId {
10091037
parent: variant,
10101038
local_id: i,
1011-
})));
1039+
}));
10121040
self.consume_with_pat(p, arg);
10131041
self.current_capture_span_stack.pop();
10141042
}
@@ -1017,7 +1045,7 @@ impl<'db> InferenceContext<'_, 'db> {
10171045
}
10181046
Pat::Ref { pat, mutability: _ } => {
10191047
self.current_capture_span_stack.push(MirSpan::PatId(tgt_pat));
1020-
place.projections.push(ProjectionElem::Deref);
1048+
place.projections.push(HirPlaceProjection::Deref);
10211049
self.consume_with_pat(place, *pat);
10221050
self.current_capture_span_stack.pop();
10231051
}
@@ -1071,7 +1099,7 @@ impl<'db> InferenceContext<'_, 'db> {
10711099
CaptureKind::ByRef(BorrowKind::Mut {
10721100
kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow
10731101
})
1074-
) && !item.place.projections.contains(&ProjectionElem::Deref)
1102+
) && !item.place.projections.contains(&HirPlaceProjection::Deref)
10751103
{
10761104
// FIXME: remove the `mutated_bindings_in_closure` completely and add proper fake reads in
10771105
// MIR. I didn't do that due duplicate diagnostics.
@@ -1221,7 +1249,7 @@ fn apply_adjusts_to_place(
12211249
match &adj.kind {
12221250
Adjust::Deref(None) => {
12231251
current_capture_span_stack.push(span);
1224-
r.projections.push(ProjectionElem::Deref);
1252+
r.projections.push(HirPlaceProjection::Deref);
12251253
}
12261254
_ => return None,
12271255
}

0 commit comments

Comments
 (0)