Merge pull request #21344 from flodiebold/push-prxvkyqorowx
internal: Don't use MIR ProjectionElem in closure analysis
This commit is contained in:
commit
0146f8dc31
3 changed files with 152 additions and 99 deletions
|
|
@ -1,10 +1,10 @@
|
|||
//! Post-inference closure analysis: captures and closure kind.
|
||||
|
||||
use std::{cmp, convert::Infallible, mem};
|
||||
use std::{cmp, mem};
|
||||
|
||||
use either::Either;
|
||||
use base_db::Crate;
|
||||
use hir_def::{
|
||||
DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId,
|
||||
DefWithBodyId, FieldId, HasModule, VariantId,
|
||||
expr_store::path::Path,
|
||||
hir::{
|
||||
Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, ExprOrPatId, Pat, PatId,
|
||||
|
|
@ -23,33 +23,97 @@ use syntax::utils::is_raw_identifier;
|
|||
use crate::{
|
||||
Adjust, Adjustment, BindingMode,
|
||||
db::{HirDatabase, InternedClosure, InternedClosureId},
|
||||
display::{DisplayTarget, HirDisplay as _},
|
||||
infer::InferenceContext,
|
||||
mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem},
|
||||
next_solver::{DbInterner, GenericArgs, StoredEarlyBinder, StoredTy, Ty, TyKind},
|
||||
mir::{BorrowKind, MirSpan, MutBorrowKind},
|
||||
next_solver::{
|
||||
DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, StoredEarlyBinder, StoredTy, Ty,
|
||||
TyKind,
|
||||
infer::{InferCtxt, traits::ObligationCause},
|
||||
obligation_ctxt::ObligationCtxt,
|
||||
},
|
||||
traits::FnTrait,
|
||||
};
|
||||
|
||||
// The below functions handle capture and closure kind (Fn, FnMut, ..)
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub(crate) enum HirPlaceProjection {
|
||||
Deref,
|
||||
Field(FieldId),
|
||||
TupleField(u32),
|
||||
}
|
||||
|
||||
impl HirPlaceProjection {
|
||||
fn projected_ty<'db>(
|
||||
self,
|
||||
infcx: &InferCtxt<'db>,
|
||||
env: ParamEnv<'db>,
|
||||
mut base: Ty<'db>,
|
||||
krate: Crate,
|
||||
) -> Ty<'db> {
|
||||
let interner = infcx.interner;
|
||||
let db = interner.db;
|
||||
if base.is_ty_error() {
|
||||
return Ty::new_error(interner, ErrorGuaranteed);
|
||||
}
|
||||
|
||||
if matches!(base.kind(), TyKind::Alias(..)) {
|
||||
let mut ocx = ObligationCtxt::new(infcx);
|
||||
match ocx.structurally_normalize_ty(&ObligationCause::dummy(), env, base) {
|
||||
Ok(it) => base = it,
|
||||
Err(_) => return Ty::new_error(interner, ErrorGuaranteed),
|
||||
}
|
||||
}
|
||||
match self {
|
||||
HirPlaceProjection::Deref => match base.kind() {
|
||||
TyKind::RawPtr(inner, _) | TyKind::Ref(_, inner, _) => inner,
|
||||
TyKind::Adt(adt_def, subst) if adt_def.is_box() => subst.type_at(0),
|
||||
_ => {
|
||||
never!(
|
||||
"Overloaded deref on type {} is not a projection",
|
||||
base.display(db, DisplayTarget::from_crate(db, krate))
|
||||
);
|
||||
Ty::new_error(interner, ErrorGuaranteed)
|
||||
}
|
||||
},
|
||||
HirPlaceProjection::Field(f) => match base.kind() {
|
||||
TyKind::Adt(_, subst) => {
|
||||
db.field_types(f.parent)[f.local_id].get().instantiate(interner, subst)
|
||||
}
|
||||
ty => {
|
||||
never!("Only adt has field, found {:?}", ty);
|
||||
Ty::new_error(interner, ErrorGuaranteed)
|
||||
}
|
||||
},
|
||||
HirPlaceProjection::TupleField(idx) => match base.kind() {
|
||||
TyKind::Tuple(subst) => {
|
||||
subst.as_slice().get(idx as usize).copied().unwrap_or_else(|| {
|
||||
never!("Out of bound tuple field");
|
||||
Ty::new_error(interner, ErrorGuaranteed)
|
||||
})
|
||||
}
|
||||
ty => {
|
||||
never!("Only tuple has tuple field: {:?}", ty);
|
||||
Ty::new_error(interner, ErrorGuaranteed)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, salsa::Update)]
|
||||
pub(crate) struct HirPlace {
|
||||
pub(crate) local: BindingId,
|
||||
pub(crate) projections: Vec<ProjectionElem<Infallible>>,
|
||||
pub(crate) projections: Vec<HirPlaceProjection>,
|
||||
}
|
||||
|
||||
impl HirPlace {
|
||||
fn ty<'db>(&self, ctx: &mut InferenceContext<'_, 'db>) -> Ty<'db> {
|
||||
let krate = ctx.krate();
|
||||
let mut ty = ctx.table.resolve_completely(ctx.result.binding_ty(self.local));
|
||||
for p in &self.projections {
|
||||
ty = p.projected_ty(
|
||||
&ctx.table.infer_ctxt,
|
||||
ctx.table.param_env,
|
||||
ty,
|
||||
|_, _, _| {
|
||||
unreachable!("Closure field only happens in MIR");
|
||||
},
|
||||
ctx.owner.module(ctx.db).krate(ctx.db),
|
||||
);
|
||||
ty = p.projected_ty(ctx.infcx(), ctx.table.param_env, ty, krate);
|
||||
}
|
||||
ty
|
||||
}
|
||||
|
|
@ -62,7 +126,7 @@ impl HirPlace {
|
|||
if let CaptureKind::ByRef(BorrowKind::Mut {
|
||||
kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow,
|
||||
}) = current_capture
|
||||
&& self.projections[len..].contains(&ProjectionElem::Deref)
|
||||
&& self.projections[len..].contains(&HirPlaceProjection::Deref)
|
||||
{
|
||||
current_capture =
|
||||
CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture });
|
||||
|
|
@ -98,7 +162,7 @@ impl CapturedItem {
|
|||
|
||||
/// Returns whether this place has any field (aka. non-deref) projections.
|
||||
pub fn has_field_projections(&self) -> bool {
|
||||
self.place.projections.iter().any(|it| !matches!(it, ProjectionElem::Deref))
|
||||
self.place.projections.iter().any(|it| !matches!(it, HirPlaceProjection::Deref))
|
||||
}
|
||||
|
||||
pub fn ty<'db>(&self, db: &'db dyn HirDatabase, subst: GenericArgs<'db>) -> Ty<'db> {
|
||||
|
|
@ -120,8 +184,8 @@ impl CapturedItem {
|
|||
let mut result = body[self.place.local].name.as_str().to_owned();
|
||||
for proj in &self.place.projections {
|
||||
match proj {
|
||||
ProjectionElem::Deref => {}
|
||||
ProjectionElem::Field(Either::Left(f)) => {
|
||||
HirPlaceProjection::Deref => {}
|
||||
HirPlaceProjection::Field(f) => {
|
||||
let variant_data = f.parent.fields(db);
|
||||
match variant_data.shape {
|
||||
FieldsShape::Record => {
|
||||
|
|
@ -138,14 +202,8 @@ impl CapturedItem {
|
|||
FieldsShape::Unit => {}
|
||||
}
|
||||
}
|
||||
ProjectionElem::Field(Either::Right(f)) => format_to!(result, "_{}", f.index),
|
||||
&ProjectionElem::ClosureField(field) => format_to!(result, "_{field}"),
|
||||
ProjectionElem::Index(_)
|
||||
| ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::Subslice { .. }
|
||||
| ProjectionElem::OpaqueCast(_) => {
|
||||
never!("Not happen in closure capture");
|
||||
continue;
|
||||
HirPlaceProjection::TupleField(idx) => {
|
||||
format_to!(result, "_{idx}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -163,8 +221,8 @@ impl CapturedItem {
|
|||
for proj in &self.place.projections {
|
||||
match proj {
|
||||
// In source code autoderef kicks in.
|
||||
ProjectionElem::Deref => {}
|
||||
ProjectionElem::Field(Either::Left(f)) => {
|
||||
HirPlaceProjection::Deref => {}
|
||||
HirPlaceProjection::Field(f) => {
|
||||
let variant_data = f.parent.fields(db);
|
||||
match variant_data.shape {
|
||||
FieldsShape::Record => format_to!(
|
||||
|
|
@ -184,19 +242,8 @@ impl CapturedItem {
|
|||
FieldsShape::Unit => {}
|
||||
}
|
||||
}
|
||||
ProjectionElem::Field(Either::Right(f)) => {
|
||||
let field = f.index;
|
||||
format_to!(result, ".{field}");
|
||||
}
|
||||
&ProjectionElem::ClosureField(field) => {
|
||||
format_to!(result, ".{field}");
|
||||
}
|
||||
ProjectionElem::Index(_)
|
||||
| ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::Subslice { .. }
|
||||
| ProjectionElem::OpaqueCast(_) => {
|
||||
never!("Not happen in closure capture");
|
||||
continue;
|
||||
HirPlaceProjection::TupleField(idx) => {
|
||||
format_to!(result, ".{idx}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -205,7 +252,7 @@ impl CapturedItem {
|
|||
.projections
|
||||
.iter()
|
||||
.rev()
|
||||
.take_while(|proj| matches!(proj, ProjectionElem::Deref))
|
||||
.take_while(|proj| matches!(proj, HirPlaceProjection::Deref))
|
||||
.count();
|
||||
result.insert_str(0, &"*".repeat(final_derefs_count));
|
||||
result
|
||||
|
|
@ -219,11 +266,11 @@ impl CapturedItem {
|
|||
let mut field_need_paren = false;
|
||||
for proj in &self.place.projections {
|
||||
match proj {
|
||||
ProjectionElem::Deref => {
|
||||
HirPlaceProjection::Deref => {
|
||||
result = format!("*{result}");
|
||||
field_need_paren = true;
|
||||
}
|
||||
ProjectionElem::Field(Either::Left(f)) => {
|
||||
HirPlaceProjection::Field(f) => {
|
||||
if field_need_paren {
|
||||
result = format!("({result})");
|
||||
}
|
||||
|
|
@ -243,28 +290,13 @@ impl CapturedItem {
|
|||
result = format!("{result}.{field}");
|
||||
field_need_paren = false;
|
||||
}
|
||||
ProjectionElem::Field(Either::Right(f)) => {
|
||||
let field = f.index;
|
||||
HirPlaceProjection::TupleField(idx) => {
|
||||
if field_need_paren {
|
||||
result = format!("({result})");
|
||||
}
|
||||
result = format!("{result}.{field}");
|
||||
result = format!("{result}.{idx}");
|
||||
field_need_paren = false;
|
||||
}
|
||||
&ProjectionElem::ClosureField(field) => {
|
||||
if field_need_paren {
|
||||
result = format!("({result})");
|
||||
}
|
||||
result = format!("{result}.{field}");
|
||||
field_need_paren = false;
|
||||
}
|
||||
ProjectionElem::Index(_)
|
||||
| ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::Subslice { .. }
|
||||
| ProjectionElem::OpaqueCast(_) => {
|
||||
never!("Not happen in closure capture");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
|
|
@ -345,7 +377,9 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
let mut place = self.place_of_expr(*expr)?;
|
||||
let field = self.result.field_resolution(tgt_expr)?;
|
||||
self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr));
|
||||
place.projections.push(ProjectionElem::Field(field));
|
||||
place.projections.push(field.either(HirPlaceProjection::Field, |f| {
|
||||
HirPlaceProjection::TupleField(f.index)
|
||||
}));
|
||||
return Some(place);
|
||||
}
|
||||
Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
|
||||
|
|
@ -357,7 +391,7 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
if is_builtin_deref {
|
||||
let mut place = self.place_of_expr(*expr)?;
|
||||
self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr));
|
||||
place.projections.push(ProjectionElem::Deref);
|
||||
place.projections.push(HirPlaceProjection::Deref);
|
||||
return Some(place);
|
||||
}
|
||||
}
|
||||
|
|
@ -832,9 +866,6 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
&self.table.infer_ctxt,
|
||||
self.table.param_env,
|
||||
ty,
|
||||
|_, _, _| {
|
||||
unreachable!("Closure field only happens in MIR");
|
||||
},
|
||||
self.owner.module(self.db).krate(self.db),
|
||||
);
|
||||
if ty.is_raw_ptr() || ty.is_union() {
|
||||
|
|
@ -853,7 +884,7 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
let mut current_captures = std::mem::take(&mut self.current_captures);
|
||||
for capture in &mut current_captures {
|
||||
if let Some(first_deref) =
|
||||
capture.place.projections.iter().position(|proj| *proj == ProjectionElem::Deref)
|
||||
capture.place.projections.iter().position(|proj| *proj == HirPlaceProjection::Deref)
|
||||
{
|
||||
self.truncate_capture_spans(capture, first_deref);
|
||||
capture.place.projections.truncate(first_deref);
|
||||
|
|
@ -876,7 +907,7 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
}
|
||||
match it.next() {
|
||||
Some(it) => {
|
||||
lookup_place.projections.push(it.clone());
|
||||
lookup_place.projections.push(*it);
|
||||
}
|
||||
None => break None,
|
||||
}
|
||||
|
|
@ -903,7 +934,7 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
fn consume_with_pat(&mut self, mut place: HirPlace, tgt_pat: PatId) {
|
||||
let adjustments_count =
|
||||
self.result.pat_adjustments.get(&tgt_pat).map(|it| it.len()).unwrap_or_default();
|
||||
place.projections.extend((0..adjustments_count).map(|_| ProjectionElem::Deref));
|
||||
place.projections.extend((0..adjustments_count).map(|_| HirPlaceProjection::Deref));
|
||||
self.current_capture_span_stack
|
||||
.extend((0..adjustments_count).map(|_| MirSpan::PatId(tgt_pat)));
|
||||
'reset_span_stack: {
|
||||
|
|
@ -920,10 +951,7 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
for (&arg, i) in it {
|
||||
let mut p = place.clone();
|
||||
self.current_capture_span_stack.push(MirSpan::PatId(arg));
|
||||
p.projections.push(ProjectionElem::Field(Either::Right(TupleFieldId {
|
||||
tuple: TupleId(!0), // dummy this, as its unused anyways
|
||||
index: i as u32,
|
||||
})));
|
||||
p.projections.push(HirPlaceProjection::TupleField(i as u32));
|
||||
self.consume_with_pat(p, arg);
|
||||
self.current_capture_span_stack.pop();
|
||||
}
|
||||
|
|
@ -950,10 +978,10 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
};
|
||||
let mut p = place.clone();
|
||||
self.current_capture_span_stack.push(MirSpan::PatId(arg));
|
||||
p.projections.push(ProjectionElem::Field(Either::Left(FieldId {
|
||||
p.projections.push(HirPlaceProjection::Field(FieldId {
|
||||
parent: variant,
|
||||
local_id,
|
||||
})));
|
||||
}));
|
||||
self.consume_with_pat(p, arg);
|
||||
self.current_capture_span_stack.pop();
|
||||
}
|
||||
|
|
@ -1005,10 +1033,10 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
for (&arg, (i, _)) in it {
|
||||
let mut p = place.clone();
|
||||
self.current_capture_span_stack.push(MirSpan::PatId(arg));
|
||||
p.projections.push(ProjectionElem::Field(Either::Left(FieldId {
|
||||
p.projections.push(HirPlaceProjection::Field(FieldId {
|
||||
parent: variant,
|
||||
local_id: i,
|
||||
})));
|
||||
}));
|
||||
self.consume_with_pat(p, arg);
|
||||
self.current_capture_span_stack.pop();
|
||||
}
|
||||
|
|
@ -1017,7 +1045,7 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
}
|
||||
Pat::Ref { pat, mutability: _ } => {
|
||||
self.current_capture_span_stack.push(MirSpan::PatId(tgt_pat));
|
||||
place.projections.push(ProjectionElem::Deref);
|
||||
place.projections.push(HirPlaceProjection::Deref);
|
||||
self.consume_with_pat(place, *pat);
|
||||
self.current_capture_span_stack.pop();
|
||||
}
|
||||
|
|
@ -1071,7 +1099,7 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
CaptureKind::ByRef(BorrowKind::Mut {
|
||||
kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow
|
||||
})
|
||||
) && !item.place.projections.contains(&ProjectionElem::Deref)
|
||||
) && !item.place.projections.contains(&HirPlaceProjection::Deref)
|
||||
{
|
||||
// FIXME: remove the `mutated_bindings_in_closure` completely and add proper fake reads in
|
||||
// MIR. I didn't do that due duplicate diagnostics.
|
||||
|
|
@ -1221,7 +1249,7 @@ fn apply_adjusts_to_place(
|
|||
match &adj.kind {
|
||||
Adjust::Deref(None) => {
|
||||
current_capture_span_stack.push(span);
|
||||
r.projections.push(ProjectionElem::Deref);
|
||||
r.projections.push(HirPlaceProjection::Deref);
|
||||
}
|
||||
_ => return None,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,10 @@ use crate::{
|
|||
db::{HirDatabase, InternedClosure, InternedClosureId},
|
||||
display::{DisplayTarget, HirDisplay, hir_display_with_store},
|
||||
generics::generics,
|
||||
infer::{CaptureKind, CapturedItem, TypeMismatch, cast::CastTy},
|
||||
infer::{
|
||||
CaptureKind, CapturedItem, TypeMismatch, cast::CastTy,
|
||||
closure::analysis::HirPlaceProjection,
|
||||
},
|
||||
inhabitedness::is_ty_uninhabited_from,
|
||||
layout::LayoutError,
|
||||
method_resolution::CandidateId,
|
||||
|
|
@ -1258,22 +1261,16 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
|
|||
.clone()
|
||||
.into_iter()
|
||||
.map(|it| match it {
|
||||
ProjectionElem::Deref => ProjectionElem::Deref,
|
||||
ProjectionElem::Field(it) => ProjectionElem::Field(it),
|
||||
ProjectionElem::ClosureField(it) => {
|
||||
ProjectionElem::ClosureField(it)
|
||||
HirPlaceProjection::Deref => ProjectionElem::Deref,
|
||||
HirPlaceProjection::Field(field_id) => {
|
||||
ProjectionElem::Field(Either::Left(field_id))
|
||||
}
|
||||
ProjectionElem::ConstantIndex { offset, from_end } => {
|
||||
ProjectionElem::ConstantIndex { offset, from_end }
|
||||
HirPlaceProjection::TupleField(idx) => {
|
||||
ProjectionElem::Field(Either::Right(TupleFieldId {
|
||||
tuple: TupleId(!0), // Dummy as it's unused
|
||||
index: idx,
|
||||
}))
|
||||
}
|
||||
ProjectionElem::Subslice { from, to } => {
|
||||
ProjectionElem::Subslice { from, to }
|
||||
}
|
||||
ProjectionElem::OpaqueCast(it) => {
|
||||
ProjectionElem::OpaqueCast(it)
|
||||
}
|
||||
#[allow(unreachable_patterns)]
|
||||
ProjectionElem::Index(it) => match it {},
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
|
|
@ -2173,10 +2170,13 @@ pub fn mir_body_for_closure_query<'db>(
|
|||
for (it, y) in p.projection.lookup(store).iter().zip(it.0.place.projections.iter())
|
||||
{
|
||||
match (it, y) {
|
||||
(ProjectionElem::Deref, ProjectionElem::Deref) => (),
|
||||
(ProjectionElem::Field(it), ProjectionElem::Field(y)) if it == y => (),
|
||||
(ProjectionElem::ClosureField(it), ProjectionElem::ClosureField(y))
|
||||
(ProjectionElem::Deref, HirPlaceProjection::Deref) => (),
|
||||
(ProjectionElem::Field(Either::Left(it)), HirPlaceProjection::Field(y))
|
||||
if it == y => {}
|
||||
(
|
||||
ProjectionElem::Field(Either::Right(it)),
|
||||
HirPlaceProjection::TupleField(y),
|
||||
) if it.index == *y => (),
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -503,3 +503,28 @@ fn main() {
|
|||
expect!["73..149;37..38;103..104 ByValue b Option<Box>"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alias_needs_to_be_normalized() {
|
||||
check_closure_captures(
|
||||
r#"
|
||||
//- minicore:copy
|
||||
trait Trait {
|
||||
type Associated;
|
||||
}
|
||||
struct A;
|
||||
struct B { x: i32 }
|
||||
impl Trait for A {
|
||||
type Associated = B;
|
||||
}
|
||||
struct C { b: <A as Trait>::Associated }
|
||||
fn main() {
|
||||
let c: C = C { b: B { x: 1 } };
|
||||
let closure = || {
|
||||
let _move = c.b.x;
|
||||
};
|
||||
}
|
||||
"#,
|
||||
expect!["220..257;174..175;245..250 ByRef(Shared) c.b.x &'? i32"],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue