Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir/src/lib.rs')
-rw-r--r--crates/hir/src/lib.rs245
1 files changed, 145 insertions, 100 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index dd77934826..d24e2c0cb5 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -85,14 +85,14 @@ use hir_ty::{
GenericPredicates, InferenceResult, ParamEnvAndCrate, TyDefId, TyLoweringDiagnostic,
ValueTyDefId, all_super_traits, autoderef, check_orphan_rules,
consteval::try_const_usize,
- db::{InternedClosureId, InternedCoroutineClosureId},
+ db::{InternedClosure, InternedClosureId, InternedCoroutineClosureId},
diagnostics::BodyValidationDiagnostic,
direct_super_traits, known_const_to_ast,
layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding},
method_resolution::{
self, InherentImpls, MethodResolutionContext, MethodResolutionUnstableFeatures,
},
- mir::{MutBorrowKind, interpret_mir},
+ mir::interpret_mir,
next_solver::{
AliasTy, AnyImplId, ClauseKind, ConstKind, DbInterner, EarlyBinder, EarlyParamRegion,
ErrorGuaranteed, GenericArg, GenericArgs, ParamConst, ParamEnv, PolyFnSig, Region,
@@ -108,9 +108,8 @@ use rustc_type_ir::{
TypeVisitor, fast_reject,
inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _},
};
-use smallvec::SmallVec;
use span::{AstIdNode, Edition, FileId};
-use stdx::{format_to, impl_from, never, variance::PhantomCovariantLifetime};
+use stdx::{format_to, impl_from, never};
use syntax::{
AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange, ToSmolStr,
ast::{self, HasName as _, HasVisibility as _},
@@ -2305,11 +2304,9 @@ impl DefWithBody {
}
}
(mir::MutabilityReason::Not, true) => {
- if !infer.mutated_bindings_in_closure.contains(&binding_id) {
- let should_ignore = body[binding_id].name.as_str().starts_with('_');
- if !should_ignore {
- acc.push(UnusedMut { local }.into())
- }
+ let should_ignore = body[binding_id].name.as_str().starts_with('_');
+ if !should_ignore {
+ acc.push(UnusedMut { local }.into())
}
}
}
@@ -5158,59 +5155,33 @@ impl<'db> Closure<'db> {
}
pub fn captured_items(&self, db: &'db dyn HirDatabase) -> Vec<ClosureCapture<'db>> {
- let AnyClosureId::ClosureId(id) = self.id else {
- // FIXME: Infer coroutine closures' captures.
- return Vec::new();
+ let closure = match self.id {
+ AnyClosureId::ClosureId(it) => it.loc(db),
+ AnyClosureId::CoroutineClosureId(it) => it.loc(db),
};
- let owner = id.loc(db).0;
+ let InternedClosure(owner, closure) = closure;
let infer = InferenceResult::of(db, owner);
- let info = infer.closure_info(id);
- info.0
- .iter()
- .cloned()
- .map(|capture| ClosureCapture {
- owner,
- closure: id,
- capture,
- _marker: PhantomCovariantLifetime::new(),
- })
+ let param_env = body_param_env_from_has_crate(db, owner);
+ infer.closures_data[&closure]
+ .min_captures
+ .values()
+ .flatten()
+ .map(|capture| ClosureCapture { owner, closure, capture, param_env })
.collect()
}
- pub fn capture_types(&self, db: &'db dyn HirDatabase) -> Vec<Type<'db>> {
- let AnyClosureId::ClosureId(id) = self.id else {
- // FIXME: Infer coroutine closures' captures.
- return Vec::new();
- };
- let owner = id.loc(db).0;
- let Some(body_owner) = owner.as_def_with_body() else {
- return Vec::new();
- };
- let infer = InferenceResult::of(db, body_owner);
- let (captures, _) = infer.closure_info(id);
- let env = body_param_env_from_has_crate(db, body_owner);
- captures.iter().map(|capture| Type { env, ty: capture.ty(db, self.subst) }).collect()
- }
-
- pub fn fn_trait(&self, db: &dyn HirDatabase) -> FnTrait {
+ pub fn fn_trait(&self, _db: &dyn HirDatabase) -> FnTrait {
match self.id {
- AnyClosureId::ClosureId(id) => {
- let owner = id.loc(db).0;
- let Some(body_owner) = owner.as_def_with_body() else {
- return FnTrait::FnOnce;
- };
- let infer = InferenceResult::of(db, body_owner);
- let info = infer.closure_info(id);
- info.1.into()
- }
- AnyClosureId::CoroutineClosureId(_id) => {
- // FIXME: Infer kind for coroutine closures.
- match self.subst.as_coroutine_closure().kind() {
- rustc_type_ir::ClosureKind::Fn => FnTrait::AsyncFn,
- rustc_type_ir::ClosureKind::FnMut => FnTrait::AsyncFnMut,
- rustc_type_ir::ClosureKind::FnOnce => FnTrait::AsyncFnOnce,
- }
- }
+ AnyClosureId::ClosureId(_) => match self.subst.as_closure().kind() {
+ rustc_type_ir::ClosureKind::Fn => FnTrait::Fn,
+ rustc_type_ir::ClosureKind::FnMut => FnTrait::FnMut,
+ rustc_type_ir::ClosureKind::FnOnce => FnTrait::FnOnce,
+ },
+ AnyClosureId::CoroutineClosureId(_) => match self.subst.as_coroutine_closure().kind() {
+ rustc_type_ir::ClosureKind::Fn => FnTrait::AsyncFn,
+ rustc_type_ir::ClosureKind::FnMut => FnTrait::AsyncFnMut,
+ rustc_type_ir::ClosureKind::FnOnce => FnTrait::AsyncFnOnce,
+ },
}
}
}
@@ -5283,51 +5254,120 @@ impl FnTrait {
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ClosureCapture<'db> {
owner: ExpressionStoreOwnerId,
- closure: InternedClosureId,
- capture: hir_ty::CapturedItem,
- _marker: PhantomCovariantLifetime<'db>,
+ closure: ExprId,
+ capture: &'db hir_ty::closure_analysis::CapturedPlace,
+ param_env: ParamEnvAndCrate<'db>,
}
impl<'db> ClosureCapture<'db> {
pub fn local(&self) -> Local {
- Local { parent: self.owner, binding_id: self.capture.local() }
+ Local { parent: self.owner, binding_id: self.capture.captured_local() }
}
/// Returns whether this place has any field (aka. non-deref) projections.
pub fn has_field_projections(&self) -> bool {
- self.capture.has_field_projections()
+ self.capture
+ .place
+ .projections
+ .iter()
+ .any(|proj| matches!(proj.kind, hir_ty::closure_analysis::ProjectionKind::Field { .. }))
}
- pub fn usages(&self) -> CaptureUsages {
- CaptureUsages { parent: self.owner, spans: self.capture.spans() }
+ pub fn usages(&self) -> CaptureUsages<'db> {
+ CaptureUsages { parent: self.owner, sources: &self.capture.info.sources }
}
pub fn kind(&self) -> CaptureKind {
- match self.capture.kind() {
- hir_ty::CaptureKind::ByRef(
- hir_ty::mir::BorrowKind::Shallow | hir_ty::mir::BorrowKind::Shared,
+ match self.capture.info.capture_kind {
+ hir_ty::closure_analysis::UpvarCapture::ByValue => CaptureKind::Move,
+ hir_ty::closure_analysis::UpvarCapture::ByUse => CaptureKind::SharedRef, // Good enough?
+ hir_ty::closure_analysis::UpvarCapture::ByRef(
+ hir_ty::closure_analysis::BorrowKind::Immutable,
) => CaptureKind::SharedRef,
- hir_ty::CaptureKind::ByRef(hir_ty::mir::BorrowKind::Mut {
- kind: MutBorrowKind::ClosureCapture,
- }) => CaptureKind::UniqueSharedRef,
- hir_ty::CaptureKind::ByRef(hir_ty::mir::BorrowKind::Mut {
- kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow,
- }) => CaptureKind::MutableRef,
- hir_ty::CaptureKind::ByValue => CaptureKind::Move,
+ hir_ty::closure_analysis::UpvarCapture::ByRef(
+ hir_ty::closure_analysis::BorrowKind::UniqueImmutable,
+ ) => CaptureKind::UniqueSharedRef,
+ hir_ty::closure_analysis::UpvarCapture::ByRef(
+ hir_ty::closure_analysis::BorrowKind::Mutable,
+ ) => CaptureKind::MutableRef,
}
}
/// Converts the place to a name that can be inserted into source code.
- pub fn place_to_name(&self, db: &dyn HirDatabase) -> String {
- self.capture.place_to_name(self.owner, db)
+ pub fn place_to_name(&self, db: &dyn HirDatabase, edition: Edition) -> String {
+ let mut result = self.local().name(db).display(db, edition).to_string();
+ for (i, proj) in self.capture.place.projections.iter().enumerate() {
+ match proj.kind {
+ hir_ty::closure_analysis::ProjectionKind::Deref => {}
+ hir_ty::closure_analysis::ProjectionKind::Field { field_idx, variant_idx } => {
+ let ty = self.capture.place.ty_before_projection(i);
+ match ty.kind() {
+ TyKind::Tuple(_) => format_to!(result, "_{field_idx}"),
+ TyKind::Adt(adt_def, _) => {
+ let variant = match adt_def.def_id().0 {
+ AdtId::StructId(id) => VariantId::from(id),
+ AdtId::UnionId(id) => id.into(),
+ AdtId::EnumId(id) => {
+ id.enum_variants(db).variants[variant_idx as usize].0.into()
+ }
+ };
+ let field = &variant.fields(db).fields()
+ [LocalFieldId::from_raw(la_arena::RawIdx::from_u32(field_idx))];
+ format_to!(result, "_{}", field.name.display(db, edition));
+ }
+ _ => never!("mismatching projection type"),
+ }
+ }
+ _ => never!("unexpected projection kind"),
+ }
+ }
+ result
}
- pub fn display_place_source_code(&self, db: &dyn HirDatabase) -> String {
- self.capture.display_place_source_code(self.owner, db)
+ pub fn display_place_source_code(&self, db: &dyn HirDatabase, edition: Edition) -> String {
+ let mut result = self.local().name(db).display(db, edition).to_string();
+ // We only need the derefs that have no field access after them, autoderef will do the rest.
+ let mut last_derefs = 0;
+ for (i, proj) in self.capture.place.projections.iter().enumerate() {
+ match proj.kind {
+ hir_ty::closure_analysis::ProjectionKind::Deref => last_derefs += 1,
+ hir_ty::closure_analysis::ProjectionKind::Field { field_idx, variant_idx } => {
+ last_derefs = 0;
+
+ let ty = self.capture.place.ty_before_projection(i);
+ match ty.kind() {
+ TyKind::Tuple(_) => format_to!(result, ".{field_idx}"),
+ TyKind::Adt(adt_def, _) => {
+ let variant = match adt_def.def_id().0 {
+ AdtId::StructId(id) => VariantId::from(id),
+ AdtId::UnionId(id) => id.into(),
+ AdtId::EnumId(id) => {
+ // Can't really do that for an enum, unfortunately, so try to do something alike.
+ id.enum_variants(db).variants[variant_idx as usize].0.into()
+ }
+ };
+ let field = &variant.fields(db).fields()
+ [LocalFieldId::from_raw(la_arena::RawIdx::from_u32(field_idx))];
+ format_to!(result, ".{}", field.name.display(db, edition));
+ }
+ _ => never!("mismatching projection type"),
+ }
+ }
+ _ => never!("unexpected projection kind"),
+ }
+ }
+ result.insert_str(0, &"*".repeat(last_derefs));
+ result
+ }
+
+ pub fn ty(&self, _db: &'db dyn HirDatabase) -> Type<'db> {
+ Type { env: self.param_env, ty: self.capture.place.ty() }
}
- pub fn display_place(&self, db: &dyn HirDatabase) -> String {
- self.capture.display_place(self.owner, db)
+ /// The type that is stored in the closure, which is different from [`Self::ty()`], representing
+ /// the place's type, when the capture is by ref.
+ pub fn captured_ty(&self, db: &'db dyn HirDatabase) -> Type<'db> {
+ Type { env: self.param_env, ty: self.capture.captured_ty(db) }
}
}
@@ -5340,38 +5380,43 @@ pub enum CaptureKind {
}
#[derive(Debug, Clone)]
-pub struct CaptureUsages {
+pub struct CaptureUsages<'db> {
parent: ExpressionStoreOwnerId,
- spans: SmallVec<[mir::MirSpan; 3]>,
-}
+ sources: &'db [hir_ty::closure_analysis::CaptureSourceStack],
+}
+
+impl CaptureUsages<'_> {
+ fn is_ref(store: &ExpressionStore, id: ExprOrPatId) -> bool {
+ match id {
+ ExprOrPatId::ExprId(expr) => matches!(store[expr], Expr::Ref { .. }),
+ // FIXME: Figure out if this is correct wrt. match ergonomics.
+ ExprOrPatId::PatId(pat) => match store[pat] {
+ Pat::Bind { id: binding, .. } => matches!(
+ store[binding].mode,
+ BindingAnnotation::Ref | BindingAnnotation::RefMut
+ ),
+ _ => false,
+ },
+ }
+ }
-impl CaptureUsages {
pub fn sources(&self, db: &dyn HirDatabase) -> Vec<CaptureUsageSource> {
- let (body, source_map) = ExpressionStore::with_source_map(db, self.parent);
- let mut result = Vec::with_capacity(self.spans.len());
- for &span in self.spans.iter() {
- let is_ref = span.is_ref_span(body);
- match span {
- mir::MirSpan::ExprId(expr) => {
+ let (store, source_map) = ExpressionStore::with_source_map(db, self.parent);
+ let mut result = Vec::with_capacity(self.sources.len());
+ for source in self.sources {
+ let source = source.final_source();
+ let is_ref = Self::is_ref(store, source);
+ match source {
+ ExprOrPatId::ExprId(expr) => {
if let Ok(expr) = source_map.expr_syntax(expr) {
result.push(CaptureUsageSource { is_ref, source: expr })
}
}
- mir::MirSpan::PatId(pat) => {
+ ExprOrPatId::PatId(pat) => {
if let Ok(pat) = source_map.pat_syntax(pat) {
result.push(CaptureUsageSource { is_ref, source: pat });
}
}
- mir::MirSpan::BindingId(binding) => result.extend(
- source_map
- .patterns_for_binding(binding)
- .iter()
- .filter_map(|&pat| source_map.pat_syntax(pat).ok())
- .map(|pat| CaptureUsageSource { is_ref, source: pat }),
- ),
- mir::MirSpan::SelfParam | mir::MirSpan::Unknown => {
- unreachable!("invalid capture usage span")
- }
}
}
result