Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #21823 from Veykril/push-zvmkkvrwwppl
Implement signature type inference
Lukas Wirth 8 weeks ago
parent b42b63f · parent f32fe40 · commit 29a0c1e
-rw-r--r--crates/hir-def/src/db.rs25
-rw-r--r--crates/hir-def/src/expr_store.rs54
-rw-r--r--crates/hir-def/src/expr_store/body.rs2
-rw-r--r--crates/hir-def/src/expr_store/lower.rs77
-rw-r--r--crates/hir-def/src/expr_store/lower/generics.rs17
-rw-r--r--crates/hir-def/src/expr_store/lower/path/tests.rs2
-rw-r--r--crates/hir-def/src/expr_store/scope.rs51
-rw-r--r--crates/hir-def/src/lib.rs73
-rw-r--r--crates/hir-def/src/resolver.rs52
-rw-r--r--crates/hir-def/src/signatures.rs10
-rw-r--r--crates/hir-def/src/test_db.rs2
-rw-r--r--crates/hir-ty/src/consteval.rs20
-rw-r--r--crates/hir-ty/src/db.rs10
-rw-r--r--crates/hir-ty/src/display.rs14
-rw-r--r--crates/hir-ty/src/drop.rs7
-rw-r--r--crates/hir-ty/src/infer.rs132
-rw-r--r--crates/hir-ty/src/infer/closure/analysis.rs28
-rw-r--r--crates/hir-ty/src/infer/coerce.rs5
-rw-r--r--crates/hir-ty/src/infer/expr.rs24
-rw-r--r--crates/hir-ty/src/infer/mutability.rs12
-rw-r--r--crates/hir-ty/src/infer/pat.rs28
-rw-r--r--crates/hir-ty/src/infer/path.rs4
-rw-r--r--crates/hir-ty/src/infer/unify.rs4
-rw-r--r--crates/hir-ty/src/layout.rs5
-rw-r--r--crates/hir-ty/src/lower.rs73
-rw-r--r--crates/hir-ty/src/method_resolution/confirm.rs2
-rw-r--r--crates/hir-ty/src/mir.rs8
-rw-r--r--crates/hir-ty/src/mir/borrowck.rs11
-rw-r--r--crates/hir-ty/src/mir/eval.rs8
-rw-r--r--crates/hir-ty/src/mir/eval/shim.rs5
-rw-r--r--crates/hir-ty/src/mir/lower.rs14
-rw-r--r--crates/hir-ty/src/next_solver/def_id.rs31
-rw-r--r--crates/hir-ty/src/next_solver/interner.rs52
-rw-r--r--crates/hir-ty/src/next_solver/solver.rs7
-rw-r--r--crates/hir-ty/src/next_solver/ty.rs2
-rw-r--r--crates/hir-ty/src/tests.rs97
-rw-r--r--crates/hir-ty/src/tests/closure_captures.rs7
-rw-r--r--crates/hir-ty/src/tests/incremental.rs14
-rw-r--r--crates/hir-ty/src/tests/patterns.rs2
-rw-r--r--crates/hir-ty/src/tests/regression.rs1
-rw-r--r--crates/hir-ty/src/tests/regression/new_solver.rs8
-rw-r--r--crates/hir-ty/src/tests/simple.rs39
-rw-r--r--crates/hir-ty/src/tests/traits.rs6
-rw-r--r--crates/hir/src/has_source.rs3
-rw-r--r--crates/hir/src/lib.rs65
-rw-r--r--crates/hir/src/semantics.rs28
-rw-r--r--crates/hir/src/source_analyzer.rs93
-rw-r--r--crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs6
-rw-r--r--crates/ide/src/inlay_hints/discriminant.rs2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_general.html2
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs2
-rw-r--r--crates/parser/src/grammar/items/adt.rs4
-rw-r--r--crates/parser/test_data/parser/inline/ok/record_field_default_values.rast5
-rw-r--r--crates/parser/test_data/parser/inline/ok/variant_discriminant.rast5
-rw-r--r--crates/parser/test_data/parser/ok/0019_enums.rast5
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs165
-rw-r--r--crates/syntax/rust.ungram4
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs4
-rw-r--r--crates/syntax/src/ast/syntax_factory/constructors.rs6
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">&lt;</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">&gt;</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</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">&lt;</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">&gt;</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</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">&lt;</span><span class="brace">{</span> <span class="const_param const">FOO</span> <span class="brace">}</span><span class="angle">&gt;</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);