Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #21823 from Veykril/push-zvmkkvrwwppl
Implement signature type inference
59 files changed, 1089 insertions, 355 deletions
diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs index b0b652a150..ba0efe0ff1 100644 --- a/crates/hir-def/src/db.rs +++ b/crates/hir-def/src/db.rs @@ -8,12 +8,13 @@ use la_arena::ArenaMap; use triomphe::Arc; use crate::{ - AssocItemId, AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, - EnumVariantId, EnumVariantLoc, ExternBlockId, ExternBlockLoc, ExternCrateId, ExternCrateLoc, - FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalFieldId, Macro2Id, Macro2Loc, - MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ProcMacroId, - ProcMacroLoc, StaticId, StaticLoc, StructId, StructLoc, TraitId, TraitLoc, TypeAliasId, - TypeAliasLoc, UnionId, UnionLoc, UseId, UseLoc, VariantId, + AnonConstId, AnonConstLoc, AssocItemId, AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, + DefWithBodyId, EnumId, EnumLoc, EnumVariantId, EnumVariantLoc, ExpressionStoreOwner, + ExternBlockId, ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, + GenericDefId, ImplId, ImplLoc, LocalFieldId, Macro2Id, Macro2Loc, MacroExpander, MacroId, + MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ProcMacroId, ProcMacroLoc, StaticId, + StaticLoc, StructId, StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, + UnionLoc, UseId, UseLoc, VariantId, attrs::AttrFlags, expr_store::{ Body, BodySourceMap, ExpressionStore, ExpressionStoreSourceMap, scope::ExprScopes, @@ -62,6 +63,9 @@ pub trait InternDatabase: RootQueryDb { fn intern_static(&self, loc: StaticLoc) -> StaticId; #[salsa::interned] + fn intern_anon_const(&self, loc: AnonConstLoc) -> AnonConstId; + + #[salsa::interned] fn intern_trait(&self, loc: TraitLoc) -> TraitId; #[salsa::interned] @@ -212,8 +216,15 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + SourceDatabase { #[salsa::invoke(Body::body_query)] fn body(&self, def: DefWithBodyId) -> Arc<Body>; + #[salsa::invoke(ExprScopes::body_expr_scopes_query)] + fn body_expr_scopes(&self, def: DefWithBodyId) -> Arc<ExprScopes>; + + #[salsa::invoke(ExprScopes::sig_expr_scopes_query)] + fn sig_expr_scopes(&self, def: GenericDefId) -> Arc<ExprScopes>; + + #[salsa::transparent] #[salsa::invoke(ExprScopes::expr_scopes_query)] - fn expr_scopes(&self, def: DefWithBodyId) -> Arc<ExprScopes>; + fn expr_scopes(&self, def: ExpressionStoreOwner) -> Arc<ExprScopes>; #[salsa::transparent] #[salsa::invoke(GenericParams::new)] diff --git a/crates/hir-def/src/expr_store.rs b/crates/hir-def/src/expr_store.rs index 1ce4c881e7..b5436f3ba3 100644 --- a/crates/hir-def/src/expr_store.rs +++ b/crates/hir-def/src/expr_store.rs @@ -94,9 +94,24 @@ pub type TypeSource = InFile<TypePtr>; pub type LifetimePtr = AstPtr<ast::Lifetime>; pub type LifetimeSource = InFile<LifetimePtr>; +/// Describes where a const expression originated from. +/// +/// Used by signature/body inference to determine the expected type for each +/// const expression root. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum ConstExprOrigin { + /// Array length expression: `[T; <expr>]` — expected type is `usize`. + ArrayLength, + /// Const parameter default value: `const N: usize = <expr>`. + ConstParam(crate::hir::generics::LocalTypeOrConstParamId), + /// Const generic argument in a path: `SomeType::<{ <expr> }>` or `some_fn::<{ <expr> }>()`. + /// Determining the expected type requires path resolution, so it is deferred. + GenericArgsPath, +} + // We split the store into types-only and expressions, because most stores (e.g. generics) // don't store any expressions and this saves memory. Same thing for the source map. -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] struct ExpressionOnlyStore { exprs: Arena<Expr>, pats: Arena<Pat>, @@ -113,9 +128,15 @@ struct ExpressionOnlyStore { /// Expressions (and destructuing patterns) that can be recorded here are single segment path, although not all single segments path refer /// to variables and have hygiene (some refer to items, we don't know at this stage). ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>, + + /// Maps const expression roots to their origin. + /// + /// Populated during lowering. Used by signature inference to determine expected types, + /// and by `signature_const_expr_roots()` to enumerate roots for scope computation. + const_expr_origins: ThinVec<(ExprId, ConstExprOrigin)>, } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct ExpressionStore { expr_only: Option<Box<ExpressionOnlyStore>>, pub types: Arena<TypeRef>, @@ -226,6 +247,7 @@ pub struct ExpressionStoreBuilder { pub types: Arena<TypeRef>, block_scopes: Vec<BlockId>, ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>, + pub const_expr_origins: Option<ThinVec<(ExprId, ConstExprOrigin)>>, // AST expressions can create patterns in destructuring assignments. Therefore, `ExprSource` can also map // to `PatId`, and `PatId` can also map to `ExprSource` (the other way around is unaffected). @@ -297,6 +319,7 @@ impl ExpressionStoreBuilder { mut bindings, mut binding_owners, mut ident_hygiene, + mut const_expr_origins, mut types, mut lifetimes, @@ -356,6 +379,9 @@ impl ExpressionStoreBuilder { let store = { let expr_only = if has_exprs { + if let Some(const_expr_origins) = &mut const_expr_origins { + const_expr_origins.shrink_to_fit(); + } Some(Box::new(ExpressionOnlyStore { exprs, pats, @@ -364,6 +390,7 @@ impl ExpressionStoreBuilder { binding_owners, block_scopes: block_scopes.into_boxed_slice(), ident_hygiene, + const_expr_origins: const_expr_origins.unwrap_or_default(), })) } else { None @@ -413,6 +440,29 @@ impl ExpressionStore { EMPTY.clone() } + /// Returns all const expression root `ExprId`s found in this store. + /// + /// Used to compute expression scopes for signature stores. + pub fn signature_const_expr_roots(&self) -> impl Iterator<Item = ExprId> { + self.const_expr_origins().iter().map(|&(id, _)| id) + } + + /// Like [`Self::signature_const_expr_roots`], but also returns the origin + /// of each const expression. + /// + /// This is used by signature inference to determine the expected type for + /// each root expression. + pub fn signature_const_expr_roots_with_origins( + &self, + ) -> impl Iterator<Item = (ExprId, ConstExprOrigin)> { + self.const_expr_origins().iter().map(|&(id, origin)| (id, origin)) + } + + /// Returns the map of const expression roots to their origins. + pub fn const_expr_origins(&self) -> &[(ExprId, ConstExprOrigin)] { + self.expr_only.as_ref().map_or(&[], |it| &it.const_expr_origins) + } + /// Returns an iterator over all block expressions in this store that define inner items. pub fn blocks<'a>( &'a self, diff --git a/crates/hir-def/src/expr_store/body.rs b/crates/hir-def/src/expr_store/body.rs index c955393b9c..ad8fa73ad8 100644 --- a/crates/hir-def/src/expr_store/body.rs +++ b/crates/hir-def/src/expr_store/body.rs @@ -99,7 +99,7 @@ impl Body { DefWithBodyId::VariantId(v) => { let s = v.lookup(db); let src = s.source(db); - src.map(|it| it.expr()) + src.map(|it| it.const_arg()?.expr()) } } }; diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs index 1cecd1976b..67ce5eafc6 100644 --- a/crates/hir-def/src/expr_store/lower.rs +++ b/crates/hir-def/src/expr_store/lower.rs @@ -37,7 +37,7 @@ use crate::{ attrs::AttrFlags, db::DefDatabase, expr_store::{ - Body, BodySourceMap, ExprPtr, ExpressionStore, ExpressionStoreBuilder, + Body, BodySourceMap, ConstExprOrigin, ExprPtr, ExpressionStore, ExpressionStoreBuilder, ExpressionStoreDiagnostics, ExpressionStoreSourceMap, HygieneId, LabelPtr, LifetimePtr, PatPtr, TypePtr, expander::Expander, @@ -79,7 +79,7 @@ pub(super) fn lower_body( let mut self_param = None; let mut source_map_self_param = None; let mut params = vec![]; - let mut collector = ExprCollector::new(db, module, current_file_id); + let mut collector = ExprCollector::body(db, module, current_file_id); let skip_body = AttrFlags::query( db, @@ -186,7 +186,7 @@ pub(crate) fn lower_type_ref( module: ModuleId, type_ref: InFile<Option<ast::Type>>, ) -> (ExpressionStore, ExpressionStoreSourceMap, TypeRefId) { - let mut expr_collector = ExprCollector::new(db, module, type_ref.file_id); + let mut expr_collector = ExprCollector::signature(db, module, type_ref.file_id); let type_ref = expr_collector.lower_type_ref_opt(type_ref.value, &mut ExprCollector::impl_trait_allocator); let (store, source_map) = expr_collector.store.finish(); @@ -201,7 +201,7 @@ pub(crate) fn lower_generic_params( param_list: Option<ast::GenericParamList>, where_clause: Option<ast::WhereClause>, ) -> (Arc<ExpressionStore>, Arc<GenericParams>, ExpressionStoreSourceMap) { - let mut expr_collector = ExprCollector::new(db, module, file_id); + let mut expr_collector = ExprCollector::signature(db, module, file_id); let mut collector = generics::GenericParamsCollector::new(def); collector.lower(&mut expr_collector, param_list, where_clause); let params = collector.finish(); @@ -215,7 +215,7 @@ pub(crate) fn lower_impl( impl_syntax: InFile<ast::Impl>, impl_id: ImplId, ) -> (ExpressionStore, ExpressionStoreSourceMap, TypeRefId, Option<TraitRef>, Arc<GenericParams>) { - let mut expr_collector = ExprCollector::new(db, module, impl_syntax.file_id); + let mut expr_collector = ExprCollector::signature(db, module, impl_syntax.file_id); let self_ty = expr_collector.lower_type_ref_opt_disallow_impl_trait(impl_syntax.value.self_ty()); let trait_ = impl_syntax.value.trait_().and_then(|it| match &it { @@ -243,7 +243,7 @@ pub(crate) fn lower_trait( trait_syntax: InFile<ast::Trait>, trait_id: TraitId, ) -> (ExpressionStore, ExpressionStoreSourceMap, Arc<GenericParams>) { - let mut expr_collector = ExprCollector::new(db, module, trait_syntax.file_id); + let mut expr_collector = ExprCollector::signature(db, module, trait_syntax.file_id); let mut collector = generics::GenericParamsCollector::with_self_param( &mut expr_collector, trait_id.into(), @@ -271,7 +271,7 @@ pub(crate) fn lower_type_alias( Box<[TypeBound]>, Option<TypeRefId>, ) { - let mut expr_collector = ExprCollector::new(db, module, alias.file_id); + let mut expr_collector = ExprCollector::signature(db, module, alias.file_id); let bounds = alias .value .type_bound_list() @@ -313,7 +313,7 @@ pub(crate) fn lower_function( bool, bool, ) { - let mut expr_collector = ExprCollector::new(db, module, fn_.file_id); + let mut expr_collector = ExprCollector::signature(db, module, fn_.file_id); let mut collector = generics::GenericParamsCollector::new(function_id.into()); collector.lower(&mut expr_collector, fn_.value.generic_param_list(), fn_.value.where_clause()); let mut params = vec![]; @@ -532,7 +532,20 @@ impl BindingList { } impl<'db> ExprCollector<'db> { - pub fn new( + /// Creates a collector for a signature store, this will populate `const_expr_origins` to any + /// top level const arg roots. + pub fn signature( + db: &dyn DefDatabase, + module: ModuleId, + current_file_id: HirFileId, + ) -> ExprCollector<'_> { + let mut this = Self::body(db, module, current_file_id); + this.store.const_expr_origins = Some(Default::default()); + this + } + + /// Creates a collector for a bidy store. + pub fn body( db: &dyn DefDatabase, module: ModuleId, current_file_id: HirFileId, @@ -577,7 +590,10 @@ impl<'db> ExprCollector<'db> { self.expander.span_map() } - pub fn lower_lifetime_ref(&mut self, lifetime: ast::Lifetime) -> LifetimeRefId { + pub(in crate::expr_store) fn lower_lifetime_ref( + &mut self, + lifetime: ast::Lifetime, + ) -> LifetimeRefId { // FIXME: Keyword check? let lifetime_ref = match &*lifetime.text() { "" | "'" => LifetimeRef::Error, @@ -588,7 +604,10 @@ impl<'db> ExprCollector<'db> { self.alloc_lifetime_ref(lifetime_ref, AstPtr::new(&lifetime)) } - pub fn lower_lifetime_ref_opt(&mut self, lifetime: Option<ast::Lifetime>) -> LifetimeRefId { + pub(in crate::expr_store) fn lower_lifetime_ref_opt( + &mut self, + lifetime: Option<ast::Lifetime>, + ) -> LifetimeRefId { match lifetime { Some(lifetime) => self.lower_lifetime_ref(lifetime), None => self.alloc_lifetime_ref_desugared(LifetimeRef::Placeholder), @@ -596,7 +615,7 @@ impl<'db> ExprCollector<'db> { } /// Converts an `ast::TypeRef` to a `hir::TypeRef`. - pub fn lower_type_ref( + pub(in crate::expr_store) fn lower_type_ref( &mut self, node: ast::Type, impl_trait_lower_fn: ImplTraitLowerFn<'_>, @@ -621,6 +640,9 @@ impl<'db> ExprCollector<'db> { } ast::Type::ArrayType(inner) => { let len = self.lower_const_arg_opt(inner.const_arg()); + if let Some(const_expr_origins) = &mut self.store.const_expr_origins { + const_expr_origins.push((len.expr, ConstExprOrigin::ArrayLength)); + } TypeRef::Array(ArrayType { ty: self.lower_type_ref_opt(inner.ty(), impl_trait_lower_fn), len, @@ -810,7 +832,7 @@ impl<'db> ExprCollector<'db> { /// Collect `GenericArgs` from the parts of a fn-like path, i.e. `Fn(X, Y) /// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`). - pub fn lower_generic_args_from_fn_path( + pub(in crate::expr_store) fn lower_generic_args_from_fn_path( &mut self, args: Option<ast::ParenthesizedArgList>, ret_type: Option<ast::RetType>, @@ -905,6 +927,9 @@ impl<'db> ExprCollector<'db> { } ast::GenericArg::ConstArg(arg) => { let arg = self.lower_const_arg(arg); + if let Some(const_expr_origins) = &mut self.store.const_expr_origins { + const_expr_origins.push((arg.expr, ConstExprOrigin::GenericArgsPath)); + } args.push(GenericArg::Const(arg)) } } @@ -1045,17 +1070,30 @@ impl<'db> ExprCollector<'db> { } fn lower_const_arg_opt(&mut self, arg: Option<ast::ConstArg>) -> ConstRef { - ConstRef { expr: self.collect_expr_opt(arg.and_then(|it| it.expr())) } + let const_expr_origins = self.store.const_expr_origins.take(); + let r = ConstRef { expr: self.collect_expr_opt(arg.and_then(|it| it.expr())) }; + self.store.const_expr_origins = const_expr_origins; + r } - fn lower_const_arg(&mut self, arg: ast::ConstArg) -> ConstRef { - ConstRef { expr: self.collect_expr_opt(arg.expr()) } + pub fn lower_const_arg(&mut self, arg: ast::ConstArg) -> ConstRef { + let const_expr_origins = self.store.const_expr_origins.take(); + let r = ConstRef { expr: self.collect_expr_opt(arg.expr()) }; + self.store.const_expr_origins = const_expr_origins; + r } fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { self.maybe_collect_expr(expr).unwrap_or_else(|| self.missing_expr()) } + pub(in crate::expr_store) fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId { + match expr { + Some(expr) => self.collect_expr(expr), + None => self.missing_expr(), + } + } + /// Returns `None` if and only if the expression is `#[cfg]`d out. fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option<ExprId> { let syntax_ptr = AstPtr::new(&expr); @@ -2065,13 +2103,6 @@ impl<'db> ExprCollector<'db> { } } - pub fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId { - match expr { - Some(expr) => self.collect_expr(expr), - None => self.missing_expr(), - } - } - fn collect_macro_as_stmt( &mut self, statements: &mut Vec<Statement>, diff --git a/crates/hir-def/src/expr_store/lower/generics.rs b/crates/hir-def/src/expr_store/lower/generics.rs index c570df42b2..03de7937ba 100644 --- a/crates/hir-def/src/expr_store/lower/generics.rs +++ b/crates/hir-def/src/expr_store/lower/generics.rs @@ -141,12 +141,17 @@ impl GenericParamsCollector { const_param.ty(), &mut ExprCollector::impl_trait_error_allocator, ); - let param = ConstParamData { - name, - ty, - default: const_param.default_val().map(|it| ec.lower_const_arg(it)), - }; - let _idx = self.type_or_consts.alloc(param.into()); + let default = const_param.default_val().map(|it| ec.lower_const_arg(it)); + let param = ConstParamData { name, ty, default }; + let idx = self.type_or_consts.alloc(param.into()); + if let Some(default) = default + && let Some(const_expr_origins) = &mut ec.store.const_expr_origins + { + const_expr_origins.push(( + default.expr, + crate::expr_store::ConstExprOrigin::ConstParam(idx), + )); + } } ast::GenericParam::LifetimeParam(lifetime_param) => { let lifetime = ec.lower_lifetime_ref_opt(lifetime_param.lifetime()); diff --git a/crates/hir-def/src/expr_store/lower/path/tests.rs b/crates/hir-def/src/expr_store/lower/path/tests.rs index f507841a91..6819eb3deb 100644 --- a/crates/hir-def/src/expr_store/lower/path/tests.rs +++ b/crates/hir-def/src/expr_store/lower/path/tests.rs @@ -21,7 +21,7 @@ fn lower_path(path: ast::Path) -> (TestDB, ExpressionStore, Option<Path>) { let (db, file_id) = TestDB::with_single_file(""); let krate = db.fetch_test_crate(); let mut ctx = - ExprCollector::new(&db, crate_def_map(&db, krate).root_module_id(), file_id.into()); + ExprCollector::signature(&db, crate_def_map(&db, krate).root_module_id(), file_id.into()); let lowered_path = ctx.lower_path(path, &mut ExprCollector::impl_trait_allocator); let (store, _) = ctx.store.finish(); (db, store, lowered_path) diff --git a/crates/hir-def/src/expr_store/scope.rs b/crates/hir-def/src/expr_store/scope.rs index 1952dae9d7..43ce053836 100644 --- a/crates/hir-def/src/expr_store/scope.rs +++ b/crates/hir-def/src/expr_store/scope.rs @@ -4,7 +4,7 @@ use la_arena::{Arena, ArenaMap, Idx, IdxRange, RawIdx}; use triomphe::Arc; use crate::{ - BlockId, DefWithBodyId, + BlockId, DefWithBodyId, ExpressionStoreOwner, GenericDefId, db::DefDatabase, expr_store::{Body, ExpressionStore, HygieneId}, hir::{Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement}, @@ -51,13 +51,37 @@ pub struct ScopeData { } impl ExprScopes { - pub(crate) fn expr_scopes_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<ExprScopes> { + pub(crate) fn expr_scopes_query( + db: &dyn DefDatabase, + def: ExpressionStoreOwner, + ) -> Arc<ExprScopes> { + match def { + ExpressionStoreOwner::Body(def) => db.body_expr_scopes(def), + ExpressionStoreOwner::Signature(def) => db.sig_expr_scopes(def), + } + } + + pub(crate) fn body_expr_scopes_query( + db: &dyn DefDatabase, + def: DefWithBodyId, + ) -> Arc<ExprScopes> { let body = db.body(def); let mut scopes = ExprScopes::new_body(&body); scopes.shrink_to_fit(); Arc::new(scopes) } + pub(crate) fn sig_expr_scopes_query( + db: &dyn DefDatabase, + def: GenericDefId, + ) -> Arc<ExprScopes> { + let (_, store) = db.generic_params_and_store(def); + let roots = store.signature_const_expr_roots(); + let mut scopes = ExprScopes::new_store(&store, roots); + scopes.shrink_to_fit(); + Arc::new(scopes) + } + pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] { &self.scope_entries[self.scopes[scope].entries.clone()] } @@ -119,6 +143,22 @@ impl ExprScopes { scopes } + fn new_store(store: &ExpressionStore, roots: impl IntoIterator<Item = ExprId>) -> ExprScopes { + let mut scopes = ExprScopes { + scopes: Arena::default(), + scope_entries: Arena::default(), + scope_by_expr: ArenaMap::with_capacity( + store.expr_only.as_ref().map_or(0, |it| it.exprs.len()), + ), + }; + let root = scopes.root_scope(); + for root_expr in roots { + let mut scope = scopes.new_scope(root); + compute_expr_scopes(root_expr, store, &mut scopes, &mut scope); + } + scopes + } + fn root_scope(&mut self) -> ScopeId { self.scopes.alloc(ScopeData { parent: None, @@ -327,7 +367,8 @@ mod tests { use test_utils::{assert_eq_text, extract_offset}; use crate::{ - FunctionId, ModuleDefId, db::DefDatabase, nameres::crate_def_map, test_db::TestDB, + DefWithBodyId, FunctionId, ModuleDefId, db::DefDatabase, nameres::crate_def_map, + test_db::TestDB, }; fn find_function(db: &TestDB, file_id: FileId) -> FunctionId { @@ -363,7 +404,7 @@ mod tests { let marker: ast::PathExpr = find_node_at_offset(&file_syntax, offset).unwrap(); let function = find_function(&db, file_id); - let scopes = db.expr_scopes(function.into()); + let scopes = db.expr_scopes(DefWithBodyId::from(function).into()); let (_body, source_map) = db.body_with_source_map(function.into()); let expr_id = source_map @@ -522,7 +563,7 @@ fn foo() { let function = find_function(&db, file_id); - let scopes = db.expr_scopes(function.into()); + let scopes = db.expr_scopes(DefWithBodyId::from(function).into()); let (_, source_map) = db.body_with_source_map(function.into()); let expr_scope = { diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index de674be05f..ffad5fee47 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -86,7 +86,10 @@ use crate::{ builtin_type::BuiltinType, db::DefDatabase, expr_store::ExpressionStoreSourceMap, - hir::generics::{GenericParams, LocalLifetimeParamId, LocalTypeOrConstParamId}, + hir::{ + ExprId, + generics::{GenericParams, LocalLifetimeParamId, LocalTypeOrConstParamId}, + }, nameres::{ LocalDefMap, assoc::{ImplItems, TraitItems}, @@ -306,6 +309,19 @@ impl_intern!(ConstId, ConstLoc, intern_const, lookup_intern_const); pub type StaticLoc = AssocItemLoc<ast::Static>; impl_intern!(StaticId, StaticLoc, intern_static, lookup_intern_static); +/// An anonymous const expression that appears in a type position (e.g., array lengths, +/// const generic arguments like `{ N + 1 }`). Unlike named constants, these don't have +/// their own `Body` — their expressions live in the parent's signature `ExpressionStore`. +#[derive(Debug, Hash, PartialEq, Eq, Clone)] +pub struct AnonConstLoc { + /// The owner store containing this expression. + pub owner: ExpressionStoreOwner, + /// The ExprId within the owner's ExpressionStore that is the root + /// of this anonymous const expression. + pub expr: ExprId, +} +impl_intern!(AnonConstId, AnonConstLoc, intern_anon_const, lookup_intern_anon_const); + pub type TraitLoc = ItemLoc<ast::Trait>; impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait); @@ -706,15 +722,17 @@ impl From<DefWithBodyId> for ModuleDefId { pub enum GeneralConstId { ConstId(ConstId), StaticId(StaticId), + AnonConstId(AnonConstId), } -impl_from!(ConstId, StaticId for GeneralConstId); +impl_from!(ConstId, StaticId, AnonConstId for GeneralConstId); impl GeneralConstId { - pub fn generic_def(self, _db: &dyn DefDatabase) -> Option<GenericDefId> { + pub fn generic_def(self, db: &dyn DefDatabase) -> Option<GenericDefId> { match self { GeneralConstId::ConstId(it) => Some(it.into()), GeneralConstId::StaticId(it) => Some(it.into()), + GeneralConstId::AnonConstId(it) => Some(it.lookup(db).owner.generic_def(db)), } } @@ -729,6 +747,7 @@ impl GeneralConstId { |name| name.display(db, Edition::CURRENT).to_string(), ) } + GeneralConstId::AnonConstId(_) => "{anon const}".to_owned(), } } } @@ -814,6 +833,45 @@ impl_from!( for GenericDefId ); +/// Owner of an expression store - either a body or a signature. +/// This is used for queries that operate on expression stores generically, +/// such as `expr_scopes`. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum ExpressionStoreOwner { + Signature(GenericDefId), + Body(DefWithBodyId), +} + +impl ExpressionStoreOwner { + pub fn as_def_with_body(self) -> Option<DefWithBodyId> { + if let Self::Body(v) = self { Some(v) } else { None } + } + + pub fn generic_def(self, db: &dyn DefDatabase) -> GenericDefId { + match self { + ExpressionStoreOwner::Signature(generic_def_id) => generic_def_id, + ExpressionStoreOwner::Body(def_with_body_id) => match def_with_body_id { + DefWithBodyId::FunctionId(id) => GenericDefId::FunctionId(id), + DefWithBodyId::StaticId(id) => GenericDefId::StaticId(id), + DefWithBodyId::ConstId(id) => GenericDefId::ConstId(id), + DefWithBodyId::VariantId(it) => it.lookup(db).parent.into(), + }, + } + } +} + +impl From<GenericDefId> for ExpressionStoreOwner { + fn from(id: GenericDefId) -> Self { + ExpressionStoreOwner::Signature(id) + } +} + +impl From<DefWithBodyId> for ExpressionStoreOwner { + fn from(id: DefWithBodyId) -> Self { + ExpressionStoreOwner::Body(id) + } +} + impl GenericDefId { pub fn file_id_and_params_of( self, @@ -1172,6 +1230,15 @@ impl HasModule for DefWithBodyId { } } +impl HasModule for ExpressionStoreOwner { + fn module(&self, db: &dyn DefDatabase) -> ModuleId { + match self { + ExpressionStoreOwner::Signature(def) => def.module(db), + ExpressionStoreOwner::Body(def) => def.module(db), + } + } +} + impl HasModule for GenericDefId { fn module(&self, db: &dyn DefDatabase) -> ModuleId { match self { diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index d32e53fc6b..38b461770c 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -16,11 +16,11 @@ use syntax::ast::HasName; use triomphe::Arc; use crate::{ - AdtId, AstIdLoc, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, - ExternCrateId, FunctionId, FxIndexMap, GenericDefId, GenericParamId, HasModule, ImplId, - ItemContainerId, LifetimeParamId, Lookup, Macro2Id, MacroId, MacroRulesId, ModuleDefId, - ModuleId, ProcMacroId, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, - TypeParamId, UseId, VariantId, + AdtId, AstIdLoc, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, + ExpressionStoreOwner, ExternBlockId, ExternCrateId, FunctionId, FxIndexMap, GenericDefId, + GenericParamId, HasModule, ImplId, ItemContainerId, LifetimeParamId, Lookup, Macro2Id, MacroId, + MacroRulesId, ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitId, TypeAliasId, + TypeOrConstParamId, TypeParamId, UseId, VariantId, builtin_type::BuiltinType, db::DefDatabase, expr_store::{ @@ -66,7 +66,7 @@ impl fmt::Debug for ModuleItemMap<'_> { #[derive(Clone)] struct ExprScope { - owner: DefWithBodyId, + owner: ExpressionStoreOwner, expr_scopes: Arc<ExprScopes>, scope_id: ScopeId, } @@ -738,7 +738,10 @@ impl<'db> Resolver<'db> { pub fn body_owner(&self) -> Option<DefWithBodyId> { self.scopes().find_map(|scope| match scope { - Scope::ExprScope(it) => Some(it.owner), + Scope::ExprScope(it) => match it.owner { + ExpressionStoreOwner::Body(def) => Some(def), + ExpressionStoreOwner::Signature(_) => None, + }, _ => None, }) } @@ -854,14 +857,23 @@ impl<'db> Resolver<'db> { pub fn update_to_inner_scope( &mut self, db: &'db dyn DefDatabase, - owner: DefWithBodyId, + owner: impl Into<ExpressionStoreOwner>, + expr_id: ExprId, + ) -> UpdateGuard { + self.update_to_inner_scope_(db, owner.into(), expr_id) + } + + fn update_to_inner_scope_( + &mut self, + db: &'db dyn DefDatabase, + owner: ExpressionStoreOwner, expr_id: ExprId, ) -> UpdateGuard { #[inline(always)] fn append_expr_scope<'db>( db: &'db dyn DefDatabase, resolver: &mut Resolver<'db>, - owner: DefWithBodyId, + owner: ExpressionStoreOwner, expr_scopes: &Arc<ExprScopes>, scope_id: ScopeId, ) { @@ -1060,12 +1072,13 @@ impl<'db> Scope<'db> { pub fn resolver_for_scope( db: &dyn DefDatabase, - owner: DefWithBodyId, + owner: impl Into<ExpressionStoreOwner> + HasResolver, scope_id: Option<ScopeId>, ) -> Resolver<'_> { - let r = owner.resolver(db); - let scopes = db.expr_scopes(owner); - resolver_for_scope_(db, scopes, scope_id, r, owner) + let store_owner = owner.into(); + let r = store_owner.resolver(db); + let scopes = db.expr_scopes(store_owner); + resolver_for_scope_(db, scopes, scope_id, r, store_owner) } fn resolver_for_scope_<'db>( @@ -1073,7 +1086,7 @@ fn resolver_for_scope_<'db>( scopes: Arc<ExprScopes>, scope_id: Option<ScopeId>, mut r: Resolver<'db>, - owner: DefWithBodyId, + owner: ExpressionStoreOwner, ) -> Resolver<'db> { let scope_chain = scopes.scope_chain(scope_id).collect::<Vec<_>>(); r.scopes.reserve(scope_chain.len()); @@ -1124,7 +1137,7 @@ impl<'db> Resolver<'db> { fn push_expr_scope( self, - owner: DefWithBodyId, + owner: ExpressionStoreOwner, expr_scopes: Arc<ExprScopes>, scope_id: ScopeId, ) -> Resolver<'db> { @@ -1409,6 +1422,15 @@ impl HasResolver for GenericDefId { } } +impl HasResolver for ExpressionStoreOwner { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { + match self { + ExpressionStoreOwner::Signature(def) => def.resolver(db), + ExpressionStoreOwner::Body(def) => def.resolver(db), + } + } +} + impl HasResolver for EnumVariantId { fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { self.lookup(db).parent.resolver(db) diff --git a/crates/hir-def/src/signatures.rs b/crates/hir-def/src/signatures.rs index 37c8f762fe..ce76158151 100644 --- a/crates/hir-def/src/signatures.rs +++ b/crates/hir-def/src/signatures.rs @@ -32,7 +32,7 @@ use crate::{ hir::{ExprId, PatId, generics::GenericParams}, item_tree::{FieldsShape, RawVisibility, visibility_from_ast}, src::HasSource, - type_ref::{TraitRef, TypeBound, TypeRefId}, + type_ref::{ConstRef, TraitRef, TypeBound, TypeRefId}, }; #[inline] @@ -754,7 +754,7 @@ pub struct FieldData { pub type_ref: TypeRefId, pub visibility: RawVisibility, pub is_unsafe: bool, - pub default_value: Option<ExprId>, + pub default_value: Option<ConstRef>, } pub type LocalFieldId = Idx<FieldData>; @@ -873,7 +873,7 @@ fn lower_fields<Field: ast::HasAttrs + ast::HasVisibility>( override_visibility: Option<Option<ast::Visibility>>, ) -> Option<(Arena<FieldData>, ExpressionStore, ExpressionStoreSourceMap)> { let cfg_options = module.krate(db).cfg_options(db); - let mut col = ExprCollector::new(db, module, fields.file_id); + let mut col = ExprCollector::signature(db, module, fields.file_id); let override_visibility = override_visibility.map(|vis| { LazyCell::new(|| { let span_map = db.span_map(fields.file_id); @@ -907,9 +907,9 @@ fn lower_fields<Field: ast::HasAttrs + ast::HasVisibility>( // Check if field has default value (only for record fields) let default_value = ast::RecordField::cast(field.syntax().clone()) - .and_then(|rf| rf.eq_token().is_some().then_some(rf.expr())) + .and_then(|rf| rf.eq_token().is_some().then_some(rf.default_val())) .flatten() - .map(|expr| col.collect_expr_opt(Some(expr))); + .map(|expr| col.lower_const_arg(expr)); arena.alloc(FieldData { name, type_ref, visibility, is_unsafe, default_value }); idx += 1; diff --git a/crates/hir-def/src/test_db.rs b/crates/hir-def/src/test_db.rs index e8377fde49..d2921db6f4 100644 --- a/crates/hir-def/src/test_db.rs +++ b/crates/hir-def/src/test_db.rs @@ -285,7 +285,7 @@ impl TestDB { let (def_with_body, file_id) = fn_def?; let def_with_body = def_with_body.into(); let source_map = self.body_with_source_map(def_with_body).1; - let scopes = self.expr_scopes(def_with_body); + let scopes = self.expr_scopes(def_with_body.into()); let root_syntax_node = self.parse(file_id).syntax_node(); let scope_iter = diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs index 07e9f70fae..c294238030 100644 --- a/crates/hir-ty/src/consteval.rs +++ b/crates/hir-ty/src/consteval.rs @@ -8,7 +8,7 @@ use hir_def::{ ConstId, EnumVariantId, GeneralConstId, HasModule, StaticId, attrs::AttrFlags, builtin_type::{BuiltinInt, BuiltinType, BuiltinUint}, - expr_store::Body, + expr_store::ExpressionStore, hir::{Expr, ExprId, Literal}, }; use hir_expand::Lookup; @@ -235,6 +235,7 @@ pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option<u let ec = db.const_eval_static(id).ok()?; try_const_usize(db, ec) } + GeneralConstId::AnonConstId(_) => None, }, ConstKind::Value(val) => Some(u128::from_le_bytes(pad16(&val.value.inner().memory, false))), ConstKind::Error(_) => None, @@ -258,6 +259,7 @@ pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option< let ec = db.const_eval_static(id).ok()?; try_const_isize(db, &ec) } + GeneralConstId::AnonConstId(_) => None, }, ConstKind::Value(val) => Some(i128::from_le_bytes(pad16(&val.value.inner().memory, true))), ConstKind::Error(_) => None, @@ -309,23 +311,23 @@ pub(crate) fn const_eval_discriminant_variant( // and make this function private. See the fixme comment on `InferenceContext::resolve_all`. pub(crate) fn eval_to_const<'db>(expr: ExprId, ctx: &mut InferenceContext<'_, 'db>) -> Const<'db> { let infer = ctx.fixme_resolve_all_clone(); - fn has_closure(body: &Body, expr: ExprId) -> bool { - if matches!(body[expr], Expr::Closure { .. }) { + fn has_closure(store: &ExpressionStore, expr: ExprId) -> bool { + if matches!(store[expr], Expr::Closure { .. }) { return true; } let mut r = false; - body.walk_child_exprs(expr, |idx| r |= has_closure(body, idx)); + store.walk_child_exprs(expr, |idx| r |= has_closure(store, idx)); r } - if has_closure(ctx.body, expr) { + if has_closure(ctx.store, expr) { // Type checking clousres need an isolated body (See the above FIXME). Bail out early to prevent panic. return Const::error(ctx.interner()); } - if let Expr::Path(p) = &ctx.body[expr] { + if let Expr::Path(p) = &ctx.store[expr] { let mut ctx = TyLoweringContext::new( ctx.db, &ctx.resolver, - ctx.body, + ctx.store, ctx.generic_def, LifetimeElisionKind::Infer, ); @@ -333,7 +335,9 @@ pub(crate) fn eval_to_const<'db>(expr: ExprId, ctx: &mut InferenceContext<'_, 'd return c; } } - if let Ok(mir_body) = lower_body_to_mir(ctx.db, ctx.owner, ctx.body, &infer, expr) + if let Some(body_owner) = ctx.owner.as_def_with_body() + && let Ok(mir_body) = + lower_body_to_mir(ctx.db, body_owner, &ctx.db.body(body_owner), &infer, expr) && let Ok((Ok(result), _)) = interpret_mir(ctx.db, Arc::new(mir_body), true, None) { return result; diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index 70474fc469..ca5b1b7716 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs @@ -5,9 +5,9 @@ use base_db::{Crate, target::TargetLoadError}; use either::Either; use hir_def::{ AdtId, BuiltinDeriveImplId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, - FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, StaticId, TraitId, - TypeAliasId, VariantId, builtin_derive::BuiltinDeriveImplMethod, db::DefDatabase, hir::ExprId, - layout::TargetDataLayout, + ExpressionStoreOwner, FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, + StaticId, TraitId, TypeAliasId, VariantId, builtin_derive::BuiltinDeriveImplMethod, + db::DefDatabase, hir::ExprId, layout::TargetDataLayout, }; use la_arena::ArenaMap; use salsa::plumbing::AsId; @@ -240,7 +240,7 @@ pub struct InternedOpaqueTyId { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct InternedClosure(pub DefWithBodyId, pub ExprId); +pub struct InternedClosure(pub ExpressionStoreOwner, pub ExprId); #[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] @@ -249,7 +249,7 @@ pub struct InternedClosureId { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct InternedCoroutine(pub DefWithBodyId, pub ExprId); +pub struct InternedCoroutine(pub ExpressionStoreOwner, pub ExprId); #[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 4e77e8be36..f4d0ed1484 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -1336,7 +1336,11 @@ impl<'db> HirDisplay<'db> for Ty<'db> { } let sig = interner.signature_unclosure(substs.as_closure().sig(), Safety::Safe); let sig = sig.skip_binder(); - let InternedClosure(def, _) = db.lookup_intern_closure(id); + let InternedClosure(owner, _) = db.lookup_intern_closure(id); + let Some(def) = owner.as_def_with_body() else { + write!(f, "{{closure}}")?; + return Ok(()); + }; let infer = InferenceResult::for_body(db, def); let (_, kind) = infer.closure_info(id); match f.closure_style { @@ -1526,7 +1530,13 @@ impl<'db> HirDisplay<'db> for Ty<'db> { let InternedCoroutine(owner, expr_id) = coroutine_id.0.loc(db); let CoroutineArgsParts { resume_ty, yield_ty, return_ty, .. } = subst.split_coroutine_args(); - let body = db.body(owner); + let Some(body_owner) = owner.as_def_with_body() else { + write!(f, "impl Future<Output = ")?; + return_ty.hir_fmt(f)?; + write!(f, ">")?; + return Ok(()); + }; + let body = db.body(body_owner); let expr = &body[expr_id]; match expr { hir_def::hir::Expr::Closure { diff --git a/crates/hir-ty/src/drop.rs b/crates/hir-ty/src/drop.rs index 9d6869eee9..1303e08801 100644 --- a/crates/hir-ty/src/drop.rs +++ b/crates/hir-ty/src/drop.rs @@ -132,9 +132,12 @@ fn has_drop_glue_impl<'db>( TyKind::Slice(ty) => has_drop_glue_impl(infcx, ty, env, visited), TyKind::Closure(closure_id, subst) => { let owner = db.lookup_intern_closure(closure_id.0).0; - let infer = InferenceResult::for_body(db, owner); + let Some(body_owner) = owner.as_def_with_body() else { + return DropGlue::None; + }; + let infer = InferenceResult::for_body(db, body_owner); let (captures, _) = infer.closure_info(closure_id.0); - let env = db.trait_environment_for_body(owner); + let env = db.trait_environment_for_body(body_owner); captures .iter() .map(|capture| has_drop_glue_impl(infcx, capture.ty(db, subst), env, visited)) diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 991acda14b..9d78f5de9e 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -33,9 +33,10 @@ use std::{cell::OnceCell, convert::identity, iter}; use base_db::Crate; use either::Either; use hir_def::{ - AdtId, AssocItemId, ConstId, DefWithBodyId, FieldId, FunctionId, GenericDefId, GenericParamId, - ItemContainerId, LocalFieldId, Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId, - expr_store::{Body, ExpressionStore, HygieneId, path::Path}, + AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, ExpressionStoreOwner, FieldId, + FunctionId, GenericDefId, GenericParamId, ItemContainerId, LocalFieldId, Lookup, TraitId, + TupleFieldId, TupleId, TypeAliasId, TypeOrConstParamId, VariantId, + expr_store::{ConstExprOrigin, ExpressionStore, HygieneId, path::Path}, hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId}, lang_item::LangItems, layout::Integer, @@ -105,16 +106,14 @@ pub fn infer_query_with_inspect<'db>( let _p = tracing::info_span!("infer_query").entered(); let resolver = def.resolver(db); let body = db.body(def); - let mut ctx = InferenceContext::new(db, def, &body, resolver); + let mut ctx = InferenceContext::new(db, ExpressionStoreOwner::Body(def), &body.store, resolver); if let Some(inspect) = inspect { ctx.table.infer_ctxt.attach_obligation_inspector(inspect); } match def { - DefWithBodyId::FunctionId(f) => { - ctx.collect_fn(f); - } + DefWithBodyId::FunctionId(f) => ctx.collect_fn(f, body.self_param, &body.params), DefWithBodyId::ConstId(c) => ctx.collect_const(c, &db.const_signature(c)), DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_signature(s)), DefWithBodyId::VariantId(v) => { @@ -143,10 +142,14 @@ pub fn infer_query_with_inspect<'db>( } } - ctx.infer_body(); + ctx.infer_body(body.body_expr); + + ctx.infer_mut_body(body.body_expr); - ctx.infer_mut_body(); + finalize_infer(ctx) +} +fn finalize_infer(mut ctx: InferenceContext<'_, '_>) -> InferenceResult { ctx.handle_opaque_type_uses(); ctx.type_inference_fallback(); @@ -179,6 +182,55 @@ fn infer_cycle_result(db: &dyn HirDatabase, _: salsa::Id, _: DefWithBodyId) -> I } } +/// Infer types for all const expressions in an item's signature. +/// +/// This handles const expressions that appear in type positions within a generic +/// item's signature, such as array lengths (`[T; N]`) and const generic arguments +/// (`Foo<{ expr }>`). Each root expression is inferred independently within +/// a shared `InferenceContext`, accumulating results into a single `InferenceResult`. +fn infer_signature_query(db: &dyn HirDatabase, def: GenericDefId) -> InferenceResult { + let _p = tracing::info_span!("infer_signature_query").entered(); + let (_, store) = db.generic_params_and_store(def); + let mut roots = store.signature_const_expr_roots_with_origins().peekable(); + let Some(_) = roots.peek() else { + return InferenceResult::new(crate::next_solver::default_types(db).types.error); + }; + + let resolver = def.resolver(db); + let owner = ExpressionStoreOwner::Signature(def); + + let mut ctx = InferenceContext::new(db, owner, &store, resolver); + + for (root_expr, origin) in roots { + let expected = match origin { + // Array lengths are always `usize`. + ConstExprOrigin::ArrayLength => Expectation::has_type(ctx.types.types.usize), + // Const parameter default: look up the param's declared type. + ConstExprOrigin::ConstParam(local_id) => Expectation::has_type(db.const_param_ty_ns( + ConstParamId::from_unchecked(TypeOrConstParamId { parent: def, local_id }), + )), + // Path const generic args: determining the expected type requires + // path resolution. + // FIXME + ConstExprOrigin::GenericArgsPath => Expectation::None, + }; + ctx.infer_expr(root_expr, &expected, ExprIsRead::Yes); + } + + finalize_infer(ctx) +} + +fn infer_signature_cycle_result( + db: &dyn HirDatabase, + _: salsa::Id, + _: GenericDefId, +) -> InferenceResult { + InferenceResult { + has_errors: true, + ..InferenceResult::new(Ty::new_error(DbInterner::new_no_crate(db), ErrorGuaranteed)) + } +} + /// Binding modes inferred for patterns. /// <https://doc.rust-lang.org/reference/patterns.html#binding-modes> #[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] @@ -555,6 +607,16 @@ impl InferenceResult { pub fn for_body(db: &dyn HirDatabase, def: DefWithBodyId) -> InferenceResult { infer_query(db, def) } + + /// Infer types for all const expressions in an item's signature. + /// + /// Returns an `InferenceResult` containing type information for array lengths, + /// const generic arguments, and other const expressions appearing in type + /// positions within the item's signature. + #[salsa::tracked(returns(ref), cycle_result = infer_signature_cycle_result)] + pub fn for_signature(db: &dyn HirDatabase, def: GenericDefId) -> InferenceResult { + infer_signature_query(db, def) + } } impl InferenceResult { @@ -754,8 +816,8 @@ impl InferenceResult { #[derive(Clone, Debug)] pub(crate) struct InferenceContext<'body, 'db> { pub(crate) db: &'db dyn HirDatabase, - pub(crate) owner: DefWithBodyId, - pub(crate) body: &'body Body, + pub(crate) owner: ExpressionStoreOwner, + pub(crate) store: &'body ExpressionStore, /// Generally you should not resolve things via this resolver. Instead create a TyLoweringContext /// and resolve the path via its methods. This will ensure proper error reporting. pub(crate) resolver: Resolver<'db>, @@ -855,11 +917,16 @@ fn find_continuable<'a, 'db>( impl<'body, 'db> InferenceContext<'body, 'db> { fn new( db: &'db dyn HirDatabase, - owner: DefWithBodyId, - body: &'body Body, + owner: ExpressionStoreOwner, + store: &'body ExpressionStore, resolver: Resolver<'db>, ) -> Self { - let trait_env = db.trait_environment_for_body(owner); + let trait_env = match owner { + ExpressionStoreOwner::Signature(generic_def_id) => db.trait_environment(generic_def_id), + ExpressionStoreOwner::Body(def_with_body_id) => { + db.trait_environment_for_body(def_with_body_id) + } + }; let table = unify::InferenceTable::new(db, trait_env, resolver.krate(), Some(owner)); let types = crate::next_solver::default_types(db); InferenceContext { @@ -878,13 +945,8 @@ impl<'body, 'db> InferenceContext<'body, 'db> { return_coercion: None, db, owner, - generic_def: match owner { - DefWithBodyId::FunctionId(it) => it.into(), - DefWithBodyId::StaticId(it) => it.into(), - DefWithBodyId::ConstId(it) => it.into(), - DefWithBodyId::VariantId(it) => it.lookup(db).parent.into(), - }, - body, + generic_def: owner.generic_def(db), + store, traits_in_scope: resolver.traits_in_scope(db), resolver, diverges: Diverges::Maybe, @@ -908,7 +970,9 @@ impl<'body, 'db> InferenceContext<'body, 'db> { fn target_features(&self) -> (&TargetFeatures<'db>, TargetFeatureIsSafeInTarget) { let (target_features, target_feature_is_safe) = self.target_features.get_or_init(|| { let target_features = match self.owner { - DefWithBodyId::FunctionId(id) => TargetFeatures::from_fn(self.db, id), + ExpressionStoreOwner::Body(DefWithBodyId::FunctionId(id)) => { + TargetFeatures::from_fn(self.db, id) + } _ => TargetFeatures::default(), }; let target_feature_is_safe = match &self.krate().workspace_data(self.db).target { @@ -1102,7 +1166,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.return_ty = return_ty; } - fn collect_fn(&mut self, func: FunctionId) { + fn collect_fn(&mut self, func: FunctionId, self_param: Option<BindingId>, params: &[PatId]) { let data = self.db.function_signature(func); let mut param_tys = self.with_ty_lowering( &data.store, @@ -1130,13 +1194,13 @@ impl<'body, 'db> InferenceContext<'body, 'db> { param_tys.push(va_list_ty); } let mut param_tys = param_tys.into_iter().chain(iter::repeat(self.table.next_ty_var())); - if let Some(self_param) = self.body.self_param + if let Some(self_param) = self_param && let Some(ty) = param_tys.next() { let ty = self.process_user_written_ty(ty); self.write_binding_ty(self_param, ty); } - for (ty, pat) in param_tys.zip(&*self.body.params) { + for (ty, pat) in param_tys.zip(params) { let ty = self.process_user_written_ty(ty); self.infer_top_pat(*pat, ty, None); @@ -1170,12 +1234,12 @@ impl<'body, 'db> InferenceContext<'body, 'db> { &self.table.infer_ctxt } - fn infer_body(&mut self) { + fn infer_body(&mut self, body_expr: ExprId) { match self.return_coercion { - Some(_) => self.infer_return(self.body.body_expr), + Some(_) => self.infer_return(body_expr), None => { _ = self.infer_expr_coerce( - self.body.body_expr, + body_expr, &Expectation::has_type(self.return_ty), ExprIsRead::Yes, ) @@ -1282,7 +1346,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { f: impl FnOnce(&mut TyLoweringContext<'db, '_>) -> R, ) -> R { self.with_ty_lowering( - self.body, + self.store, InferenceTyDiagnosticSource::Body, LifetimeElisionKind::Infer, f, @@ -1324,7 +1388,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { pub(crate) fn make_body_ty(&mut self, type_ref: TypeRefId) -> Ty<'db> { self.make_ty( type_ref, - self.body, + self.store, InferenceTyDiagnosticSource::Body, LifetimeElisionKind::Infer, ) @@ -1332,7 +1396,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { pub(crate) fn make_body_const(&mut self, const_ref: ConstRef, ty: Ty<'db>) -> Const<'db> { let const_ = self.with_ty_lowering( - self.body, + self.store, InferenceTyDiagnosticSource::Body, LifetimeElisionKind::Infer, |ctx| ctx.lower_const(const_ref, ty), @@ -1342,7 +1406,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { pub(crate) fn make_path_as_body_const(&mut self, path: &Path, ty: Ty<'db>) -> Const<'db> { let const_ = self.with_ty_lowering( - self.body, + self.store, InferenceTyDiagnosticSource::Body, LifetimeElisionKind::Infer, |ctx| ctx.lower_path_as_const(path, ty), @@ -1356,7 +1420,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { pub(crate) fn make_body_lifetime(&mut self, lifetime_ref: LifetimeRefId) -> Region<'db> { let lt = self.with_ty_lowering( - self.body, + self.store, InferenceTyDiagnosticSource::Body, LifetimeElisionKind::Infer, |ctx| ctx.lower_lifetime(lifetime_ref), @@ -1571,7 +1635,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { let mut ctx = TyLoweringContext::new( self.db, &self.resolver, - &self.body.store, + self.store, &self.diagnostics, InferenceTyDiagnosticSource::Body, self.generic_def, diff --git a/crates/hir-ty/src/infer/closure/analysis.rs b/crates/hir-ty/src/infer/closure/analysis.rs index 5a3eba1a71..2b9d3a7f5d 100644 --- a/crates/hir-ty/src/infer/closure/analysis.rs +++ b/crates/hir-ty/src/infer/closure/analysis.rs @@ -346,7 +346,7 @@ impl<'db> InferenceContext<'_, 'db> { if path.type_anchor().is_some() { return None; } - let hygiene = self.body.expr_or_pat_path_hygiene(id); + let hygiene = self.store.expr_or_pat_path_hygiene(id); self.resolver.resolve_path_in_value_ns_fully(self.db, path, hygiene).and_then(|result| { match result { ValueNs::LocalBinding(binding) => { @@ -365,7 +365,7 @@ impl<'db> InferenceContext<'_, 'db> { /// Changes `current_capture_span_stack` to contain the stack of spans for this expr. fn place_of_expr_without_adjust(&mut self, tgt_expr: ExprId) -> Option<HirPlace> { self.current_capture_span_stack.clear(); - match &self.body[tgt_expr] { + match &self.store[tgt_expr] { Expr::Path(p) => { let resolver_guard = self.resolver.update_to_inner_scope(self.db, self.owner, tgt_expr); @@ -416,7 +416,7 @@ impl<'db> InferenceContext<'_, 'db> { let mut actual_truncate_to = 0; for &span in &*span_stack { actual_truncate_to += 1; - if !span.is_ref_span(self.body) { + if !span.is_ref_span(self.store) { remained -= 1; if remained == 0 { break; @@ -424,7 +424,7 @@ impl<'db> InferenceContext<'_, 'db> { } } if actual_truncate_to < span_stack.len() - && span_stack[actual_truncate_to].is_ref_span(self.body) + && span_stack[actual_truncate_to].is_ref_span(self.store) { // Include the ref operator if there is one, we will fix it later (in `strip_captures_ref_span()`) if it's incorrect. actual_truncate_to += 1; @@ -533,7 +533,7 @@ impl<'db> InferenceContext<'_, 'db> { } fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) { - match &self.body[tgt_expr] { + match &self.store[tgt_expr] { Expr::OffsetOf(_) => (), Expr::InlineAsm(e) => e.operands.iter().for_each(|(_, op)| match op { AsmOperand::In { expr, .. } @@ -733,7 +733,7 @@ impl<'db> InferenceContext<'_, 'db> { self.consume_with_pat(rhs_place, target); self.inside_assignment = false; } - None => self.body.walk_pats(target, &mut |pat| match &self.body[pat] { + None => self.store.walk_pats(target, &mut |pat| match &self.store[pat] { Pat::Path(path) => self.mutate_path_pat(path, pat), &Pat::Expr(expr) => { let place = self.place_of_expr(expr); @@ -775,7 +775,7 @@ impl<'db> InferenceContext<'_, 'db> { update_result: &mut impl FnMut(CaptureKind), mut for_mut: BorrowKind, ) { - match &self.body[p] { + match &self.store[p] { Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing @@ -819,13 +819,13 @@ impl<'db> InferenceContext<'_, 'db> { if self.result.pat_adjustments.get(&p).is_some_and(|it| !it.is_empty()) { for_mut = BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }; } - self.body.walk_pats_shallow(p, |p| self.walk_pat_inner(p, update_result, for_mut)); + self.store.walk_pats_shallow(p, |p| self.walk_pat_inner(p, update_result, for_mut)); } fn is_upvar(&self, place: &HirPlace) -> bool { if let Some(c) = self.current_closure { let InternedClosure(_, root) = self.db.lookup_intern_closure(c); - return self.body.is_binding_upvar(place.local, root); + return self.store.is_binding_upvar(place.local, root); } false } @@ -866,7 +866,7 @@ impl<'db> InferenceContext<'_, 'db> { &self.table.infer_ctxt, self.table.param_env, ty, - self.owner.module(self.db).krate(self.db), + self.owner.krate(self.db), ); if ty.is_raw_ptr() || ty.is_union() { capture.kind = CaptureKind::ByRef(BorrowKind::Shared); @@ -938,7 +938,7 @@ impl<'db> InferenceContext<'_, 'db> { self.current_capture_span_stack .extend((0..adjustments_count).map(|_| MirSpan::PatId(tgt_pat))); 'reset_span_stack: { - match &self.body[tgt_pat] { + match &self.store[tgt_pat] { Pat::Missing | Pat::Wild => (), Pat::Tuple { args, ellipsis } => { let (al, ar) = args.split_at(ellipsis.map_or(args.len(), |it| it as usize)); @@ -1089,7 +1089,7 @@ impl<'db> InferenceContext<'_, 'db> { fn analyze_closure(&mut self, closure: InternedClosureId) -> FnTrait { let InternedClosure(_, root) = self.db.lookup_intern_closure(closure); self.current_closure = Some(closure); - let Expr::Closure { body, capture_by, .. } = &self.body[root] else { + let Expr::Closure { body, capture_by, .. } = &self.store[root] else { unreachable!("Closure expression id is always closure"); }; self.consume_expr(*body); @@ -1133,7 +1133,7 @@ impl<'db> InferenceContext<'_, 'db> { for capture in &mut captures { if matches!(capture.kind, CaptureKind::ByValue) { for span_stack in &mut capture.span_stacks { - if span_stack[span_stack.len() - 1].is_ref_span(self.body) { + if span_stack[span_stack.len() - 1].is_ref_span(self.store) { span_stack.truncate(span_stack.len() - 1); } } @@ -1149,7 +1149,7 @@ impl<'db> InferenceContext<'_, 'db> { let kind = self.analyze_closure(closure); for (derefed_callee, callee_ty, params, expr) in exprs { - if let &Expr::Call { callee, .. } = &self.body[expr] { + if let &Expr::Call { callee, .. } = &self.store[expr] { let mut adjustments = self.result.expr_adjustments.remove(&callee).unwrap_or_default().into_vec(); self.write_fn_trait_method_resolution( diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs index e79868f4ae..47a7049248 100644 --- a/crates/hir-ty/src/infer/coerce.rs +++ b/crates/hir-ty/src/infer/coerce.rs @@ -1718,6 +1718,9 @@ fn coerce<'db>( fn is_capturing_closure(db: &dyn HirDatabase, closure: InternedClosureId) -> bool { let InternedClosure(owner, expr) = closure.loc(db); - upvars_mentioned(db, owner) + let Some(body_owner) = owner.as_def_with_body() else { + return false; + }; + upvars_mentioned(db, body_owner) .is_some_and(|upvars| upvars.get(&expr).is_some_and(|upvars| !upvars.is_empty())) } diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 45b181eff8..2e80bad614 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -155,7 +155,7 @@ impl<'db> InferenceContext<'_, 'db> { /// it is matching against. This is used to determine whether we should /// perform `NeverToAny` coercions. fn pat_guaranteed_to_constitute_read_for_never(&self, pat: PatId) -> bool { - match &self.body[pat] { + match &self.store[pat] { // Does not constitute a read. Pat::Wild => false, @@ -197,25 +197,25 @@ impl<'db> InferenceContext<'_, 'db> { // FIXME(tschottdorf): this is problematic as the HIR is being scraped, but // ref bindings are be implicit after #42640 (default match binding modes). See issue #44848. fn contains_explicit_ref_binding(&self, pat: PatId) -> bool { - if let Pat::Bind { id, .. } = self.body[pat] - && matches!(self.body[id].mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) + if let Pat::Bind { id, .. } = self.store[pat] + && matches!(self.store[id].mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) { return true; } let mut result = false; - self.body.walk_pats_shallow(pat, |pat| result |= self.contains_explicit_ref_binding(pat)); + self.store.walk_pats_shallow(pat, |pat| result |= self.contains_explicit_ref_binding(pat)); result } fn is_syntactic_place_expr(&self, expr: ExprId) -> bool { - match &self.body[expr] { + match &self.store[expr] { // Lang item paths cannot currently be local variables or statics. Expr::Path(Path::LangItem(_, _)) => false, Expr::Path(Path::Normal(path)) => path.type_anchor.is_none(), Expr::Path(path) => self .resolver - .resolve_path_in_value_ns_fully(self.db, path, self.body.expr_path_hygiene(expr)) + .resolve_path_in_value_ns_fully(self.db, path, self.store.expr_path_hygiene(expr)) .is_none_or(|res| matches!(res, ValueNs::LocalBinding(_) | ValueNs::StaticId(_))), Expr::Underscore => true, Expr::UnaryOp { op: UnaryOp::Deref, .. } => true, @@ -311,7 +311,7 @@ impl<'db> InferenceContext<'_, 'db> { ) -> Ty<'db> { self.db.unwind_if_revision_cancelled(); - let expr = &self.body[tgt_expr]; + let expr = &self.store[tgt_expr]; tracing::trace!(?expr); let ty = match expr { Expr::Missing => self.err_ty(), @@ -717,7 +717,7 @@ impl<'db> InferenceContext<'_, 'db> { // instantiations in RHS can be coerced to it. Note that this // cannot happen in destructuring assignments because of how // they are desugared. - let lhs_ty = match &self.body[target] { + let lhs_ty = match &self.store[target] { // LHS of assignment doesn't constitute reads. &Pat::Expr(expr) => { Some(self.infer_expr(expr, &Expectation::none(), ExprIsRead::No)) @@ -728,7 +728,7 @@ impl<'db> InferenceContext<'_, 'db> { let resolution = self.resolver.resolve_path_in_value_ns_fully( self.db, path, - self.body.pat_path_hygiene(target), + self.store.pat_path_hygiene(target), ); self.resolver.reset_to_guard(resolver_guard); @@ -1351,7 +1351,7 @@ impl<'db> InferenceContext<'_, 'db> { ExprIsRead::Yes, ); let usize = self.types.types.usize; - let len = match self.body[repeat] { + let len = match self.store[repeat] { Expr::Underscore => { self.write_expr_ty(repeat, usize); self.table.next_const_var() @@ -1491,7 +1491,7 @@ impl<'db> InferenceContext<'_, 'db> { } else { ExprIsRead::No }; - let ty = if contains_explicit_ref_binding(this.body, *pat) { + let ty = if contains_explicit_ref_binding(this.store, *pat) { this.infer_expr( *expr, &Expectation::has_type(decl_ty), @@ -2117,7 +2117,7 @@ impl<'db> InferenceContext<'_, 'db> { // the return value of an argument-position async block to an argument-position // closure wrapped in a block. // See <https://github.com/rust-lang/rust/issues/112225>. - let is_closure = if let Expr::Closure { closure_kind, .. } = self.body[*arg] { + let is_closure = if let Expr::Closure { closure_kind, .. } = self.store[*arg] { !matches!(closure_kind, ClosureKind::Coroutine(_)) } else { false diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs index 45fa141b6d..bfe43fc928 100644 --- a/crates/hir-ty/src/infer/mutability.rs +++ b/crates/hir-ty/src/infer/mutability.rs @@ -14,8 +14,8 @@ use crate::{ }; impl<'db> InferenceContext<'_, 'db> { - pub(crate) fn infer_mut_body(&mut self) { - self.infer_mut_expr(self.body.body_expr, Mutability::Not); + pub(crate) fn infer_mut_body(&mut self, body_expr: ExprId) { + self.infer_mut_expr(body_expr, Mutability::Not); } fn infer_mut_expr(&mut self, tgt_expr: ExprId, mut mutability: Mutability) { @@ -52,7 +52,7 @@ impl<'db> InferenceContext<'_, 'db> { } fn infer_mut_expr_without_adjust(&mut self, tgt_expr: ExprId, mutability: Mutability) { - match &self.body[tgt_expr] { + match &self.store[tgt_expr] { Expr::Missing => (), Expr::InlineAsm(e) => { e.operands.iter().for_each(|(_, op)| match op { @@ -173,7 +173,7 @@ impl<'db> InferenceContext<'_, 'db> { self.infer_mut_expr(*rhs, Mutability::Not); } &Expr::Assignment { target, value } => { - self.body.walk_pats(target, &mut |pat| match self.body[pat] { + self.store.walk_pats(target, &mut |pat| match self.store[pat] { Pat::Expr(expr) => self.infer_mut_expr(expr, Mutability::Mut), Pat::ConstBlock(block) => self.infer_mut_expr(block, Mutability::Not), _ => {} @@ -220,8 +220,8 @@ impl<'db> InferenceContext<'_, 'db> { /// `let (ref x0, ref x1) = *it;` we should use `Deref`. fn pat_bound_mutability(&self, pat: PatId) -> Mutability { let mut r = Mutability::Not; - self.body.walk_bindings_in_pat(pat, |b| { - if self.body[b].mode == BindingAnnotation::RefMut { + self.store.walk_bindings_in_pat(pat, |b| { + if self.store[b].mode == BindingAnnotation::RefMut { r = Mutability::Mut; } }); diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index 87fd0dace3..0b6c9977f0 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -3,8 +3,8 @@ use std::{cmp, iter}; use hir_def::{ - HasModule, - expr_store::{Body, path::Path}, + HasModule as _, + expr_store::{ExpressionStore, path::Path}, hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, Literal, Pat, PatId}, }; use hir_expand::name::Name; @@ -260,14 +260,14 @@ impl<'db> InferenceContext<'_, 'db> { ) -> Ty<'db> { let mut expected = self.table.structurally_resolve_type(expected); - if matches!(&self.body[pat], Pat::Ref { .. }) || self.inside_assignment { + if matches!(&self.store[pat], Pat::Ref { .. }) || self.inside_assignment { cov_mark::hit!(match_ergonomics_ref); // When you encounter a `&pat` pattern, reset to Move. // This is so that `w` is by value: `let (_, &w) = &(1, &2);` // Destructuring assignments also reset the binding mode and // don't do match ergonomics. default_bm = BindingMode::Move; - } else if self.is_non_ref_pat(self.body, pat) { + } else if self.is_non_ref_pat(self.store, pat) { let mut pat_adjustments = Vec::new(); while let TyKind::Ref(_lifetime, inner, mutability) = expected.kind() { pat_adjustments.push(expected.store()); @@ -289,7 +289,7 @@ impl<'db> InferenceContext<'_, 'db> { let default_bm = default_bm; let expected = expected; - let ty = match &self.body[pat] { + let ty = match &self.store[pat] { Pat::Tuple { args, ellipsis } => { self.infer_tuple_pat_like(pat, expected, default_bm, *ellipsis, args, decl) } @@ -485,7 +485,7 @@ impl<'db> InferenceContext<'_, 'db> { expected: Ty<'db>, decl: Option<DeclContext>, ) -> Ty<'db> { - let Binding { mode, .. } = self.body[binding]; + let Binding { mode, .. } = self.store[binding]; let mode = if mode == BindingAnnotation::Unannotated { default_bm } else { @@ -569,7 +569,7 @@ impl<'db> InferenceContext<'_, 'db> { fn infer_lit_pat(&mut self, expr: ExprId, expected: Ty<'db>) -> Ty<'db> { // Like slice patterns, byte string patterns can denote both `&[u8; N]` and `&[u8]`. - if let Expr::Literal(Literal::ByteString(_)) = self.body[expr] + if let Expr::Literal(Literal::ByteString(_)) = self.store[expr] && let TyKind::Ref(_, inner, _) = expected.kind() { let inner = self.table.try_structurally_resolve_type(inner); @@ -590,14 +590,14 @@ impl<'db> InferenceContext<'_, 'db> { self.infer_expr(expr, &Expectation::has_type(expected), ExprIsRead::Yes) } - fn is_non_ref_pat(&mut self, body: &hir_def::expr_store::Body, pat: PatId) -> bool { - match &body[pat] { + fn is_non_ref_pat(&mut self, store: &hir_def::expr_store::ExpressionStore, pat: PatId) -> bool { + match &store[pat] { Pat::Tuple { .. } | Pat::TupleStruct { .. } | Pat::Record { .. } | Pat::Range { .. } | Pat::Slice { .. } => true, - Pat::Or(pats) => pats.iter().all(|p| self.is_non_ref_pat(body, *p)), + Pat::Or(pats) => pats.iter().all(|p| self.is_non_ref_pat(store, *p)), Pat::Path(path) => { // A const is a reference pattern, but other value ns things aren't (see #16131). let resolved = self.resolve_value_path_inner(path, pat.into(), true); @@ -605,7 +605,7 @@ impl<'db> InferenceContext<'_, 'db> { } Pat::ConstBlock(..) => false, Pat::Lit(expr) => !matches!( - body[*expr], + store[*expr], Expr::Literal(Literal::String(..) | Literal::CString(..) | Literal::ByteString(..)) ), Pat::Wild @@ -670,10 +670,10 @@ impl<'db> InferenceContext<'_, 'db> { } } -pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool { +pub(super) fn contains_explicit_ref_binding(store: &ExpressionStore, pat_id: PatId) -> bool { let mut res = false; - body.walk_pats(pat_id, &mut |pat| { - res |= matches!(body[pat], Pat::Bind { id, .. } if matches!(body[id].mode, BindingAnnotation::Ref | BindingAnnotation::RefMut)); + store.walk_pats(pat_id, &mut |pat| { + res |= matches!(store[pat], Pat::Bind { id, .. } if matches!(store[id].mode, BindingAnnotation::Ref | BindingAnnotation::RefMut)); }); res } diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs index 40c6fdf3cc..17d4901123 100644 --- a/crates/hir-ty/src/infer/path.rs +++ b/crates/hir-ty/src/infer/path.rs @@ -136,7 +136,7 @@ impl<'db> InferenceContext<'_, 'db> { let mut ctx = TyLoweringContext::new( self.db, &self.resolver, - self.body, + self.store, &self.diagnostics, InferenceTyDiagnosticSource::Body, self.generic_def, @@ -159,7 +159,7 @@ impl<'db> InferenceContext<'_, 'db> { let ty = self.table.process_user_written_ty(ty); self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))? } else { - let hygiene = self.body.expr_or_pat_path_hygiene(id); + let hygiene = self.store.expr_or_pat_path_hygiene(id); // FIXME: report error, unresolved first path segment let value_or_partial = path_ctx.resolve_path_in_value_ns(hygiene)?; diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index 2057159c46..77f76c97b8 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -3,7 +3,7 @@ use std::fmt; use base_db::Crate; -use hir_def::{AdtId, DefWithBodyId, GenericParamId}; +use hir_def::{AdtId, ExpressionStoreOwner, GenericParamId}; use hir_expand::name::Name; use intern::sym; use rustc_hash::FxHashSet; @@ -147,7 +147,7 @@ impl<'db> InferenceTable<'db> { db: &'db dyn HirDatabase, trait_env: ParamEnv<'db>, krate: Crate, - owner: Option<DefWithBodyId>, + owner: Option<ExpressionStoreOwner>, ) -> Self { let interner = DbInterner::new_with(db, krate); let typing_mode = match owner { diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index 525100439f..a325b2e174 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -333,7 +333,10 @@ pub fn layout_of_ty_query( } TyKind::Closure(id, args) => { let def = db.lookup_intern_closure(id.0); - let infer = InferenceResult::for_body(db, def.0); + let Some(body_owner) = def.0.as_def_with_body() else { + return Err(LayoutError::HasErrorType); + }; + let infer = InferenceResult::for_body(db, body_owner); let (captures, _) = infer.closure_info(id.0); let fields = captures .iter() diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index 49594f34fd..45500cfd22 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -279,11 +279,12 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } pub(crate) fn lower_const(&mut self, const_ref: ConstRef, const_type: Ty<'db>) -> Const<'db> { - let const_ref = &self.store[const_ref.expr]; - match const_ref { - hir_def::hir::Expr::Path(path) => { - self.path_to_const(path).unwrap_or_else(|| unknown_const(const_type)) - } + let expr_id = const_ref.expr; + let expr = &self.store[expr_id]; + match expr { + hir_def::hir::Expr::Path(path) => self + .path_to_const(path) + .unwrap_or_else(|| Const::new(self.interner, ConstKind::Error(ErrorGuaranteed))), hir_def::hir::Expr::Literal(literal) => { intern_const_ref(self.db, literal, const_type, self.resolver.krate()) } @@ -300,20 +301,74 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { self.resolver.krate(), ) } else { - unknown_const(const_type) + Const::new(self.interner, ConstKind::Error(ErrorGuaranteed)) } } // For unsigned integers, chars, bools, etc., negation is not meaningful - _ => unknown_const(const_type), + _ => Const::new(self.interner, ConstKind::Error(ErrorGuaranteed)), } } else { - unknown_const(const_type) + // Complex negation expression (e.g. `-N` where N is a const param) + self.lower_const_as_unevaluated(expr_id, const_type) } } - _ => unknown_const(const_type), + hir_def::hir::Expr::Underscore => { + Const::new(self.interner, ConstKind::Error(ErrorGuaranteed)) + } + // Any other complex expression becomes an unevaluated anonymous const. + _ => self.lower_const_as_unevaluated(expr_id, const_type), } } + /// Lower a complex const expression to an `UnevaluatedConst` backed by an `AnonConstId`. + /// + /// The `expected_ty_ref` is `None` for array lengths (implicitly `usize`) or + /// `Some(type_ref_id)` for const generic arguments where the expected type comes + /// from the const parameter declaration. + fn lower_const_as_unevaluated( + &mut self, + _expr: hir_def::hir::ExprId, + _expected_ty: Ty<'db>, + ) -> Const<'db> { + // /// Build the identity generic args for the current generic context. + // /// + // /// This maps each generic parameter to itself (as a `ParamTy`, `ParamConst`, + // /// or `EarlyParamRegion`), which is the correct substitution when creating + // /// an `UnevaluatedConst` during type lowering — the anon const inherits the + // /// parent's generics and they haven't been substituted yet. + // fn current_generic_args(&self) -> GenericArgs<'db> { + // let generics = self.generics(); + // let interner = self.interner; + // GenericArgs::new_from_iter( + // interner, + // generics.iter_id().enumerate().map(|(index, id)| match id { + // GenericParamId::TypeParamId(id) => { + // GenericArg::from(Ty::new_param(interner, id, index as u32)) + // } + // GenericParamId::ConstParamId(id) => GenericArg::from(Const::new_param( + // interner, + // ParamConst { id, index: index as u32 }, + // )), + // GenericParamId::LifetimeParamId(id) => GenericArg::from(Region::new_early_param( + // interner, + // EarlyParamRegion { id, index: index as u32 }, + // )), + // }), + // ) + // } + // let loc = AnonConstLoc { owner: self.def, expr }; + // let id = loc.intern(self.db); + // let args = self.current_generic_args(); + // Const::new( + // self.interner, + // ConstKind::Unevaluated(UnevaluatedConst::new( + // GeneralConstId::AnonConstId(id).into(), + // args, + // )), + // ) + Const::new(self.interner, ConstKind::Error(ErrorGuaranteed)) + } + pub(crate) fn path_to_const(&mut self, path: &Path) -> Option<Const<'db>> { match self.resolver.resolve_path_in_value_ns_fully(self.db, path, HygieneId::ROOT) { Some(ValueNs::GenericParam(p)) => { diff --git a/crates/hir-ty/src/method_resolution/confirm.rs b/crates/hir-ty/src/method_resolution/confirm.rs index 0024ca16a5..ec589085a8 100644 --- a/crates/hir-ty/src/method_resolution/confirm.rs +++ b/crates/hir-ty/src/method_resolution/confirm.rs @@ -456,7 +456,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { substs_from_args_and_bindings( self.db(), - self.ctx.body, + self.ctx.store, generic_args, self.candidate.into(), true, diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs index bf17c78468..a8865cd54e 100644 --- a/crates/hir-ty/src/mir.rs +++ b/crates/hir-ty/src/mir.rs @@ -6,7 +6,7 @@ use base_db::Crate; use either::Either; use hir_def::{ DefWithBodyId, FieldId, StaticId, TupleFieldId, UnionId, VariantId, - expr_store::Body, + expr_store::ExpressionStore, hir::{BindingAnnotation, BindingId, Expr, ExprId, Ordering, PatId}, }; use la_arena::{Arena, ArenaMap, Idx, RawIdx}; @@ -1210,12 +1210,12 @@ pub enum MirSpan { } impl MirSpan { - pub fn is_ref_span(&self, body: &Body) -> bool { + pub fn is_ref_span(&self, store: &ExpressionStore) -> bool { match *self { - MirSpan::ExprId(expr) => matches!(body[expr], Expr::Ref { .. }), + MirSpan::ExprId(expr) => matches!(store[expr], Expr::Ref { .. }), // FIXME: Figure out if this is correct wrt. match ergonomics. MirSpan::BindingId(binding) => { - matches!(body[binding].mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) + matches!(store[binding].mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) } MirSpan::PatId(_) | MirSpan::SelfParam | MirSpan::Unknown => false, } diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs index dece61a57d..2dcc2d1062 100644 --- a/crates/hir-ty/src/mir/borrowck.rs +++ b/crates/hir-ty/src/mir/borrowck.rs @@ -8,7 +8,7 @@ use std::iter; use hir_def::{DefWithBodyId, HasModule}; use la_arena::ArenaMap; use rustc_hash::FxHashMap; -use rustc_type_ir::inherent::GenericArgs as _; +use rustc_type_ir::inherent::{GenericArgs as _, Ty as _}; use stdx::never; use triomphe::Arc; @@ -18,7 +18,7 @@ use crate::{ display::DisplayTarget, mir::OperandKind, next_solver::{ - DbInterner, GenericArgs, ParamEnv, StoredTy, Ty, TypingMode, + DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, StoredTy, Ty, TypingMode, infer::{DbInternerInferExt, InferCtxt}, }, }; @@ -121,11 +121,14 @@ fn make_fetch_closure_field<'db>( db: &'db dyn HirDatabase, ) -> impl FnOnce(InternedClosureId, GenericArgs<'db>, usize) -> Ty<'db> + use<'db> { |c: InternedClosureId, subst: GenericArgs<'db>, f: usize| { - let InternedClosure(def, _) = db.lookup_intern_closure(c); + let InternedClosure(owner, _) = db.lookup_intern_closure(c); + let interner = DbInterner::new_no_crate(db); + let Some(def) = owner.as_def_with_body() else { + return Ty::new_error(interner, ErrorGuaranteed); + }; let infer = InferenceResult::for_body(db, def); let (captures, _) = infer.closure_info(c); let parent_subst = subst.as_closure().parent_args(); - let interner = DbInterner::new_no_crate(db); captures.get(f).expect("broken closure field").ty.get().instantiate(interner, parent_subst) } } diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index ec0723c3f8..a85b3ef50a 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -730,7 +730,10 @@ impl<'db> Evaluator<'db> { self.param_env.param_env, ty, |c, subst, f| { - let InternedClosure(def, _) = self.db.lookup_intern_closure(c); + let InternedClosure(owner, _) = self.db.lookup_intern_closure(c); + let Some(def) = owner.as_def_with_body() else { + return Ty::new_error(self.interner(), ErrorGuaranteed); + }; let infer = InferenceResult::for_body(self.db, def); let (captures, _) = infer.closure_info(c); let parent_subst = subst.as_closure().parent_args(); @@ -1954,6 +1957,9 @@ impl<'db> Evaluator<'db> { MirEvalError::ConstEvalError(name, Box::new(e)) })? } + GeneralConstId::AnonConstId(_) => { + not_supported!("anonymous const evaluation") + } }; if let ConstKind::Value(value) = result_owner.kind() { break 'b value; diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index 76c8701ea2..a0dd3b5846 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -152,7 +152,10 @@ impl<'db> Evaluator<'db> { not_supported!("wrong arg count for clone"); }; let addr = Address::from_bytes(arg.get(self)?)?; - let InternedClosure(closure_owner, _) = self.db.lookup_intern_closure(id.0); + let InternedClosure(owner, _) = self.db.lookup_intern_closure(id.0); + let Some(closure_owner) = owner.as_def_with_body() else { + not_supported!("closure in non-body context"); + }; let infer = InferenceResult::for_body(self.db, closure_owner); let (captures, _) = infer.closure_info(id.0); let layout = self.layout(self_ty)?; diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 2e849bcf3a..269d8729ba 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -1546,6 +1546,9 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { MirLowerError::ConstEvalError(name.into(), Box::new(e)) })? } + GeneralConstId::AnonConstId(_) => { + return Err(MirLowerError::IncompleteExpr); + } } }; let ty = self @@ -1553,6 +1556,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { .value_ty(match const_id { GeneralConstId::ConstId(id) => id.into(), GeneralConstId::StaticId(id) => id.into(), + GeneralConstId::AnonConstId(_) => unreachable!("handled above"), }) .unwrap() .instantiate(self.interner(), subst); @@ -2106,8 +2110,10 @@ pub fn mir_body_for_closure_query<'db>( closure: InternedClosureId, ) -> Result<'db, Arc<MirBody>> { let InternedClosure(owner, expr) = db.lookup_intern_closure(closure); - let body = db.body(owner); - let infer = InferenceResult::for_body(db, owner); + let body_owner = + owner.as_def_with_body().expect("MIR lowering should only happen for body-owned closures"); + let body = db.body(body_owner); + let infer = InferenceResult::for_body(db, body_owner); let Expr::Closure { args, body: root, .. } = &body[expr] else { implementation_error!("closure expression is not closure"); }; @@ -2115,7 +2121,7 @@ pub fn mir_body_for_closure_query<'db>( implementation_error!("closure expression is not closure"); }; let (captures, kind) = infer.closure_info(closure); - let mut ctx = MirLowerCtx::new(db, owner, &body.store, infer); + let mut ctx = MirLowerCtx::new(db, body_owner, &body.store, infer); // 0 is return local ctx.result.locals.alloc(Local { ty: infer.expr_ty(*root).store() }); let closure_local = ctx.result.locals.alloc(Local { @@ -2138,7 +2144,7 @@ pub fn mir_body_for_closure_query<'db>( }); ctx.result.param_locals.push(closure_local); let sig = ctx.interner().signature_unclosure(substs.as_closure().sig(), Safety::Safe); - let resolver_guard = ctx.resolver.update_to_inner_scope(db, owner, expr); + let resolver_guard = ctx.resolver.update_to_inner_scope(db, body_owner, expr); let current = ctx.lower_params_and_bindings( args.iter().zip(sig.skip_binder().inputs().iter()).map(|(it, y)| (*it, *y)), None, diff --git a/crates/hir-ty/src/next_solver/def_id.rs b/crates/hir-ty/src/next_solver/def_id.rs index aa6caefc4a..f725af320d 100644 --- a/crates/hir-ty/src/next_solver/def_id.rs +++ b/crates/hir-ty/src/next_solver/def_id.rs @@ -1,9 +1,9 @@ //! Definition of `SolverDefId` use hir_def::{ - AdtId, AttrDefId, BuiltinDeriveImplId, CallableDefId, ConstId, DefWithBodyId, EnumId, - EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId, StaticId, StructId, TraitId, - TypeAliasId, UnionId, + AdtId, AnonConstId, AttrDefId, BuiltinDeriveImplId, CallableDefId, ConstId, DefWithBodyId, + EnumId, EnumVariantId, ExpressionStoreOwner, FunctionId, GeneralConstId, GenericDefId, ImplId, + StaticId, StructId, TraitId, TypeAliasId, UnionId, }; use rustc_type_ir::inherent; use stdx::impl_from; @@ -12,13 +12,13 @@ use crate::db::{InternedClosureId, InternedCoroutineId, InternedOpaqueTyId}; use super::DbInterner; -#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)] +#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash)] pub enum Ctor { Struct(StructId), Enum(EnumVariantId), } -#[derive(PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)] +#[derive(PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash)] pub enum SolverDefId { AdtId(AdtId), ConstId(ConstId), @@ -26,13 +26,13 @@ pub enum SolverDefId { ImplId(ImplId), BuiltinDeriveImplId(BuiltinDeriveImplId), StaticId(StaticId), + AnonConstId(AnonConstId), TraitId(TraitId), TypeAliasId(TypeAliasId), InternedClosureId(InternedClosureId), InternedCoroutineId(InternedCoroutineId), InternedOpaqueTyId(InternedOpaqueTyId), EnumVariantId(EnumVariantId), - // FIXME(next-solver): Do we need the separation of `Ctor`? It duplicates some variants. Ctor(Ctor), } @@ -88,6 +88,7 @@ impl std::fmt::Debug for SolverDefId { )) .finish() } + SolverDefId::AnonConstId(id) => f.debug_tuple("AnonConstId").field(&id).finish(), SolverDefId::Ctor(Ctor::Struct(id)) => { f.debug_tuple("Ctor").field(&db.struct_signature(id).name.as_str()).finish() } @@ -112,6 +113,7 @@ impl_from!( ImplId, BuiltinDeriveImplId, StaticId, + AnonConstId, TraitId, TypeAliasId, InternedClosureId, @@ -136,12 +138,22 @@ impl From<GenericDefId> for SolverDefId { } } +impl From<ExpressionStoreOwner> for SolverDefId { + fn from(value: ExpressionStoreOwner) -> Self { + match value { + ExpressionStoreOwner::Signature(generic_def_id) => generic_def_id.into(), + ExpressionStoreOwner::Body(def_with_body_id) => def_with_body_id.into(), + } + } +} + impl From<GeneralConstId> for SolverDefId { #[inline] fn from(value: GeneralConstId) -> Self { match value { GeneralConstId::ConstId(const_id) => SolverDefId::ConstId(const_id), GeneralConstId::StaticId(static_id) => SolverDefId::StaticId(static_id), + GeneralConstId::AnonConstId(anon_const_id) => SolverDefId::AnonConstId(anon_const_id), } } } @@ -176,7 +188,8 @@ impl TryFrom<SolverDefId> for AttrDefId { SolverDefId::BuiltinDeriveImplId(_) | SolverDefId::InternedClosureId(_) | SolverDefId::InternedCoroutineId(_) - | SolverDefId::InternedOpaqueTyId(_) => Err(()), + | SolverDefId::InternedOpaqueTyId(_) + | SolverDefId::AnonConstId(_) => Err(()), } } } @@ -199,6 +212,7 @@ impl TryFrom<SolverDefId> for DefWithBodyId { | SolverDefId::InternedClosureId(_) | SolverDefId::InternedCoroutineId(_) | SolverDefId::Ctor(Ctor::Struct(_)) + | SolverDefId::AnonConstId(_) | SolverDefId::AdtId(_) => return Err(()), }; Ok(id) @@ -222,6 +236,7 @@ impl TryFrom<SolverDefId> for GenericDefId { | SolverDefId::InternedOpaqueTyId(_) | SolverDefId::EnumVariantId(_) | SolverDefId::BuiltinDeriveImplId(_) + | SolverDefId::AnonConstId(_) | SolverDefId::Ctor(_) => return Err(()), }) } @@ -343,6 +358,7 @@ impl From<GeneralConstIdWrapper> for SolverDefId { match value.0 { GeneralConstId::ConstId(id) => SolverDefId::ConstId(id), GeneralConstId::StaticId(id) => SolverDefId::StaticId(id), + GeneralConstId::AnonConstId(id) => SolverDefId::AnonConstId(id), } } } @@ -353,6 +369,7 @@ impl TryFrom<SolverDefId> for GeneralConstIdWrapper { match value { SolverDefId::ConstId(it) => Ok(Self(it.into())), SolverDefId::StaticId(it) => Ok(Self(it.into())), + SolverDefId::AnonConstId(it) => Ok(Self(it.into())), _ => Err(()), } } diff --git a/crates/hir-ty/src/next_solver/interner.rs b/crates/hir-ty/src/next_solver/interner.rs index e17bdac68c..dba4e74730 100644 --- a/crates/hir-ty/src/next_solver/interner.rs +++ b/crates/hir-ty/src/next_solver/interner.rs @@ -10,8 +10,8 @@ pub use tls_db::{attach_db, attach_db_allow_change, with_attached_db}; use base_db::Crate; use hir_def::{ - AdtId, CallableDefId, DefWithBodyId, EnumVariantId, HasModule, ItemContainerId, StructId, - UnionId, VariantId, + AdtId, CallableDefId, DefWithBodyId, EnumVariantId, ExpressionStoreOwner, HasModule, + ItemContainerId, StructId, UnionId, VariantId, attrs::AttrFlags, lang_item::LangItems, signatures::{FieldData, FnFlags, ImplFlags, StructFlags, TraitFlags}, @@ -1193,7 +1193,8 @@ impl<'db> Interner for DbInterner<'db> { | SolverDefId::ImplId(_) | SolverDefId::BuiltinDeriveImplId(_) | SolverDefId::InternedClosureId(_) - | SolverDefId::InternedCoroutineId(_) => { + | SolverDefId::InternedCoroutineId(_) + | SolverDefId::AnonConstId(_) => { return VariancesOf::empty(self); } }; @@ -1260,7 +1261,9 @@ impl<'db> Interner for DbInterner<'db> { }, // rustc creates an `AnonConst` for consts, and evaluates them with CTFE (normalizing projections // via selection, similar to ours `find_matching_impl()`, and not with the trait solver), so mimic it. - SolverDefId::ConstId(_) => AliasTermKind::UnevaluatedConst, + SolverDefId::ConstId(_) | SolverDefId::AnonConstId(_) => { + AliasTermKind::UnevaluatedConst + } _ => unimplemented!("Unexpected alias: {:?}", alias.def_id), } } @@ -1308,22 +1311,10 @@ impl<'db> Interner for DbInterner<'db> { SolverDefId::TypeAliasId(it) => it.lookup(self.db()).container, SolverDefId::ConstId(it) => it.lookup(self.db()).container, SolverDefId::InternedClosureId(it) => { - return self - .db() - .lookup_intern_closure(it) - .0 - .as_generic_def_id(self.db()) - .unwrap() - .into(); + return self.db().lookup_intern_closure(it).0.generic_def(self.db()).into(); } SolverDefId::InternedCoroutineId(it) => { - return self - .db() - .lookup_intern_coroutine(it) - .0 - .as_generic_def_id(self.db()) - .unwrap() - .into(); + return self.db().lookup_intern_coroutine(it).0.generic_def(self.db()).into(); } SolverDefId::StaticId(_) | SolverDefId::AdtId(_) @@ -1332,7 +1323,8 @@ impl<'db> Interner for DbInterner<'db> { | SolverDefId::BuiltinDeriveImplId(_) | SolverDefId::EnumVariantId(..) | SolverDefId::Ctor(..) - | SolverDefId::InternedOpaqueTyId(..) => panic!(), + | SolverDefId::InternedOpaqueTyId(..) + | SolverDefId::AnonConstId(_) => panic!(), }; match container { @@ -1361,7 +1353,10 @@ impl<'db> Interner for DbInterner<'db> { // FIXME: Make this a query? I don't believe this can be accessed from bodies other than // the current infer query, except with revealed opaques - is it rare enough to not matter? let InternedCoroutine(owner, expr_id) = def_id.0.loc(self.db); - let body = self.db.body(owner); + let Some(body_owner) = owner.as_def_with_body() else { + return rustc_ast_ir::Movability::Static; + }; + let body = self.db.body(body_owner); let expr = &body[expr_id]; match *expr { hir_def::hir::Expr::Closure { closure_kind, .. } => match closure_kind { @@ -1795,6 +1790,7 @@ impl<'db> Interner for DbInterner<'db> { | SolverDefId::InternedCoroutineId(_) | SolverDefId::InternedOpaqueTyId(_) | SolverDefId::EnumVariantId(_) + | SolverDefId::AnonConstId(_) | SolverDefId::Ctor(_) => return None, }; module.block(self.db) @@ -2006,7 +2002,10 @@ impl<'db> Interner for DbInterner<'db> { // FIXME: Make this a query? I don't believe this can be accessed from bodies other than // the current infer query, except with revealed opaques - is it rare enough to not matter? let InternedCoroutine(owner, expr_id) = def_id.0.loc(self.db); - let body = self.db.body(owner); + let Some(body_owner) = owner.as_def_with_body() else { + return false; + }; + let body = self.db.body(body_owner); matches!( body[expr_id], hir_def::hir::Expr::Closure { @@ -2020,7 +2019,10 @@ impl<'db> Interner for DbInterner<'db> { // FIXME: Make this a query? I don't believe this can be accessed from bodies other than // the current infer query, except with revealed opaques - is it rare enough to not matter? let InternedCoroutine(owner, expr_id) = def_id.0.loc(self.db); - let body = self.db.body(owner); + let Some(body_owner) = owner.as_def_with_body() else { + return false; + }; + let body = self.db.body(body_owner); matches!( body[expr_id], hir_def::hir::Expr::Closure { closure_kind: hir_def::hir::ClosureKind::Async, .. } @@ -2154,8 +2156,10 @@ impl<'db> Interner for DbInterner<'db> { .. } ) { - let coroutine = - InternedCoroutineId::new(self.db, InternedCoroutine(def_id, expr_id)); + let coroutine = InternedCoroutineId::new( + self.db, + InternedCoroutine(ExpressionStoreOwner::Body(def_id), expr_id), + ); result.push(coroutine.into()); } }); diff --git a/crates/hir-ty/src/next_solver/solver.rs b/crates/hir-ty/src/next_solver/solver.rs index 15d6e2e451..54baa8ac41 100644 --- a/crates/hir-ty/src/next_solver/solver.rs +++ b/crates/hir-ty/src/next_solver/solver.rs @@ -18,7 +18,7 @@ use crate::next_solver::{ }; use super::{ - DbInterner, ErrorGuaranteed, GenericArg, SolverDefId, Span, + Const, DbInterner, ErrorGuaranteed, GenericArg, SolverDefId, Span, infer::{DbInternerInferExt, InferCtxt, canonical::instantiate::CanonicalExt}, }; @@ -256,6 +256,11 @@ impl<'db> SolverDelegate for SolverContext<'db> { let ec = self.cx().db.const_eval_static(c).ok()?; Some(ec) } + // TODO: Wire up const_eval_anon query in Phase 5. + // For now, return an error const so normalization resolves the + // unevaluated const to Error (matching the old behavior where + // complex expressions produced ConstKind::Error directly). + GeneralConstId::AnonConstId(_) => Some(Const::error(self.cx())), } } diff --git a/crates/hir-ty/src/next_solver/ty.rs b/crates/hir-ty/src/next_solver/ty.rs index 1173028a10..09b1585866 100644 --- a/crates/hir-ty/src/next_solver/ty.rs +++ b/crates/hir-ty/src/next_solver/ty.rs @@ -714,7 +714,7 @@ impl<'db> Ty<'db> { } TyKind::Coroutine(coroutine_id, _args) => { let InternedCoroutine(owner, _) = coroutine_id.0.loc(db); - let krate = owner.module(db).krate(db); + let krate = owner.krate(db); if let Some(future_trait) = hir_def::lang_item::lang_items(db, krate).Future { // This is only used by type walking. // Parameters will be walked outside, and projection predicate is not used. diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs index 67ab89f5ec..042f0568f3 100644 --- a/crates/hir-ty/src/tests.rs +++ b/crates/hir-ty/src/tests.rs @@ -16,9 +16,10 @@ mod traits; use base_db::{Crate, SourceDatabase}; use expect_test::Expect; use hir_def::{ - AssocItemId, DefWithBodyId, HasModule, Lookup, ModuleDefId, ModuleId, SyntheticSyntax, + AssocItemId, DefWithBodyId, GenericDefId, HasModule, Lookup, ModuleDefId, ModuleId, + SyntheticSyntax, db::DefDatabase, - expr_store::{Body, BodySourceMap}, + expr_store::{Body, BodySourceMap, ExpressionStore, ExpressionStoreSourceMap}, hir::{ExprId, Pat, PatId}, item_scope::ItemScope, nameres::DefMap, @@ -34,7 +35,6 @@ use syntax::{ ast::{self, AstNode, HasName}, }; use test_fixture::WithFixture; -use triomphe::Arc; use crate::{ InferenceResult, @@ -321,16 +321,20 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let mut buf = String::new(); let mut infer_def = |inference_result: &InferenceResult, - body: Arc<Body>, - body_source_map: Arc<BodySourceMap>, + store: &ExpressionStore, + source_map: &ExpressionStoreSourceMap, + self_param: Option<( + hir_def::hir::BindingId, + Option<InFile<hir_def::expr_store::SelfParamPtr>>, + )>, krate: Crate| { let display_target = DisplayTarget::from_crate(&db, krate); let mut types: Vec<(InFile<SyntaxNode>, Ty<'_>)> = Vec::new(); let mut mismatches: Vec<(InFile<SyntaxNode>, &TypeMismatch)> = Vec::new(); - if let Some(self_param) = body.self_param { - let ty = &inference_result.type_of_binding[self_param]; - if let Some(syntax_ptr) = body_source_map.self_param_syntax() { + if let Some((binding_id, syntax_ptr)) = self_param { + let ty = &inference_result.type_of_binding[binding_id]; + if let Some(syntax_ptr) = syntax_ptr { let root = db.parse_or_expand(syntax_ptr.file_id); let node = syntax_ptr.map(|ptr| ptr.to_node(&root).syntax().clone()); types.push((node, ty.as_ref())); @@ -338,10 +342,10 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { } for (pat, mut ty) in inference_result.type_of_pat.iter() { - if let Pat::Bind { id, .. } = body[pat] { + if let Pat::Bind { id, .. } = store[pat] { ty = &inference_result.type_of_binding[id]; } - let node = match body_source_map.pat_syntax(pat) { + let node = match source_map.pat_syntax(pat) { Ok(sp) => { let root = db.parse_or_expand(sp.file_id); sp.map(|ptr| ptr.to_node(&root).syntax().clone()) @@ -355,7 +359,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { } for (expr, ty) in inference_result.type_of_expr.iter() { - let node = match body_source_map.expr_syntax(expr) { + let node = match source_map.expr_syntax(expr) { Ok(sp) => { let root = db.parse_or_expand(sp.file_id); sp.map(|ptr| ptr.to_node(&root).syntax().clone()) @@ -414,16 +418,56 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let def_map = module.def_map(&db); let mut defs: Vec<(DefWithBodyId, Crate)> = Vec::new(); + let mut generic_defs: Vec<(GenericDefId, Crate)> = Vec::new(); visit_module(&db, def_map, module, &mut |it| { - let def = match it { - ModuleDefId::FunctionId(it) => it.into(), - ModuleDefId::EnumVariantId(it) => it.into(), - ModuleDefId::ConstId(it) => it.into(), - ModuleDefId::StaticId(it) => it.into(), - _ => return, - }; - defs.push((def, module.krate(&db))) + let krate = module.krate(&db); + match it { + ModuleDefId::FunctionId(it) => { + defs.push((it.into(), krate)); + generic_defs.push((it.into(), krate)); + } + ModuleDefId::EnumVariantId(it) => { + defs.push((it.into(), krate)); + } + ModuleDefId::ConstId(it) => { + defs.push((it.into(), krate)); + generic_defs.push((it.into(), krate)); + } + ModuleDefId::StaticId(it) => { + defs.push((it.into(), krate)); + generic_defs.push((it.into(), krate)); + } + ModuleDefId::AdtId(it) => { + generic_defs.push((it.into(), krate)); + } + ModuleDefId::TraitId(it) => { + generic_defs.push((it.into(), krate)); + } + ModuleDefId::TypeAliasId(it) => { + generic_defs.push((it.into(), krate)); + } + _ => {} + } }); + // Also collect impls + for impl_id in def_map[module].scope.impls() { + generic_defs.push((impl_id.into(), module.krate(&db))); + let impl_data = impl_id.impl_items(&db); + for &(_, item) in impl_data.items.iter() { + match item { + AssocItemId::FunctionId(it) => { + generic_defs.push((it.into(), module.krate(&db))); + } + AssocItemId::ConstId(it) => { + generic_defs.push((it.into(), module.krate(&db))); + } + AssocItemId::TypeAliasId(it) => { + generic_defs.push((it.into(), module.krate(&db))); + } + } + } + } + defs.sort_by_key(|(def, _)| match def { DefWithBodyId::FunctionId(it) => { let loc = it.lookup(&db); @@ -445,7 +489,20 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { for (def, krate) in defs { let (body, source_map) = db.body_with_source_map(def); let infer = InferenceResult::for_body(&db, def); - infer_def(infer, body, source_map, krate); + let self_param = body.self_param.map(|id| (id, source_map.self_param_syntax())); + infer_def(infer, &body, &source_map, self_param, krate); + } + + // Also infer signature const expressions (array lengths, const generic args, etc.) + generic_defs.dedup(); + for (def, krate) in generic_defs { + let (_, store, source_map) = db.generic_params_and_store_and_source_map(def); + // Skip if there are no const expressions in the signature + if store.const_expr_origins().is_empty() { + continue; + } + let infer = InferenceResult::for_signature(&db, def); + infer_def(infer, &store, &source_map, None, krate); } buf.truncate(buf.trim_end().len()); diff --git a/crates/hir-ty/src/tests/closure_captures.rs b/crates/hir-ty/src/tests/closure_captures.rs index f089120cd7..6e55641e56 100644 --- a/crates/hir-ty/src/tests/closure_captures.rs +++ b/crates/hir-ty/src/tests/closure_captures.rs @@ -40,7 +40,8 @@ fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expec captures_info.extend(infer.closure_info.iter().flat_map( |(closure_id, (captures, _))| { let closure = db.lookup_intern_closure(*closure_id); - let source_map = db.body_with_source_map(closure.0).1; + let body_owner = closure.0.as_def_with_body().unwrap(); + let source_map = db.body_with_source_map(body_owner).1; let closure_text_range = source_map .expr_syntax(closure.1) .expect("failed to map closure to SyntaxNode") @@ -56,7 +57,7 @@ fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expec } // FIXME: Deduplicate this with hir::Local::sources(). - let (body, source_map) = db.body_with_source_map(closure.0); + let (body, source_map) = db.body_with_source_map(body_owner); let local_text_range = match body.self_param.zip(source_map.self_param_syntax()) { Some((param, source)) if param == capture.local() => { @@ -71,7 +72,7 @@ fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expec .map(|it| format!("{it:?}")) .join(", "), }; - let place = capture.display_place(closure.0, db); + let place = capture.display_place(body_owner, db); let capture_ty = capture .ty .get() diff --git a/crates/hir-ty/src/tests/incremental.rs b/crates/hir-ty/src/tests/incremental.rs index faa7b80a89..0fbf8acf53 100644 --- a/crates/hir-ty/src/tests/incremental.rs +++ b/crates/hir-ty/src/tests/incremental.rs @@ -48,7 +48,7 @@ fn foo() -> i32 { "crate_lang_items", "GenericPredicates::query_with_diagnostics_", "ImplTraits::return_type_impl_traits_", - "expr_scopes_shim", + "body_expr_scopes_shim", ] "#]], ); @@ -136,7 +136,7 @@ fn baz() -> i32 { "crate_lang_items", "GenericPredicates::query_with_diagnostics_", "ImplTraits::return_type_impl_traits_", - "expr_scopes_shim", + "body_expr_scopes_shim", "InferenceResult::for_body_", "function_signature_shim", "function_signature_with_source_map_shim", @@ -146,7 +146,7 @@ fn baz() -> i32 { "trait_environment_query", "GenericPredicates::query_with_diagnostics_", "ImplTraits::return_type_impl_traits_", - "expr_scopes_shim", + "body_expr_scopes_shim", "InferenceResult::for_body_", "function_signature_shim", "function_signature_with_source_map_shim", @@ -156,7 +156,7 @@ fn baz() -> i32 { "trait_environment_query", "GenericPredicates::query_with_diagnostics_", "ImplTraits::return_type_impl_traits_", - "expr_scopes_shim", + "body_expr_scopes_shim", ] "#]], ); @@ -204,7 +204,7 @@ fn baz() -> i32 { "body_with_source_map_shim", "body_shim", "InferenceResult::for_body_", - "expr_scopes_shim", + "body_expr_scopes_shim", "AttrFlags::query_", "function_signature_with_source_map_shim", "function_signature_shim", @@ -600,7 +600,7 @@ fn main() { "trait_environment_query", "GenericPredicates::query_with_diagnostics_", "ImplTraits::return_type_impl_traits_", - "expr_scopes_shim", + "body_expr_scopes_shim", "struct_signature_shim", "struct_signature_with_source_map_shim", "AttrFlags::query_", @@ -690,7 +690,7 @@ fn main() { "function_signature_with_source_map_shim", "GenericPredicates::query_with_diagnostics_", "ImplTraits::return_type_impl_traits_", - "expr_scopes_shim", + "body_expr_scopes_shim", "struct_signature_with_source_map_shim", "AttrFlags::query_", "GenericPredicates::query_with_diagnostics_", diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs index 8c7d29f993..42dc074309 100644 --- a/crates/hir-ty/src/tests/patterns.rs +++ b/crates/hir-ty/src/tests/patterns.rs @@ -421,6 +421,8 @@ fn infer_pattern_match_byte_string_literal() { 254..256 '&v': &'? [u8; 3] 255..256 'v': [u8; 3] 257..259 '{}': () + 199..200 '3': usize + 62..63 'N': usize "#]], ); } diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index d88801a57b..e4fc7e56c6 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/crates/hir-ty/src/tests/regression.rs @@ -2754,6 +2754,7 @@ where 664..680 'filter...ter_fn': dyn Fn(&'? T) -> bool + 'static 691..698 'loop {}': ! 696..698 '{}': () + 512..513 'N': usize "#]], ); } diff --git a/crates/hir-ty/src/tests/regression/new_solver.rs b/crates/hir-ty/src/tests/regression/new_solver.rs index f47a26d429..e6b3244cda 100644 --- a/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/crates/hir-ty/src/tests/regression/new_solver.rs @@ -34,6 +34,7 @@ impl Space for [u8; 1] { 223..227 'iter': IntoIter<u8> 230..231 'a': Vec<u8> 230..243 'a.into_iter()': IntoIter<u8> + 322..323 '1': usize "#]], ); } @@ -472,6 +473,8 @@ fn foo() { 249..257 'to_bytes': fn to_bytes() -> [u8; _] 249..259 'to_bytes()': [u8; _] 249..268 'to_byt..._vec()': Vec<<[u8; _] as Foo>::Item> + 205..206 '_': usize + 156..157 'N': usize "#]], ); } @@ -541,6 +544,11 @@ fn test_at_most() { 617..620 'num': Between<0, 1, char> 623..626 ''9'': char 623..641 ''9'.at...:<1>()': Between<0, 1, char> + 320..335 '{ Consts::MAX }': usize + 322..333 'Consts::MAX': usize + 421..422 '0': i32 + 144..159 '{ Consts::MAX }': usize + 146..157 'Consts::MAX': usize "#]], ); } diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs index dab8bdb54b..3ea21f8265 100644 --- a/crates/hir-ty/src/tests/simple.rs +++ b/crates/hir-ty/src/tests/simple.rs @@ -3868,6 +3868,8 @@ fn main() { 208..209 'c': u8 213..214 'a': A 213..221 'a.into()': [u8; 2] + 33..34 '2': usize + 111..112 '3': usize "#]], ); } @@ -4061,6 +4063,8 @@ fn foo() { 248..282 'LazyLo..._LOCK)': &'? [u32; _] 264..281 '&VALUE...Y_LOCK': &'? LazyLock<[u32; _]> 265..281 'VALUES...Y_LOCK': LazyLock<[u32; _]> + 197..202 '{ 0 }': usize + 199..200 '0': usize "#]], ); } @@ -4109,3 +4113,38 @@ fn foo() { "#, ); } + +#[test] +fn signature_inference() { + check_infer( + r#" +trait Trait<const A: u8> {} +struct S<T: Trait<2>, const C: f32 = 0.0> +where + (): Trait<2> +{ + field: [(); { C as usize }], + field2: *mut S<T, 5.0> +} + +struct S2<const C: u16>; + +type Alias = S2<0>; +impl S2<0> {} +enum E { + V(S2<0>) = 0, +} +union U { + field: S2<0> +} + "#, + expect![[r#" + 242..243 '0': isize + 46..47 '2': i32 + 65..68 '0.0': f32 + 90..91 '2': i32 + 200..201 '0': i32 + 212..213 '0': i32 + "#]], + ); +} diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index 74e9a8dac0..22359d8f1f 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -1271,6 +1271,7 @@ fn bar() { 241..245 'R::B': fn B<(), i32>(i32) -> R<(), i32> 241..248 'R::B(7)': R<(), i32> 246..247 '7': i32 + 46..47 '2': usize "#]], ); } @@ -3781,6 +3782,8 @@ fn main() { 371..373 'v4': usize 376..378 'v3': [u8; 4] 376..389 'v3.do_thing()': usize + 86..87 '4': usize + 192..193 '2': usize "#]], ) } @@ -3820,6 +3823,9 @@ fn main() { 240..242 'v2': [u8; 2] 245..246 'v': [u8; 2] 245..257 'v.do_thing()': [u8; 2] + 130..131 'L': usize + 102..103 'L': usize + 130..131 'L': usize "#]], ) } diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs index e032a16989..6a1aeb64f3 100644 --- a/crates/hir/src/has_source.rs +++ b/crates/hir/src/has_source.rs @@ -293,7 +293,8 @@ impl HasSource for Param<'_> { } Callee::Closure(closure, _) => { let InternedClosure(owner, expr_id) = db.lookup_intern_closure(closure); - let (_, source_map) = db.body_with_source_map(owner); + let body_owner = owner.as_def_with_body()?; + let (_, source_map) = db.body_with_source_map(body_owner); let ast @ InFile { file_id, value } = source_map.expr_syntax(expr_id).ok()?; let root = db.parse_or_expand(file_id); match value.to_node(&root) { diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 0b3515fd04..34372a4a95 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -508,6 +508,21 @@ impl ModuleDef { } } + pub fn as_generic_def(self) -> Option<GenericDef> { + match self { + ModuleDef::Function(it) => Some(it.into()), + ModuleDef::Adt(it) => Some(it.into()), + ModuleDef::Trait(it) => Some(it.into()), + ModuleDef::TypeAlias(it) => Some(it.into()), + ModuleDef::Static(it) => Some(it.into()), + ModuleDef::Const(it) => Some(it.into()), + ModuleDef::Variant(_) + | ModuleDef::Module(_) + | ModuleDef::BuiltinType(_) + | ModuleDef::Macro(_) => None, + } + } + pub fn attrs(&self, db: &dyn HirDatabase) -> Option<AttrsWithOwner> { Some(match self { ModuleDef::Module(it) => it.attrs(db), @@ -1737,7 +1752,7 @@ impl Variant { } pub fn value(self, db: &dyn HirDatabase) -> Option<ast::Expr> { - self.source(db)?.value.expr() + self.source(db)?.value.const_arg()?.expr() } pub fn eval(self, db: &dyn HirDatabase) -> Result<i128, ConstEvalError> { @@ -2891,11 +2906,12 @@ impl<'db> Param<'db> { } Callee::Closure(closure, _) => { let c = db.lookup_intern_closure(closure); - let body = db.body(c.0); + let body_owner = c.0.as_def_with_body()?; + let body = db.body(body_owner); if let Expr::Closure { args, .. } = &body[c.1] && let Pat::Bind { id, .. } = &body[args[self.idx]] { - return Some(Local { parent: c.0, binding_id: *id }); + return Some(Local { parent: body_owner, binding_id: *id }); } None } @@ -3971,6 +3987,30 @@ impl_from!( ); impl GenericDef { + pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { + match self { + GenericDef::Function(it) => Some(it.name(db)), + GenericDef::Adt(it) => Some(it.name(db)), + GenericDef::Trait(it) => Some(it.name(db)), + GenericDef::TypeAlias(it) => Some(it.name(db)), + GenericDef::Impl(_) => None, + GenericDef::Const(it) => it.name(db), + GenericDef::Static(it) => Some(it.name(db)), + } + } + + pub fn module(self, db: &dyn HirDatabase) -> Module { + match self { + GenericDef::Function(it) => it.module(db), + GenericDef::Adt(it) => it.module(db), + GenericDef::Trait(it) => it.module(db), + GenericDef::TypeAlias(it) => it.module(db), + GenericDef::Impl(it) => it.module(db), + GenericDef::Const(it) => it.module(db), + GenericDef::Static(it) => it.module(db), + } + } + pub fn params(self, db: &dyn HirDatabase) -> Vec<GenericParam> { let Ok(id) = self.try_into() else { // Let's pretend builtin derive impls don't have generic parameters. @@ -5012,13 +5052,16 @@ impl<'db> Closure<'db> { return Vec::new(); }; let owner = db.lookup_intern_closure(id).0; - let infer = InferenceResult::for_body(db, owner); + let Some(body_owner) = owner.as_def_with_body() else { + return Vec::new(); + }; + let infer = InferenceResult::for_body(db, body_owner); let info = infer.closure_info(id); info.0 .iter() .cloned() .map(|capture| ClosureCapture { - owner, + owner: body_owner, closure: id, capture, _marker: PhantomCovariantLifetime::new(), @@ -5032,9 +5075,12 @@ impl<'db> Closure<'db> { return Vec::new(); }; let owner = db.lookup_intern_closure(id).0; - let infer = InferenceResult::for_body(db, owner); + let Some(body_owner) = owner.as_def_with_body() else { + return Vec::new(); + }; + let infer = InferenceResult::for_body(db, body_owner); let (captures, _) = infer.closure_info(id); - let env = body_param_env_from_has_crate(db, owner); + let env = body_param_env_from_has_crate(db, body_owner); captures.iter().map(|capture| Type { env, ty: capture.ty(db, self.subst) }).collect() } @@ -5042,7 +5088,10 @@ impl<'db> Closure<'db> { match self.id { AnyClosureId::ClosureId(id) => { let owner = db.lookup_intern_closure(id).0; - let infer = InferenceResult::for_body(db, owner); + let Some(body_owner) = owner.as_def_with_body() else { + return FnTrait::FnOnce; + }; + let infer = InferenceResult::for_body(db, body_owner); let info = infer.closure_info(id); info.1.into() } diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index c816fe967c..a231f4aff5 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -2149,7 +2149,7 @@ impl<'db> SemanticsImpl<'db> { node: InFile<&SyntaxNode>, offset: Option<TextSize>, // replace this, just make the inference result a `LazyCell` - infer_body: bool, + infer: bool, ) -> Option<SourceAnalyzer<'db>> { let _p = tracing::info_span!("SemanticsImpl::analyze_impl").entered(); @@ -2157,7 +2157,7 @@ impl<'db> SemanticsImpl<'db> { let resolver = match container { ChildContainer::DefWithBodyId(def) => { - return Some(if infer_body { + return Some(if infer { SourceAnalyzer::new_for_body(self.db, def, node, offset) } else { SourceAnalyzer::new_for_body_no_infer(self.db, def, node, offset) @@ -2167,16 +2167,32 @@ impl<'db> SemanticsImpl<'db> { return Some(SourceAnalyzer::new_variant_body(self.db, def, node, offset)); } ChildContainer::TraitId(it) => { - return Some(SourceAnalyzer::new_generic_def(self.db, it.into(), node, offset)); + return Some(if infer { + SourceAnalyzer::new_generic_def(self.db, it.into(), node, offset) + } else { + SourceAnalyzer::new_generic_def_no_infer(self.db, it.into(), node, offset) + }); } ChildContainer::ImplId(it) => { - return Some(SourceAnalyzer::new_generic_def(self.db, it.into(), node, offset)); + return Some(if infer { + SourceAnalyzer::new_generic_def(self.db, it.into(), node, offset) + } else { + SourceAnalyzer::new_generic_def_no_infer(self.db, it.into(), node, offset) + }); } ChildContainer::EnumId(it) => { - return Some(SourceAnalyzer::new_generic_def(self.db, it.into(), node, offset)); + return Some(if infer { + SourceAnalyzer::new_generic_def(self.db, it.into(), node, offset) + } else { + SourceAnalyzer::new_generic_def_no_infer(self.db, it.into(), node, offset) + }); } ChildContainer::GenericDefId(it) => { - return Some(SourceAnalyzer::new_generic_def(self.db, it, node, offset)); + return Some(if infer { + SourceAnalyzer::new_generic_def(self.db, it, node, offset) + } else { + SourceAnalyzer::new_generic_def_no_infer(self.db, it, node, offset) + }); } ChildContainer::ModuleId(it) => it.resolver(self.db), }; diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index c6f2d151f5..1d3cfc748e 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -17,7 +17,7 @@ use hir_def::{ path::Path, scope::{ExprScopes, ScopeId}, }, - hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat, PatId}, + hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat, PatId, generics::GenericParams}, lang_item::LangItems, nameres::MacroSubNs, resolver::{HasResolver, Resolver, TypeNs, ValueNs, resolver_for_scope}, @@ -92,7 +92,9 @@ pub(crate) enum BodyOrSig<'db> { def: GenericDefId, store: Arc<ExpressionStore>, source_map: Arc<ExpressionStoreSourceMap>, - // infer: Option<Arc<InferenceResult>>, + infer: Option<&'db InferenceResult>, + #[expect(dead_code)] + generics: Arc<GenericParams>, }, } @@ -123,7 +125,7 @@ impl<'db> SourceAnalyzer<'db> { infer: Option<&'db InferenceResult>, ) -> SourceAnalyzer<'db> { let (body, source_map) = db.body_with_source_map(def); - let scopes = db.expr_scopes(def); + let scopes = db.expr_scopes(def.into()); let scope = match offset { None => scope_for(db, &scopes, &source_map, node), Some(offset) => { @@ -147,14 +149,51 @@ impl<'db> SourceAnalyzer<'db> { pub(crate) fn new_generic_def( db: &'db dyn HirDatabase, def: GenericDefId, - InFile { file_id, .. }: InFile<&SyntaxNode>, - _offset: Option<TextSize>, + node: InFile<&SyntaxNode>, + offset: Option<TextSize>, ) -> SourceAnalyzer<'db> { - let (_params, store, source_map) = db.generic_params_and_store_and_source_map(def); - let resolver = def.resolver(db); + Self::new_generic_def_(db, def, node, offset, true) + } + + pub(crate) fn new_generic_def_no_infer( + db: &'db dyn HirDatabase, + def: GenericDefId, + node: InFile<&SyntaxNode>, + offset: Option<TextSize>, + ) -> SourceAnalyzer<'db> { + Self::new_generic_def_(db, def, node, offset, false) + } + + pub(crate) fn new_generic_def_( + db: &'db dyn HirDatabase, + def: GenericDefId, + node @ InFile { file_id, .. }: InFile<&SyntaxNode>, + offset: Option<TextSize>, + infer: bool, + ) -> SourceAnalyzer<'db> { + let (generics, store, source_map) = db.generic_params_and_store_and_source_map(def); + let scopes = db.expr_scopes(def.into()); + let scope = match offset { + None => scope_for(db, &scopes, &source_map, node), + Some(offset) => { + debug_assert!( + node.text_range().contains_inclusive(offset), + "{:?} not in {:?}", + offset, + node.text_range() + ); + scope_for_offset(db, &scopes, &source_map, node.file_id, offset) + } + }; + let resolver = resolver_for_scope(db, def, scope); + let infer = if infer && !Arc::ptr_eq(&store, &ExpressionStore::empty_singleton().0) { + Some(InferenceResult::for_signature(db, def)) + } else { + None + }; SourceAnalyzer { resolver, - body_or_sig: Some(BodyOrSig::Sig { def, store, source_map }), + body_or_sig: Some(BodyOrSig::Sig { def, store, source_map, generics, infer }), file_id, } } @@ -197,17 +236,8 @@ impl<'db> SourceAnalyzer<'db> { fn infer(&self) -> Option<&InferenceResult> { self.body_or_sig.as_ref().and_then(|it| match it { - BodyOrSig::Sig { .. } => None, BodyOrSig::VariantFields { .. } => None, - BodyOrSig::Body { infer, .. } => infer.as_deref(), - }) - } - - fn body(&self) -> Option<&Body> { - self.body_or_sig.as_ref().and_then(|it| match it { - BodyOrSig::Sig { .. } => None, - BodyOrSig::VariantFields { .. } => None, - BodyOrSig::Body { body, .. } => Some(&**body), + BodyOrSig::Sig { infer, .. } | BodyOrSig::Body { infer, .. } => infer.as_deref(), }) } @@ -232,11 +262,13 @@ impl<'db> SourceAnalyzer<'db> { } fn trait_environment(&self, db: &'db dyn HirDatabase) -> ParamEnvAndCrate<'db> { - self.param_and( - self.body_() - .map(|(def, ..)| def) - .map_or_else(ParamEnv::empty, |def| db.trait_environment_for_body(def)), - ) + self.param_and(self.body_or_sig.as_ref().map_or_else(ParamEnv::empty, |body_or_sig| { + match *body_or_sig { + BodyOrSig::Body { def, .. } => db.trait_environment_for_body(def), + BodyOrSig::VariantFields { .. } => ParamEnv::empty(), + BodyOrSig::Sig { def, .. } => db.trait_environment(def), + } + })) } pub(crate) fn expr_id(&self, expr: ast::Expr) -> Option<ExprOrPatId> { @@ -371,7 +403,10 @@ impl<'db> SourceAnalyzer<'db> { db: &'db dyn HirDatabase, _param: &ast::SelfParam, ) -> Option<Type<'db>> { - let binding = self.body()?.self_param?; + let binding = match self.body_or_sig.as_ref()? { + BodyOrSig::Sig { .. } | BodyOrSig::VariantFields { .. } => return None, + BodyOrSig::Body { body, .. } => body.self_param?, + }; let ty = self.infer()?.binding_ty(binding); Some(Type::new_with_resolver(db, &self.resolver, ty)) } @@ -1045,7 +1080,7 @@ impl<'db> SourceAnalyzer<'db> { } // FIXME: collectiong here shouldnt be necessary? - let mut collector = ExprCollector::new(db, self.resolver.module(), self.file_id); + let mut collector = ExprCollector::body(db, self.resolver.module(), self.file_id); let hir_path = collector.lower_path(path.clone(), &mut ExprCollector::impl_trait_error_allocator)?; let parent_hir_path = path @@ -1253,7 +1288,7 @@ impl<'db> SourceAnalyzer<'db> { db: &dyn HirDatabase, path: &ast::Path, ) -> Option<PathResolutionPerNs> { - let mut collector = ExprCollector::new(db, self.resolver.module(), self.file_id); + let mut collector = ExprCollector::body(db, self.resolver.module(), self.file_id); let hir_path = collector.lower_path(path.clone(), &mut ExprCollector::impl_trait_error_allocator)?; let (store, _) = collector.store.finish(); @@ -1526,7 +1561,7 @@ impl<'db> SourceAnalyzer<'db> { fn scope_for( db: &dyn HirDatabase, scopes: &ExprScopes, - source_map: &BodySourceMap, + source_map: &ExpressionStoreSourceMap, node: InFile<&SyntaxNode>, ) -> Option<ScopeId> { node.ancestors_with_macros(db) @@ -1545,7 +1580,7 @@ fn scope_for( fn scope_for_offset( db: &dyn HirDatabase, scopes: &ExprScopes, - source_map: &BodySourceMap, + source_map: &ExpressionStoreSourceMap, from_file: HirFileId, offset: TextSize, ) -> Option<ScopeId> { @@ -1579,7 +1614,7 @@ fn scope_for_offset( fn adjust( db: &dyn HirDatabase, scopes: &ExprScopes, - source_map: &BodySourceMap, + source_map: &ExpressionStoreSourceMap, expr_range: TextRange, from_file: HirFileId, offset: TextSize, diff --git a/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs b/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs index 7960373e61..75c5f84b85 100644 --- a/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs +++ b/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs @@ -47,7 +47,7 @@ pub(crate) fn add_explicit_enum_discriminant( // Don't offer the assist if the enum has no variants or if all variants already have an // explicit discriminant. - if variant_list.variants().all(|variant_node| variant_node.expr().is_some()) { + if variant_list.variants().all(|variant_node| variant_node.const_arg().is_some()) { return None; } @@ -72,7 +72,9 @@ fn add_variant_discriminant( variant_node: &ast::Variant, radix: &mut Radix, ) { - if let Some(expr) = variant_node.expr() { + if let Some(expr) = variant_node.const_arg() + && let Some(expr) = expr.expr() + { *radix = expr_radix(&expr).unwrap_or(*radix); return; } diff --git a/crates/ide/src/inlay_hints/discriminant.rs b/crates/ide/src/inlay_hints/discriminant.rs index 5b9267126f..e845faec56 100644 --- a/crates/ide/src/inlay_hints/discriminant.rs +++ b/crates/ide/src/inlay_hints/discriminant.rs @@ -46,7 +46,7 @@ fn variant_hints( enum_: &ast::Enum, variant: &ast::Variant, ) -> Option<()> { - if variant.expr().is_some() { + if variant.const_arg().is_some() { return None; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/crates/ide/src/syntax_highlighting/test_data/highlight_general.html index c6dbc435c0..1184739cc2 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_general.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_general.html @@ -105,7 +105,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd <span class="keyword control">loop</span> <span class="brace">{</span><span class="brace">}</span> <span class="brace">}</span> -<span class="keyword">fn</span> <span class="function declaration">const_param</span><span class="angle"><</span><span class="keyword const">const</span> <span class="const_param const declaration">FOO</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="angle">></span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="builtin_type">usize</span> <span class="brace">{</span> +<span class="keyword">fn</span> <span class="function declaration">const_param</span><span class="angle"><</span><span class="keyword const">const</span> <span class="const_param const declaration">FOO</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="angle">></span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="builtin_type">usize</span> <span class="keyword">where</span> <span class="bracket">[</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="const_param const">FOO</span><span class="bracket">]</span><span class="colon">:</span> <span class="trait default_library library">Sized</span> <span class="brace">{</span> <span class="function">const_param</span><span class="operator">::</span><span class="angle"><</span><span class="brace">{</span> <span class="const_param const">FOO</span> <span class="brace">}</span><span class="angle">></span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="const_param const">FOO</span> <span class="brace">}</span> diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index c6aebd0b0c..aecd1d3fdb 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs @@ -210,7 +210,7 @@ fn never() -> ! { loop {} } -fn const_param<const FOO: usize>() -> usize { +fn const_param<const FOO: usize>() -> usize where [(); FOO]: Sized { const_param::<{ FOO }>(); FOO } diff --git a/crates/parser/src/grammar/items/adt.rs b/crates/parser/src/grammar/items/adt.rs index a375696140..cfba4c3a77 100644 --- a/crates/parser/src/grammar/items/adt.rs +++ b/crates/parser/src/grammar/items/adt.rs @@ -96,7 +96,9 @@ pub(crate) fn variant_list(p: &mut Parser<'_>) { // test variant_discriminant // enum E { X(i32) = 10 } if p.eat(T![=]) { + let m = p.start(); expressions::expr(p); + m.complete(p, CONST_ARG); } m.complete(p, VARIANT); } else { @@ -139,7 +141,9 @@ pub(crate) fn record_field_list(p: &mut Parser<'_>) { // test record_field_default_values // struct S { f: f32 = 0.0 } if p.eat(T![=]) { + let m = p.start(); expressions::expr(p); + m.complete(p, CONST_ARG); } m.complete(p, RECORD_FIELD); } else { diff --git a/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast b/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast index 33088f2cab..e53b886bbf 100644 --- a/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast +++ b/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast @@ -21,8 +21,9 @@ SOURCE_FILE WHITESPACE " " EQ "=" WHITESPACE " " - LITERAL - FLOAT_NUMBER "0.0" + CONST_ARG + LITERAL + FLOAT_NUMBER "0.0" WHITESPACE " " R_CURLY "}" WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/variant_discriminant.rast b/crates/parser/test_data/parser/inline/ok/variant_discriminant.rast index 9f0c5a7610..3494085e88 100644 --- a/crates/parser/test_data/parser/inline/ok/variant_discriminant.rast +++ b/crates/parser/test_data/parser/inline/ok/variant_discriminant.rast @@ -23,8 +23,9 @@ SOURCE_FILE WHITESPACE " " EQ "=" WHITESPACE " " - LITERAL - INT_NUMBER "10" + CONST_ARG + LITERAL + INT_NUMBER "10" WHITESPACE " " R_CURLY "}" WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/ok/0019_enums.rast b/crates/parser/test_data/parser/ok/0019_enums.rast index dd47e3aa47..51837e5372 100644 --- a/crates/parser/test_data/parser/ok/0019_enums.rast +++ b/crates/parser/test_data/parser/ok/0019_enums.rast @@ -78,8 +78,9 @@ SOURCE_FILE WHITESPACE " " EQ "=" WHITESPACE " " - LITERAL - INT_NUMBER "92" + CONST_ARG + LITERAL + INT_NUMBER "92" COMMA "," WHITESPACE "\n " VARIANT diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index ad1cca08cb..3ee3797654 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -10,8 +10,8 @@ use std::{ use cfg::{CfgAtom, CfgDiff}; use hir::{ - Adt, AssocItem, Crate, DefWithBody, FindPathConfig, HasCrate, HasSource, HirDisplay, ModuleDef, - Name, crate_lang_items, + Adt, AssocItem, Crate, DefWithBody, FindPathConfig, GenericDef, HasCrate, HasSource, + HirDisplay, ModuleDef, Name, crate_lang_items, db::{DefDatabase, ExpandDatabase, HirDatabase}, next_solver::{DbInterner, GenericArgs}, }; @@ -229,6 +229,7 @@ impl flags::AnalysisStats { eprint!(" crates: {num_crates}"); let mut num_decls = 0; let mut bodies = Vec::new(); + let mut signatures = Vec::new(); let mut adts = Vec::new(); let mut file_ids = Vec::new(); @@ -267,24 +268,32 @@ impl flags::AnalysisStats { }, _ => (), }; + if let Some(g) = decl.as_generic_def() { + signatures.push(g); + } } for impl_def in module.impl_defs(db) { + signatures.push(impl_def.into()); for item in impl_def.items(db) { num_decls += 1; match item { - AssocItem::Function(f) => bodies.push(DefWithBody::from(f)), + AssocItem::Function(f) => { + bodies.push(DefWithBody::from(f)); + signatures.push(f.into()) + } AssocItem::Const(c) => { bodies.push(DefWithBody::from(c)); + signatures.push(c.into()); } - _ => (), + AssocItem::TypeAlias(t) => signatures.push(t.into()), } } } } } eprintln!( - ", mods: {}, decls: {num_decls}, bodies: {}, adts: {}, consts: {}", + ", mods: {}, decls: {num_decls}, bodies: {}, adts: {}, consts: {}, signatures: {}", visited_modules.len(), bodies.len(), adts.len(), @@ -292,6 +301,7 @@ impl flags::AnalysisStats { .iter() .filter(|it| matches!(it, DefWithBody::Const(_) | DefWithBody::Static(_))) .count(), + signatures.len(), ); eprintln!(" Workspace:"); @@ -327,15 +337,15 @@ impl flags::AnalysisStats { } if !self.skip_lowering { - self.run_body_lowering(db, &vfs, &bodies, verbosity); + self.run_body_lowering(db, &vfs, &bodies, &signatures, verbosity); } if !self.skip_inference { - self.run_inference(db, &vfs, &bodies, verbosity); + self.run_inference(db, &vfs, &bodies, &signatures, verbosity); } if !self.skip_mir_stats { - self.run_mir_lowering(db, &bodies, verbosity); + self.run_mir_lowering(db, &bodies, &signatures, verbosity); } if !self.skip_data_layout { @@ -343,7 +353,7 @@ impl flags::AnalysisStats { } if !self.skip_const_eval { - self.run_const_eval(db, &bodies, verbosity); + self.run_const_eval(db, &bodies, &signatures, verbosity); } }); @@ -416,7 +426,13 @@ impl flags::AnalysisStats { report_metric("data layout time", data_layout_time.time.as_millis() as u64, "ms"); } - fn run_const_eval(&self, db: &RootDatabase, bodies: &[DefWithBody], verbosity: Verbosity) { + fn run_const_eval( + &self, + db: &RootDatabase, + bodies: &[DefWithBody], + _signatures: &[GenericDef], + verbosity: Verbosity, + ) { let len = bodies .iter() .filter(|body| matches!(body, DefWithBody::Const(_) | DefWithBody::Static(_))) @@ -431,7 +447,9 @@ impl flags::AnalysisStats { let mut all = 0; let mut fail = 0; for &b in bodies { - bar.set_message(move || format!("const eval: {}", full_name(db, b, b.module(db)))); + bar.set_message(move || { + format!("const eval: {}", full_name(db, || b.name(db), b.module(db))) + }); let res = match b { DefWithBody::Const(c) => c.eval(db), DefWithBody::Static(s) => s.eval(db), @@ -687,7 +705,13 @@ impl flags::AnalysisStats { bar.finish_and_clear(); } - fn run_mir_lowering(&self, db: &RootDatabase, bodies: &[DefWithBody], verbosity: Verbosity) { + fn run_mir_lowering( + &self, + db: &RootDatabase, + bodies: &[DefWithBody], + _signatures: &[GenericDef], + verbosity: Verbosity, + ) { let mut bar = match verbosity { Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(), _ if self.parallel || self.output.is_some() => ProgressReport::hidden(), @@ -698,14 +722,14 @@ impl flags::AnalysisStats { let mut fail = 0; for &body in bodies { bar.set_message(move || { - format!("mir lowering: {}", full_name(db, body, body.module(db))) + format!("mir lowering: {}", full_name(db, || body.name(db), body.module(db))) }); bar.inc(1); if matches!(body, DefWithBody::Variant(_)) { continue; } let module = body.module(db); - if !self.should_process(db, body, module) { + if !self.should_process(db, || body.name(db), module) { continue; } @@ -743,6 +767,7 @@ impl flags::AnalysisStats { db: &RootDatabase, vfs: &Vfs, bodies: &[DefWithBody], + signatures: &[GenericDef], verbosity: Verbosity, ) { let mut bar = match verbosity { @@ -757,10 +782,19 @@ impl flags::AnalysisStats { bodies .par_iter() .map_with(db.clone(), |snap, &body| { - snap.body(body); InferenceResult::for_body(snap, body); }) .count(); + let signatures = signatures + .iter() + .filter_map(|&signatures| signatures.try_into().ok()) + .collect::<Vec<_>>(); + signatures + .par_iter() + .map_with(db.clone(), |snap, &signatures| { + InferenceResult::for_signature(snap, signatures); + }) + .count(); eprintln!("{:<20} {}", "Parallel Inference:", inference_sw.elapsed()); } @@ -782,7 +816,7 @@ impl flags::AnalysisStats { let display_target = module.krate(db).to_display_target(db); if let Some(only_name) = self.only.as_deref() && name.display(db, Edition::LATEST).to_string() != only_name - && full_name(db, body_id, module) != only_name + && full_name(db, || body_id.name(db), module) != only_name { continue; } @@ -800,15 +834,15 @@ impl flags::AnalysisStats { let syntax_range = src.text_range(); format!( "processing: {} ({} {:?})", - full_name(db, body_id, module), + full_name(db, || body_id.name(db), module), path, syntax_range ) } else { - format!("processing: {}", full_name(db, body_id, module)) + format!("processing: {}", full_name(db, || body_id.name(db), module)) } } else { - format!("processing: {}", full_name(db, body_id, module)) + format!("processing: {}", full_name(db, || body_id.name(db), module)) } }; if verbosity.is_spammy() { @@ -822,11 +856,22 @@ impl flags::AnalysisStats { Ok(inference_result) => inference_result, Err(p) => { if let Some(s) = p.downcast_ref::<&str>() { - eprintln!("infer panicked for {}: {}", full_name(db, body_id, module), s); + eprintln!( + "infer panicked for {}: {}", + full_name(db, || body_id.name(db), module), + s + ); } else if let Some(s) = p.downcast_ref::<String>() { - eprintln!("infer panicked for {}: {}", full_name(db, body_id, module), s); + eprintln!( + "infer panicked for {}: {}", + full_name(db, || body_id.name(db), module), + s + ); } else { - eprintln!("infer panicked for {}", full_name(db, body_id, module)); + eprintln!( + "infer panicked for {}", + full_name(db, || body_id.name(db), module) + ); } panics += 1; bar.inc(1); @@ -932,7 +977,7 @@ impl flags::AnalysisStats { if verbosity.is_spammy() { bar.println(format!( "In {}: {} exprs, {} unknown, {} partial", - full_name(db, body_id, module), + full_name(db, || body_id.name(db), module), num_exprs - previous_exprs, num_exprs_unknown - previous_unknown, num_exprs_partially_unknown - previous_partially_unknown @@ -1034,7 +1079,7 @@ impl flags::AnalysisStats { if verbosity.is_spammy() { bar.println(format!( "In {}: {} pats, {} unknown, {} partial", - full_name(db, body_id, module), + full_name(db, || body_id.name(db), module), num_pats - previous_pats, num_pats_unknown - previous_unknown, num_pats_partially_unknown - previous_partially_unknown @@ -1078,20 +1123,65 @@ impl flags::AnalysisStats { db: &RootDatabase, vfs: &Vfs, bodies: &[DefWithBody], + signatures: &[GenericDef], verbosity: Verbosity, ) { let mut bar = match verbosity { Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(), _ if self.output.is_some() => ProgressReport::hidden(), - _ => ProgressReport::new(bodies.len()), + _ => ProgressReport::new(bodies.len() + signatures.len()), }; let mut sw = self.stop_watch(); bar.tick(); + for &signature in signatures { + let Ok(signature_id) = signature.try_into() else { continue }; + let module = signature.module(db); + if !self.should_process(db, || signature.name(db), module) { + continue; + } + let msg = move || { + if verbosity.is_verbose() { + let source = match signature { + GenericDef::Function(it) => it.source(db).map(|it| it.syntax().cloned()), + GenericDef::Static(it) => it.source(db).map(|it| it.syntax().cloned()), + GenericDef::Const(it) => it.source(db).map(|it| it.syntax().cloned()), + GenericDef::Adt(adt) => adt.source(db).map(|it| it.syntax().cloned()), + GenericDef::Trait(it) => it.source(db).map(|it| it.syntax().cloned()), + GenericDef::TypeAlias(type_alias) => { + type_alias.source(db).map(|it| it.syntax().cloned()) + } + GenericDef::Impl(it) => it.source(db).map(|it| it.syntax().cloned()), + }; + if let Some(src) = source { + let original_file = src.file_id.original_file(db); + let path = vfs.file_path(original_file.file_id(db)); + let syntax_range = src.text_range(); + format!( + "processing: {} ({} {:?})", + full_name(db, || signature.name(db), module), + path, + syntax_range + ) + } else { + format!("processing: {}", full_name(db, || signature.name(db), module)) + } + } else { + format!("processing: {}", full_name(db, || signature.name(db), module)) + } + }; + if verbosity.is_spammy() { + bar.println(msg()); + } + bar.set_message(msg); + db.generic_params_and_store(signature_id); + bar.inc(1); + } + for &body_id in bodies { let Ok(body_def_id) = body_id.try_into() else { continue }; let module = body_id.module(db); - if !self.should_process(db, body_id, module) { + if !self.should_process(db, || body_id.name(db), module) { continue; } let msg = move || { @@ -1108,15 +1198,15 @@ impl flags::AnalysisStats { let syntax_range = src.text_range(); format!( "processing: {} ({} {:?})", - full_name(db, body_id, module), + full_name(db, || body_id.name(db), module), path, syntax_range ) } else { - format!("processing: {}", full_name(db, body_id, module)) + format!("processing: {}", full_name(db, || body_id.name(db), module)) } } else { - format!("processing: {}", full_name(db, body_id, module)) + format!("processing: {}", full_name(db, || body_id.name(db), module)) } }; if verbosity.is_spammy() { @@ -1129,7 +1219,7 @@ impl flags::AnalysisStats { bar.finish_and_clear(); let body_lowering_time = sw.elapsed(); - eprintln!("{:<20} {}", "Body lowering:", body_lowering_time); + eprintln!("{:<20} {}", "Expression Store Lowering:", body_lowering_time); report_metric("body lowering time", body_lowering_time.time.as_millis() as u64, "ms"); } @@ -1283,12 +1373,17 @@ impl flags::AnalysisStats { eprintln!("{:<20} {} ({} files)", "IDE:", ide_time, file_ids.len()); } - fn should_process(&self, db: &RootDatabase, body_id: DefWithBody, module: hir::Module) -> bool { + fn should_process( + &self, + db: &RootDatabase, + name_fn: impl Fn() -> Option<Name>, + module: hir::Module, + ) -> bool { if let Some(only_name) = self.only.as_deref() { - let name = body_id.name(db).unwrap_or_else(Name::missing); + let name = name_fn().unwrap_or_else(Name::missing); if name.display(db, Edition::LATEST).to_string() != only_name - && full_name(db, body_id, module) != only_name + && full_name(db, name_fn, module) != only_name { return false; } @@ -1301,7 +1396,7 @@ impl flags::AnalysisStats { } } -fn full_name(db: &RootDatabase, body_id: DefWithBody, module: hir::Module) -> String { +fn full_name(db: &RootDatabase, name: impl Fn() -> Option<Name>, module: hir::Module) -> String { module .krate(db) .display_name(db) @@ -1313,7 +1408,7 @@ fn full_name(db: &RootDatabase, body_id: DefWithBody, module: hir::Module) -> St .into_iter() .filter_map(|it| it.name(db)) .rev() - .chain(Some(body_id.name(db).unwrap_or_else(Name::missing))) + .chain(Some(name().unwrap_or_else(Name::missing))) .map(|it| it.display(db, Edition::LATEST).to_string()), ) .join("::") diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram index 544053408f..3113fc7430 100644 --- a/crates/syntax/rust.ungram +++ b/crates/syntax/rust.ungram @@ -245,7 +245,7 @@ RecordFieldList = RecordField = Attr* Visibility? 'unsafe'? - Name ':' Type ('=' Expr)? + Name ':' Type ('=' default_val:ConstArg)? TupleFieldList = '(' fields:(TupleField (',' TupleField)* ','?)? ')' @@ -268,7 +268,7 @@ VariantList = Variant = Attr* Visibility? - Name FieldList? ('=' Expr)? + Name FieldList? ('=' ConstArg)? Union = Attr* Visibility? diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index c4e72eafa7..7334de0fd9 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs @@ -1337,7 +1337,7 @@ impl ast::HasName for RecordField {} impl ast::HasVisibility for RecordField {} impl RecordField { #[inline] - pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } + pub fn default_val(&self) -> Option<ConstArg> { support::child(&self.syntax) } #[inline] pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) } #[inline] @@ -1896,7 +1896,7 @@ impl ast::HasName for Variant {} impl ast::HasVisibility for Variant {} impl Variant { #[inline] - pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } + pub fn const_arg(&self) -> Option<ConstArg> { support::child(&self.syntax) } #[inline] pub fn field_list(&self) -> Option<FieldList> { support::child(&self.syntax) } #[inline] diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs index 55c80ed167..87ee3bc76e 100644 --- a/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -1665,8 +1665,10 @@ impl SyntaxFactory { } if let Some(discriminant) = discriminant { - builder - .map_node(discriminant.syntax().clone(), ast.expr().unwrap().syntax().clone()); + builder.map_node( + discriminant.syntax().clone(), + ast.const_arg().unwrap().syntax().clone(), + ); } builder.finish(&mut mapping); |