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.rs | 245 |
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 |