Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir-ty/src/infer/closure/analysis.rs190
-rw-r--r--crates/hir-ty/src/mir/lower.rs36
-rw-r--r--crates/hir-ty/src/tests/closure_captures.rs25
3 files changed, 152 insertions, 99 deletions
diff --git a/crates/hir-ty/src/infer/closure/analysis.rs b/crates/hir-ty/src/infer/closure/analysis.rs
index 6ae6f75525..b25901cc3b 100644
--- a/crates/hir-ty/src/infer/closure/analysis.rs
+++ b/crates/hir-ty/src/infer/closure/analysis.rs
@@ -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;
- if field_need_paren {
- result = format!("({result})");
- }
- result = format!("{result}.{field}");
- field_need_paren = false;
- }
- &ProjectionElem::ClosureField(field) => {
+ HirPlaceProjection::TupleField(idx) => {
if field_need_paren {
result = format!("({result})");
}
- result = format!("{result}.{field}");
+ result = format!("{result}.{idx}");
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,
}
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 762f91fb0d..1579f00e92 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -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)
- }
- ProjectionElem::ConstantIndex { offset, from_end } => {
- ProjectionElem::ConstantIndex { offset, from_end }
+ HirPlaceProjection::Deref => ProjectionElem::Deref,
+ HirPlaceProjection::Field(field_id) => {
+ ProjectionElem::Field(Either::Left(field_id))
}
- ProjectionElem::Subslice { from, to } => {
- ProjectionElem::Subslice { from, to }
+ HirPlaceProjection::TupleField(idx) => {
+ ProjectionElem::Field(Either::Right(TupleFieldId {
+ tuple: TupleId(!0), // Dummy as it's unused
+ index: idx,
+ }))
}
- 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,
}
}
diff --git a/crates/hir-ty/src/tests/closure_captures.rs b/crates/hir-ty/src/tests/closure_captures.rs
index 3bdc72d015..8408c0a7bf 100644
--- a/crates/hir-ty/src/tests/closure_captures.rs
+++ b/crates/hir-ty/src/tests/closure_captures.rs
@@ -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"],
+ );
+}