Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #21200 from ChayimFriedman2/fake-impls
perf: Do not really expand builtin derives, instead treat them specifically
Lukas Wirth 4 months ago
parent 97ac158 · parent 6ed7084 · commit 8c5a68e
-rw-r--r--crates/hir-def/src/attrs.rs2
-rw-r--r--crates/hir-def/src/builtin_derive.rs149
-rw-r--r--crates/hir-def/src/item_scope.rs22
-rw-r--r--crates/hir-def/src/lang_item.rs51
-rw-r--r--crates/hir-def/src/lib.rs44
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mod.rs9
-rw-r--r--crates/hir-def/src/macro_expansion_tests/proc_macros.rs8
-rw-r--r--crates/hir-def/src/nameres.rs19
-rw-r--r--crates/hir-def/src/nameres/collector.rs229
-rw-r--r--crates/hir-def/src/nameres/tests/macros.rs8
-rw-r--r--crates/hir-expand/src/builtin/derive_macro.rs5
-rw-r--r--crates/hir-ty/src/builtin_derive.rs599
-rw-r--r--crates/hir-ty/src/consteval/tests.rs1
-rw-r--r--crates/hir-ty/src/db.rs10
-rw-r--r--crates/hir-ty/src/display.rs49
-rw-r--r--crates/hir-ty/src/drop.rs2
-rw-r--r--crates/hir-ty/src/lib.rs1
-rw-r--r--crates/hir-ty/src/lower.rs30
-rw-r--r--crates/hir-ty/src/method_resolution.rs119
-rw-r--r--crates/hir-ty/src/method_resolution/probe.rs6
-rw-r--r--crates/hir-ty/src/mir/eval.rs7
-rw-r--r--crates/hir-ty/src/mir/eval/shim.rs19
-rw-r--r--crates/hir-ty/src/mir/eval/shim/simd.rs15
-rw-r--r--crates/hir-ty/src/next_solver/def_id.rs74
-rw-r--r--crates/hir-ty/src/next_solver/format_proof_tree.rs31
-rw-r--r--crates/hir-ty/src/next_solver/generics.rs18
-rw-r--r--crates/hir-ty/src/next_solver/infer/select.rs8
-rw-r--r--crates/hir-ty/src/next_solver/interner.rs113
-rw-r--r--crates/hir-ty/src/next_solver/solver.rs10
-rw-r--r--crates/hir-ty/src/tests/incremental.rs40
-rw-r--r--crates/hir/src/attrs.rs45
-rw-r--r--crates/hir/src/display.rs343
-rw-r--r--crates/hir/src/from_id.rs102
-rw-r--r--crates/hir/src/has_source.rs72
-rw-r--r--crates/hir/src/lib.rs962
-rw-r--r--crates/hir/src/semantics.rs82
-rw-r--r--crates/hir/src/source_analyzer.rs54
-rw-r--r--crates/ide-db/src/test_data/test_symbol_index_collection.txt24
-rw-r--r--crates/ide/src/inlay_hints.rs28
-rw-r--r--crates/ide/src/inlay_hints/implicit_drop.rs5
-rw-r--r--crates/ide/src/navigation_target.rs62
-rw-r--r--crates/ide/src/references.rs2
-rw-r--r--crates/ide/src/runnables.rs8
-rw-r--r--crates/intern/src/symbol/symbols.rs5
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs32
45 files changed, 2728 insertions, 796 deletions
diff --git a/crates/hir-def/src/attrs.rs b/crates/hir-def/src/attrs.rs
index 34a9230794..e91d72a701 100644
--- a/crates/hir-def/src/attrs.rs
+++ b/crates/hir-def/src/attrs.rs
@@ -188,6 +188,7 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: Meta) -> ControlFlow<Infal
"deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED),
"macro_export" => attr_flags.insert(AttrFlags::IS_MACRO_EXPORT),
"no_mangle" => attr_flags.insert(AttrFlags::NO_MANGLE),
+ "pointee" => attr_flags.insert(AttrFlags::IS_POINTEE),
"non_exhaustive" => attr_flags.insert(AttrFlags::NON_EXHAUSTIVE),
"ignore" => attr_flags.insert(AttrFlags::IS_IGNORE),
"bench" => attr_flags.insert(AttrFlags::IS_BENCH),
@@ -289,6 +290,7 @@ bitflags::bitflags! {
const RUSTC_PAREN_SUGAR = 1 << 42;
const RUSTC_COINDUCTIVE = 1 << 43;
const RUSTC_FORCE_INLINE = 1 << 44;
+ const IS_POINTEE = 1 << 45;
}
}
diff --git a/crates/hir-def/src/builtin_derive.rs b/crates/hir-def/src/builtin_derive.rs
new file mode 100644
index 0000000000..32385516ab
--- /dev/null
+++ b/crates/hir-def/src/builtin_derive.rs
@@ -0,0 +1,149 @@
+//! Definition of builtin derive impls.
+//!
+//! To save time and memory, builtin derives are not really expanded. Instead, we record them
+//! and create their impls based on lowered data, see crates/hir-ty/src/builtin_derive.rs.
+
+use hir_expand::{InFile, builtin::BuiltinDeriveExpander, name::Name};
+use intern::{Symbol, sym};
+use tt::TextRange;
+
+use crate::{
+ AdtId, BuiltinDeriveImplId, BuiltinDeriveImplLoc, FunctionId, HasModule, db::DefDatabase,
+};
+
+macro_rules! declare_enum {
+ ( $( $trait:ident => [ $( $method:ident ),* ] ),* $(,)? ) => {
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+ pub enum BuiltinDeriveImplTrait {
+ $( $trait, )*
+ }
+
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+ #[allow(non_camel_case_types)]
+ pub enum BuiltinDeriveImplMethod {
+ $( $( $method, )* )*
+ }
+
+ impl BuiltinDeriveImplTrait {
+ #[inline]
+ pub fn name(self) -> Symbol {
+ match self {
+ $( Self::$trait => sym::$trait, )*
+ }
+ }
+
+ #[inline]
+ pub fn get_id(self, lang_items: &crate::lang_item::LangItems) -> Option<crate::TraitId> {
+ match self {
+ $( Self::$trait => lang_items.$trait, )*
+ }
+ }
+
+ #[inline]
+ pub fn get_method(self, method_name: &Symbol) -> Option<BuiltinDeriveImplMethod> {
+ match self {
+ $(
+ Self::$trait => {
+ match method_name {
+ $( _ if *method_name == sym::$method => Some(BuiltinDeriveImplMethod::$method), )*
+ _ => None,
+ }
+ }
+ )*
+ }
+ }
+
+ #[inline]
+ pub fn all_methods(self) -> &'static [BuiltinDeriveImplMethod] {
+ match self {
+ $( Self::$trait => &[ $(BuiltinDeriveImplMethod::$method),* ], )*
+ }
+ }
+ }
+
+ impl BuiltinDeriveImplMethod {
+ #[inline]
+ pub fn name(self) -> Symbol {
+ match self {
+ $( $( BuiltinDeriveImplMethod::$method => sym::$method, )* )*
+ }
+ }
+ }
+ };
+}
+
+declare_enum!(
+ Copy => [],
+ Clone => [clone],
+ Default => [default],
+ Debug => [fmt],
+ Hash => [hash],
+ Ord => [cmp],
+ PartialOrd => [partial_cmp],
+ Eq => [],
+ PartialEq => [eq],
+ CoerceUnsized => [],
+ DispatchFromDyn => [],
+);
+
+impl BuiltinDeriveImplMethod {
+ pub fn trait_method(
+ self,
+ db: &dyn DefDatabase,
+ impl_: BuiltinDeriveImplId,
+ ) -> Option<FunctionId> {
+ let loc = impl_.loc(db);
+ let lang_items = crate::lang_item::lang_items(db, loc.krate(db));
+ let trait_ = impl_.loc(db).trait_.get_id(lang_items)?;
+ trait_.trait_items(db).method_by_name(&Name::new_symbol_root(self.name()))
+ }
+}
+
+pub(crate) fn with_derive_traits(
+ derive: BuiltinDeriveExpander,
+ mut f: impl FnMut(BuiltinDeriveImplTrait),
+) {
+ let trait_ = match derive {
+ BuiltinDeriveExpander::Copy => BuiltinDeriveImplTrait::Copy,
+ BuiltinDeriveExpander::Clone => BuiltinDeriveImplTrait::Clone,
+ BuiltinDeriveExpander::Default => BuiltinDeriveImplTrait::Default,
+ BuiltinDeriveExpander::Debug => BuiltinDeriveImplTrait::Debug,
+ BuiltinDeriveExpander::Hash => BuiltinDeriveImplTrait::Hash,
+ BuiltinDeriveExpander::Ord => BuiltinDeriveImplTrait::Ord,
+ BuiltinDeriveExpander::PartialOrd => BuiltinDeriveImplTrait::PartialOrd,
+ BuiltinDeriveExpander::Eq => BuiltinDeriveImplTrait::Eq,
+ BuiltinDeriveExpander::PartialEq => BuiltinDeriveImplTrait::PartialEq,
+ BuiltinDeriveExpander::CoercePointee => {
+ f(BuiltinDeriveImplTrait::CoerceUnsized);
+ f(BuiltinDeriveImplTrait::DispatchFromDyn);
+ return;
+ }
+ };
+ f(trait_);
+}
+
+impl BuiltinDeriveImplLoc {
+ pub fn source(&self, db: &dyn DefDatabase) -> InFile<TextRange> {
+ let (adt_ast_id, module) = match self.adt {
+ AdtId::StructId(adt) => {
+ let adt_loc = adt.loc(db);
+ (adt_loc.id.upcast(), adt_loc.container)
+ }
+ AdtId::UnionId(adt) => {
+ let adt_loc = adt.loc(db);
+ (adt_loc.id.upcast(), adt_loc.container)
+ }
+ AdtId::EnumId(adt) => {
+ let adt_loc = adt.loc(db);
+ (adt_loc.id.upcast(), adt_loc.container)
+ }
+ };
+ let derive_range = self.derive_attr_id.find_derive_range(
+ db,
+ module.krate(db),
+ adt_ast_id,
+ self.derive_index,
+ );
+ adt_ast_id.with_value(derive_range)
+ }
+}
diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs
index 3ffeebfaf2..9e7868b273 100644
--- a/crates/hir-def/src/item_scope.rs
+++ b/crates/hir-def/src/item_scope.rs
@@ -9,15 +9,15 @@ use indexmap::map::Entry;
use itertools::Itertools;
use la_arena::Idx;
use rustc_hash::{FxHashMap, FxHashSet};
-use smallvec::{SmallVec, smallvec};
+use smallvec::SmallVec;
use span::Edition;
use stdx::format_to;
use syntax::ast;
use thin_vec::ThinVec;
use crate::{
- AdtId, BuiltinType, ConstId, ExternBlockId, ExternCrateId, FxIndexMap, HasModule, ImplId,
- Lookup, MacroCallStyles, MacroId, ModuleDefId, ModuleId, TraitId, UseId,
+ AdtId, BuiltinDeriveImplId, BuiltinType, ConstId, ExternBlockId, ExternCrateId, FxIndexMap,
+ HasModule, ImplId, Lookup, MacroCallStyles, MacroId, ModuleDefId, ModuleId, TraitId, UseId,
db::DefDatabase,
per_ns::{Item, MacrosItem, PerNs, TypesItem, ValuesItem},
visibility::Visibility,
@@ -159,6 +159,7 @@ pub struct ItemScope {
declarations: ThinVec<ModuleDefId>,
impls: ThinVec<ImplId>,
+ builtin_derive_impls: ThinVec<BuiltinDeriveImplId>,
extern_blocks: ThinVec<ExternBlockId>,
unnamed_consts: ThinVec<ConstId>,
/// Traits imported via `use Trait as _;`.
@@ -329,6 +330,10 @@ impl ItemScope {
self.impls.iter().copied()
}
+ pub fn builtin_derive_impls(&self) -> impl ExactSizeIterator<Item = BuiltinDeriveImplId> + '_ {
+ self.builtin_derive_impls.iter().copied()
+ }
+
pub fn all_macro_calls(&self) -> impl Iterator<Item = MacroCallId> + '_ {
self.macro_invocations.values().copied().chain(self.attr_macros.values().copied()).chain(
self.derive_macros.values().flat_map(|it| {
@@ -471,6 +476,10 @@ impl ItemScope {
self.impls.push(imp);
}
+ pub(crate) fn define_builtin_derive_impl(&mut self, imp: BuiltinDeriveImplId) {
+ self.builtin_derive_impls.push(imp);
+ }
+
pub(crate) fn define_extern_block(&mut self, extern_block: ExternBlockId) {
self.extern_blocks.push(extern_block);
}
@@ -522,12 +531,13 @@ impl ItemScope {
adt: AstId<ast::Adt>,
attr_id: AttrId,
attr_call_id: MacroCallId,
- len: usize,
+ mut derive_call_ids: SmallVec<[Option<MacroCallId>; 4]>,
) {
+ derive_call_ids.shrink_to_fit();
self.derive_macros.entry(adt).or_default().push(DeriveMacroInvocation {
attr_id,
attr_call_id,
- derive_call_ids: smallvec![None; len],
+ derive_call_ids,
});
}
@@ -811,6 +821,7 @@ impl ItemScope {
unresolved,
declarations,
impls,
+ builtin_derive_impls,
unnamed_consts,
unnamed_trait_imports,
legacy_macros,
@@ -834,6 +845,7 @@ impl ItemScope {
unresolved.shrink_to_fit();
declarations.shrink_to_fit();
impls.shrink_to_fit();
+ builtin_derive_impls.shrink_to_fit();
unnamed_consts.shrink_to_fit();
unnamed_trait_imports.shrink_to_fit();
legacy_macros.shrink_to_fit();
diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs
index fd693477a4..41d69c1fd6 100644
--- a/crates/hir-def/src/lang_item.rs
+++ b/crates/hir-def/src/lang_item.rs
@@ -2,6 +2,7 @@
//!
//! This attribute to tell the compiler about semi built-in std library
//! features, such as Fn family of traits.
+use hir_expand::name::Name;
use intern::{Symbol, sym};
use stdx::impl_from;
@@ -10,7 +11,7 @@ use crate::{
StaticId, StructId, TraitId, TypeAliasId, UnionId,
attrs::AttrFlags,
db::DefDatabase,
- nameres::{assoc::TraitItems, crate_def_map, crate_local_def_map},
+ nameres::{DefMap, assoc::TraitItems, crate_def_map, crate_local_def_map},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -93,6 +94,10 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option<Box<LangIt
}
}
+ if matches!(krate.data(db).origin, base_db::CrateOrigin::Lang(base_db::LangCrateOrigin::Core)) {
+ lang_items.fill_non_lang_core_traits(db, crate_def_map);
+ }
+
if lang_items.is_empty() { None } else { Some(Box::new(lang_items)) }
}
@@ -135,6 +140,31 @@ impl LangItems {
}
}
+fn resolve_core_trait(
+ db: &dyn DefDatabase,
+ core_def_map: &DefMap,
+ modules: &[Symbol],
+ name: Symbol,
+) -> Option<TraitId> {
+ let mut current = &core_def_map[core_def_map.root];
+ for module in modules {
+ let Some((ModuleDefId::ModuleId(cur), _)) =
+ current.scope.type_(&Name::new_symbol_root(module.clone()))
+ else {
+ return None;
+ };
+ if cur.krate(db) != core_def_map.krate() || cur.block(db) != core_def_map.block_id() {
+ return None;
+ }
+ current = &core_def_map[cur];
+ }
+ let Some((ModuleDefId::TraitId(trait_), _)) = current.scope.type_(&Name::new_symbol_root(name))
+ else {
+ return None;
+ };
+ Some(trait_)
+}
+
#[salsa::tracked(returns(as_deref))]
pub(crate) fn crate_notable_traits(db: &dyn DefDatabase, krate: Crate) -> Option<Box<[TraitId]>> {
let mut traits = Vec::new();
@@ -158,6 +188,10 @@ macro_rules! language_item_table {
(
$LangItems:ident =>
$( $(#[$attr:meta])* $lang_item:ident, $module:ident :: $name:ident, $target:ident; )*
+
+ @non_lang_core_traits:
+
+ $( core::$($non_lang_module:ident)::*, $non_lang_trait:ident; )*
) => {
#[allow(non_snake_case)] // FIXME: Should we remove this?
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
@@ -166,6 +200,9 @@ macro_rules! language_item_table {
$(#[$attr])*
pub $lang_item: Option<$target>,
)*
+ $(
+ pub $non_lang_trait: Option<TraitId>,
+ )*
}
impl LangItems {
@@ -176,6 +213,7 @@ macro_rules! language_item_table {
/// Merges `self` with `other`, with preference to `self` items.
fn merge_prefer_self(&mut self, other: &Self) {
$( self.$lang_item = self.$lang_item.or(other.$lang_item); )*
+ $( self.$non_lang_trait = self.$non_lang_trait.or(other.$non_lang_trait); )*
}
fn assign_lang_item(&mut self, name: Symbol, target: LangItemTarget) {
@@ -190,6 +228,10 @@ macro_rules! language_item_table {
_ => {}
}
}
+
+ fn fill_non_lang_core_traits(&mut self, db: &dyn DefDatabase, core_def_map: &DefMap) {
+ $( self.$non_lang_trait = resolve_core_trait(db, core_def_map, &[ $(sym::$non_lang_module),* ], sym::$non_lang_trait); )*
+ }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -426,4 +468,11 @@ language_item_table! { LangItems =>
String, sym::String, StructId;
CStr, sym::CStr, StructId;
Ordering, sym::Ordering, EnumId;
+
+ @non_lang_core_traits:
+ core::default, Default;
+ core::fmt, Debug;
+ core::hash, Hash;
+ core::cmp, Ord;
+ core::cmp, Eq;
}
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index 97af8ad93d..8d6c418d75 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -30,6 +30,7 @@ pub mod dyn_map;
pub mod item_tree;
+pub mod builtin_derive;
pub mod lang_item;
pub mod hir;
@@ -63,6 +64,7 @@ use base_db::{Crate, impl_intern_key};
use hir_expand::{
AstId, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallStyles,
MacroDefId, MacroDefKind,
+ attrs::AttrId,
builtin::{BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerExpander},
db::ExpandDatabase,
eager::expand_eager_macro_input,
@@ -80,6 +82,7 @@ pub use hir_expand::{Intern, Lookup, tt};
use crate::{
attrs::AttrFlags,
+ builtin_derive::BuiltinDeriveImplTrait,
builtin_type::BuiltinType,
db::DefDatabase,
expr_store::ExpressionStoreSourceMap,
@@ -331,6 +334,21 @@ impl ImplId {
}
}
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct BuiltinDeriveImplLoc {
+ pub adt: AdtId,
+ pub trait_: BuiltinDeriveImplTrait,
+ pub derive_attr_id: AttrId,
+ pub derive_index: u32,
+}
+
+#[salsa::interned(debug, no_lifetime)]
+#[derive(PartialOrd, Ord)]
+pub struct BuiltinDeriveImplId {
+ #[returns(ref)]
+ pub loc: BuiltinDeriveImplLoc,
+}
+
type UseLoc = ItemLoc<ast::Use>;
impl_intern!(UseId, UseLoc, intern_use, lookup_intern_use);
@@ -660,6 +678,18 @@ impl_from!(
for ModuleDefId
);
+impl From<DefWithBodyId> for ModuleDefId {
+ #[inline]
+ fn from(value: DefWithBodyId) -> Self {
+ match value {
+ DefWithBodyId::FunctionId(id) => id.into(),
+ DefWithBodyId::StaticId(id) => id.into(),
+ DefWithBodyId::ConstId(id) => id.into(),
+ DefWithBodyId::VariantId(id) => id.into(),
+ }
+ }
+}
+
/// A constant, which might appears as a const item, an anonymous const block in expressions
/// or patterns, or as a constant in types with const generics.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
@@ -1009,6 +1039,20 @@ fn module_for_assoc_item_loc<'db>(
id.lookup(db).container.module(db)
}
+impl HasModule for BuiltinDeriveImplLoc {
+ #[inline]
+ fn module(&self, db: &dyn DefDatabase) -> ModuleId {
+ self.adt.module(db)
+ }
+}
+
+impl HasModule for BuiltinDeriveImplId {
+ #[inline]
+ fn module(&self, db: &dyn DefDatabase) -> ModuleId {
+ self.loc(db).module(db)
+ }
+}
+
impl HasModule for FunctionId {
#[inline]
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs
index 5cb271b01d..3f136bc591 100644
--- a/crates/hir-def/src/macro_expansion_tests/mod.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -53,6 +53,8 @@ use crate::{
#[track_caller]
fn check_errors(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
+ crate::nameres::ENABLE_BUILTIN_DERIVE_FAST_PATH.set(false);
+
let db = TestDB::with_files(ra_fixture);
let krate = db.fetch_test_crate();
let def_map = crate_def_map(&db, krate);
@@ -80,10 +82,15 @@ fn check_errors(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect)
.sorted_unstable_by_key(|(range, _)| range.start())
.format_with("\n", |(range, err), format| format(&format_args!("{range:?}: {err}")))
.to_string();
+
+ crate::nameres::ENABLE_BUILTIN_DERIVE_FAST_PATH.set(true);
+
expect.assert_eq(&errors);
}
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, mut expect: Expect) {
+ crate::nameres::ENABLE_BUILTIN_DERIVE_FAST_PATH.set(false);
+
let extra_proc_macros = vec![(
r#"
#[proc_macro_attribute]
@@ -246,6 +253,8 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
}
}
+ crate::nameres::ENABLE_BUILTIN_DERIVE_FAST_PATH.set(true);
+
expect.indent(false);
expect.assert_eq(&expanded_text);
}
diff --git a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
index 6f30ca04af..bf04a500a5 100644
--- a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
+++ b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
@@ -123,15 +123,15 @@ struct Foo {
}
#[attr1]
+#[derive(Bar)]
+#[attr2] struct S;
+#[attr1]
#[my_cool_derive()] struct Foo {
v1: i32, #[attr3]v2: fn(#[attr4]param2: u32), v3: Foo< {
456
}
>,
-}
-#[attr1]
-#[derive(Bar)]
-#[attr2] struct S;"#]],
+}"#]],
);
}
diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs
index 3f29619bcb..59ca38c7c0 100644
--- a/crates/hir-def/src/nameres.rs
+++ b/crates/hir-def/src/nameres.rs
@@ -87,6 +87,25 @@ use crate::{
pub use self::path_resolution::ResolvePathResultPrefixInfo;
+#[cfg(test)]
+thread_local! {
+ /// HACK: In order to test builtin derive expansion, we gate their fast path with this atomic when cfg(test).
+ pub(crate) static ENABLE_BUILTIN_DERIVE_FAST_PATH: std::cell::Cell<bool> =
+ const { std::cell::Cell::new(true) };
+}
+
+#[inline]
+#[cfg(test)]
+fn enable_builtin_derive_fast_path() -> bool {
+ ENABLE_BUILTIN_DERIVE_FAST_PATH.get()
+}
+
+#[inline(always)]
+#[cfg(not(test))]
+fn enable_builtin_derive_fast_path() -> bool {
+ true
+}
+
const PREDEFINED_TOOLS: &[SmolStr] = &[
SmolStr::new_static("clippy"),
SmolStr::new_static("rustfmt"),
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index 7e1ec526a7..a7f687a316 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -12,7 +12,7 @@ use hir_expand::{
AttrMacroAttrIds, EditionedFileId, ErasedAstId, ExpandTo, HirFileId, InFile, MacroCallId,
MacroCallKind, MacroDefId, MacroDefKind,
attrs::{Attr, AttrId},
- builtin::{find_builtin_attr, find_builtin_derive, find_builtin_macro},
+ builtin::{BuiltinDeriveExpander, find_builtin_attr, find_builtin_derive, find_builtin_macro},
mod_path::{ModPath, PathKind},
name::{AsName, Name},
proc_macro::CustomProcMacroExpander,
@@ -23,15 +23,17 @@ use la_arena::Idx;
use rustc_hash::{FxHashMap, FxHashSet};
use smallvec::SmallVec;
use span::{Edition, FileAstId, SyntaxContext};
+use stdx::always;
use syntax::ast;
use triomphe::Arc;
use crate::{
- AdtId, AssocItemId, AstId, AstIdWithPath, ConstLoc, EnumLoc, ExternBlockLoc, ExternCrateId,
- ExternCrateLoc, FunctionId, FunctionLoc, FxIndexMap, ImplLoc, Intern, ItemContainerId, Lookup,
- Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags,
- ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc,
- UnionLoc, UnresolvedMacro, UseId, UseLoc,
+ AdtId, AssocItemId, AstId, AstIdWithPath, BuiltinDeriveImplId, BuiltinDeriveImplLoc, ConstLoc,
+ EnumLoc, ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, FxIndexMap,
+ ImplLoc, Intern, ItemContainerId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId,
+ MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId,
+ ProcMacroLoc, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId,
+ UseLoc,
db::DefDatabase,
item_scope::{GlobId, ImportId, ImportOrExternCrate, PerNsGlobImports},
item_tree::{
@@ -104,6 +106,7 @@ pub(super) fn collect_defs(
prev_active_attrs: Default::default(),
unresolved_extern_crates: Default::default(),
is_proc_macro: krate.is_proc_macro,
+ deferred_builtin_derives: Default::default(),
};
if tree_id.is_block() {
collector.seed_with_inner(tree_id);
@@ -214,6 +217,17 @@ enum MacroDirectiveKind<'db> {
},
}
+#[derive(Debug)]
+struct DeferredBuiltinDerive {
+ call_id: MacroCallId,
+ derive: BuiltinDeriveExpander,
+ module_id: ModuleId,
+ depth: usize,
+ container: ItemContainerId,
+ derive_attr_id: AttrId,
+ derive_index: u32,
+}
+
/// Walks the tree of module recursively
struct DefCollector<'db> {
db: &'db dyn DefDatabase,
@@ -252,6 +266,11 @@ struct DefCollector<'db> {
/// on the same item. Therefore, this holds all active attributes that we already
/// expanded.
prev_active_attrs: FxHashMap<AstId<ast::Item>, SmallVec<[AttrId; 1]>>,
+ /// To save memory, we do not really expand builtin derives. Instead, we save them as a `BuiltinDeriveImplId`.
+ ///
+ /// However, we can only do that when the derive is directly above the item, and there is no attribute in between.
+ /// Otherwise, all sorts of weird things can happen, like the item name resolving to something else.
+ deferred_builtin_derives: FxHashMap<AstId<ast::Item>, Vec<DeferredBuiltinDerive>>,
}
impl<'db> DefCollector<'db> {
@@ -1241,7 +1260,7 @@ impl<'db> DefCollector<'db> {
fn resolve_macros(&mut self) -> ReachedFixedPoint {
let mut macros = mem::take(&mut self.unresolved_macros);
let mut resolved = Vec::new();
- let mut push_resolved = |directive: &MacroDirective<'_>, call_id| {
+ let push_resolved = |resolved: &mut Vec<_>, directive: &MacroDirective<'_>, call_id| {
let attr_macro_item = match &directive.kind {
MacroDirectiveKind::Attr { ast_id, .. } => Some(ast_id.ast_id),
MacroDirectiveKind::FnLike { .. } | MacroDirectiveKind::Derive { .. } => None,
@@ -1271,8 +1290,8 @@ impl<'db> DefCollector<'db> {
MacroSubNs::Attr
}
};
- let resolver = |path: &_| {
- let resolved_res = self.def_map.resolve_path_fp_with_macro(
+ let resolver = |def_map: &DefMap, path: &_| {
+ let resolved_res = def_map.resolve_path_fp_with_macro(
self.crate_local_def_map.unwrap_or(&self.local_def_map),
self.db,
ResolveMode::Other,
@@ -1283,7 +1302,7 @@ impl<'db> DefCollector<'db> {
);
resolved_res.resolved_def.take_macros().map(|it| (it, self.db.macro_def(it)))
};
- let resolver_def_id = |path: &_| resolver(path).map(|(_, it)| it);
+ let resolver_def_id = |path: &_| resolver(&self.def_map, path).map(|(_, it)| it);
match &directive.kind {
MacroDirectiveKind::FnLike { ast_id, expand_to, ctxt: call_site } => {
@@ -1306,7 +1325,7 @@ impl<'db> DefCollector<'db> {
.scope
.add_macro_invoc(ast_id.ast_id, call_id);
- push_resolved(directive, call_id);
+ push_resolved(&mut resolved, directive, call_id);
res = ReachedFixedPoint::No;
return Resolved::Yes;
@@ -1320,6 +1339,7 @@ impl<'db> DefCollector<'db> {
ctxt: call_site,
derive_macro_id,
} => {
+ // FIXME: This code is almost duplicate below.
let id = derive_macro_as_call_id(
self.db,
ast_id,
@@ -1327,7 +1347,7 @@ impl<'db> DefCollector<'db> {
*derive_pos as u32,
*call_site,
self.def_map.krate,
- resolver,
+ |path| resolver(&self.def_map, path),
*derive_macro_id,
);
@@ -1354,7 +1374,8 @@ impl<'db> DefCollector<'db> {
}
}
- push_resolved(directive, call_id);
+ push_resolved(&mut resolved, directive, call_id);
+
res = ReachedFixedPoint::No;
return Resolved::Yes;
}
@@ -1460,29 +1481,85 @@ impl<'db> DefCollector<'db> {
let ast_id = ast_id.with_value(ast_adt_id);
+ let mut derive_call_ids = SmallVec::new();
match attr.parse_path_comma_token_tree(self.db) {
Some(derive_macros) => {
let call_id = call_id();
- let mut len = 0;
for (idx, (path, call_site, _)) in derive_macros.enumerate() {
let ast_id = AstIdWithPath::new(
file_id,
ast_id.value,
Interned::new(path),
);
- self.unresolved_macros.push(MacroDirective {
- module_id: directive.module_id,
- depth: directive.depth + 1,
- kind: MacroDirectiveKind::Derive {
- ast_id,
- derive_attr: *attr_id,
- derive_pos: idx,
- ctxt: call_site.ctx,
- derive_macro_id: call_id,
- },
- container: directive.container,
- });
- len = idx;
+
+ // Try to resolve the derive immediately. If we succeed, we can also use the fast path
+ // for builtin derives. If not, we cannot use it, as it can cause the ADT to become
+ // interned while the derive is still unresolved, which will cause it to get forgotten.
+ let id = derive_macro_as_call_id(
+ self.db,
+ &ast_id,
+ *attr_id,
+ idx as u32,
+ call_site.ctx,
+ self.def_map.krate,
+ |path| resolver(&self.def_map, path),
+ call_id,
+ );
+
+ if let Ok((macro_id, def_id, call_id)) = id {
+ derive_call_ids.push(Some(call_id));
+ // Record its helper attributes.
+ if def_id.krate != self.def_map.krate {
+ let def_map = crate_def_map(self.db, def_id.krate);
+ if let Some(helpers) =
+ def_map.data.exported_derives.get(&macro_id)
+ {
+ self.def_map
+ .derive_helpers_in_scope
+ .entry(ast_id.ast_id.map(|it| it.upcast()))
+ .or_default()
+ .extend(izip!(
+ helpers.iter().cloned(),
+ iter::repeat(macro_id),
+ iter::repeat(call_id),
+ ));
+ }
+ }
+
+ if super::enable_builtin_derive_fast_path()
+ && let MacroDefKind::BuiltInDerive(_, builtin_derive) =
+ def_id.kind
+ {
+ self.deferred_builtin_derives
+ .entry(ast_id.ast_id.upcast())
+ .or_default()
+ .push(DeferredBuiltinDerive {
+ call_id,
+ derive: builtin_derive,
+ module_id: directive.module_id,
+ container: directive.container,
+ depth: directive.depth,
+ derive_attr_id: *attr_id,
+ derive_index: idx as u32,
+ });
+ } else {
+ push_resolved(&mut resolved, directive, call_id);
+ }
+ } else {
+ derive_call_ids.push(None);
+ self.unresolved_macros.push(MacroDirective {
+ module_id: directive.module_id,
+ depth: directive.depth + 1,
+ kind: MacroDirectiveKind::Derive {
+ ast_id,
+ derive_attr: *attr_id,
+ derive_pos: idx,
+ ctxt: call_site.ctx,
+ derive_macro_id: call_id,
+ },
+ container: directive.container,
+ });
+ }
}
// We treat the #[derive] macro as an attribute call, but we do not resolve it for nameres collection.
@@ -1491,7 +1568,12 @@ impl<'db> DefCollector<'db> {
// Check the comment in [`builtin_attr_macro`].
self.def_map.modules[directive.module_id]
.scope
- .init_derive_attribute(ast_id, *attr_id, call_id, len + 1);
+ .init_derive_attribute(
+ ast_id,
+ *attr_id,
+ call_id,
+ derive_call_ids,
+ );
}
None => {
let diag = DefDiagnostic::malformed_derive(
@@ -1522,12 +1604,25 @@ impl<'db> DefCollector<'db> {
}
}
+ // Clear deferred derives for this item, unfortunately we cannot use them due to the attribute.
+ if let Some(deferred_derives) = self.deferred_builtin_derives.remove(&ast_id) {
+ resolved.extend(deferred_derives.into_iter().map(|derive| {
+ (
+ derive.module_id,
+ derive.depth,
+ derive.container,
+ derive.call_id,
+ Some(ast_id),
+ )
+ }));
+ }
+
let call_id = call_id();
self.def_map.modules[directive.module_id]
.scope
.add_attr_macro_invoc(ast_id, call_id);
- push_resolved(directive, call_id);
+ push_resolved(&mut resolved, directive, call_id);
res = ReachedFixedPoint::No;
return Resolved::Yes;
}
@@ -1709,6 +1804,12 @@ impl<'db> DefCollector<'db> {
));
}
+ always!(
+ self.deferred_builtin_derives.is_empty(),
+ "self.deferred_builtin_derives={:#?}",
+ self.deferred_builtin_derives,
+ );
+
(self.def_map, self.local_def_map)
}
}
@@ -1751,6 +1852,33 @@ impl ModCollector<'_, '_> {
}
let db = self.def_collector.db;
let module_id = self.module_id;
+ let consider_deferred_derives =
+ |file_id: HirFileId,
+ deferred_derives: &mut FxHashMap<_, Vec<DeferredBuiltinDerive>>,
+ ast_id: FileAstId<ast::Adt>,
+ id: AdtId,
+ def_map: &mut DefMap| {
+ let Some(deferred_derives) =
+ deferred_derives.remove(&InFile::new(file_id, ast_id.upcast()))
+ else {
+ return;
+ };
+ let module = &mut def_map.modules[module_id];
+ for deferred_derive in deferred_derives {
+ crate::builtin_derive::with_derive_traits(deferred_derive.derive, |trait_| {
+ let impl_id = BuiltinDeriveImplId::new(
+ db,
+ BuiltinDeriveImplLoc {
+ adt: id,
+ trait_,
+ derive_attr_id: deferred_derive.derive_attr_id,
+ derive_index: deferred_derive.derive_index,
+ },
+ );
+ module.scope.define_builtin_derive_impl(impl_id);
+ });
+ }
+ };
let update_def =
|def_collector: &mut DefCollector<'_>, id, name: &Name, vis, has_constructor| {
def_collector.def_map.modules[module_id].scope.declare(id);
@@ -1928,11 +2056,21 @@ impl ModCollector<'_, '_> {
let it = &self.item_tree[id];
let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
+ let interned = StructLoc {
+ container: module_id,
+ id: InFile::new(self.tree_id.file_id(), id),
+ }
+ .intern(db);
+ consider_deferred_derives(
+ self.tree_id.file_id(),
+ &mut self.def_collector.deferred_builtin_derives,
+ id.upcast(),
+ interned.into(),
+ def_map,
+ );
update_def(
self.def_collector,
- StructLoc { container: module_id, id: InFile::new(self.file_id(), id) }
- .intern(db)
- .into(),
+ interned.into(),
&it.name,
vis,
!matches!(it.shape, FieldsShape::Record),
@@ -1942,15 +2080,19 @@ impl ModCollector<'_, '_> {
let it = &self.item_tree[id];
let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
- update_def(
- self.def_collector,
- UnionLoc { container: module_id, id: InFile::new(self.file_id(), id) }
- .intern(db)
- .into(),
- &it.name,
- vis,
- false,
+ let interned = UnionLoc {
+ container: module_id,
+ id: InFile::new(self.tree_id.file_id(), id),
+ }
+ .intern(db);
+ consider_deferred_derives(
+ self.tree_id.file_id(),
+ &mut self.def_collector.deferred_builtin_derives,
+ id.upcast(),
+ interned.into(),
+ def_map,
);
+ update_def(self.def_collector, interned.into(), &it.name, vis, false);
}
ModItemId::Enum(id) => {
let it = &self.item_tree[id];
@@ -1960,6 +2102,13 @@ impl ModCollector<'_, '_> {
}
.intern(db);
+ consider_deferred_derives(
+ self.tree_id.file_id(),
+ &mut self.def_collector.deferred_builtin_derives,
+ id.upcast(),
+ enum_.into(),
+ def_map,
+ );
let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
update_def(self.def_collector, enum_.into(), &it.name, vis, false);
}
diff --git a/crates/hir-def/src/nameres/tests/macros.rs b/crates/hir-def/src/nameres/tests/macros.rs
index c8eb968b35..a943f6f0ac 100644
--- a/crates/hir-def/src/nameres/tests/macros.rs
+++ b/crates/hir-def/src/nameres/tests/macros.rs
@@ -784,7 +784,7 @@ macro_rules! foo {
pub use core::clone::Clone;
"#,
- |map| assert_eq!(map.modules[map.root].scope.impls().len(), 1),
+ |map| assert_eq!(map.modules[map.root].scope.builtin_derive_impls().len(), 1),
);
}
@@ -806,7 +806,7 @@ pub macro Copy {}
#[rustc_builtin_macro]
pub macro Clone {}
"#,
- |map| assert_eq!(map.modules[map.root].scope.impls().len(), 2),
+ |map| assert_eq!(map.modules[map.root].scope.builtin_derive_impls().len(), 2),
);
}
@@ -849,7 +849,7 @@ pub macro derive($item:item) {}
#[rustc_builtin_macro]
pub macro Clone {}
"#,
- |map| assert_eq!(map.modules[map.root].scope.impls().len(), 1),
+ |map| assert_eq!(map.modules[map.root].scope.builtin_derive_impls().len(), 1),
);
}
@@ -1609,7 +1609,7 @@ macro_rules! derive { () => {} }
#[derive(Clone)]
struct S;
"#,
- |map| assert_eq!(map.modules[map.root].scope.impls().len(), 1),
+ |map| assert_eq!(map.modules[map.root].scope.builtin_derive_impls().len(), 1),
);
}
diff --git a/crates/hir-expand/src/builtin/derive_macro.rs b/crates/hir-expand/src/builtin/derive_macro.rs
index 6582f4b075..c805197425 100644
--- a/crates/hir-expand/src/builtin/derive_macro.rs
+++ b/crates/hir-expand/src/builtin/derive_macro.rs
@@ -28,7 +28,7 @@ use syntax::{
};
macro_rules! register_builtin {
- ( $($trait:ident => $expand:ident),* ) => {
+ ( $($trait:ident => $expand:ident),* $(,)? ) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BuiltinDeriveExpander {
$($trait),*
@@ -48,7 +48,6 @@ macro_rules! register_builtin {
}
}
}
-
};
}
@@ -75,7 +74,7 @@ register_builtin! {
PartialOrd => partial_ord_expand,
Eq => eq_expand,
PartialEq => partial_eq_expand,
- CoercePointee => coerce_pointee_expand
+ CoercePointee => coerce_pointee_expand,
}
pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander> {
diff --git a/crates/hir-ty/src/builtin_derive.rs b/crates/hir-ty/src/builtin_derive.rs
new file mode 100644
index 0000000000..f3e67d01e5
--- /dev/null
+++ b/crates/hir-ty/src/builtin_derive.rs
@@ -0,0 +1,599 @@
+//! Implementation of builtin derive impls.
+
+use std::ops::ControlFlow;
+
+use hir_def::{
+ AdtId, BuiltinDeriveImplId, BuiltinDeriveImplLoc, HasModule, LocalFieldId, TraitId,
+ TypeOrConstParamId, TypeParamId,
+ attrs::AttrFlags,
+ builtin_derive::BuiltinDeriveImplTrait,
+ hir::generics::{GenericParams, TypeOrConstParamData},
+};
+use itertools::Itertools;
+use la_arena::ArenaMap;
+use rustc_type_ir::{
+ AliasTyKind, Interner, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast,
+ inherent::{GenericArgs as _, IntoKind},
+};
+
+use crate::{
+ GenericPredicates,
+ db::HirDatabase,
+ next_solver::{
+ Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, ParamEnv, StoredEarlyBinder,
+ StoredTy, TraitRef, Ty, TyKind, fold::fold_tys, generics::Generics,
+ },
+};
+
+fn coerce_pointee_new_type_param(trait_id: TraitId) -> TypeParamId {
+ // HACK: Fake the param.
+ // We cannot use a dummy param here, because it can leak into the IDE layer and that'll cause panics
+ // when e.g. trying to display it. So we use an existing param.
+ TypeParamId::from_unchecked(TypeOrConstParamId {
+ parent: trait_id.into(),
+ local_id: la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(1)),
+ })
+}
+
+pub(crate) fn generics_of<'db>(interner: DbInterner<'db>, id: BuiltinDeriveImplId) -> Generics {
+ let db = interner.db;
+ let loc = id.loc(db);
+ match loc.trait_ {
+ BuiltinDeriveImplTrait::Copy
+ | BuiltinDeriveImplTrait::Clone
+ | BuiltinDeriveImplTrait::Default
+ | BuiltinDeriveImplTrait::Debug
+ | BuiltinDeriveImplTrait::Hash
+ | BuiltinDeriveImplTrait::Ord
+ | BuiltinDeriveImplTrait::PartialOrd
+ | BuiltinDeriveImplTrait::Eq
+ | BuiltinDeriveImplTrait::PartialEq => interner.generics_of(loc.adt.into()),
+ BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
+ let mut generics = interner.generics_of(loc.adt.into());
+ let trait_id = loc
+ .trait_
+ .get_id(interner.lang_items())
+ .expect("we don't pass the impl to the solver if we can't resolve the trait");
+ generics.push_param(coerce_pointee_new_type_param(trait_id).into());
+ generics
+ }
+ }
+}
+
+pub fn generic_params_count(db: &dyn HirDatabase, id: BuiltinDeriveImplId) -> usize {
+ let loc = id.loc(db);
+ let adt_params = GenericParams::new(db, loc.adt.into());
+ let extra_params_count = match loc.trait_ {
+ BuiltinDeriveImplTrait::Copy
+ | BuiltinDeriveImplTrait::Clone
+ | BuiltinDeriveImplTrait::Default
+ | BuiltinDeriveImplTrait::Debug
+ | BuiltinDeriveImplTrait::Hash
+ | BuiltinDeriveImplTrait::Ord
+ | BuiltinDeriveImplTrait::PartialOrd
+ | BuiltinDeriveImplTrait::Eq
+ | BuiltinDeriveImplTrait::PartialEq => 0,
+ BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => 1,
+ };
+ adt_params.len() + extra_params_count
+}
+
+pub fn impl_trait<'db>(
+ interner: DbInterner<'db>,
+ id: BuiltinDeriveImplId,
+) -> EarlyBinder<'db, TraitRef<'db>> {
+ let db = interner.db;
+ let loc = id.loc(db);
+ let trait_id = loc
+ .trait_
+ .get_id(interner.lang_items())
+ .expect("we don't pass the impl to the solver if we can't resolve the trait");
+ match loc.trait_ {
+ BuiltinDeriveImplTrait::Copy
+ | BuiltinDeriveImplTrait::Clone
+ | BuiltinDeriveImplTrait::Default
+ | BuiltinDeriveImplTrait::Debug
+ | BuiltinDeriveImplTrait::Hash
+ | BuiltinDeriveImplTrait::Ord
+ | BuiltinDeriveImplTrait::Eq => {
+ let self_ty = Ty::new_adt(
+ interner,
+ loc.adt,
+ GenericArgs::identity_for_item(interner, loc.adt.into()),
+ );
+ EarlyBinder::bind(TraitRef::new(interner, trait_id.into(), [self_ty]))
+ }
+ BuiltinDeriveImplTrait::PartialOrd | BuiltinDeriveImplTrait::PartialEq => {
+ let self_ty = Ty::new_adt(
+ interner,
+ loc.adt,
+ GenericArgs::identity_for_item(interner, loc.adt.into()),
+ );
+ EarlyBinder::bind(TraitRef::new(interner, trait_id.into(), [self_ty, self_ty]))
+ }
+ BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
+ let generic_params = GenericParams::new(db, loc.adt.into());
+ let interner = DbInterner::new_no_crate(db);
+ let args = GenericArgs::identity_for_item(interner, loc.adt.into());
+ let self_ty = Ty::new_adt(interner, loc.adt, args);
+ let Some((pointee_param_idx, _, new_param_ty)) =
+ coerce_pointee_params(interner, loc, &generic_params, trait_id)
+ else {
+ // Malformed derive.
+ return EarlyBinder::bind(TraitRef::new(
+ interner,
+ trait_id.into(),
+ [self_ty, self_ty],
+ ));
+ };
+ let changed_args = replace_pointee(interner, pointee_param_idx, new_param_ty, args);
+ let changed_self_ty = Ty::new_adt(interner, loc.adt, changed_args);
+ EarlyBinder::bind(TraitRef::new(interner, trait_id.into(), [self_ty, changed_self_ty]))
+ }
+ }
+}
+
+#[salsa::tracked(returns(ref), unsafe(non_update_types))]
+pub fn predicates<'db>(db: &'db dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPredicates {
+ let loc = impl_.loc(db);
+ let generic_params = GenericParams::new(db, loc.adt.into());
+ let interner = DbInterner::new_with(db, loc.module(db).krate(db));
+ let adt_predicates = GenericPredicates::query(db, loc.adt.into());
+ let trait_id = loc
+ .trait_
+ .get_id(interner.lang_items())
+ .expect("we don't pass the impl to the solver if we can't resolve the trait");
+ match loc.trait_ {
+ BuiltinDeriveImplTrait::Copy
+ | BuiltinDeriveImplTrait::Clone
+ | BuiltinDeriveImplTrait::Debug
+ | BuiltinDeriveImplTrait::Hash
+ | BuiltinDeriveImplTrait::Ord
+ | BuiltinDeriveImplTrait::PartialOrd
+ | BuiltinDeriveImplTrait::Eq
+ | BuiltinDeriveImplTrait::PartialEq => {
+ simple_trait_predicates(interner, loc, &generic_params, adt_predicates, trait_id)
+ }
+ BuiltinDeriveImplTrait::Default => {
+ if matches!(loc.adt, AdtId::EnumId(_)) {
+ // Enums don't have extra bounds.
+ GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
+ Clauses::new_from_slice(adt_predicates.explicit_predicates().skip_binder())
+ .store(),
+ ))
+ } else {
+ simple_trait_predicates(interner, loc, &generic_params, adt_predicates, trait_id)
+ }
+ }
+ BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
+ let Some((pointee_param_idx, pointee_param_id, new_param_ty)) =
+ coerce_pointee_params(interner, loc, &generic_params, trait_id)
+ else {
+ // Malformed derive.
+ return GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
+ Clauses::default().store(),
+ ));
+ };
+ let duplicated_bounds =
+ adt_predicates.explicit_predicates().iter_identity_copied().filter_map(|pred| {
+ let mentions_pointee =
+ pred.visit_with(&mut MentionsPointee { pointee_param_idx }).is_break();
+ if !mentions_pointee {
+ return None;
+ }
+ let transformed =
+ replace_pointee(interner, pointee_param_idx, new_param_ty, pred);
+ Some(transformed)
+ });
+ let unsize_trait = interner.lang_items().Unsize;
+ let unsize_bound = unsize_trait.map(|unsize_trait| {
+ let pointee_param_ty = Ty::new_param(interner, pointee_param_id, pointee_param_idx);
+ TraitRef::new(interner, unsize_trait.into(), [pointee_param_ty, new_param_ty])
+ .upcast(interner)
+ });
+ GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
+ Clauses::new_from_iter(
+ interner,
+ adt_predicates
+ .explicit_predicates()
+ .iter_identity_copied()
+ .chain(duplicated_bounds)
+ .chain(unsize_bound),
+ )
+ .store(),
+ ))
+ }
+ }
+}
+
+/// Not cached in a query, currently used in `hir` only. If you need this in `hir-ty` consider introducing a query.
+pub fn param_env<'db>(interner: DbInterner<'db>, id: BuiltinDeriveImplId) -> ParamEnv<'db> {
+ let predicates = predicates(interner.db, id);
+ crate::lower::param_env_from_predicates(interner, predicates)
+}
+
+struct MentionsPointee {
+ pointee_param_idx: u32,
+}
+
+impl<'db> TypeVisitor<DbInterner<'db>> for MentionsPointee {
+ type Result = ControlFlow<()>;
+
+ fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result {
+ if let TyKind::Param(param) = t.kind()
+ && param.index == self.pointee_param_idx
+ {
+ ControlFlow::Break(())
+ } else {
+ t.super_visit_with(self)
+ }
+ }
+}
+
+fn replace_pointee<'db, T: TypeFoldable<DbInterner<'db>>>(
+ interner: DbInterner<'db>,
+ pointee_param_idx: u32,
+ new_param_ty: Ty<'db>,
+ t: T,
+) -> T {
+ fold_tys(interner, t, |ty| match ty.kind() {
+ TyKind::Param(param) if param.index == pointee_param_idx => new_param_ty,
+ _ => ty,
+ })
+}
+
+fn simple_trait_predicates<'db>(
+ interner: DbInterner<'db>,
+ loc: &BuiltinDeriveImplLoc,
+ generic_params: &GenericParams,
+ adt_predicates: &GenericPredicates,
+ trait_id: TraitId,
+) -> GenericPredicates {
+ let extra_predicates = generic_params
+ .iter_type_or_consts()
+ .filter(|(_, data)| matches!(data, TypeOrConstParamData::TypeParamData(_)))
+ .map(|(param_idx, _)| {
+ let param_id = TypeParamId::from_unchecked(TypeOrConstParamId {
+ parent: loc.adt.into(),
+ local_id: param_idx,
+ });
+ let param_idx =
+ param_idx.into_raw().into_u32() + (generic_params.len_lifetimes() as u32);
+ let param_ty = Ty::new_param(interner, param_id, param_idx);
+ let trait_ref = TraitRef::new(interner, trait_id.into(), [param_ty]);
+ trait_ref.upcast(interner)
+ });
+ let mut assoc_type_bounds = Vec::new();
+ match loc.adt {
+ AdtId::StructId(id) => extend_assoc_type_bounds(
+ interner,
+ &mut assoc_type_bounds,
+ interner.db.field_types(id.into()),
+ trait_id,
+ ),
+ AdtId::UnionId(id) => extend_assoc_type_bounds(
+ interner,
+ &mut assoc_type_bounds,
+ interner.db.field_types(id.into()),
+ trait_id,
+ ),
+ AdtId::EnumId(id) => {
+ for &(variant_id, _, _) in &id.enum_variants(interner.db).variants {
+ extend_assoc_type_bounds(
+ interner,
+ &mut assoc_type_bounds,
+ interner.db.field_types(variant_id.into()),
+ trait_id,
+ )
+ }
+ }
+ }
+ GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
+ Clauses::new_from_iter(
+ interner,
+ adt_predicates
+ .explicit_predicates()
+ .iter_identity_copied()
+ .chain(extra_predicates)
+ .chain(assoc_type_bounds),
+ )
+ .store(),
+ ))
+}
+
+fn extend_assoc_type_bounds<'db>(
+ interner: DbInterner<'db>,
+ assoc_type_bounds: &mut Vec<Clause<'db>>,
+ fields: &ArenaMap<LocalFieldId, StoredEarlyBinder<StoredTy>>,
+ trait_: TraitId,
+) {
+ struct ProjectionFinder<'a, 'db> {
+ interner: DbInterner<'db>,
+ assoc_type_bounds: &'a mut Vec<Clause<'db>>,
+ trait_: TraitId,
+ }
+
+ impl<'db> TypeVisitor<DbInterner<'db>> for ProjectionFinder<'_, 'db> {
+ type Result = ();
+
+ fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result {
+ if let TyKind::Alias(AliasTyKind::Projection, _) = t.kind() {
+ self.assoc_type_bounds.push(
+ TraitRef::new(self.interner, self.trait_.into(), [t]).upcast(self.interner),
+ );
+ }
+
+ t.super_visit_with(self)
+ }
+ }
+
+ let mut visitor = ProjectionFinder { interner, assoc_type_bounds, trait_ };
+ for (_, field) in fields.iter() {
+ field.get().instantiate_identity().visit_with(&mut visitor);
+ }
+}
+
+fn coerce_pointee_params<'db>(
+ interner: DbInterner<'db>,
+ loc: &BuiltinDeriveImplLoc,
+ generic_params: &GenericParams,
+ trait_id: TraitId,
+) -> Option<(u32, TypeParamId, Ty<'db>)> {
+ let pointee_param = {
+ if let Ok((pointee_param, _)) = generic_params
+ .iter_type_or_consts()
+ .filter(|param| matches!(param.1, TypeOrConstParamData::TypeParamData(_)))
+ .exactly_one()
+ {
+ pointee_param
+ } else {
+ let (_, generic_param_attrs) =
+ AttrFlags::query_generic_params(interner.db, loc.adt.into());
+ generic_param_attrs
+ .iter()
+ .find(|param| param.1.contains(AttrFlags::IS_POINTEE))
+ .map(|(param, _)| param)
+ .or_else(|| {
+ generic_params
+ .iter_type_or_consts()
+ .find(|param| matches!(param.1, TypeOrConstParamData::TypeParamData(_)))
+ .map(|(idx, _)| idx)
+ })?
+ }
+ };
+ let pointee_param_id = TypeParamId::from_unchecked(TypeOrConstParamId {
+ parent: loc.adt.into(),
+ local_id: pointee_param,
+ });
+ let pointee_param_idx =
+ pointee_param.into_raw().into_u32() + (generic_params.len_lifetimes() as u32);
+ let new_param_idx = generic_params.len() as u32;
+ let new_param_id = coerce_pointee_new_type_param(trait_id);
+ let new_param_ty = Ty::new_param(interner, new_param_id, new_param_idx);
+ Some((pointee_param_idx, pointee_param_id, new_param_ty))
+}
+
+#[cfg(test)]
+mod tests {
+ use expect_test::{Expect, expect};
+ use hir_def::nameres::crate_def_map;
+ use itertools::Itertools;
+ use stdx::format_to;
+ use test_fixture::WithFixture;
+
+ use crate::{builtin_derive::impl_trait, next_solver::DbInterner, test_db::TestDB};
+
+ fn check_trait_refs(#[rust_analyzer::rust_fixture] ra_fixture: &str, expectation: Expect) {
+ let db = TestDB::with_files(ra_fixture);
+ let def_map = crate_def_map(&db, db.test_crate());
+
+ let interner = DbInterner::new_with(&db, db.test_crate());
+ crate::attach_db(&db, || {
+ let mut trait_refs = Vec::new();
+ for (_, module) in def_map.modules() {
+ for derive in module.scope.builtin_derive_impls() {
+ let trait_ref = impl_trait(interner, derive).skip_binder();
+ trait_refs.push(format!("{trait_ref:?}"));
+ }
+ }
+
+ expectation.assert_eq(&trait_refs.join("\n"));
+ });
+ }
+
+ fn check_predicates(#[rust_analyzer::rust_fixture] ra_fixture: &str, expectation: Expect) {
+ let db = TestDB::with_files(ra_fixture);
+ let def_map = crate_def_map(&db, db.test_crate());
+
+ crate::attach_db(&db, || {
+ let mut predicates = String::new();
+ for (_, module) in def_map.modules() {
+ for derive in module.scope.builtin_derive_impls() {
+ let preds = super::predicates(&db, derive).all_predicates().skip_binder();
+ format_to!(
+ predicates,
+ "{}\n\n",
+ preds.iter().format_with("\n", |pred, formatter| formatter(&format_args!(
+ "{pred:?}"
+ ))),
+ );
+ }
+ }
+
+ expectation.assert_eq(&predicates);
+ });
+ }
+
+ #[test]
+ fn simple_macros_trait_ref() {
+ check_trait_refs(
+ r#"
+//- minicore: derive, clone, copy, eq, ord, hash, fmt
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+struct Simple;
+
+trait Trait {}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+struct WithGenerics<'a, T: Trait, const N: usize>(&'a [T; N]);
+ "#,
+ expect![[r#"
+ Simple: Debug
+ Simple: Clone
+ Simple: Copy
+ Simple: PartialEq<[Simple]>
+ Simple: Eq
+ Simple: PartialOrd<[Simple]>
+ Simple: Ord
+ Simple: Hash
+ WithGenerics<#0, #1, #2>: Debug
+ WithGenerics<#0, #1, #2>: Clone
+ WithGenerics<#0, #1, #2>: Copy
+ WithGenerics<#0, #1, #2>: PartialEq<[WithGenerics<#0, #1, #2>]>
+ WithGenerics<#0, #1, #2>: Eq
+ WithGenerics<#0, #1, #2>: PartialOrd<[WithGenerics<#0, #1, #2>]>
+ WithGenerics<#0, #1, #2>: Ord
+ WithGenerics<#0, #1, #2>: Hash"#]],
+ );
+ }
+
+ #[test]
+ fn coerce_pointee_trait_ref() {
+ check_trait_refs(
+ r#"
+//- minicore: derive, coerce_pointee
+use core::marker::CoercePointee;
+
+#[derive(CoercePointee)]
+struct Simple<T: ?Sized>(*const T);
+
+#[derive(CoercePointee)]
+struct MultiGenericParams<'a, T, #[pointee] U: ?Sized, const N: usize>(*const U);
+ "#,
+ expect![[r#"
+ Simple<#0>: CoerceUnsized<[Simple<#1>]>
+ Simple<#0>: DispatchFromDyn<[Simple<#1>]>
+ MultiGenericParams<#0, #1, #2, #3>: CoerceUnsized<[MultiGenericParams<#0, #1, #4, #3>]>
+ MultiGenericParams<#0, #1, #2, #3>: DispatchFromDyn<[MultiGenericParams<#0, #1, #4, #3>]>"#]],
+ );
+ }
+
+ #[test]
+ fn simple_macros_predicates() {
+ check_predicates(
+ r#"
+//- minicore: derive, clone, copy, eq, ord, hash, fmt
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+struct Simple;
+
+trait Trait {}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+struct WithGenerics<'a, T: Trait, const N: usize>(&'a [T; N]);
+ "#,
+ expect![[r#"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Debug, polarity:Positive), bound_vars: [] })
+
+ Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Clone, polarity:Positive), bound_vars: [] })
+
+ Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Copy, polarity:Positive), bound_vars: [] })
+
+ Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: PartialEq, polarity:Positive), bound_vars: [] })
+
+ Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Eq, polarity:Positive), bound_vars: [] })
+
+ Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: PartialOrd, polarity:Positive), bound_vars: [] })
+
+ Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Ord, polarity:Positive), bound_vars: [] })
+
+ Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Hash, polarity:Positive), bound_vars: [] })
+
+ "#]],
+ );
+ }
+
+ #[test]
+ fn coerce_pointee_predicates() {
+ check_predicates(
+ r#"
+//- minicore: derive, coerce_pointee
+use core::marker::CoercePointee;
+
+#[derive(CoercePointee)]
+struct Simple<T: ?Sized>(*const T);
+
+trait Trait<T> {}
+
+#[derive(CoercePointee)]
+struct MultiGenericParams<'a, T, #[pointee] U: ?Sized, const N: usize>(*const U)
+where
+ T: Trait<U>,
+ U: Trait<U>;
+ "#,
+ expect![[r#"
+ Clause(Binder { value: TraitPredicate(#0: Unsize<[#1]>, polarity:Positive), bound_vars: [] })
+
+ Clause(Binder { value: TraitPredicate(#0: Unsize<[#1]>, polarity:Positive), bound_vars: [] })
+
+ Clause(Binder { value: TraitPredicate(#1: Trait<[#2]>, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#2: Trait<[#2]>, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: ConstArgHasType(#3, usize), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Trait<[#4]>, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#4: Trait<[#4]>, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#2: Unsize<[#4]>, polarity:Positive), bound_vars: [] })
+
+ Clause(Binder { value: TraitPredicate(#1: Trait<[#2]>, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#2: Trait<[#2]>, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: ConstArgHasType(#3, usize), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#1: Trait<[#4]>, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#4: Trait<[#4]>, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(#2: Unsize<[#4]>, polarity:Positive), bound_vars: [] })
+
+ "#]],
+ );
+ }
+}
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index 8816e13ba7..08f201fea9 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -1568,6 +1568,7 @@ const GOAL: u8 = {
}
#[test]
+#[ignore = "builtin derive macros are currently not working with MIR eval"]
fn builtin_derive_macro() {
check_number(
r#"
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs
index f0f65eedbc..70474fc469 100644
--- a/crates/hir-ty/src/db.rs
+++ b/crates/hir-ty/src/db.rs
@@ -2,10 +2,12 @@
//! type inference-related queries.
use base_db::{Crate, target::TargetLoadError};
+use either::Either;
use hir_def::{
- AdtId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId,
- GenericDefId, ImplId, LifetimeParamId, LocalFieldId, StaticId, TraitId, TypeAliasId, VariantId,
- db::DefDatabase, hir::ExprId, layout::TargetDataLayout,
+ 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,
};
use la_arena::ArenaMap;
use salsa::plumbing::AsId;
@@ -83,7 +85,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
env: ParamEnvAndCrate<'db>,
func: FunctionId,
fn_subst: GenericArgs<'db>,
- ) -> (FunctionId, GenericArgs<'db>);
+ ) -> (Either<FunctionId, (BuiltinDeriveImplId, BuiltinDeriveImplMethod)>, GenericArgs<'db>);
// endregion:mir
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index 0f989d6c58..44bbd84003 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -7,7 +7,7 @@ use std::{
mem,
};
-use base_db::Crate;
+use base_db::{Crate, FxIndexMap};
use either::Either;
use hir_def::{
FindPathConfig, GenericDefId, HasModule, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId,
@@ -143,11 +143,11 @@ impl<'db> BoundsFormattingCtx<'db> {
}
impl<'db> HirFormatter<'_, 'db> {
- fn start_location_link(&mut self, location: ModuleDefId) {
+ pub fn start_location_link(&mut self, location: ModuleDefId) {
self.fmt.start_location_link(location);
}
- fn end_location_link(&mut self) {
+ pub fn end_location_link(&mut self) {
self.fmt.end_location_link();
}
@@ -1971,6 +1971,49 @@ fn write_bounds_like_dyn_trait<'db>(
Ok(())
}
+pub fn write_params_bounds<'db>(
+ f: &mut HirFormatter<'_, 'db>,
+ predicates: &[Clause<'db>],
+) -> Result {
+ // Use an FxIndexMap to keep user's order, as far as possible.
+ let mut per_type = FxIndexMap::<_, Vec<_>>::default();
+ for &predicate in predicates {
+ let base_ty = match predicate.kind().skip_binder() {
+ ClauseKind::Trait(clause) => Either::Left(clause.self_ty()),
+ ClauseKind::RegionOutlives(clause) => Either::Right(clause.0),
+ ClauseKind::TypeOutlives(clause) => Either::Left(clause.0),
+ ClauseKind::Projection(clause) => Either::Left(clause.self_ty()),
+ ClauseKind::ConstArgHasType(..)
+ | ClauseKind::WellFormed(_)
+ | ClauseKind::ConstEvaluatable(_)
+ | ClauseKind::HostEffect(..)
+ | ClauseKind::UnstableFeature(_) => continue,
+ };
+ per_type.entry(base_ty).or_default().push(predicate);
+ }
+
+ for (base_ty, clauses) in per_type {
+ f.write_str(" ")?;
+ match base_ty {
+ Either::Left(it) => it.hir_fmt(f)?,
+ Either::Right(it) => it.hir_fmt(f)?,
+ }
+ f.write_str(": ")?;
+ // Rudimentary approximation: type params are `Sized` by default, everything else not.
+ // FIXME: This is not correct, really. But I'm not sure how we can from the ty representation
+ // to extract the default sizedness, and if it's possible at all.
+ let default_sized = match base_ty {
+ Either::Left(ty) if matches!(ty.kind(), TyKind::Param(_)) => {
+ SizedByDefault::Sized { anchor: f.krate() }
+ }
+ _ => SizedByDefault::NotSized,
+ };
+ write_bounds_like_dyn_trait(f, base_ty, &clauses, default_sized)?;
+ f.write_str(",\n")?;
+ }
+ Ok(())
+}
+
impl<'db> HirDisplay<'db> for TraitRef<'db> {
fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
let trait_ = self.def_id.0;
diff --git a/crates/hir-ty/src/drop.rs b/crates/hir-ty/src/drop.rs
index 66692143bc..9d6869eee9 100644
--- a/crates/hir-ty/src/drop.rs
+++ b/crates/hir-ty/src/drop.rs
@@ -32,7 +32,7 @@ fn has_destructor(interner: DbInterner<'_>, adt: AdtId) -> bool {
},
None => TraitImpls::for_crate(db, module.krate(db)),
};
- !impls.for_trait_and_self_ty(drop_trait, &SimplifiedType::Adt(adt.into())).is_empty()
+ !impls.for_trait_and_self_ty(drop_trait, &SimplifiedType::Adt(adt.into())).0.is_empty()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index 1674771413..373862229b 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -25,6 +25,7 @@ extern crate ra_ap_rustc_next_trait_solver as rustc_next_trait_solver;
extern crate self as hir_ty;
+pub mod builtin_derive;
mod infer;
mod inhabitedness;
mod lower;
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 62a5837f34..9307868f39 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -1791,6 +1791,13 @@ impl<'db> GenericPredicates {
impl GenericPredicates {
#[inline]
+ pub(crate) fn from_explicit_own_predicates(
+ predicates: StoredEarlyBinder<StoredClauses>,
+ ) -> Self {
+ Self { predicates, own_predicates_start: 0, is_trait: false, parent_is_trait: false }
+ }
+
+ #[inline]
pub fn query(db: &dyn HirDatabase, def: GenericDefId) -> &GenericPredicates {
&Self::query_with_diagnostics(db, def).0
}
@@ -1848,6 +1855,20 @@ pub(crate) fn trait_environment_for_body_query(
db.trait_environment(def)
}
+pub(crate) fn param_env_from_predicates<'db>(
+ interner: DbInterner<'db>,
+ predicates: &'db GenericPredicates,
+) -> ParamEnv<'db> {
+ let clauses = rustc_type_ir::elaborate::elaborate(
+ interner,
+ predicates.all_predicates().iter_identity_copied(),
+ );
+ let clauses = Clauses::new_from_iter(interner, clauses);
+
+ // FIXME: We should normalize projections here, like rustc does.
+ ParamEnv { clauses }
+}
+
pub(crate) fn trait_environment<'db>(db: &'db dyn HirDatabase, def: GenericDefId) -> ParamEnv<'db> {
return ParamEnv { clauses: trait_environment_query(db, def).as_ref() };
@@ -1858,13 +1879,8 @@ pub(crate) fn trait_environment<'db>(db: &'db dyn HirDatabase, def: GenericDefId
) -> StoredClauses {
let module = def.module(db);
let interner = DbInterner::new_with(db, module.krate(db));
- let predicates = GenericPredicates::query_all(db, def);
- let clauses =
- rustc_type_ir::elaborate::elaborate(interner, predicates.iter_identity_copied());
- let clauses = Clauses::new_from_iter(interner, clauses);
-
- // FIXME: We should normalize projections here, like rustc does.
- clauses.store()
+ let predicates = GenericPredicates::query(db, def);
+ param_env_from_predicates(interner, predicates).clauses.store()
}
}
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index c370330a87..50dbd87d69 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -13,11 +13,13 @@ use tracing::{debug, instrument};
use base_db::Crate;
use hir_def::{
- AssocItemId, BlockId, ConstId, FunctionId, GenericParamId, HasModule, ImplId, ItemContainerId,
- ModuleId, TraitId,
+ AssocItemId, BlockId, BuiltinDeriveImplId, ConstId, FunctionId, GenericParamId, HasModule,
+ ImplId, ItemContainerId, ModuleId, TraitId,
attrs::AttrFlags,
+ builtin_derive::BuiltinDeriveImplMethod,
expr_store::path::GenericArgs as HirGenericArgs,
hir::ExprId,
+ lang_item::LangItems,
nameres::{DefMap, block_def_map, crate_def_map},
resolver::Resolver,
};
@@ -37,7 +39,7 @@ use crate::{
infer::{InferenceContext, unify::InferenceTable},
lower::GenericPredicates,
next_solver::{
- Binder, ClauseKind, DbInterner, FnSig, GenericArgs, ParamEnv, PredicateKind,
+ AnyImplId, Binder, ClauseKind, DbInterner, FnSig, GenericArgs, ParamEnv, PredicateKind,
SimplifiedType, SolverDefId, TraitRef, Ty, TyKind, TypingMode,
infer::{
BoundRegionConversionTime, DbInternerInferExt, InferCtxt, InferOk,
@@ -132,7 +134,7 @@ pub enum MethodError<'db> {
// candidate can arise. Used for error reporting only.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum CandidateSource {
- Impl(ImplId),
+ Impl(AnyImplId),
Trait(TraitId),
}
@@ -371,9 +373,13 @@ pub fn lookup_impl_const<'db>(
};
lookup_impl_assoc_item_for_trait_ref(infcx, trait_ref, env, name)
- .and_then(
- |assoc| if let (AssocItemId::ConstId(id), s) = assoc { Some((id, s)) } else { None },
- )
+ .and_then(|assoc| {
+ if let (Either::Left(AssocItemId::ConstId(id)), s) = assoc {
+ Some((id, s))
+ } else {
+ None
+ }
+ })
.unwrap_or((const_id, subs))
}
@@ -419,12 +425,12 @@ pub(crate) fn lookup_impl_method_query<'db>(
env: ParamEnvAndCrate<'db>,
func: FunctionId,
fn_subst: GenericArgs<'db>,
-) -> (FunctionId, GenericArgs<'db>) {
+) -> (Either<FunctionId, (BuiltinDeriveImplId, BuiltinDeriveImplMethod)>, GenericArgs<'db>) {
let interner = DbInterner::new_with(db, env.krate);
let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
let ItemContainerId::TraitId(trait_id) = func.loc(db).container else {
- return (func, fn_subst);
+ return (Either::Left(func), fn_subst);
};
let trait_params = db.generic_params(trait_id.into()).len();
let trait_ref = TraitRef::new_from_args(
@@ -434,16 +440,19 @@ pub(crate) fn lookup_impl_method_query<'db>(
);
let name = &db.function_signature(func).name;
- let Some((impl_fn, impl_subst)) = lookup_impl_assoc_item_for_trait_ref(
- &infcx,
- trait_ref,
- env.param_env,
- name,
- )
- .and_then(|assoc| {
- if let (AssocItemId::FunctionId(id), subst) = assoc { Some((id, subst)) } else { None }
- }) else {
- return (func, fn_subst);
+ let Some((impl_fn, impl_subst)) =
+ lookup_impl_assoc_item_for_trait_ref(&infcx, trait_ref, env.param_env, name).and_then(
+ |(assoc, impl_args)| {
+ let assoc = match assoc {
+ Either::Left(AssocItemId::FunctionId(id)) => Either::Left(id),
+ Either::Right(it) => Either::Right(it),
+ _ => return None,
+ };
+ Some((assoc, impl_args))
+ },
+ )
+ else {
+ return (Either::Left(func), fn_subst);
};
(
@@ -460,22 +469,33 @@ fn lookup_impl_assoc_item_for_trait_ref<'db>(
trait_ref: TraitRef<'db>,
env: ParamEnv<'db>,
name: &Name,
-) -> Option<(AssocItemId, GenericArgs<'db>)> {
+) -> Option<(Either<AssocItemId, (BuiltinDeriveImplId, BuiltinDeriveImplMethod)>, GenericArgs<'db>)>
+{
let (impl_id, impl_subst) = find_matching_impl(infcx, env, trait_ref)?;
+ let impl_id = match impl_id {
+ AnyImplId::ImplId(it) => it,
+ AnyImplId::BuiltinDeriveImplId(impl_) => {
+ return impl_
+ .loc(infcx.interner.db)
+ .trait_
+ .get_method(name.symbol())
+ .map(|method| (Either::Right((impl_, method)), impl_subst));
+ }
+ };
let item =
impl_id.impl_items(infcx.interner.db).items.iter().find_map(|(n, it)| match *it {
AssocItemId::FunctionId(f) => (n == name).then_some(AssocItemId::FunctionId(f)),
AssocItemId::ConstId(c) => (n == name).then_some(AssocItemId::ConstId(c)),
AssocItemId::TypeAliasId(_) => None,
})?;
- Some((item, impl_subst))
+ Some((Either::Left(item), impl_subst))
}
pub(crate) fn find_matching_impl<'db>(
infcx: &InferCtxt<'db>,
env: ParamEnv<'db>,
trait_ref: TraitRef<'db>,
-) -> Option<(ImplId, GenericArgs<'db>)> {
+) -> Option<(AnyImplId, GenericArgs<'db>)> {
let trait_ref = infcx.at(&ObligationCause::dummy(), env).deeply_normalize(trait_ref).ok()?;
let obligation = Obligation::new(infcx.interner, ObligationCause::dummy(), env, trait_ref);
@@ -635,13 +655,13 @@ impl InherentImpls {
#[derive(Debug, PartialEq)]
struct OneTraitImpls {
- non_blanket_impls: FxHashMap<SimplifiedType, Box<[ImplId]>>,
+ non_blanket_impls: FxHashMap<SimplifiedType, (Box<[ImplId]>, Box<[BuiltinDeriveImplId]>)>,
blanket_impls: Box<[ImplId]>,
}
#[derive(Default)]
struct OneTraitImplsBuilder {
- non_blanket_impls: FxHashMap<SimplifiedType, Vec<ImplId>>,
+ non_blanket_impls: FxHashMap<SimplifiedType, (Vec<ImplId>, Vec<BuiltinDeriveImplId>)>,
blanket_impls: Vec<ImplId>,
}
@@ -650,7 +670,9 @@ impl OneTraitImplsBuilder {
let mut non_blanket_impls = self
.non_blanket_impls
.into_iter()
- .map(|(self_ty, impls)| (self_ty, impls.into_boxed_slice()))
+ .map(|(self_ty, (impls, builtin_derive_impls))| {
+ (self_ty, (impls.into_boxed_slice(), builtin_derive_impls.into_boxed_slice()))
+ })
.collect::<FxHashMap<_, _>>();
non_blanket_impls.shrink_to_fit();
let blanket_impls = self.blanket_impls.into_boxed_slice();
@@ -691,8 +713,9 @@ impl TraitImpls {
impl TraitImpls {
fn collect_def_map(db: &dyn HirDatabase, def_map: &DefMap) -> Self {
+ let lang_items = hir_def::lang_item::lang_items(db, def_map.krate());
let mut map = FxHashMap::default();
- collect(db, def_map, &mut map);
+ collect(db, def_map, lang_items, &mut map);
let mut map = map
.into_iter()
.map(|(trait_id, trait_map)| (trait_id, trait_map.finish()))
@@ -703,6 +726,7 @@ impl TraitImpls {
fn collect(
db: &dyn HirDatabase,
def_map: &DefMap,
+ lang_items: &LangItems,
map: &mut FxHashMap<TraitId, OneTraitImplsBuilder>,
) {
for (_module_id, module_data) in def_map.modules() {
@@ -727,18 +751,29 @@ impl TraitImpls {
let entry = map.entry(trait_ref.def_id.0).or_default();
match simplify_type(interner, self_ty, TreatParams::InstantiateWithInfer) {
Some(self_ty) => {
- entry.non_blanket_impls.entry(self_ty).or_default().push(impl_id)
+ entry.non_blanket_impls.entry(self_ty).or_default().0.push(impl_id)
}
None => entry.blanket_impls.push(impl_id),
}
}
+ for impl_id in module_data.scope.builtin_derive_impls() {
+ let loc = impl_id.loc(db);
+ let Some(trait_id) = loc.trait_.get_id(lang_items) else { continue };
+ let entry = map.entry(trait_id).or_default();
+ let entry = entry
+ .non_blanket_impls
+ .entry(SimplifiedType::Adt(loc.adt.into()))
+ .or_default();
+ entry.1.push(impl_id);
+ }
+
// To better support custom derives, collect impls in all unnamed const items.
// const _: () = { ... };
for konst in module_data.scope.unnamed_consts() {
let body = db.body(konst.into());
for (_, block_def_map) in body.blocks(db) {
- collect(db, block_def_map, map);
+ collect(db, block_def_map, lang_items, map);
}
}
}
@@ -761,27 +796,41 @@ impl TraitImpls {
})
}
- pub fn for_trait_and_self_ty(&self, trait_: TraitId, self_ty: &SimplifiedType) -> &[ImplId] {
+ pub fn for_trait_and_self_ty(
+ &self,
+ trait_: TraitId,
+ self_ty: &SimplifiedType,
+ ) -> (&[ImplId], &[BuiltinDeriveImplId]) {
self.map
.get(&trait_)
.and_then(|map| map.non_blanket_impls.get(self_ty))
- .map(|it| &**it)
+ .map(|it| (&*it.0, &*it.1))
.unwrap_or_default()
}
- pub fn for_trait(&self, trait_: TraitId, mut callback: impl FnMut(&[ImplId])) {
+ pub fn for_trait(
+ &self,
+ trait_: TraitId,
+ mut callback: impl FnMut(Either<&[ImplId], &[BuiltinDeriveImplId]>),
+ ) {
if let Some(impls) = self.map.get(&trait_) {
- callback(&impls.blanket_impls);
+ callback(Either::Left(&impls.blanket_impls));
for impls in impls.non_blanket_impls.values() {
- callback(impls);
+ callback(Either::Left(&impls.0));
+ callback(Either::Right(&impls.1));
}
}
}
- pub fn for_self_ty(&self, self_ty: &SimplifiedType, mut callback: impl FnMut(&[ImplId])) {
+ pub fn for_self_ty(
+ &self,
+ self_ty: &SimplifiedType,
+ mut callback: impl FnMut(Either<&[ImplId], &[BuiltinDeriveImplId]>),
+ ) {
for for_trait in self.map.values() {
if let Some(for_ty) = for_trait.non_blanket_impls.get(self_ty) {
- callback(for_ty);
+ callback(Either::Left(&for_ty.0));
+ callback(Either::Right(&for_ty.1));
}
}
}
diff --git a/crates/hir-ty/src/method_resolution/probe.rs b/crates/hir-ty/src/method_resolution/probe.rs
index cb9b810686..4a7c7d9353 100644
--- a/crates/hir-ty/src/method_resolution/probe.rs
+++ b/crates/hir-ty/src/method_resolution/probe.rs
@@ -1001,7 +1001,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
self.with_impl_item(impl_def_id, |this, item| {
if !this.has_applicable_self(item) {
// No receiver declared. Not a candidate.
- this.record_static_candidate(CandidateSource::Impl(impl_def_id));
+ this.record_static_candidate(CandidateSource::Impl(impl_def_id.into()));
return;
}
this.push_candidate(
@@ -1490,7 +1490,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
/// so do not use to make a decision that may lead to a successful compilation.
fn candidate_source(&self, candidate: &Candidate<'db>, self_ty: Ty<'db>) -> CandidateSource {
match candidate.kind {
- InherentImplCandidate { impl_def_id, .. } => CandidateSource::Impl(impl_def_id),
+ InherentImplCandidate { impl_def_id, .. } => CandidateSource::Impl(impl_def_id.into()),
ObjectCandidate(trait_ref) | WhereClauseCandidate(trait_ref) => {
CandidateSource::Trait(trait_ref.def_id().0)
}
@@ -1524,7 +1524,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
fn candidate_source_from_pick(&self, pick: &Pick<'db>) -> CandidateSource {
match pick.kind {
- InherentImplPick(impl_) => CandidateSource::Impl(impl_),
+ InherentImplPick(impl_) => CandidateSource::Impl(impl_.into()),
ObjectPick(trait_) | TraitPick(trait_) => CandidateSource::Trait(trait_),
WhereClausePick(trait_ref) => CandidateSource::Trait(trait_ref.skip_binder().def_id.0),
}
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 2d1368858b..5de08313f4 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -77,12 +77,14 @@ macro_rules! from_bytes {
}).into())
};
}
+use from_bytes;
macro_rules! not_supported {
($it: expr) => {
- return Err(MirEvalError::NotSupported(format!($it)))
+ return Err($crate::mir::eval::MirEvalError::NotSupported(format!($it)))
};
}
+use not_supported;
#[derive(Debug, Default, Clone, PartialEq, Eq, GenericTypeVisitable)]
pub struct VTableMap<'db> {
@@ -2622,6 +2624,9 @@ impl<'db> Evaluator<'db> {
def,
generic_args,
);
+ let Either::Left(imp) = imp else {
+ not_supported!("evaluating builtin derive impls is not supported")
+ };
let mir_body = self
.db
diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs
index a47a8c4400..76c8701ea2 100644
--- a/crates/hir-ty/src/mir/eval/shim.rs
+++ b/crates/hir-ty/src/mir/eval/shim.rs
@@ -16,29 +16,14 @@ use crate::{
mir::eval::{
Address, AdtId, Arc, Evaluator, FunctionId, GenericArgs, HasModule, HirDisplay,
InternedClosure, Interval, IntervalAndTy, IntervalOrOwned, ItemContainerId, Layout, Locals,
- Lookup, MirEvalError, MirSpan, Mutability, Result, Ty, TyKind, pad16,
+ Lookup, MirEvalError, MirSpan, Mutability, Result, Ty, TyKind, from_bytes, not_supported,
+ pad16,
},
next_solver::Region,
};
mod simd;
-macro_rules! from_bytes {
- ($ty:tt, $value:expr) => {
- ($ty::from_le_bytes(match ($value).try_into() {
- Ok(it) => it,
- #[allow(unreachable_patterns)]
- Err(_) => return Err(MirEvalError::InternalError("mismatched size".into())),
- }))
- };
-}
-
-macro_rules! not_supported {
- ($it: expr) => {
- return Err(MirEvalError::NotSupported(format!($it)))
- };
-}
-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum EvalLangItem {
BeginPanic,
diff --git a/crates/hir-ty/src/mir/eval/shim/simd.rs b/crates/hir-ty/src/mir/eval/shim/simd.rs
index 3896917cab..e0b3e571b8 100644
--- a/crates/hir-ty/src/mir/eval/shim/simd.rs
+++ b/crates/hir-ty/src/mir/eval/shim/simd.rs
@@ -6,21 +6,6 @@ use crate::consteval::try_const_usize;
use super::*;
-macro_rules! from_bytes {
- ($ty:tt, $value:expr) => {
- ($ty::from_le_bytes(match ($value).try_into() {
- Ok(it) => it,
- Err(_) => return Err(MirEvalError::InternalError("mismatched size".into())),
- }))
- };
-}
-
-macro_rules! not_supported {
- ($it: expr) => {
- return Err(MirEvalError::NotSupported(format!($it)))
- };
-}
-
impl<'db> Evaluator<'db> {
fn detect_simd_ty(&self, ty: Ty<'db>) -> Result<'db, (usize, Ty<'db>)> {
match ty.kind() {
diff --git a/crates/hir-ty/src/next_solver/def_id.rs b/crates/hir-ty/src/next_solver/def_id.rs
index b6167b4a09..aa6caefc4a 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, CallableDefId, ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId,
- GeneralConstId, GenericDefId, HasModule, ImplId, ModuleId, StaticId, StructId, TraitId,
- TypeAliasId, UnionId, db::DefDatabase,
+ AdtId, AttrDefId, BuiltinDeriveImplId, CallableDefId, ConstId, DefWithBodyId, EnumId,
+ EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId, StaticId, StructId, TraitId,
+ TypeAliasId, UnionId,
};
use rustc_type_ir::inherent;
use stdx::impl_from;
@@ -24,6 +24,7 @@ pub enum SolverDefId {
ConstId(ConstId),
FunctionId(FunctionId),
ImplId(ImplId),
+ BuiltinDeriveImplId(BuiltinDeriveImplId),
StaticId(StaticId),
TraitId(TraitId),
TypeAliasId(TypeAliasId),
@@ -57,6 +58,7 @@ impl std::fmt::Debug for SolverDefId {
f.debug_tuple("FunctionId").field(&db.function_signature(id).name.as_str()).finish()
}
SolverDefId::ImplId(id) => f.debug_tuple("ImplId").field(&id).finish(),
+ SolverDefId::BuiltinDeriveImplId(id) => f.debug_tuple("ImplId").field(&id).finish(),
SolverDefId::StaticId(id) => {
f.debug_tuple("StaticId").field(&db.static_signature(id).name.as_str()).finish()
}
@@ -108,6 +110,7 @@ impl_from!(
ConstId,
FunctionId,
ImplId,
+ BuiltinDeriveImplId,
StaticId,
TraitId,
TypeAliasId,
@@ -170,7 +173,8 @@ impl TryFrom<SolverDefId> for AttrDefId {
SolverDefId::EnumVariantId(it) => Ok(it.into()),
SolverDefId::Ctor(Ctor::Struct(it)) => Ok(it.into()),
SolverDefId::Ctor(Ctor::Enum(it)) => Ok(it.into()),
- SolverDefId::InternedClosureId(_)
+ SolverDefId::BuiltinDeriveImplId(_)
+ | SolverDefId::InternedClosureId(_)
| SolverDefId::InternedCoroutineId(_)
| SolverDefId::InternedOpaqueTyId(_) => Err(()),
}
@@ -191,6 +195,7 @@ impl TryFrom<SolverDefId> for DefWithBodyId {
| SolverDefId::TraitId(_)
| SolverDefId::TypeAliasId(_)
| SolverDefId::ImplId(_)
+ | SolverDefId::BuiltinDeriveImplId(_)
| SolverDefId::InternedClosureId(_)
| SolverDefId::InternedCoroutineId(_)
| SolverDefId::Ctor(Ctor::Struct(_))
@@ -216,6 +221,7 @@ impl TryFrom<SolverDefId> for GenericDefId {
| SolverDefId::InternedCoroutineId(_)
| SolverDefId::InternedOpaqueTyId(_)
| SolverDefId::EnumVariantId(_)
+ | SolverDefId::BuiltinDeriveImplId(_)
| SolverDefId::Ctor(_) => return Err(()),
})
}
@@ -241,28 +247,6 @@ impl SolverDefId {
}
}
-impl HasModule for SolverDefId {
- fn module(&self, db: &dyn DefDatabase) -> ModuleId {
- match *self {
- SolverDefId::AdtId(id) => id.module(db),
- SolverDefId::ConstId(id) => id.module(db),
- SolverDefId::FunctionId(id) => id.module(db),
- SolverDefId::ImplId(id) => id.module(db),
- SolverDefId::StaticId(id) => id.module(db),
- SolverDefId::TraitId(id) => id.module(db),
- SolverDefId::TypeAliasId(id) => id.module(db),
- SolverDefId::InternedClosureId(id) => id.loc(db).0.module(db),
- SolverDefId::InternedCoroutineId(id) => id.loc(db).0.module(db),
- SolverDefId::InternedOpaqueTyId(id) => match id.loc(db) {
- crate::ImplTraitId::ReturnTypeImplTrait(owner, _) => owner.module(db),
- crate::ImplTraitId::TypeAliasImplTrait(owner, _) => owner.module(db),
- },
- SolverDefId::Ctor(Ctor::Enum(id)) | SolverDefId::EnumVariantId(id) => id.module(db),
- SolverDefId::Ctor(Ctor::Struct(id)) => id.module(db),
- }
- }
-}
-
impl<'db> inherent::DefId<DbInterner<'db>> for SolverDefId {
fn as_local(self) -> Option<SolverDefId> {
Some(self)
@@ -332,7 +316,6 @@ declare_id_wrapper!(TypeAliasIdWrapper, TypeAliasId);
declare_id_wrapper!(ClosureIdWrapper, InternedClosureId);
declare_id_wrapper!(CoroutineIdWrapper, InternedCoroutineId);
declare_id_wrapper!(AdtIdWrapper, AdtId);
-declare_id_wrapper!(ImplIdWrapper, ImplId);
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct GeneralConstIdWrapper(pub GeneralConstId);
@@ -433,3 +416,40 @@ impl<'db> inherent::DefId<DbInterner<'db>> for CallableIdWrapper {
true
}
}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum AnyImplId {
+ ImplId(ImplId),
+ BuiltinDeriveImplId(BuiltinDeriveImplId),
+}
+
+impl_from!(ImplId, BuiltinDeriveImplId for AnyImplId);
+
+impl From<AnyImplId> for SolverDefId {
+ #[inline]
+ fn from(value: AnyImplId) -> SolverDefId {
+ match value {
+ AnyImplId::ImplId(it) => it.into(),
+ AnyImplId::BuiltinDeriveImplId(it) => it.into(),
+ }
+ }
+}
+impl TryFrom<SolverDefId> for AnyImplId {
+ type Error = ();
+ #[inline]
+ fn try_from(value: SolverDefId) -> Result<Self, Self::Error> {
+ match value {
+ SolverDefId::ImplId(it) => Ok(it.into()),
+ SolverDefId::BuiltinDeriveImplId(it) => Ok(it.into()),
+ _ => Err(()),
+ }
+ }
+}
+impl<'db> inherent::DefId<DbInterner<'db>> for AnyImplId {
+ fn as_local(self) -> Option<SolverDefId> {
+ Some(self.into())
+ }
+ fn is_local(self) -> bool {
+ true
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/format_proof_tree.rs b/crates/hir-ty/src/next_solver/format_proof_tree.rs
index 59fb0d65c5..66da6d5400 100644
--- a/crates/hir-ty/src/next_solver/format_proof_tree.rs
+++ b/crates/hir-ty/src/next_solver/format_proof_tree.rs
@@ -1,8 +1,8 @@
use rustc_type_ir::{solve::GoalSource, solve::inspect::GoalEvaluation};
use serde_derive::{Deserialize, Serialize};
-use crate::next_solver::infer::InferCtxt;
use crate::next_solver::inspect::{InspectCandidate, InspectGoal};
+use crate::next_solver::{AnyImplId, infer::InferCtxt};
use crate::next_solver::{DbInterner, Span};
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -76,14 +76,31 @@ impl<'a, 'db> ProofTreeSerializer<'a, 'db> {
use rustc_type_ir::solve::inspect::ProbeKind;
match candidate.kind() {
ProbeKind::TraitCandidate { source, .. } => {
+ use hir_def::{Lookup, src::HasSource};
use rustc_type_ir::solve::CandidateSource;
+ let db = self.infcx.interner.db;
match source {
- CandidateSource::Impl(impl_def_id) => {
- use hir_def::{Lookup, src::HasSource};
- let db = self.infcx.interner.db;
- let impl_src = impl_def_id.0.lookup(db).source(db);
- Some(impl_src.value.to_string())
- }
+ CandidateSource::Impl(impl_def_id) => match impl_def_id {
+ AnyImplId::ImplId(impl_def_id) => {
+ let impl_src = impl_def_id.lookup(db).source(db);
+ Some(impl_src.value.to_string())
+ }
+ AnyImplId::BuiltinDeriveImplId(impl_id) => {
+ let impl_loc = impl_id.loc(db);
+ let adt_src = match impl_loc.adt {
+ hir_def::AdtId::StructId(adt) => {
+ adt.loc(db).source(db).value.to_string()
+ }
+ hir_def::AdtId::UnionId(adt) => {
+ adt.loc(db).source(db).value.to_string()
+ }
+ hir_def::AdtId::EnumId(adt) => {
+ adt.loc(db).source(db).value.to_string()
+ }
+ };
+ Some(format!("#[derive(${})]\n{}", impl_loc.trait_.name(), adt_src))
+ }
+ },
_ => None,
}
}
diff --git a/crates/hir-ty/src/next_solver/generics.rs b/crates/hir-ty/src/next_solver/generics.rs
index 4d164a7e3b..a8288b4e82 100644
--- a/crates/hir-ty/src/next_solver/generics.rs
+++ b/crates/hir-ty/src/next_solver/generics.rs
@@ -4,14 +4,15 @@ use hir_def::{
ConstParamId, GenericDefId, GenericParamId, LifetimeParamId, TypeOrConstParamId, TypeParamId,
hir::generics::{GenericParams, TypeOrConstParamData},
};
+use rustc_type_ir::inherent::GenericsOf;
-use crate::{db::HirDatabase, generics::parent_generic_def};
+use crate::generics::parent_generic_def;
use super::SolverDefId;
use super::DbInterner;
-pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics {
+pub(crate) fn generics(interner: DbInterner<'_>, def: SolverDefId) -> Generics {
let mk_lt = |parent, index, local_id| {
let id = GenericParamId::LifetimeParamId(LifetimeParamId { parent, local_id });
GenericParamDef { index, id }
@@ -50,6 +51,7 @@ pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics {
result
};
+ let db = interner.db;
let (parent, own_params) = match (def.try_into(), def) {
(Ok(def), _) => (
parent_generic_def(db, def),
@@ -66,9 +68,12 @@ pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics {
}
}
}
+ (_, SolverDefId::BuiltinDeriveImplId(id)) => {
+ return crate::builtin_derive::generics_of(interner, id);
+ }
_ => panic!("No generics for {def:?}"),
};
- let parent_generics = parent.map(|def| Box::new(generics(db, def.into())));
+ let parent_generics = parent.map(|def| Box::new(generics(interner, def.into())));
Generics {
parent,
@@ -84,6 +89,13 @@ pub struct Generics {
pub own_params: Vec<GenericParamDef>,
}
+impl Generics {
+ pub(crate) fn push_param(&mut self, id: GenericParamId) {
+ let index = self.count() as u32;
+ self.own_params.push(GenericParamDef { index, id });
+ }
+}
+
#[derive(Debug)]
pub struct GenericParamDef {
index: u32,
diff --git a/crates/hir-ty/src/next_solver/infer/select.rs b/crates/hir-ty/src/next_solver/infer/select.rs
index 52ad410df6..bd407fd157 100644
--- a/crates/hir-ty/src/next_solver/infer/select.rs
+++ b/crates/hir-ty/src/next_solver/infer/select.rs
@@ -2,7 +2,7 @@
use std::ops::ControlFlow;
-use hir_def::{ImplId, TraitId};
+use hir_def::TraitId;
use macros::{TypeFoldable, TypeVisitable};
use rustc_type_ir::{
Interner,
@@ -12,7 +12,7 @@ use rustc_type_ir::{
use crate::{
db::InternedOpaqueTyId,
next_solver::{
- Const, ErrorGuaranteed, GenericArgs, Goal, TraitRef, Ty, TypeError,
+ AnyImplId, Const, ErrorGuaranteed, GenericArgs, Goal, TraitRef, Ty, TypeError,
infer::{
InferCtxt,
select::EvaluationResult::*,
@@ -249,7 +249,7 @@ impl<'db, N> ImplSource<'db, N> {
pub(crate) struct ImplSourceUserDefinedData<'db, N> {
#[type_visitable(ignore)]
#[type_foldable(identity)]
- pub(crate) impl_def_id: ImplId,
+ pub(crate) impl_def_id: AnyImplId,
pub(crate) args: GenericArgs<'db>,
pub(crate) nested: Vec<N>,
}
@@ -395,7 +395,7 @@ fn to_selection<'db>(cand: InspectCandidate<'_, 'db>) -> Option<Selection<'db>>
// FIXME: Remove this in favor of storing this in the tree
// For impl candidates, we do the rematch manually to compute the args.
ImplSource::UserDefined(ImplSourceUserDefinedData {
- impl_def_id: impl_def_id.0,
+ impl_def_id,
args: cand.instantiate_impl_args(),
nested,
})
diff --git a/crates/hir-ty/src/next_solver/interner.rs b/crates/hir-ty/src/next_solver/interner.rs
index 2ebc5b81ba..2a3df1d32a 100644
--- a/crates/hir-ty/src/next_solver/interner.rs
+++ b/crates/hir-ty/src/next_solver/interner.rs
@@ -38,10 +38,10 @@ use crate::{
lower::GenericPredicates,
method_resolution::TraitImpls,
next_solver::{
- AdtIdWrapper, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper,
- CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, GeneralConstIdWrapper, ImplIdWrapper,
- OpaqueTypeKey, RegionAssumptions, SimplifiedType, SolverContext, SolverDefIds,
- TraitIdWrapper, TypeAliasIdWrapper, UnevaluatedConst, util::explicit_item_bounds,
+ AdtIdWrapper, AnyImplId, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper,
+ CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, GeneralConstIdWrapper, OpaqueTypeKey,
+ RegionAssumptions, SimplifiedType, SolverContext, SolverDefIds, TraitIdWrapper,
+ TypeAliasIdWrapper, UnevaluatedConst, util::explicit_item_bounds,
},
};
@@ -1020,7 +1020,7 @@ impl<'db> Interner for DbInterner<'db> {
type CoroutineClosureId = CoroutineIdWrapper;
type CoroutineId = CoroutineIdWrapper;
type AdtId = AdtIdWrapper;
- type ImplId = ImplIdWrapper;
+ type ImplId = AnyImplId;
type UnevaluatedConstId = GeneralConstIdWrapper;
type Span = Span;
@@ -1164,7 +1164,7 @@ impl<'db> Interner for DbInterner<'db> {
}
fn generics_of(self, def_id: Self::DefId) -> Self::GenericsOf {
- generics(self.db(), def_id)
+ generics(self, def_id)
}
fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf {
@@ -1190,6 +1190,7 @@ impl<'db> Interner for DbInterner<'db> {
| SolverDefId::TraitId(_)
| SolverDefId::TypeAliasId(_)
| SolverDefId::ImplId(_)
+ | SolverDefId::BuiltinDeriveImplId(_)
| SolverDefId::InternedClosureId(_)
| SolverDefId::InternedCoroutineId(_) => {
return VariancesOf::empty(self);
@@ -1327,6 +1328,7 @@ impl<'db> Interner for DbInterner<'db> {
| SolverDefId::AdtId(_)
| SolverDefId::TraitId(_)
| SolverDefId::ImplId(_)
+ | SolverDefId::BuiltinDeriveImplId(_)
| SolverDefId::EnumVariantId(..)
| SolverDefId::Ctor(..)
| SolverDefId::InternedOpaqueTyId(..) => panic!(),
@@ -1445,8 +1447,7 @@ impl<'db> Interner for DbInterner<'db> {
self,
def_id: Self::DefId,
) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> {
- GenericPredicates::query_all(self.db, def_id.try_into().unwrap())
- .map_bound(|it| it.iter().copied())
+ predicates_of(self.db, def_id).all_predicates().map_bound(|it| it.iter().copied())
}
#[tracing::instrument(level = "debug", skip(self), ret)]
@@ -1454,8 +1455,7 @@ impl<'db> Interner for DbInterner<'db> {
self,
def_id: Self::DefId,
) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> {
- GenericPredicates::query_own(self.db, def_id.try_into().unwrap())
- .map_bound(|it| it.iter().copied())
+ predicates_of(self.db, def_id).own_predicates().map_bound(|it| it.iter().copied())
}
#[tracing::instrument(skip(self), ret)]
@@ -1500,32 +1500,30 @@ impl<'db> Interner for DbInterner<'db> {
}
}
- GenericPredicates::query_explicit(self.db, def_id.try_into().unwrap()).map_bound(
- |predicates| {
- predicates
- .iter()
- .copied()
- .filter(|p| match p.kind().skip_binder() {
- ClauseKind::Trait(it) => is_self_or_assoc(it.self_ty()),
- ClauseKind::TypeOutlives(it) => is_self_or_assoc(it.0),
- ClauseKind::Projection(it) => is_self_or_assoc(it.self_ty()),
- ClauseKind::HostEffect(it) => is_self_or_assoc(it.self_ty()),
- // FIXME: Not sure is this correct to allow other clauses but we might replace
- // `generic_predicates_ns` query here with something closer to rustc's
- // `implied_bounds_with_filter`, which is more granular lowering than this
- // "lower at once and then filter" implementation.
- _ => true,
- })
- .map(|p| (p, Span::dummy()))
- },
- )
+ predicates_of(self.db, def_id).explicit_predicates().map_bound(|predicates| {
+ predicates
+ .iter()
+ .copied()
+ .filter(|p| match p.kind().skip_binder() {
+ ClauseKind::Trait(it) => is_self_or_assoc(it.self_ty()),
+ ClauseKind::TypeOutlives(it) => is_self_or_assoc(it.0),
+ ClauseKind::Projection(it) => is_self_or_assoc(it.self_ty()),
+ ClauseKind::HostEffect(it) => is_self_or_assoc(it.self_ty()),
+ // FIXME: Not sure is this correct to allow other clauses but we might replace
+ // `generic_predicates_ns` query here with something closer to rustc's
+ // `implied_bounds_with_filter`, which is more granular lowering than this
+ // "lower at once and then filter" implementation.
+ _ => true,
+ })
+ .map(|p| (p, Span::dummy()))
+ })
}
fn impl_super_outlives(
self,
impl_id: Self::ImplId,
) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> {
- let trait_ref = self.db().impl_trait(impl_id.0).expect("expected an impl of trait");
+ let trait_ref = self.impl_trait_ref(impl_id);
trait_ref.map_bound(|trait_ref| {
let clause: Clause<'_> = trait_ref.upcast(self);
elaborate(self, [clause]).filter(|clause| {
@@ -1790,6 +1788,7 @@ impl<'db> Interner for DbInterner<'db> {
SolverDefId::ConstId(_)
| SolverDefId::FunctionId(_)
| SolverDefId::ImplId(_)
+ | SolverDefId::BuiltinDeriveImplId(_)
| SolverDefId::StaticId(_)
| SolverDefId::InternedClosureId(_)
| SolverDefId::InternedCoroutineId(_)
@@ -1805,7 +1804,12 @@ impl<'db> Interner for DbInterner<'db> {
type_block,
trait_block,
&mut |impls| {
- for &impl_ in impls.for_trait_and_self_ty(trait_def_id.0, &simp) {
+ let (regular_impls, builtin_derive_impls) =
+ impls.for_trait_and_self_ty(trait_def_id.0, &simp);
+ for &impl_ in regular_impls {
+ f(impl_.into());
+ }
+ for &impl_ in builtin_derive_impls {
f(impl_.into());
}
},
@@ -1927,7 +1931,10 @@ impl<'db> Interner for DbInterner<'db> {
}
fn impl_is_default(self, impl_def_id: Self::ImplId) -> bool {
- self.db.impl_signature(impl_def_id.0).is_default()
+ match impl_def_id {
+ AnyImplId::ImplId(impl_id) => self.db.impl_signature(impl_id).is_default(),
+ AnyImplId::BuiltinDeriveImplId(_) => false,
+ }
}
#[tracing::instrument(skip(self), ret)]
@@ -1935,14 +1942,24 @@ impl<'db> Interner for DbInterner<'db> {
self,
impl_id: Self::ImplId,
) -> EarlyBinder<Self, rustc_type_ir::TraitRef<Self>> {
- let db = self.db();
- db.impl_trait(impl_id.0)
- // ImplIds for impls where the trait ref can't be resolved should never reach trait solving
- .expect("invalid impl passed to trait solver")
+ match impl_id {
+ AnyImplId::ImplId(impl_id) => {
+ let db = self.db();
+ db.impl_trait(impl_id)
+ // ImplIds for impls where the trait ref can't be resolved should never reach trait solving
+ .expect("invalid impl passed to trait solver")
+ }
+ AnyImplId::BuiltinDeriveImplId(impl_id) => {
+ crate::builtin_derive::impl_trait(self, impl_id)
+ }
+ }
}
fn impl_polarity(self, impl_id: Self::ImplId) -> rustc_type_ir::ImplPolarity {
- let impl_data = self.db().impl_signature(impl_id.0);
+ let AnyImplId::ImplId(impl_id) = impl_id else {
+ return ImplPolarity::Positive;
+ };
+ let impl_data = self.db().impl_signature(impl_id);
if impl_data.flags.contains(ImplFlags::NEGATIVE) {
ImplPolarity::Negative
} else {
@@ -2230,11 +2247,13 @@ impl<'db> Interner for DbInterner<'db> {
specializing_impl_def_id: Self::ImplId,
parent_impl_def_id: Self::ImplId,
) -> bool {
- crate::specialization::specializes(
- self.db,
- specializing_impl_def_id.0,
- parent_impl_def_id.0,
- )
+ let (AnyImplId::ImplId(specializing_impl_def_id), AnyImplId::ImplId(parent_impl_def_id)) =
+ (specializing_impl_def_id, parent_impl_def_id)
+ else {
+ // No builtin derive allow specialization currently.
+ return false;
+ };
+ crate::specialization::specializes(self.db, specializing_impl_def_id, parent_impl_def_id)
}
fn next_trait_solver_globally(self) -> bool {
@@ -2349,6 +2368,14 @@ impl<'db> DbInterner<'db> {
}
}
+fn predicates_of(db: &dyn HirDatabase, def_id: SolverDefId) -> &GenericPredicates {
+ if let SolverDefId::BuiltinDeriveImplId(impl_) = def_id {
+ crate::builtin_derive::predicates(db, impl_)
+ } else {
+ GenericPredicates::query(db, def_id.try_into().unwrap())
+ }
+}
+
macro_rules! TrivialTypeTraversalImpls {
($($ty:ty,)+) => {
$(
@@ -2396,7 +2423,7 @@ TrivialTypeTraversalImpls! {
ClosureIdWrapper,
CoroutineIdWrapper,
AdtIdWrapper,
- ImplIdWrapper,
+ AnyImplId,
GeneralConstIdWrapper,
Safety,
FnAbi,
diff --git a/crates/hir-ty/src/next_solver/solver.rs b/crates/hir-ty/src/next_solver/solver.rs
index d800925ba4..21fbd64dd0 100644
--- a/crates/hir-ty/src/next_solver/solver.rs
+++ b/crates/hir-ty/src/next_solver/solver.rs
@@ -12,7 +12,7 @@ use rustc_type_ir::{
use tracing::debug;
use crate::next_solver::{
- AliasTy, CanonicalVarKind, Clause, ClauseKind, CoercePredicate, GenericArgs, ImplIdWrapper,
+ AliasTy, AnyImplId, CanonicalVarKind, Clause, ClauseKind, CoercePredicate, GenericArgs,
ParamEnv, Predicate, PredicateKind, SubtypePredicate, Ty, TyKind, fold::fold_tys,
util::sizedness_fast_path,
};
@@ -174,9 +174,13 @@ impl<'db> SolverDelegate for SolverContext<'db> {
&self,
_goal_trait_ref: rustc_type_ir::TraitRef<Self::Interner>,
trait_assoc_def_id: SolverDefId,
- impl_id: ImplIdWrapper,
+ impl_id: AnyImplId,
) -> Result<Option<SolverDefId>, ErrorGuaranteed> {
- let impl_items = impl_id.0.impl_items(self.0.interner.db());
+ let AnyImplId::ImplId(impl_id) = impl_id else {
+ // Builtin derive traits don't have type/consts assoc items.
+ return Ok(None);
+ };
+ let impl_items = impl_id.impl_items(self.0.interner.db());
let id =
match trait_assoc_def_id {
SolverDefId::TypeAliasId(trait_assoc_id) => {
diff --git a/crates/hir-ty/src/tests/incremental.rs b/crates/hir-ty/src/tests/incremental.rs
index 6558d2179f..1457bb2e10 100644
--- a/crates/hir-ty/src/tests/incremental.rs
+++ b/crates/hir-ty/src/tests/incremental.rs
@@ -243,6 +243,10 @@ $0",
"parse_shim",
"real_span_map_shim",
"TraitImpls::for_crate_",
+ "lang_items",
+ "crate_lang_items",
+ "AttrFlags::query_",
+ "AttrFlags::query_",
]
"#]],
);
@@ -279,6 +283,10 @@ pub struct NewStruct {
"real_span_map_shim",
"crate_local_def_map",
"TraitImpls::for_crate_",
+ "crate_lang_items",
+ "AttrFlags::query_",
+ "AttrFlags::query_",
+ "AttrFlags::query_",
]
"#]],
);
@@ -314,6 +322,10 @@ $0",
"parse_shim",
"real_span_map_shim",
"TraitImpls::for_crate_",
+ "lang_items",
+ "crate_lang_items",
+ "AttrFlags::query_",
+ "AttrFlags::query_",
]
"#]],
);
@@ -351,6 +363,13 @@ pub enum SomeEnum {
"real_span_map_shim",
"crate_local_def_map",
"TraitImpls::for_crate_",
+ "crate_lang_items",
+ "AttrFlags::query_",
+ "AttrFlags::query_",
+ "AttrFlags::query_",
+ "EnumVariants::of_",
+ "AttrFlags::query_",
+ "AttrFlags::query_",
]
"#]],
);
@@ -386,6 +405,10 @@ $0",
"parse_shim",
"real_span_map_shim",
"TraitImpls::for_crate_",
+ "lang_items",
+ "crate_lang_items",
+ "AttrFlags::query_",
+ "AttrFlags::query_",
]
"#]],
);
@@ -420,6 +443,9 @@ fn bar() -> f32 {
"real_span_map_shim",
"crate_local_def_map",
"TraitImpls::for_crate_",
+ "crate_lang_items",
+ "AttrFlags::query_",
+ "AttrFlags::query_",
]
"#]],
);
@@ -459,6 +485,11 @@ $0",
"parse_shim",
"real_span_map_shim",
"TraitImpls::for_crate_",
+ "lang_items",
+ "crate_lang_items",
+ "AttrFlags::query_",
+ "AttrFlags::query_",
+ "AttrFlags::query_",
]
"#]],
);
@@ -501,17 +532,16 @@ impl SomeStruct {
"real_span_map_shim",
"crate_local_def_map",
"TraitImpls::for_crate_",
- "AttrFlags::query_",
- "impl_trait_with_diagnostics_query",
- "impl_signature_shim",
- "impl_signature_with_source_map_shim",
- "lang_items",
"crate_lang_items",
+ "AttrFlags::query_",
"ImplItems::of_",
"AttrFlags::query_",
"AttrFlags::query_",
"AttrFlags::query_",
"AttrFlags::query_",
+ "impl_trait_with_diagnostics_query",
+ "impl_signature_shim",
+ "impl_signature_with_source_map_shim",
"impl_self_ty_with_diagnostics_query",
"struct_signature_shim",
"struct_signature_with_source_map_shim",
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs
index d1056f31e1..cba1b39e52 100644
--- a/crates/hir/src/attrs.rs
+++ b/crates/hir/src/attrs.rs
@@ -35,6 +35,8 @@ pub enum AttrsOwner {
Field(FieldId),
LifetimeParam(LifetimeParamId),
TypeOrConstParam(TypeOrConstParamId),
+ /// Things that do not have attributes. Used for builtin derives.
+ Dummy,
}
impl AttrsOwner {
@@ -123,7 +125,9 @@ impl AttrsWithOwner {
let owner = match self.owner {
AttrsOwner::AttrDef(it) => Either::Left(it),
AttrsOwner::Field(it) => Either::Right(it),
- AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => return &[],
+ AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) | AttrsOwner::Dummy => {
+ return &[];
+ }
};
self.attrs.doc_aliases(db, owner)
}
@@ -133,7 +137,9 @@ impl AttrsWithOwner {
let owner = match self.owner {
AttrsOwner::AttrDef(it) => Either::Left(it),
AttrsOwner::Field(it) => Either::Right(it),
- AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => return None,
+ AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) | AttrsOwner::Dummy => {
+ return None;
+ }
};
self.attrs.cfgs(db, owner)
}
@@ -143,7 +149,9 @@ impl AttrsWithOwner {
match self.owner {
AttrsOwner::AttrDef(it) => AttrFlags::docs(db, it).as_deref(),
AttrsOwner::Field(it) => AttrFlags::field_docs(db, it),
- AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => None,
+ AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) | AttrsOwner::Dummy => {
+ None
+ }
}
}
}
@@ -156,6 +164,9 @@ pub trait HasAttrs: Sized {
AttrsOwner::Field(it) => AttrsWithOwner::new_field(db, it),
AttrsOwner::LifetimeParam(it) => AttrsWithOwner::new_lifetime_param(db, it),
AttrsOwner::TypeOrConstParam(it) => AttrsWithOwner::new_type_or_const_param(db, it),
+ AttrsOwner::Dummy => {
+ AttrsWithOwner { attrs: AttrFlags::empty(), owner: AttrsOwner::Dummy }
+ }
}
}
@@ -167,7 +178,9 @@ pub trait HasAttrs: Sized {
match self.attr_id(db) {
AttrsOwner::AttrDef(it) => AttrFlags::docs(db, it).as_deref(),
AttrsOwner::Field(it) => AttrFlags::field_docs(db, it),
- AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => None,
+ AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) | AttrsOwner::Dummy => {
+ None
+ }
}
}
}
@@ -190,12 +203,28 @@ impl_has_attrs![
(Trait, TraitId),
(TypeAlias, TypeAliasId),
(Macro, MacroId),
- (Function, FunctionId),
(Adt, AdtId),
- (Impl, ImplId),
(ExternCrateDecl, ExternCrateId),
];
+impl HasAttrs for Function {
+ fn attr_id(self, _db: &dyn HirDatabase) -> AttrsOwner {
+ match self.id {
+ crate::AnyFunctionId::FunctionId(id) => AttrsOwner::AttrDef(id.into()),
+ crate::AnyFunctionId::BuiltinDeriveImplMethod { .. } => AttrsOwner::Dummy,
+ }
+ }
+}
+
+impl HasAttrs for Impl {
+ fn attr_id(self, _db: &dyn HirDatabase) -> AttrsOwner {
+ match self.id {
+ hir_ty::next_solver::AnyImplId::ImplId(id) => AttrsOwner::AttrDef(id.into()),
+ hir_ty::next_solver::AnyImplId::BuiltinDeriveImplId(..) => AttrsOwner::Dummy,
+ }
+ }
+}
+
macro_rules! impl_has_attrs_enum {
($($variant:ident),* for $enum:ident) => {$(
impl HasAttrs for $variant {
@@ -294,7 +323,9 @@ fn resolve_doc_path_on_(
AttrsOwner::AttrDef(AttrDefId::MacroId(it)) => it.resolver(db),
AttrsOwner::AttrDef(AttrDefId::ExternCrateId(it)) => it.resolver(db),
AttrsOwner::Field(it) => it.parent.resolver(db),
- AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => return None,
+ AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) | AttrsOwner::Dummy => {
+ return None;
+ }
};
let mut modpath = doc_modpath_from_str(link)?;
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index d0d8c4877d..1f9af564c3 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -2,19 +2,22 @@
use either::Either;
use hir_def::{
- AdtId, GenericDefId,
+ AdtId, BuiltinDeriveImplId, FunctionId, GenericDefId, ImplId, ItemContainerId,
+ builtin_derive::BuiltinDeriveImplMethod,
expr_store::ExpressionStore,
hir::generics::{GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate},
item_tree::FieldsShape,
signatures::{StaticFlags, TraitFlags},
type_ref::{TypeBound, TypeRef, TypeRefId},
};
+use hir_expand::name::Name;
use hir_ty::{
GenericPredicates,
db::HirDatabase,
display::{
HirDisplay, HirDisplayWithExpressionStore, HirFormatter, Result, SizedByDefault,
- hir_display_with_store, write_bounds_like_dyn_trait_with_prefix, write_visibility,
+ hir_display_with_store, write_bounds_like_dyn_trait_with_prefix, write_params_bounds,
+ write_visibility,
},
next_solver::ClauseKind,
};
@@ -22,25 +25,78 @@ use itertools::Itertools;
use rustc_type_ir::inherent::IntoKind;
use crate::{
- Adt, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Crate, Enum,
+ Adt, AnyFunctionId, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Crate, Enum,
ExternCrateDecl, Field, Function, GenericParam, HasCrate, HasVisibility, Impl, LifetimeParam,
Macro, Module, SelfParam, Static, Struct, StructKind, Trait, TraitRef, TupleField, Type,
TypeAlias, TypeNs, TypeOrConstParam, TypeParam, Union, Variant,
};
+fn write_builtin_derive_impl_method<'db>(
+ f: &mut HirFormatter<'_, 'db>,
+ impl_: BuiltinDeriveImplId,
+ method: BuiltinDeriveImplMethod,
+) -> Result {
+ let db = f.db;
+ let loc = impl_.loc(db);
+ let (adt_params, _adt_params_store) = db.generic_params_and_store(loc.adt.into());
+
+ if f.show_container_bounds() && !adt_params.is_empty() {
+ f.write_str("impl")?;
+ write_generic_params(loc.adt.into(), f)?;
+ f.write_char(' ')?;
+ let trait_id = loc.trait_.get_id(f.lang_items());
+ if let Some(trait_id) = trait_id {
+ f.start_location_link(trait_id.into());
+ }
+ write!(f, "{}", Name::new_symbol_root(loc.trait_.name()).display(db, f.edition()))?;
+ if trait_id.is_some() {
+ f.end_location_link();
+ }
+ f.write_str(" for ")?;
+ f.start_location_link(loc.adt.into());
+ write!(f, "{}", Adt::from(loc.adt).name(db).display(db, f.edition()))?;
+ f.end_location_link();
+ write_generic_args(loc.adt.into(), f)?;
+ f.write_char('\n')?;
+ }
+
+ let Some(trait_method) = method.trait_method(db, impl_) else {
+ return write!(f, "fn {}(…)", method.name());
+ };
+ let has_written_where = write_function(f, trait_method)?;
+
+ if f.show_container_bounds() && !adt_params.is_empty() {
+ if !has_written_where {
+ f.write_str("\nwhere")?
+ }
+ write!(f, "\n // Bounds from impl:")?;
+
+ let predicates =
+ hir_ty::builtin_derive::predicates(db, impl_).explicit_predicates().skip_binder();
+ write_params_bounds(f, predicates)?;
+ }
+
+ Ok(())
+}
+
impl<'db> HirDisplay<'db> for Function {
fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
+ let id = match self.id {
+ AnyFunctionId::FunctionId(id) => id,
+ AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => {
+ return write_builtin_derive_impl_method(f, impl_, method);
+ }
+ };
+
let db = f.db;
- let data = db.function_signature(self.id);
- let container = self.as_assoc_item(db).map(|it| it.container(db));
- let mut module = self.module(db);
+ let container = id.loc(db).container;
// Write container (trait or impl)
let container_params = match container {
- Some(AssocItemContainer::Trait(trait_)) => {
- let (params, params_store) = f.db.generic_params_and_store(trait_.id.into());
+ ItemContainerId::TraitId(trait_) => {
+ let (params, params_store) = f.db.generic_params_and_store(trait_.into());
if f.show_container_bounds() && !params.is_empty() {
- write_trait_header(&trait_, f)?;
+ write_trait_header(trait_.into(), f)?;
f.write_char('\n')?;
has_disaplayable_predicates(f.db, &params, &params_store)
.then_some((params, params_store))
@@ -48,10 +104,10 @@ impl<'db> HirDisplay<'db> for Function {
None
}
}
- Some(AssocItemContainer::Impl(impl_)) => {
- let (params, params_store) = f.db.generic_params_and_store(impl_.id.into());
+ ItemContainerId::ImplId(impl_) => {
+ let (params, params_store) = f.db.generic_params_and_store(impl_.into());
if f.show_container_bounds() && !params.is_empty() {
- write_impl_header(&impl_, f)?;
+ write_impl_header(impl_, f)?;
f.write_char('\n')?;
has_disaplayable_predicates(f.db, &params, &params_store)
.then_some((params, params_store))
@@ -59,140 +115,151 @@ impl<'db> HirDisplay<'db> for Function {
None
}
}
- None => None,
+ _ => None,
};
// Write signature of the function
- // Block-local impls are "hoisted" to the nearest (non-block) module.
- if let Some(AssocItemContainer::Impl(_)) = container {
- module = module.nearest_non_block_module(db);
+ let has_written_where = write_function(f, id)?;
+ if let Some((container_params, container_params_store)) = container_params {
+ if !has_written_where {
+ f.write_str("\nwhere")?;
+ }
+ let container_name = match container {
+ ItemContainerId::TraitId(_) => "trait",
+ ItemContainerId::ImplId(_) => "impl",
+ _ => unreachable!(),
+ };
+ write!(f, "\n // Bounds from {container_name}:",)?;
+ write_where_predicates(&container_params, &container_params_store, f)?;
}
- let module_id = module.id;
-
- write_visibility(module_id, self.visibility(db), f)?;
+ Ok(())
+ }
+}
- if data.is_default() {
- f.write_str("default ")?;
- }
- if data.is_const() {
- f.write_str("const ")?;
- }
- if data.is_async() {
- f.write_str("async ")?;
- }
- // FIXME: This will show `unsafe` for functions that are `#[target_feature]` but not unsafe
- // (they are conditionally unsafe to call). We probably should show something else.
- if self.is_unsafe_to_call(db, None, f.edition()) {
- f.write_str("unsafe ")?;
- }
- if let Some(abi) = &data.abi {
- write!(f, "extern \"{}\" ", abi.as_str())?;
- }
- write!(f, "fn {}", data.name.display(f.db, f.edition()))?;
+fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Result<bool> {
+ let db = f.db;
+ let func = Function::from(func_id);
+ let data = db.function_signature(func_id);
- write_generic_params(GenericDefId::FunctionId(self.id), f)?;
+ let mut module = func.module(db);
+ // Block-local impls are "hoisted" to the nearest (non-block) module.
+ if let ItemContainerId::ImplId(_) = func_id.loc(db).container {
+ module = module.nearest_non_block_module(db);
+ }
+ let module_id = module.id;
- f.write_char('(')?;
+ write_visibility(module_id, func.visibility(db), f)?;
- let mut first = true;
- let mut skip_self = 0;
- if let Some(self_param) = self.self_param(db) {
- self_param.hir_fmt(f)?;
- first = false;
- skip_self = 1;
- }
+ if data.is_default() {
+ f.write_str("default ")?;
+ }
+ if data.is_const() {
+ f.write_str("const ")?;
+ }
+ if data.is_async() {
+ f.write_str("async ")?;
+ }
+ // FIXME: This will show `unsafe` for functions that are `#[target_feature]` but not unsafe
+ // (they are conditionally unsafe to call). We probably should show something else.
+ if func.is_unsafe_to_call(db, None, f.edition()) {
+ f.write_str("unsafe ")?;
+ }
+ if let Some(abi) = &data.abi {
+ write!(f, "extern \"{}\" ", abi.as_str())?;
+ }
+ write!(f, "fn {}", data.name.display(f.db, f.edition()))?;
- // FIXME: Use resolved `param.ty` once we no longer discard lifetimes
- let body = db.body(self.id.into());
- for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)).skip(skip_self) {
- if !first {
- f.write_str(", ")?;
- } else {
- first = false;
- }
+ write_generic_params(GenericDefId::FunctionId(func_id), f)?;
- let pat_id = body.params[param.idx - body.self_param.is_some() as usize];
- let pat_str = body.pretty_print_pat(db, self.id.into(), pat_id, true, f.edition());
- f.write_str(&pat_str)?;
+ f.write_char('(')?;
- f.write_str(": ")?;
- type_ref.hir_fmt(f, &data.store)?;
+ let mut first = true;
+ let mut skip_self = 0;
+ if let Some(self_param) = func.self_param(db) {
+ self_param.hir_fmt(f)?;
+ first = false;
+ skip_self = 1;
+ }
+
+ // FIXME: Use resolved `param.ty` once we no longer discard lifetimes
+ let body = db.body(func_id.into());
+ for (type_ref, param) in data.params.iter().zip(func.assoc_fn_params(db)).skip(skip_self) {
+ if !first {
+ f.write_str(", ")?;
+ } else {
+ first = false;
}
- if data.is_varargs() {
- if !first {
- f.write_str(", ")?;
- }
- f.write_str("...")?;
- }
-
- f.write_char(')')?;
-
- // `FunctionData::ret_type` will be `::core::future::Future<Output = ...>` for async fns.
- // Use ugly pattern match to strip the Future trait.
- // Better way?
- let ret_type = if !data.is_async() {
- data.ret_type
- } else if let Some(ret_type) = data.ret_type {
- match &data.store[ret_type] {
- TypeRef::ImplTrait(bounds) => match &bounds[0] {
- &TypeBound::Path(path, _) => Some(
- *data.store[path]
- .segments()
- .iter()
- .last()
- .unwrap()
- .args_and_bindings
- .unwrap()
- .bindings[0]
- .type_ref
- .as_ref()
- .unwrap(),
- ),
- _ => None,
- },
+ let pat_id = body.params[param.idx - body.self_param.is_some() as usize];
+ let pat_str = body.pretty_print_pat(db, func_id.into(), pat_id, true, f.edition());
+ f.write_str(&pat_str)?;
+
+ f.write_str(": ")?;
+ type_ref.hir_fmt(f, &data.store)?;
+ }
+
+ if data.is_varargs() {
+ if !first {
+ f.write_str(", ")?;
+ }
+ f.write_str("...")?;
+ }
+
+ f.write_char(')')?;
+
+ // `FunctionData::ret_type` will be `::core::future::Future<Output = ...>` for async fns.
+ // Use ugly pattern match to strip the Future trait.
+ // Better way?
+ let ret_type = if !data.is_async() {
+ data.ret_type
+ } else if let Some(ret_type) = data.ret_type {
+ match &data.store[ret_type] {
+ TypeRef::ImplTrait(bounds) => match &bounds[0] {
+ &TypeBound::Path(path, _) => Some(
+ *data.store[path]
+ .segments()
+ .iter()
+ .last()
+ .unwrap()
+ .args_and_bindings
+ .unwrap()
+ .bindings[0]
+ .type_ref
+ .as_ref()
+ .unwrap(),
+ ),
_ => None,
- }
- } else {
- None
- };
-
- if let Some(ret_type) = ret_type {
- match &data.store[ret_type] {
- TypeRef::Tuple(tup) if tup.is_empty() => {}
- _ => {
- f.write_str(" -> ")?;
- ret_type.hir_fmt(f, &data.store)?;
- }
- }
+ },
+ _ => None,
}
+ } else {
+ None
+ };
- // Write where clauses
- let has_written_where = write_where_clause(GenericDefId::FunctionId(self.id), f)?;
- if let Some((container_params, container_params_store)) = container_params {
- if !has_written_where {
- f.write_str("\nwhere")?;
+ if let Some(ret_type) = ret_type {
+ match &data.store[ret_type] {
+ TypeRef::Tuple(tup) if tup.is_empty() => {}
+ _ => {
+ f.write_str(" -> ")?;
+ ret_type.hir_fmt(f, &data.store)?;
}
- let container_name = match container.unwrap() {
- AssocItemContainer::Trait(_) => "trait",
- AssocItemContainer::Impl(_) => "impl",
- };
- write!(f, "\n // Bounds from {container_name}:",)?;
- write_where_predicates(&container_params, &container_params_store, f)?;
}
- Ok(())
}
+
+ // Write where clauses
+ let has_written_where = write_where_clause(GenericDefId::FunctionId(func_id), f)?;
+ Ok(has_written_where)
}
-fn write_impl_header<'db>(impl_: &Impl, f: &mut HirFormatter<'_, 'db>) -> Result {
+fn write_impl_header<'db>(impl_: ImplId, f: &mut HirFormatter<'_, 'db>) -> Result {
let db = f.db;
f.write_str("impl")?;
- let def_id = GenericDefId::ImplId(impl_.id);
+ let def_id = GenericDefId::ImplId(impl_);
write_generic_params(def_id, f)?;
- let impl_data = db.impl_signature(impl_.id);
+ let impl_data = db.impl_signature(impl_);
if let Some(target_trait) = &impl_data.target_trait {
f.write_char(' ')?;
hir_display_with_store(&impl_data.store[target_trait.path], &impl_data.store).hir_fmt(f)?;
@@ -200,14 +267,28 @@ fn write_impl_header<'db>(impl_: &Impl, f: &mut HirFormatter<'_, 'db>) -> Result
}
f.write_char(' ')?;
- impl_.self_ty(db).hir_fmt(f)?;
+ Impl::from(impl_).self_ty(db).hir_fmt(f)?;
Ok(())
}
impl<'db> HirDisplay<'db> for SelfParam {
fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
- let data = f.db.function_signature(self.func);
+ let func = match self.func.id {
+ AnyFunctionId::FunctionId(id) => id,
+ AnyFunctionId::BuiltinDeriveImplMethod { method, .. } => match method {
+ BuiltinDeriveImplMethod::clone
+ | BuiltinDeriveImplMethod::fmt
+ | BuiltinDeriveImplMethod::hash
+ | BuiltinDeriveImplMethod::cmp
+ | BuiltinDeriveImplMethod::partial_cmp
+ | BuiltinDeriveImplMethod::eq => return f.write_str("&self"),
+ BuiltinDeriveImplMethod::default => {
+ unreachable!("this trait method does not have a self param")
+ }
+ },
+ };
+ let data = f.db.function_signature(func);
let param = *data.params.first().unwrap();
match &data.store[param] {
TypeRef::Path(p) if p.is_self_type() => f.write_str("self"),
@@ -553,6 +634,18 @@ impl<'db> HirDisplay<'db> for ConstParam {
}
fn write_generic_params<'db>(def: GenericDefId, f: &mut HirFormatter<'_, 'db>) -> Result {
+ write_generic_params_or_args(def, f, true)
+}
+
+fn write_generic_args<'db>(def: GenericDefId, f: &mut HirFormatter<'_, 'db>) -> Result {
+ write_generic_params_or_args(def, f, false)
+}
+
+fn write_generic_params_or_args<'db>(
+ def: GenericDefId,
+ f: &mut HirFormatter<'_, 'db>,
+ include_defaults: bool,
+) -> Result {
let (params, store) = f.db.generic_params_and_store(def);
if params.iter_lt().next().is_none()
&& params.iter_type_or_consts().all(|it| it.1.const_param().is_none())
@@ -587,7 +680,7 @@ fn write_generic_params<'db>(def: GenericDefId, f: &mut HirFormatter<'_, 'db>) -
}
delim(f)?;
write!(f, "{}", name.display(f.db, f.edition()))?;
- if let Some(default) = &ty.default {
+ if include_defaults && let Some(default) = &ty.default {
f.write_str(" = ")?;
default.hir_fmt(f, &store)?;
}
@@ -597,7 +690,7 @@ fn write_generic_params<'db>(def: GenericDefId, f: &mut HirFormatter<'_, 'db>) -
write!(f, "const {}: ", name.display(f.db, f.edition()))?;
c.ty.hir_fmt(f, &store)?;
- if let Some(default) = &c.default {
+ if include_defaults && let Some(default) = &c.default {
f.write_str(" = ")?;
default.hir_fmt(f, &store)?;
}
@@ -746,7 +839,7 @@ impl<'db> HirDisplay<'db> for TraitRef<'db> {
impl<'db> HirDisplay<'db> for Trait {
fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
// FIXME(trait-alias) needs special handling to print the equal sign
- write_trait_header(self, f)?;
+ write_trait_header(*self, f)?;
let def_id = GenericDefId::TraitId(self.id);
let has_where_clause = write_where_clause(def_id, f)?;
@@ -783,7 +876,7 @@ impl<'db> HirDisplay<'db> for Trait {
}
}
-fn write_trait_header<'db>(trait_: &Trait, f: &mut HirFormatter<'_, 'db>) -> Result {
+fn write_trait_header<'db>(trait_: Trait, f: &mut HirFormatter<'_, 'db>) -> Result {
write_visibility(trait_.module(f.db).id, trait_.visibility(f.db), f)?;
let data = f.db.trait_signature(trait_.id);
if data.flags.contains(TraitFlags::UNSAFE) {
diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs
index bc025c5ef5..fc20f4b46b 100644
--- a/crates/hir/src/from_id.rs
+++ b/crates/hir/src/from_id.rs
@@ -4,14 +4,15 @@
//! are splitting the hir.
use hir_def::{
- AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, GenericParamId,
- ModuleDefId, VariantId,
+ AdtId, AssocItemId, BuiltinDeriveImplId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId,
+ GenericParamId, ModuleDefId, VariantId,
hir::{BindingId, LabelId},
};
+use hir_ty::next_solver::AnyImplId;
use crate::{
- Adt, AssocItem, BuiltinType, DefWithBody, Field, GenericDef, GenericParam, ItemInNs, Label,
- Local, ModuleDef, Variant, VariantDef,
+ Adt, AnyFunctionId, AssocItem, BuiltinType, DefWithBody, Field, GenericDef, GenericParam,
+ ItemInNs, Label, Local, ModuleDef, Variant, VariantDef,
};
macro_rules! from_id {
@@ -39,8 +40,8 @@ from_id![
(hir_def::TraitId, crate::Trait),
(hir_def::StaticId, crate::Static),
(hir_def::ConstId, crate::Const),
- (hir_def::FunctionId, crate::Function),
- (hir_def::ImplId, crate::Impl),
+ (crate::AnyFunctionId, crate::Function),
+ (hir_ty::next_solver::AnyImplId, crate::Impl),
(hir_def::TypeOrConstParamId, crate::TypeOrConstParam),
(hir_def::TypeParamId, crate::TypeParam),
(hir_def::ConstParamId, crate::ConstParam),
@@ -119,11 +120,15 @@ impl From<ModuleDefId> for ModuleDef {
}
}
-impl From<ModuleDef> for ModuleDefId {
- fn from(id: ModuleDef) -> Self {
- match id {
+impl TryFrom<ModuleDef> for ModuleDefId {
+ type Error = ();
+ fn try_from(id: ModuleDef) -> Result<Self, Self::Error> {
+ Ok(match id {
ModuleDef::Module(it) => ModuleDefId::ModuleId(it.into()),
- ModuleDef::Function(it) => ModuleDefId::FunctionId(it.into()),
+ ModuleDef::Function(it) => match it.id {
+ AnyFunctionId::FunctionId(it) => it.into(),
+ AnyFunctionId::BuiltinDeriveImplMethod { .. } => return Err(()),
+ },
ModuleDef::Adt(it) => ModuleDefId::AdtId(it.into()),
ModuleDef::Variant(it) => ModuleDefId::EnumVariantId(it.into()),
ModuleDef::Const(it) => ModuleDefId::ConstId(it.into()),
@@ -132,18 +137,22 @@ impl From<ModuleDef> for ModuleDefId {
ModuleDef::TypeAlias(it) => ModuleDefId::TypeAliasId(it.into()),
ModuleDef::BuiltinType(it) => ModuleDefId::BuiltinType(it.into()),
ModuleDef::Macro(it) => ModuleDefId::MacroId(it.into()),
- }
+ })
}
}
-impl From<DefWithBody> for DefWithBodyId {
- fn from(def: DefWithBody) -> Self {
- match def {
- DefWithBody::Function(it) => DefWithBodyId::FunctionId(it.id),
+impl TryFrom<DefWithBody> for DefWithBodyId {
+ type Error = ();
+ fn try_from(def: DefWithBody) -> Result<Self, ()> {
+ Ok(match def {
+ DefWithBody::Function(it) => match it.id {
+ AnyFunctionId::FunctionId(it) => it.into(),
+ AnyFunctionId::BuiltinDeriveImplMethod { .. } => return Err(()),
+ },
DefWithBody::Static(it) => DefWithBodyId::StaticId(it.id),
DefWithBody::Const(it) => DefWithBodyId::ConstId(it.id),
DefWithBody::Variant(it) => DefWithBodyId::VariantId(it.into()),
- }
+ })
}
}
@@ -168,17 +177,11 @@ impl From<AssocItemId> for AssocItem {
}
}
-impl From<GenericDef> for GenericDefId {
- fn from(def: GenericDef) -> Self {
- match def {
- GenericDef::Function(it) => GenericDefId::FunctionId(it.id),
- GenericDef::Adt(it) => GenericDefId::AdtId(it.into()),
- GenericDef::Trait(it) => GenericDefId::TraitId(it.id),
- GenericDef::TypeAlias(it) => GenericDefId::TypeAliasId(it.id),
- GenericDef::Impl(it) => GenericDefId::ImplId(it.id),
- GenericDef::Const(it) => GenericDefId::ConstId(it.id),
- GenericDef::Static(it) => GenericDefId::StaticId(it.id),
- }
+impl TryFrom<GenericDef> for GenericDefId {
+ type Error = ();
+
+ fn try_from(def: GenericDef) -> Result<Self, Self::Error> {
+ def.id().ok_or(())
}
}
@@ -238,13 +241,17 @@ impl From<FieldId> for Field {
}
}
-impl From<AssocItem> for GenericDefId {
- fn from(item: AssocItem) -> Self {
- match item {
- AssocItem::Function(f) => f.id.into(),
+impl TryFrom<AssocItem> for GenericDefId {
+ type Error = ();
+ fn try_from(item: AssocItem) -> Result<Self, Self::Error> {
+ Ok(match item {
+ AssocItem::Function(f) => match f.id {
+ AnyFunctionId::FunctionId(it) => it.into(),
+ AnyFunctionId::BuiltinDeriveImplMethod { .. } => return Err(()),
+ },
AssocItem::Const(c) => c.id.into(),
AssocItem::TypeAlias(t) => t.id.into(),
- }
+ })
}
}
@@ -270,13 +277,14 @@ impl From<hir_def::item_scope::ItemInNs> for ItemInNs {
}
}
-impl From<ItemInNs> for hir_def::item_scope::ItemInNs {
- fn from(it: ItemInNs) -> Self {
- match it {
- ItemInNs::Types(it) => Self::Types(it.into()),
- ItemInNs::Values(it) => Self::Values(it.into()),
+impl TryFrom<ItemInNs> for hir_def::item_scope::ItemInNs {
+ type Error = ();
+ fn try_from(it: ItemInNs) -> Result<Self, Self::Error> {
+ Ok(match it {
+ ItemInNs::Types(it) => Self::Types(it.try_into()?),
+ ItemInNs::Values(it) => Self::Values(it.try_into()?),
ItemInNs::Macros(it) => Self::Macros(it.into()),
- }
+ })
}
}
@@ -291,3 +299,21 @@ impl From<BuiltinType> for hir_def::builtin_type::BuiltinType {
it.inner
}
}
+
+impl From<hir_def::ImplId> for crate::Impl {
+ fn from(value: hir_def::ImplId) -> Self {
+ crate::Impl { id: AnyImplId::ImplId(value) }
+ }
+}
+
+impl From<BuiltinDeriveImplId> for crate::Impl {
+ fn from(value: BuiltinDeriveImplId) -> Self {
+ crate::Impl { id: AnyImplId::BuiltinDeriveImplId(value) }
+ }
+}
+
+impl From<hir_def::FunctionId> for crate::Function {
+ fn from(value: hir_def::FunctionId) -> Self {
+ crate::Function { id: AnyFunctionId::FunctionId(value) }
+ }
+}
diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs
index 1aa7994001..09c5b1cca7 100644
--- a/crates/hir/src/has_source.rs
+++ b/crates/hir/src/has_source.rs
@@ -7,18 +7,18 @@ use hir_def::{
src::{HasChildSource, HasSource as _},
};
use hir_expand::{EditionedFileId, HirFileId, InFile};
-use hir_ty::db::InternedClosure;
-use syntax::ast;
+use hir_ty::{db::InternedClosure, next_solver::AnyImplId};
+use syntax::{AstNode, ast};
use tt::TextRange;
use crate::{
- Adt, Callee, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl,
+ Adt, AnyFunctionId, Callee, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl,
InlineAsmOperand, Label, LifetimeParam, LocalSource, Macro, Module, Param, SelfParam, Static,
Struct, Trait, TypeAlias, TypeOrConstParam, Union, Variant, VariantDef, db::HirDatabase,
};
-pub trait HasSource {
- type Ast;
+pub trait HasSource: Sized {
+ type Ast: AstNode;
/// Fetches the definition's source node.
/// Using [`crate::SemanticsImpl::source`] is preferred when working with [`crate::Semantics`],
/// as that caches the parsed file in the semantics' cache.
@@ -27,6 +27,20 @@ pub trait HasSource {
/// But we made this method `Option` to support rlib in the future
/// by <https://github.com/rust-lang/rust-analyzer/issues/6913>
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>>;
+
+ /// Fetches the source node, along with its full range.
+ ///
+ /// The reason for the separate existence of this method is that some things, notably builtin derive impls,
+ /// do not really have a source node, at least not of the correct type. But we still can trace them
+ /// to source code (the derive producing them). So this method will return the range if it is supported,
+ /// and if the node is supported too it will return it as well.
+ fn source_with_range(
+ self,
+ db: &dyn HirDatabase,
+ ) -> Option<InFile<(TextRange, Option<Self::Ast>)>> {
+ let source = self.source(db)?;
+ Some(source.map(|node| (node.syntax().text_range(), Some(node))))
+ }
}
/// NB: Module is !HasSource, because it has two source nodes at the same time:
@@ -146,7 +160,30 @@ impl HasSource for Variant {
impl HasSource for Function {
type Ast = ast::Fn;
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
- Some(self.id.lookup(db).source(db))
+ match self.id {
+ AnyFunctionId::FunctionId(id) => Some(id.loc(db).source(db)),
+ // When calling `source()`, we use the trait method source, but when calling `source_with_range()`,
+ // we return `None` as the syntax node source. This is relying on the assumption that if you are calling
+ // `source_with_range()` (e.g. in navigation) you're prepared to deal with no source node, while if
+ // you call `source()` maybe you don't - therefore we fall back to the trait method, to not lose features.
+ AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => method
+ .trait_method(db, impl_)
+ .and_then(|trait_method| Function::from(trait_method).source(db)),
+ }
+ }
+
+ fn source_with_range(
+ self,
+ db: &dyn HirDatabase,
+ ) -> Option<InFile<(TextRange, Option<Self::Ast>)>> {
+ match self.id {
+ AnyFunctionId::FunctionId(id) => Some(
+ id.loc(db).source(db).map(|source| (source.syntax().text_range(), Some(source))),
+ ),
+ AnyFunctionId::BuiltinDeriveImplMethod { impl_, .. } => {
+ Some(impl_.loc(db).source(db).map(|range| (range, None)))
+ }
+ }
}
}
impl HasSource for Const {
@@ -190,7 +227,24 @@ impl HasSource for Macro {
impl HasSource for Impl {
type Ast = ast::Impl;
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
- Some(self.id.lookup(db).source(db))
+ match self.id {
+ AnyImplId::ImplId(id) => Some(id.loc(db).source(db)),
+ AnyImplId::BuiltinDeriveImplId(_) => None,
+ }
+ }
+
+ fn source_with_range(
+ self,
+ db: &dyn HirDatabase,
+ ) -> Option<InFile<(TextRange, Option<Self::Ast>)>> {
+ match self.id {
+ AnyImplId::ImplId(id) => Some(
+ id.loc(db).source(db).map(|source| (source.syntax().text_range(), Some(source))),
+ ),
+ AnyImplId::BuiltinDeriveImplId(impl_) => {
+ Some(impl_.loc(db).source(db).map(|range| (range, None)))
+ }
+ }
}
}
@@ -224,7 +278,7 @@ impl HasSource for Param<'_> {
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
match self.func {
Callee::Def(CallableDefId::FunctionId(func)) => {
- let InFile { file_id, value } = Function { id: func }.source(db)?;
+ let InFile { file_id, value } = Function::from(func).source(db)?;
let params = value.param_list()?;
if let Some(self_param) = params.self_param() {
if let Some(idx) = self.idx.checked_sub(1) {
@@ -261,7 +315,7 @@ impl HasSource for SelfParam {
type Ast = ast::SelfParam;
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
- let InFile { file_id, value } = Function::from(self.func).source(db)?;
+ let InFile { file_id, value } = self.func.source(db)?;
value
.param_list()
.and_then(|params| params.self_param())
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 9fc29de4a1..6a19603923 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -48,12 +48,13 @@ use arrayvec::ArrayVec;
use base_db::{CrateDisplayName, CrateOrigin, LangCrateOrigin};
use either::Either;
use hir_def::{
- AdtId, AssocItemId, AssocItemLoc, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumId,
- EnumVariantId, ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId,
- HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalFieldId, Lookup, MacroExpander,
- MacroId, StaticId, StructId, SyntheticSyntax, TupleId, TypeAliasId, TypeOrConstParamId,
- TypeParamId, UnionId,
+ AdtId, AssocItemId, AssocItemLoc, BuiltinDeriveImplId, CallableDefId, ConstId, ConstParamId,
+ DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, FunctionId, GenericDefId,
+ GenericParamId, HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalFieldId, Lookup,
+ MacroExpander, MacroId, StaticId, StructId, SyntheticSyntax, TupleId, TypeAliasId,
+ TypeOrConstParamId, TypeParamId, UnionId,
attrs::AttrFlags,
+ builtin_derive::BuiltinDeriveImplMethod,
expr_store::{ExpressionStoreDiagnostics, ExpressionStoreSourceMap},
hir::{
BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, LabelId, Pat,
@@ -73,7 +74,8 @@ use hir_def::{
visibility::visibility_from_ast,
};
use hir_expand::{
- AstId, MacroCallKind, RenderedExpandError, ValueResult, proc_macro::ProcMacroKind,
+ AstId, MacroCallKind, RenderedExpandError, ValueResult, builtin::BuiltinDeriveExpander,
+ proc_macro::ProcMacroKind,
};
use hir_ty::{
GenericPredicates, InferenceResult, ParamEnvAndCrate, TyDefId, TyLoweringDiagnostic,
@@ -88,8 +90,9 @@ use hir_ty::{
},
mir::{MutBorrowKind, interpret_mir},
next_solver::{
- AliasTy, ClauseKind, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs,
- ParamEnv, PolyFnSig, Region, SolverDefId, Ty, TyKind, TypingMode,
+ AliasTy, AnyImplId, ClauseKind, ConstKind, DbInterner, EarlyBinder, EarlyParamRegion,
+ ErrorGuaranteed, GenericArg, GenericArgs, ParamConst, ParamEnv, PolyFnSig, Region,
+ RegionKind, SolverDefId, Ty, TyKind, TypingMode,
infer::{DbInternerInferExt, InferCtxt},
},
traits::{self, is_inherent_impl_coherent, structurally_normalize_ty},
@@ -97,7 +100,8 @@ use hir_ty::{
use itertools::Itertools;
use rustc_hash::FxHashSet;
use rustc_type_ir::{
- AliasTyKind, TypeSuperVisitable, TypeVisitable, TypeVisitor, fast_reject,
+ AliasTyKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable,
+ TypeVisitor, fast_reject,
inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _},
};
use smallvec::SmallVec;
@@ -105,7 +109,7 @@ use span::{AstIdNode, Edition, FileId};
use stdx::{format_to, impl_from, never, variance::PhantomCovariantLifetime};
use syntax::{
AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange, ToSmolStr,
- ast::{self, HasName, HasVisibility as _},
+ ast::{self, HasName as _, HasVisibility as _},
format_smolstr,
};
use triomphe::{Arc, ThinArc};
@@ -440,7 +444,10 @@ impl ModuleDef {
Adt::Union(it) => it.id.into(),
},
ModuleDef::Trait(it) => it.id.into(),
- ModuleDef::Function(it) => it.id.into(),
+ ModuleDef::Function(it) => match it.id {
+ AnyFunctionId::FunctionId(it) => it.into(),
+ AnyFunctionId::BuiltinDeriveImplMethod { .. } => return Vec::new(),
+ },
ModuleDef::TypeAlias(it) => it.id.into(),
ModuleDef::Module(it) => it.id.into(),
ModuleDef::Const(it) => it.id.into(),
@@ -504,7 +511,7 @@ impl ModuleDef {
pub fn attrs(&self, db: &dyn HirDatabase) -> Option<AttrsWithOwner> {
Some(match self {
ModuleDef::Module(it) => it.attrs(db),
- ModuleDef::Function(it) => it.attrs(db),
+ ModuleDef::Function(it) => HasAttrs::attrs(*it, db),
ModuleDef::Adt(it) => it.attrs(db),
ModuleDef::Variant(it) => it.attrs(db),
ModuleDef::Const(it) => it.attrs(db),
@@ -772,8 +779,11 @@ impl Module {
for impl_def in self.impl_defs(db) {
GenericDef::Impl(impl_def).diagnostics(db, acc);
- let loc = impl_def.id.lookup(db);
- let (impl_signature, source_map) = db.impl_signature_with_source_map(impl_def.id);
+ let AnyImplId::ImplId(impl_id) = impl_def.id else {
+ continue;
+ };
+ let loc = impl_id.lookup(db);
+ let (impl_signature, source_map) = db.impl_signature_with_source_map(impl_id);
expr_store_diagnostics(db, acc, &source_map);
let file_id = loc.id.file_id;
@@ -789,12 +799,12 @@ impl Module {
let ast_id_map = db.ast_id_map(file_id);
- for diag in impl_def.id.impl_items_with_diagnostics(db).1.iter() {
+ for diag in impl_id.impl_items_with_diagnostics(db).1.iter() {
emit_def_diagnostic(db, acc, diag, edition, loc.container.krate(db));
}
if impl_signature.target_trait.is_none()
- && !is_inherent_impl_coherent(db, def_map, impl_def.id)
+ && !is_inherent_impl_coherent(db, def_map, impl_id)
{
acc.push(IncoherentImpl { impl_: ast_id_map.get(loc.id.value), file_id }.into())
}
@@ -822,7 +832,7 @@ impl Module {
if drop_trait != trait_.into() {
return None;
}
- let parent = impl_def.id.into();
+ let parent = impl_id.into();
let (lifetimes_attrs, type_and_consts_attrs) =
AttrFlags::query_generic_params(db, parent);
let res = lifetimes_attrs.values().any(|it| it.contains(AttrFlags::MAY_DANGLE))
@@ -851,7 +861,7 @@ impl Module {
AssocItemId::ConstId(id) => !db.const_signature(id).has_body(),
AssocItemId::TypeAliasId(it) => db.type_alias_signature(it).ty.is_none(),
});
- impl_assoc_items_scratch.extend(impl_def.id.impl_items(db).items.iter().cloned());
+ impl_assoc_items_scratch.extend(impl_id.impl_items(db).items.iter().cloned());
let redundant = impl_assoc_items_scratch
.iter()
@@ -883,11 +893,11 @@ impl Module {
.collect();
if !missing.is_empty() {
- let self_ty = db.impl_self_ty(impl_def.id).instantiate_identity();
+ let self_ty = db.impl_self_ty(impl_id).instantiate_identity();
let self_ty = structurally_normalize_ty(
&infcx,
self_ty,
- db.trait_environment(impl_def.id.into()),
+ db.trait_environment(impl_id.into()),
);
let self_ty_is_guaranteed_unsized = matches!(
self_ty.kind(),
@@ -896,7 +906,13 @@ impl Module {
if self_ty_is_guaranteed_unsized {
missing.retain(|(_, assoc_item)| {
let assoc_item = match *assoc_item {
- AssocItem::Function(it) => it.id.into(),
+ AssocItem::Function(it) => match it.id {
+ AnyFunctionId::FunctionId(id) => id.into(),
+ AnyFunctionId::BuiltinDeriveImplMethod { .. } => {
+ never!("should not have an `AnyFunctionId::BuiltinDeriveImplMethod` here");
+ return false;
+ },
+ },
AssocItem::Const(it) => it.id.into(),
AssocItem::TypeAlias(it) => it.id.into(),
};
@@ -918,20 +934,15 @@ impl Module {
impl_assoc_items_scratch.clear();
}
+ push_ty_diagnostics(db, acc, db.impl_self_ty_with_diagnostics(impl_id).1, &source_map);
push_ty_diagnostics(
db,
acc,
- db.impl_self_ty_with_diagnostics(impl_def.id).1,
- &source_map,
- );
- push_ty_diagnostics(
- db,
- acc,
- db.impl_trait_with_diagnostics(impl_def.id).and_then(|it| it.1),
+ db.impl_trait_with_diagnostics(impl_id).and_then(|it| it.1),
&source_map,
);
- for &(_, item) in impl_def.id.impl_items(db).items.iter() {
+ for &(_, item) in impl_id.impl_items(db).items.iter() {
AssocItem::from(item).diagnostics(db, acc, style_lints);
}
}
@@ -955,7 +966,8 @@ impl Module {
pub fn impl_defs(self, db: &dyn HirDatabase) -> Vec<Impl> {
let def_map = self.id.def_map(db);
- def_map[self.id].scope.impls().map(Impl::from).collect()
+ let scope = &def_map[self.id].scope;
+ scope.impls().map(Impl::from).chain(scope.builtin_derive_impls().map(Impl::from)).collect()
}
/// Finds a path that can be used to refer to the given item from within
@@ -968,7 +980,7 @@ impl Module {
) -> Option<ModPath> {
hir_def::find_path::find_path(
db,
- item.into().into(),
+ item.into().try_into().ok()?,
self.into(),
PrefixKind::Plain,
false,
@@ -985,7 +997,14 @@ impl Module {
prefix_kind: PrefixKind,
cfg: FindPathConfig,
) -> Option<ModPath> {
- hir_def::find_path::find_path(db, item.into().into(), self.into(), prefix_kind, true, cfg)
+ hir_def::find_path::find_path(
+ db,
+ item.into().try_into().ok()?,
+ self.into(),
+ prefix_kind,
+ true,
+ cfg,
+ )
}
#[inline]
@@ -1863,9 +1882,9 @@ impl VariantDef {
pub fn name(&self, db: &dyn HirDatabase) -> Name {
match self {
- VariantDef::Struct(s) => s.name(db),
- VariantDef::Union(u) => u.name(db),
- VariantDef::Variant(e) => e.name(db),
+ VariantDef::Struct(s) => (*s).name(db),
+ VariantDef::Union(u) => (*u).name(db),
+ VariantDef::Variant(e) => (*e).name(db),
}
}
}
@@ -1909,24 +1928,33 @@ impl DefWithBody {
}
}
- fn id(&self) -> DefWithBodyId {
- match self {
- DefWithBody::Function(it) => it.id.into(),
+ fn id(&self) -> Option<DefWithBodyId> {
+ Some(match self {
+ DefWithBody::Function(it) => match it.id {
+ AnyFunctionId::FunctionId(id) => id.into(),
+ AnyFunctionId::BuiltinDeriveImplMethod { .. } => return None,
+ },
DefWithBody::Static(it) => it.id.into(),
DefWithBody::Const(it) => it.id.into(),
DefWithBody::Variant(it) => it.into(),
- }
+ })
}
/// A textual representation of the HIR of this def's body for debugging purposes.
pub fn debug_hir(self, db: &dyn HirDatabase) -> String {
- let body = db.body(self.id());
- body.pretty_print(db, self.id(), Edition::CURRENT)
+ let Some(id) = self.id() else {
+ return String::new();
+ };
+ let body = db.body(id);
+ body.pretty_print(db, id, Edition::CURRENT)
}
/// A textual representation of the MIR of this def's body for debugging purposes.
pub fn debug_mir(self, db: &dyn HirDatabase) -> String {
- let body = db.mir_body(self.id());
+ let Some(id) = self.id() else {
+ return String::new();
+ };
+ let body = db.mir_body(id);
match body {
Ok(body) => body.pretty_print(db, self.module(db).krate(db).to_display_target(db)),
Err(e) => format!("error:\n{e:?}"),
@@ -1939,11 +1967,17 @@ impl DefWithBody {
acc: &mut Vec<AnyDiagnostic<'db>>,
style_lints: bool,
) {
+ let Ok(id) = self.try_into() else {
+ return;
+ };
let krate = self.module(db).id.krate(db);
- let (body, source_map) = db.body_with_source_map(self.into());
+ let (body, source_map) = db.body_with_source_map(id);
let sig_source_map = match self {
- DefWithBody::Function(id) => db.function_signature_with_source_map(id.into()).1,
+ DefWithBody::Function(id) => match id.id {
+ AnyFunctionId::FunctionId(id) => db.function_signature_with_source_map(id).1,
+ AnyFunctionId::BuiltinDeriveImplMethod { .. } => return,
+ },
DefWithBody::Static(id) => db.static_signature_with_source_map(id.into()).1,
DefWithBody::Const(id) => db.const_signature_with_source_map(id.into()).1,
DefWithBody::Variant(variant) => {
@@ -1958,11 +1992,11 @@ impl DefWithBody {
expr_store_diagnostics(db, acc, &source_map);
- let infer = InferenceResult::for_body(db, self.into());
+ let infer = InferenceResult::for_body(db, id);
for d in infer.diagnostics() {
acc.extend(AnyDiagnostic::inference_diagnostic(
db,
- self.into(),
+ id,
d,
&source_map,
&sig_source_map,
@@ -1989,14 +2023,14 @@ impl DefWithBody {
acc.push(
TypeMismatch {
expr_or_pat,
- expected: Type::new(db, DefWithBodyId::from(self), mismatch.expected.as_ref()),
- actual: Type::new(db, DefWithBodyId::from(self), mismatch.actual.as_ref()),
+ expected: Type::new(db, id, mismatch.expected.as_ref()),
+ actual: Type::new(db, id, mismatch.actual.as_ref()),
}
.into(),
);
}
- let missing_unsafe = hir_ty::diagnostics::missing_unsafe(db, self.into());
+ let missing_unsafe = hir_ty::diagnostics::missing_unsafe(db, id);
for (node, reason) in missing_unsafe.unsafe_exprs {
match source_map.expr_or_pat_syntax(node) {
Ok(node) => acc.push(
@@ -2031,7 +2065,7 @@ impl DefWithBody {
}
}
- if let Ok(borrowck_results) = db.borrowck(self.into()) {
+ if let Ok(borrowck_results) = db.borrowck(id) {
for borrowck_result in borrowck_results.iter() {
let mir_body = &borrowck_result.mir_body;
for moof in &borrowck_result.moved_out_of_ref {
@@ -2088,7 +2122,7 @@ impl DefWithBody {
{
need_mut = &mir::MutabilityReason::Not;
}
- let local = Local { parent: self.into(), binding_id };
+ let local = Local { parent: id, binding_id };
let is_mut = body[binding_id].mode == BindingAnnotation::Mutable;
match (need_mut, is_mut) {
@@ -2144,17 +2178,11 @@ impl DefWithBody {
}
}
- for diagnostic in BodyValidationDiagnostic::collect(db, self.into(), style_lints) {
+ for diagnostic in BodyValidationDiagnostic::collect(db, id, style_lints) {
acc.extend(AnyDiagnostic::body_validation_diagnostic(db, diagnostic, &source_map));
}
- let def: ModuleDef = match self {
- DefWithBody::Function(it) => it.into(),
- DefWithBody::Static(it) => it.into(),
- DefWithBody::Const(it) => it.into(),
- DefWithBody::Variant(it) => it.into(),
- };
- for diag in hir_ty::diagnostics::incorrect_case(db, def.into()) {
+ for diag in hir_ty::diagnostics::incorrect_case(db, id.into()) {
acc.push(diag.into())
}
}
@@ -2192,45 +2220,181 @@ fn expr_store_diagnostics<'db>(
.macro_calls()
.for_each(|(_ast_id, call_id)| macro_call_diagnostics(db, call_id, acc));
}
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+enum AnyFunctionId {
+ FunctionId(FunctionId),
+ BuiltinDeriveImplMethod { method: BuiltinDeriveImplMethod, impl_: BuiltinDeriveImplId },
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Function {
- pub(crate) id: FunctionId,
+ pub(crate) id: AnyFunctionId,
+}
+
+impl fmt::Debug for Function {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Debug::fmt(&self.id, f)
+ }
}
impl Function {
pub fn module(self, db: &dyn HirDatabase) -> Module {
- self.id.module(db).into()
+ match self.id {
+ AnyFunctionId::FunctionId(id) => id.module(db).into(),
+ AnyFunctionId::BuiltinDeriveImplMethod { impl_, .. } => impl_.module(db).into(),
+ }
}
pub fn name(self, db: &dyn HirDatabase) -> Name {
- db.function_signature(self.id).name.clone()
+ match self.id {
+ AnyFunctionId::FunctionId(id) => db.function_signature(id).name.clone(),
+ AnyFunctionId::BuiltinDeriveImplMethod { method, .. } => {
+ Name::new_symbol_root(method.name())
+ }
+ }
}
pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> {
- Type::from_value_def(db, self.id)
+ match self.id {
+ AnyFunctionId::FunctionId(id) => Type::from_value_def(db, id),
+ AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => {
+ // Get the type for the trait function, as we can't get the type for the impl function
+ // because it has not `CallableDefId`.
+ let krate = impl_.module(db).krate(db);
+ let interner = DbInterner::new_with(db, krate);
+ let param_env = hir_ty::builtin_derive::param_env(interner, impl_);
+ let env = ParamEnvAndCrate { param_env, krate };
+ let Some(trait_method) = method.trait_method(db, impl_) else {
+ return Type { env, ty: Ty::new_error(interner, ErrorGuaranteed) };
+ };
+ Function::from(trait_method).ty(db)
+ }
+ }
}
pub fn fn_ptr_type(self, db: &dyn HirDatabase) -> Type<'_> {
- let resolver = self.id.resolver(db);
- let interner = DbInterner::new_no_crate(db);
- // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s.
- let callable_sig = db.callable_item_signature(self.id.into()).instantiate_identity();
- let ty = Ty::new_fn_ptr(interner, callable_sig);
- Type::new_with_resolver_inner(db, &resolver, ty)
+ match self.id {
+ AnyFunctionId::FunctionId(id) => {
+ let resolver = id.resolver(db);
+ let interner = DbInterner::new_no_crate(db);
+ // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s.
+ let callable_sig = db.callable_item_signature(id.into()).instantiate_identity();
+ let ty = Ty::new_fn_ptr(interner, callable_sig);
+ Type::new_with_resolver_inner(db, &resolver, ty)
+ }
+ AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => {
+ struct ParamsShifter<'db> {
+ interner: DbInterner<'db>,
+ shift_by: i32,
+ }
+
+ impl<'db> TypeFolder<DbInterner<'db>> for ParamsShifter<'db> {
+ fn cx(&self) -> DbInterner<'db> {
+ self.interner
+ }
+
+ fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
+ if let TyKind::Param(param) = ty.kind() {
+ Ty::new_param(
+ self.interner,
+ param.id,
+ param.index.checked_add_signed(self.shift_by).unwrap(),
+ )
+ } else {
+ ty.super_fold_with(self)
+ }
+ }
+
+ fn fold_const(
+ &mut self,
+ ct: hir_ty::next_solver::Const<'db>,
+ ) -> hir_ty::next_solver::Const<'db> {
+ if let ConstKind::Param(param) = ct.kind() {
+ hir_ty::next_solver::Const::new_param(
+ self.interner,
+ ParamConst {
+ id: param.id,
+ index: param.index.checked_add_signed(self.shift_by).unwrap(),
+ },
+ )
+ } else {
+ ct.super_fold_with(self)
+ }
+ }
+
+ fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
+ if let RegionKind::ReEarlyParam(param) = r.kind() {
+ Region::new_early_param(
+ self.interner,
+ EarlyParamRegion {
+ id: param.id,
+ index: param.index.checked_add_signed(self.shift_by).unwrap(),
+ },
+ )
+ } else {
+ r
+ }
+ }
+ }
+
+ // Get the type for the trait function, as we can't get the type for the impl function
+ // because it has not `CallableDefId`.
+ let krate = impl_.module(db).krate(db);
+ let interner = DbInterner::new_with(db, krate);
+ let param_env = hir_ty::builtin_derive::param_env(interner, impl_);
+ let env = ParamEnvAndCrate { param_env, krate };
+ let Some(trait_method) = method.trait_method(db, impl_) else {
+ return Type { env, ty: Ty::new_error(interner, ErrorGuaranteed) };
+ };
+ // The procedure works as follows: the method may have additional generic parameters (e.g. `Hash::hash()`),
+ // and we want them to be params of the impl method as well. So we start with the trait method identity
+ // args and extract from them the trait method own args. In parallel, we retrieve the impl trait ref.
+ // Now we can put our args as [...impl_trait_ref.args, ...trait_method_own_args], but we have one problem:
+ // the args in `trait_method_own_args` use indices appropriate for the trait method, which are not necessarily
+ // good for the impl method. So we shift them by `impl_generics_len - trait_generics_len`, which is essentially
+ // `impl_generics_len - impl_trait_ref.args.len()`.
+ let trait_method_fn_ptr = Ty::new_fn_ptr(
+ interner,
+ db.callable_item_signature(trait_method.into()).instantiate_identity(),
+ );
+ let impl_trait_ref =
+ hir_ty::builtin_derive::impl_trait(interner, impl_).instantiate_identity();
+ let trait_method_args =
+ GenericArgs::identity_for_item(interner, trait_method.into());
+ let trait_method_own_args = GenericArgs::new_from_iter(
+ interner,
+ trait_method_args.iter().skip(impl_trait_ref.args.len()),
+ );
+ let impl_params_count = hir_ty::builtin_derive::generic_params_count(db, impl_);
+ let shift_args_by = impl_params_count as i32 - impl_trait_ref.args.len() as i32;
+ let shifted_trait_method_own_args = trait_method_own_args
+ .fold_with(&mut ParamsShifter { interner, shift_by: shift_args_by });
+ let impl_method_args = GenericArgs::new_from_iter(
+ interner,
+ impl_trait_ref.args.iter().chain(shifted_trait_method_own_args),
+ );
+ let impl_method_fn_ptr =
+ EarlyBinder::bind(trait_method_fn_ptr).instantiate(interner, impl_method_args);
+ Type { env, ty: impl_method_fn_ptr }
+ }
+ }
+ }
+
+ fn fn_sig<'db>(self, db: &'db dyn HirDatabase) -> (ParamEnvAndCrate<'db>, PolyFnSig<'db>) {
+ let fn_ptr = self.fn_ptr_type(db);
+ let TyKind::FnPtr(sig_tys, hdr) = fn_ptr.ty.kind() else {
+ unreachable!();
+ };
+ (fn_ptr.env, sig_tys.with(hdr))
}
// FIXME: Find a better API to express all combinations here, perhaps we should have `PreInstantiationType`?
/// Get this function's return type
pub fn ret_type(self, db: &dyn HirDatabase) -> Type<'_> {
- let resolver = self.id.resolver(db);
- // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s.
- let ty = db
- .callable_item_signature(self.id.into())
- .instantiate_identity()
- .skip_binder()
- .output();
- Type::new_with_resolver_inner(db, &resolver, ty)
+ let (env, sig) = self.fn_sig(db);
+ Type { env, ty: sig.skip_binder().output() }
}
// FIXME: Find better API to also handle const generics
@@ -2239,30 +2403,41 @@ impl Function {
db: &'db dyn HirDatabase,
generics: impl Iterator<Item = Type<'db>>,
) -> Type<'db> {
- let resolver = self.id.resolver(db);
+ let ret_type = self.ret_type(db);
let interner = DbInterner::new_no_crate(db);
- let args = generic_args_from_tys(interner, self.id.into(), generics.map(|ty| ty.ty));
+ let args = self.adapt_generic_args(interner, generics);
+ ret_type.derived(EarlyBinder::bind(ret_type.ty).instantiate(interner, args))
+ }
- let interner = DbInterner::new_no_crate(db);
- let ty = db
- .callable_item_signature(self.id.into())
- .instantiate(interner, args)
- .skip_binder()
- .output();
- Type::new_with_resolver_inner(db, &resolver, ty)
+ fn adapt_generic_args<'db>(
+ self,
+ interner: DbInterner<'db>,
+ generics: impl Iterator<Item = Type<'db>>,
+ ) -> GenericArgs<'db> {
+ let generics = generics.map(|ty| ty.ty);
+ match self.id {
+ AnyFunctionId::FunctionId(id) => generic_args_from_tys(interner, id.into(), generics),
+ AnyFunctionId::BuiltinDeriveImplMethod { impl_, .. } => {
+ let impl_args = GenericArgs::identity_for_item(interner, impl_.into());
+ GenericArgs::new_from_iter(
+ interner,
+ impl_args.iter().chain(generics.map(Into::into)),
+ )
+ }
+ }
}
pub fn async_ret_type<'db>(self, db: &'db dyn HirDatabase) -> Option<Type<'db>> {
+ let AnyFunctionId::FunctionId(id) = self.id else {
+ return None;
+ };
if !self.is_async(db) {
return None;
}
- let resolver = self.id.resolver(db);
+ let resolver = id.resolver(db);
// FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s.
- let ret_ty = db
- .callable_item_signature(self.id.into())
- .instantiate_identity()
- .skip_binder()
- .output();
+ let ret_ty =
+ db.callable_item_signature(id.into()).instantiate_identity().skip_binder().output();
for pred in ret_ty.impl_trait_bounds(db).into_iter().flatten() {
if let ClauseKind::Projection(projection) = pred.kind().skip_binder()
&& let Some(output_ty) = projection.term.as_type()
@@ -2274,31 +2449,47 @@ impl Function {
}
pub fn has_self_param(self, db: &dyn HirDatabase) -> bool {
- db.function_signature(self.id).has_self_param()
+ match self.id {
+ AnyFunctionId::FunctionId(id) => db.function_signature(id).has_self_param(),
+ AnyFunctionId::BuiltinDeriveImplMethod { method, .. } => match method {
+ BuiltinDeriveImplMethod::clone
+ | BuiltinDeriveImplMethod::fmt
+ | BuiltinDeriveImplMethod::hash
+ | BuiltinDeriveImplMethod::cmp
+ | BuiltinDeriveImplMethod::partial_cmp
+ | BuiltinDeriveImplMethod::eq => true,
+ BuiltinDeriveImplMethod::default => false,
+ },
+ }
}
pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> {
- self.has_self_param(db).then_some(SelfParam { func: self.id })
+ self.has_self_param(db).then_some(SelfParam { func: self })
}
pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec<Param<'_>> {
- let environment = param_env_from_has_crate(db, self.id);
- // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s.
- let callable_sig =
- db.callable_item_signature(self.id.into()).instantiate_identity().skip_binder();
- callable_sig
+ let (env, sig) = self.fn_sig(db);
+ let func = match self.id {
+ AnyFunctionId::FunctionId(id) => Callee::Def(CallableDefId::FunctionId(id)),
+ AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => {
+ Callee::BuiltinDeriveImplMethod { method, impl_ }
+ }
+ };
+ sig.skip_binder()
.inputs()
.iter()
.enumerate()
- .map(|(idx, &ty)| {
- let ty = Type { env: environment, ty };
- Param { func: Callee::Def(CallableDefId::FunctionId(self.id)), ty, idx }
- })
+ .map(|(idx, &ty)| Param { func: func.clone(), ty: Type { env, ty }, idx })
.collect()
}
pub fn num_params(self, db: &dyn HirDatabase) -> usize {
- db.function_signature(self.id).params.len()
+ match self.id {
+ AnyFunctionId::FunctionId(id) => db.function_signature(id).params.len(),
+ AnyFunctionId::BuiltinDeriveImplMethod { .. } => {
+ self.fn_sig(db).1.skip_binder().inputs().len()
+ }
+ }
}
pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param<'_>>> {
@@ -2307,21 +2498,11 @@ impl Function {
}
pub fn params_without_self(self, db: &dyn HirDatabase) -> Vec<Param<'_>> {
- let environment = param_env_from_has_crate(db, self.id);
- // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s.
- let callable_sig =
- db.callable_item_signature(self.id.into()).instantiate_identity().skip_binder();
- let skip = if db.function_signature(self.id).has_self_param() { 1 } else { 0 };
- callable_sig
- .inputs()
- .iter()
- .enumerate()
- .skip(skip)
- .map(|(idx, &ty)| {
- let ty = Type { env: environment, ty };
- Param { func: Callee::Def(CallableDefId::FunctionId(self.id)), ty, idx }
- })
- .collect()
+ let mut params = self.assoc_fn_params(db);
+ if self.has_self_param(db) {
+ params.remove(0);
+ }
+ params
}
// FIXME: Find better API to also handle const generics
@@ -2330,40 +2511,50 @@ impl Function {
db: &'db dyn HirDatabase,
generics: impl Iterator<Item = Type<'db>>,
) -> Vec<Param<'db>> {
- let environment = param_env_from_has_crate(db, self.id);
let interner = DbInterner::new_no_crate(db);
- let args = generic_args_from_tys(interner, self.id.into(), generics.map(|ty| ty.ty));
- let callable_sig =
- db.callable_item_signature(self.id.into()).instantiate(interner, args).skip_binder();
- let skip = if db.function_signature(self.id).has_self_param() { 1 } else { 0 };
- callable_sig
- .inputs()
- .iter()
- .enumerate()
- .skip(skip)
- .map(|(idx, &ty)| {
- let ty = Type { env: environment, ty };
- Param { func: Callee::Def(CallableDefId::FunctionId(self.id)), ty, idx }
+ let args = self.adapt_generic_args(interner, generics);
+ let params = self.params_without_self(db);
+ params
+ .into_iter()
+ .map(|param| Param {
+ func: param.func,
+ idx: param.idx,
+ ty: Type {
+ env: param.ty.env,
+ ty: EarlyBinder::bind(param.ty.ty).instantiate(interner, args),
+ },
})
.collect()
}
pub fn is_const(self, db: &dyn HirDatabase) -> bool {
- db.function_signature(self.id).is_const()
+ match self.id {
+ AnyFunctionId::FunctionId(id) => db.function_signature(id).is_const(),
+ AnyFunctionId::BuiltinDeriveImplMethod { .. } => false,
+ }
}
pub fn is_async(self, db: &dyn HirDatabase) -> bool {
- db.function_signature(self.id).is_async()
+ match self.id {
+ AnyFunctionId::FunctionId(id) => db.function_signature(id).is_async(),
+ AnyFunctionId::BuiltinDeriveImplMethod { .. } => false,
+ }
}
pub fn is_varargs(self, db: &dyn HirDatabase) -> bool {
- db.function_signature(self.id).is_varargs()
+ match self.id {
+ AnyFunctionId::FunctionId(id) => db.function_signature(id).is_varargs(),
+ AnyFunctionId::BuiltinDeriveImplMethod { .. } => false,
+ }
}
pub fn extern_block(self, db: &dyn HirDatabase) -> Option<ExternBlock> {
- match self.id.lookup(db).container {
- ItemContainerId::ExternBlockId(id) => Some(ExternBlock { id }),
- _ => None,
+ match self.id {
+ AnyFunctionId::FunctionId(id) => match id.lookup(db).container {
+ ItemContainerId::ExternBlockId(id) => Some(ExternBlock { id }),
+ _ => None,
+ },
+ AnyFunctionId::BuiltinDeriveImplMethod { .. } => None,
}
}
@@ -2396,33 +2587,46 @@ impl Function {
/// Does this function have `#[test]` attribute?
pub fn is_test(self, db: &dyn HirDatabase) -> bool {
- self.attrs(db).is_test()
+ self.attrs(db).contains(AttrFlags::IS_TEST)
}
/// is this a `fn main` or a function with an `export_name` of `main`?
pub fn is_main(self, db: &dyn HirDatabase) -> bool {
- self.exported_main(db)
- || self.module(db).is_crate_root(db) && db.function_signature(self.id).name == sym::main
+ match self.id {
+ AnyFunctionId::FunctionId(id) => {
+ self.exported_main(db)
+ || self.module(db).is_crate_root(db)
+ && db.function_signature(id).name == sym::main
+ }
+ AnyFunctionId::BuiltinDeriveImplMethod { .. } => false,
+ }
+ }
+
+ fn attrs(self, db: &dyn HirDatabase) -> AttrFlags {
+ match self.id {
+ AnyFunctionId::FunctionId(id) => AttrFlags::query(db, id.into()),
+ AnyFunctionId::BuiltinDeriveImplMethod { .. } => AttrFlags::empty(),
+ }
}
/// Is this a function with an `export_name` of `main`?
pub fn exported_main(self, db: &dyn HirDatabase) -> bool {
- AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_EXPORT_NAME_MAIN)
+ self.attrs(db).contains(AttrFlags::IS_EXPORT_NAME_MAIN)
}
/// Does this function have the ignore attribute?
pub fn is_ignore(self, db: &dyn HirDatabase) -> bool {
- AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_IGNORE)
+ self.attrs(db).contains(AttrFlags::IS_IGNORE)
}
/// Does this function have `#[bench]` attribute?
pub fn is_bench(self, db: &dyn HirDatabase) -> bool {
- AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_BENCH)
+ self.attrs(db).contains(AttrFlags::IS_BENCH)
}
/// Is this function marked as unstable with `#[feature]` attribute?
pub fn is_unstable(self, db: &dyn HirDatabase) -> bool {
- AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_UNSTABLE)
+ self.attrs(db).contains(AttrFlags::IS_UNSTABLE)
}
pub fn is_unsafe_to_call(
@@ -2431,9 +2635,17 @@ impl Function {
caller: Option<Function>,
call_edition: Edition,
) -> bool {
+ let AnyFunctionId::FunctionId(id) = self.id else {
+ return false;
+ };
let (target_features, target_feature_is_safe_in_target) = caller
.map(|caller| {
- let target_features = hir_ty::TargetFeatures::from_fn(db, caller.id);
+ let target_features = match caller.id {
+ AnyFunctionId::FunctionId(id) => hir_ty::TargetFeatures::from_fn(db, id),
+ AnyFunctionId::BuiltinDeriveImplMethod { .. } => {
+ hir_ty::TargetFeatures::default()
+ }
+ };
let target_feature_is_safe_in_target =
match &caller.krate(db).id.workspace_data(db).target {
Ok(target) => hir_ty::target_feature_is_safe_in_target(target),
@@ -2447,7 +2659,7 @@ impl Function {
matches!(
hir_ty::is_fn_unsafe_to_call(
db,
- self.id,
+ id,
&target_features,
call_edition,
target_feature_is_safe_in_target
@@ -2460,12 +2672,18 @@ impl Function {
///
/// This is false in the case of required (not provided) trait methods.
pub fn has_body(self, db: &dyn HirDatabase) -> bool {
- db.function_signature(self.id).has_body()
+ match self.id {
+ AnyFunctionId::FunctionId(id) => db.function_signature(id).has_body(),
+ AnyFunctionId::BuiltinDeriveImplMethod { .. } => true,
+ }
}
pub fn as_proc_macro(self, db: &dyn HirDatabase) -> Option<Macro> {
- let def_map = crate_def_map(db, HasModule::krate(&self.id, db));
- def_map.fn_as_proc_macro(self.id).map(|id| Macro { id: id.into() })
+ let AnyFunctionId::FunctionId(id) = self.id else {
+ return None;
+ };
+ let def_map = crate_def_map(db, HasModule::krate(&id, db));
+ def_map.fn_as_proc_macro(id).map(|id| Macro { id: id.into() })
}
pub fn eval(
@@ -2473,13 +2691,18 @@ impl Function {
db: &dyn HirDatabase,
span_formatter: impl Fn(FileId, TextRange) -> String,
) -> Result<String, ConstEvalError> {
+ let AnyFunctionId::FunctionId(id) = self.id else {
+ return Err(ConstEvalError::MirEvalError(MirEvalError::NotSupported(
+ "evaluation of builtin derive impl methods is not supported".to_owned(),
+ )));
+ };
let interner = DbInterner::new_no_crate(db);
let body = db.monomorphized_mir_body(
- self.id.into(),
+ id.into(),
GenericArgs::empty(interner).store(),
ParamEnvAndCrate {
- param_env: db.trait_environment(self.id.into()),
- krate: self.id.module(db).krate(db),
+ param_env: db.trait_environment(id.into()),
+ krate: id.module(db).krate(db),
}
.store(),
)?;
@@ -2596,36 +2819,47 @@ impl<'db> Param<'db> {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SelfParam {
- func: FunctionId,
+ func: Function,
}
impl SelfParam {
pub fn access(self, db: &dyn HirDatabase) -> Access {
- let func_data = db.function_signature(self.func);
- func_data
- .params
- .first()
- .map(|&param| match &func_data.store[param] {
- TypeRef::Reference(ref_) => match ref_.mutability {
- hir_def::type_ref::Mutability::Shared => Access::Shared,
- hir_def::type_ref::Mutability::Mut => Access::Exclusive,
- },
- _ => Access::Owned,
- })
- .unwrap_or(Access::Owned)
+ match self.func.id {
+ AnyFunctionId::FunctionId(id) => {
+ let func_data = db.function_signature(id);
+ func_data
+ .params
+ .first()
+ .map(|&param| match &func_data.store[param] {
+ TypeRef::Reference(ref_) => match ref_.mutability {
+ hir_def::type_ref::Mutability::Shared => Access::Shared,
+ hir_def::type_ref::Mutability::Mut => Access::Exclusive,
+ },
+ _ => Access::Owned,
+ })
+ .unwrap_or(Access::Owned)
+ }
+ AnyFunctionId::BuiltinDeriveImplMethod { method, .. } => match method {
+ BuiltinDeriveImplMethod::clone
+ | BuiltinDeriveImplMethod::fmt
+ | BuiltinDeriveImplMethod::hash
+ | BuiltinDeriveImplMethod::cmp
+ | BuiltinDeriveImplMethod::partial_cmp
+ | BuiltinDeriveImplMethod::eq => Access::Shared,
+ BuiltinDeriveImplMethod::default => {
+ unreachable!("this function does not have a self param")
+ }
+ },
+ }
}
pub fn parent_fn(&self) -> Function {
- Function::from(self.func)
+ self.func
}
pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> Type<'db> {
- // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s.
- let callable_sig =
- db.callable_item_signature(self.func.into()).instantiate_identity().skip_binder();
- let environment = param_env_from_has_crate(db, self.func);
- let ty = rustc_type_ir::inherent::SliceLike::as_slice(&callable_sig.inputs())[0];
- Type { env: environment, ty }
+ let (env, sig) = self.func.fn_sig(db);
+ Type { env, ty: sig.skip_binder().inputs()[0] }
}
// FIXME: Find better API to also handle const generics
@@ -2635,18 +2869,18 @@ impl SelfParam {
generics: impl Iterator<Item = Type<'db>>,
) -> Type<'db> {
let interner = DbInterner::new_no_crate(db);
- let args = generic_args_from_tys(interner, self.func.into(), generics.map(|ty| ty.ty));
- let callable_sig =
- db.callable_item_signature(self.func.into()).instantiate(interner, args).skip_binder();
- let environment = param_env_from_has_crate(db, self.func);
- let ty = rustc_type_ir::inherent::SliceLike::as_slice(&callable_sig.inputs())[0];
- Type { env: environment, ty }
+ let args = self.func.adapt_generic_args(interner, generics);
+ let Type { env, ty } = self.ty(db);
+ Type { env, ty: EarlyBinder::bind(ty).instantiate(interner, args) }
}
}
impl HasVisibility for Function {
fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
- db.assoc_visibility(self.id.into())
+ match self.id {
+ AnyFunctionId::FunctionId(id) => db.assoc_visibility(id.into()),
+ AnyFunctionId::BuiltinDeriveImplMethod { .. } => Visibility::Public,
+ }
}
}
@@ -2870,7 +3104,7 @@ impl Trait {
pub fn function(self, db: &dyn HirDatabase, name: impl PartialEq<Name>) -> Option<Function> {
self.id.trait_items(db).items.iter().find(|(n, _)| name == *n).and_then(|&(_, it)| match it
{
- AssocItemId::FunctionId(id) => Some(Function { id }),
+ AssocItemId::FunctionId(id) => Some(id.into()),
_ => None,
})
}
@@ -3151,15 +3385,15 @@ impl Macro {
)
}
- pub fn is_builtin_derive(&self, db: &dyn HirDatabase) -> bool {
- match self.id {
- MacroId::Macro2Id(it) => {
- matches!(it.lookup(db).expander, MacroExpander::BuiltInDerive(_))
- }
- MacroId::MacroRulesId(it) => {
- matches!(it.lookup(db).expander, MacroExpander::BuiltInDerive(_))
- }
- MacroId::ProcMacroId(_) => false,
+ pub fn builtin_derive_kind(&self, db: &dyn HirDatabase) -> Option<BuiltinDeriveMacroKind> {
+ let expander = match self.id {
+ MacroId::Macro2Id(it) => it.lookup(db).expander,
+ MacroId::MacroRulesId(it) => it.lookup(db).expander,
+ MacroId::ProcMacroId(_) => return None,
+ };
+ match expander {
+ MacroExpander::BuiltInDerive(kind) => Some(BuiltinDeriveMacroKind(kind)),
+ _ => None,
}
}
@@ -3197,6 +3431,9 @@ impl Macro {
}
}
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+pub struct BuiltinDeriveMacroKind(BuiltinDeriveExpander);
+
impl HasVisibility for Macro {
fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
match self.id {
@@ -3275,7 +3512,10 @@ pub trait AsExternAssocItem {
impl AsExternAssocItem for Function {
fn as_extern_assoc_item(self, db: &dyn HirDatabase) -> Option<ExternAssocItem> {
- as_extern_assoc_item(db, ExternAssocItem::Function, self.id)
+ let AnyFunctionId::FunctionId(id) = self.id else {
+ return None;
+ };
+ as_extern_assoc_item(db, ExternAssocItem::Function, id)
}
}
@@ -3303,7 +3543,7 @@ pub enum AssocItem {
impl From<method_resolution::CandidateId> for AssocItem {
fn from(value: method_resolution::CandidateId) -> Self {
match value {
- method_resolution::CandidateId::FunctionId(id) => AssocItem::Function(Function { id }),
+ method_resolution::CandidateId::FunctionId(id) => AssocItem::Function(id.into()),
method_resolution::CandidateId::ConstId(id) => AssocItem::Const(Const { id }),
}
}
@@ -3321,7 +3561,10 @@ pub trait AsAssocItem {
impl AsAssocItem for Function {
fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
- as_assoc_item(db, AssocItem::Function, self.id)
+ match self.id {
+ AnyFunctionId::FunctionId(id) => as_assoc_item(db, AssocItem::Function, id),
+ AnyFunctionId::BuiltinDeriveImplMethod { .. } => Some(AssocItem::Function(self)),
+ }
}
}
@@ -3450,7 +3693,14 @@ impl AssocItem {
pub fn container(self, db: &dyn HirDatabase) -> AssocItemContainer {
let container = match self {
- AssocItem::Function(it) => it.id.lookup(db).container,
+ AssocItem::Function(it) => match it.id {
+ AnyFunctionId::FunctionId(id) => id.lookup(db).container,
+ AnyFunctionId::BuiltinDeriveImplMethod { impl_, .. } => {
+ return AssocItemContainer::Impl(Impl {
+ id: AnyImplId::BuiltinDeriveImplId(impl_),
+ });
+ }
+ },
AssocItem::Const(it) => it.id.lookup(db).container,
AssocItem::TypeAlias(it) => it.id.lookup(db).container,
};
@@ -3587,9 +3837,13 @@ impl_from!(
impl GenericDef {
pub fn params(self, db: &dyn HirDatabase) -> Vec<GenericParam> {
- let generics = db.generic_params(self.into());
+ let Ok(id) = self.try_into() else {
+ // Let's pretend builtin derive impls don't have generic parameters.
+ return Vec::new();
+ };
+ let generics = db.generic_params(id);
let ty_params = generics.iter_type_or_consts().map(|(local_id, _)| {
- let toc = TypeOrConstParam { id: TypeOrConstParamId { parent: self.into(), local_id } };
+ let toc = TypeOrConstParam { id: TypeOrConstParamId { parent: id, local_id } };
match toc.split(db) {
Either::Left(it) => GenericParam::ConstParam(it),
Either::Right(it) => GenericParam::TypeParam(it),
@@ -3603,39 +3857,51 @@ impl GenericDef {
}
pub fn lifetime_params(self, db: &dyn HirDatabase) -> Vec<LifetimeParam> {
- let generics = db.generic_params(self.into());
+ let Ok(id) = self.try_into() else {
+ // Let's pretend builtin derive impls don't have generic parameters.
+ return Vec::new();
+ };
+ let generics = db.generic_params(id);
generics
.iter_lt()
- .map(|(local_id, _)| LifetimeParam {
- id: LifetimeParamId { parent: self.into(), local_id },
- })
+ .map(|(local_id, _)| LifetimeParam { id: LifetimeParamId { parent: id, local_id } })
.collect()
}
pub fn type_or_const_params(self, db: &dyn HirDatabase) -> Vec<TypeOrConstParam> {
- let generics = db.generic_params(self.into());
+ let Ok(id) = self.try_into() else {
+ // Let's pretend builtin derive impls don't have generic parameters.
+ return Vec::new();
+ };
+ let generics = db.generic_params(id);
generics
.iter_type_or_consts()
.map(|(local_id, _)| TypeOrConstParam {
- id: TypeOrConstParamId { parent: self.into(), local_id },
+ id: TypeOrConstParamId { parent: id, local_id },
})
.collect()
}
- fn id(self) -> GenericDefId {
- match self {
- GenericDef::Function(it) => it.id.into(),
+ fn id(self) -> Option<GenericDefId> {
+ Some(match self {
+ GenericDef::Function(it) => match it.id {
+ AnyFunctionId::FunctionId(it) => it.into(),
+ AnyFunctionId::BuiltinDeriveImplMethod { .. } => return None,
+ },
GenericDef::Adt(it) => it.into(),
GenericDef::Trait(it) => it.id.into(),
GenericDef::TypeAlias(it) => it.id.into(),
- GenericDef::Impl(it) => it.id.into(),
+ GenericDef::Impl(it) => match it.id {
+ AnyImplId::ImplId(it) => it.into(),
+ AnyImplId::BuiltinDeriveImplId(_) => return None,
+ },
GenericDef::Const(it) => it.id.into(),
GenericDef::Static(it) => it.id.into(),
- }
+ })
}
pub fn diagnostics<'db>(self, db: &'db dyn HirDatabase, acc: &mut Vec<AnyDiagnostic<'db>>) {
- let def = self.id();
+ let Some(def) = self.id() else { return };
let generics = db.generic_params(def);
@@ -3708,6 +3974,17 @@ impl<'db> GenericSubstitution<'db> {
Self { def, subst, env }
}
+ fn new_from_fn(
+ def: Function,
+ subst: GenericArgs<'db>,
+ env: ParamEnvAndCrate<'db>,
+ ) -> Option<Self> {
+ match def.id {
+ AnyFunctionId::FunctionId(def) => Some(Self::new(def.into(), subst, env)),
+ AnyFunctionId::BuiltinDeriveImplMethod { .. } => None,
+ }
+ }
+
pub fn types(&self, db: &'db dyn HirDatabase) -> Vec<(Symbol, Type<'db>)> {
let container = match self.def {
GenericDefId::ConstId(id) => Some(id.lookup(db).container),
@@ -3820,7 +4097,9 @@ impl Local {
pub fn as_self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> {
match self.parent {
- DefWithBodyId::FunctionId(func) if self.is_self(db) => Some(SelfParam { func }),
+ DefWithBodyId::FunctionId(func) if self.is_self(db) => {
+ Some(SelfParam { func: func.into() })
+ }
_ => None,
}
}
@@ -4308,7 +4587,7 @@ impl TypeOrConstParam {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Impl {
- pub(crate) id: ImplId,
+ pub(crate) id: AnyImplId,
}
impl Impl {
@@ -4320,6 +4599,7 @@ impl Impl {
fn extend_with_def_map(db: &dyn HirDatabase, def_map: &DefMap, result: &mut Vec<Impl>) {
for (_, module) in def_map.modules() {
result.extend(module.scope.impls().map(Impl::from));
+ result.extend(module.scope.builtin_derive_impls().map(Impl::from));
for unnamed_const in module.scope.unnamed_consts() {
for (_, block_def_map) in db.body(unnamed_const.into()).blocks(db) {
@@ -4331,7 +4611,7 @@ impl Impl {
}
pub fn all_in_module(db: &dyn HirDatabase, module: Module) -> Vec<Impl> {
- module.id.def_map(db)[module.id].scope.impls().map(Into::into).collect()
+ module.impl_defs(db)
}
/// **Note:** This is an **approximation** that strives to give the *human-perceived notion* of an "impl for type",
@@ -4347,20 +4627,19 @@ impl Impl {
else {
return Vec::new();
};
- let mut extend_with_impls =
- |impls: &[ImplId]| result.extend(impls.iter().copied().map(Impl::from));
- method_resolution::with_incoherent_inherent_impls(
- db,
- env.krate,
- &simplified_ty,
- &mut extend_with_impls,
- );
+ let mut extend_with_impls = |impls: Either<&[ImplId], &[BuiltinDeriveImplId]>| match impls {
+ Either::Left(impls) => result.extend(impls.iter().copied().map(Impl::from)),
+ Either::Right(impls) => result.extend(impls.iter().copied().map(Impl::from)),
+ };
+ method_resolution::with_incoherent_inherent_impls(db, env.krate, &simplified_ty, |impls| {
+ extend_with_impls(Either::Left(impls))
+ });
if let Some(module) = method_resolution::simplified_type_module(db, &simplified_ty) {
InherentImpls::for_each_crate_and_block(
db,
module.krate(db),
module.block(db),
- &mut |impls| extend_with_impls(impls.for_self_ty(&simplified_ty)),
+ &mut |impls| extend_with_impls(Either::Left(impls.for_self_ty(&simplified_ty))),
);
std::iter::successors(module.block(db), |block| block.loc(db).module.block(db))
.filter_map(|block| TraitImpls::for_block(db, block).as_deref())
@@ -4382,7 +4661,10 @@ impl Impl {
let module = trait_.module(db).id;
let mut all = Vec::new();
let mut handle_impls = |impls: &TraitImpls| {
- impls.for_trait(trait_.id, |impls| all.extend(impls.iter().copied().map(Impl::from)));
+ impls.for_trait(trait_.id, |impls| match impls {
+ Either::Left(impls) => all.extend(impls.iter().copied().map(Impl::from)),
+ Either::Right(impls) => all.extend(impls.iter().copied().map(Impl::from)),
+ });
};
for krate in module.krate(db).transitive_rev_deps(db) {
handle_impls(TraitImpls::for_crate(db, krate));
@@ -4396,75 +4678,118 @@ impl Impl {
}
pub fn trait_(self, db: &dyn HirDatabase) -> Option<Trait> {
- let trait_ref = db.impl_trait(self.id)?;
- let id = trait_ref.skip_binder().def_id;
- Some(Trait { id: id.0 })
+ match self.id {
+ AnyImplId::ImplId(id) => {
+ let trait_ref = db.impl_trait(id)?;
+ let id = trait_ref.skip_binder().def_id;
+ Some(Trait { id: id.0 })
+ }
+ AnyImplId::BuiltinDeriveImplId(id) => {
+ let loc = id.loc(db);
+ let lang_items = hir_def::lang_item::lang_items(db, loc.adt.module(db).krate(db));
+ loc.trait_.get_id(lang_items).map(Trait::from)
+ }
+ }
}
pub fn trait_ref(self, db: &dyn HirDatabase) -> Option<TraitRef<'_>> {
- let trait_ref = db.impl_trait(self.id)?.instantiate_identity();
- let resolver = self.id.resolver(db);
- Some(TraitRef::new_with_resolver(db, &resolver, trait_ref))
+ match self.id {
+ AnyImplId::ImplId(id) => {
+ let trait_ref = db.impl_trait(id)?.instantiate_identity();
+ let resolver = id.resolver(db);
+ Some(TraitRef::new_with_resolver(db, &resolver, trait_ref))
+ }
+ AnyImplId::BuiltinDeriveImplId(id) => {
+ let loc = id.loc(db);
+ let krate = loc.module(db).krate(db);
+ let interner = DbInterner::new_with(db, krate);
+ let env = ParamEnvAndCrate {
+ param_env: hir_ty::builtin_derive::param_env(interner, id),
+ krate,
+ };
+ let trait_ref =
+ hir_ty::builtin_derive::impl_trait(interner, id).instantiate_identity();
+ Some(TraitRef { env, trait_ref })
+ }
+ }
}
pub fn self_ty(self, db: &dyn HirDatabase) -> Type<'_> {
- let resolver = self.id.resolver(db);
- // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s.
- let ty = db.impl_self_ty(self.id).instantiate_identity();
- Type::new_with_resolver_inner(db, &resolver, ty)
+ match self.id {
+ AnyImplId::ImplId(id) => {
+ let resolver = id.resolver(db);
+ // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s.
+ let ty = db.impl_self_ty(id).instantiate_identity();
+ Type::new_with_resolver_inner(db, &resolver, ty)
+ }
+ AnyImplId::BuiltinDeriveImplId(id) => {
+ let loc = id.loc(db);
+ let krate = loc.module(db).krate(db);
+ let interner = DbInterner::new_with(db, krate);
+ let env = ParamEnvAndCrate {
+ param_env: hir_ty::builtin_derive::param_env(interner, id),
+ krate,
+ };
+ let ty = hir_ty::builtin_derive::impl_trait(interner, id)
+ .instantiate_identity()
+ .self_ty();
+ Type { env, ty }
+ }
+ }
}
pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
- self.id.impl_items(db).items.iter().map(|&(_, it)| it.into()).collect()
+ match self.id {
+ AnyImplId::ImplId(id) => {
+ id.impl_items(db).items.iter().map(|&(_, it)| it.into()).collect()
+ }
+ AnyImplId::BuiltinDeriveImplId(impl_) => impl_
+ .loc(db)
+ .trait_
+ .all_methods()
+ .iter()
+ .map(|&method| {
+ AssocItem::Function(Function {
+ id: AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ },
+ })
+ })
+ .collect(),
+ }
}
pub fn is_negative(self, db: &dyn HirDatabase) -> bool {
- db.impl_signature(self.id).flags.contains(ImplFlags::NEGATIVE)
+ match self.id {
+ AnyImplId::ImplId(id) => db.impl_signature(id).flags.contains(ImplFlags::NEGATIVE),
+ AnyImplId::BuiltinDeriveImplId(_) => false,
+ }
}
pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool {
- db.impl_signature(self.id).flags.contains(ImplFlags::UNSAFE)
+ match self.id {
+ AnyImplId::ImplId(id) => db.impl_signature(id).flags.contains(ImplFlags::UNSAFE),
+ AnyImplId::BuiltinDeriveImplId(_) => false,
+ }
}
pub fn module(self, db: &dyn HirDatabase) -> Module {
- self.id.lookup(db).container.into()
- }
-
- pub fn as_builtin_derive_path(self, db: &dyn HirDatabase) -> Option<InMacroFile<ast::Path>> {
- let src = self.source(db)?;
-
- let macro_file = src.file_id.macro_file()?;
- let loc = macro_file.lookup(db);
- let (derive_attr, derive_index) = match loc.kind {
- MacroCallKind::Derive { ast_id, derive_attr_index, derive_index, .. } => {
- let module_id = self.id.lookup(db).container;
- (
- module_id.def_map(db)[module_id]
- .scope
- .derive_macro_invoc(ast_id, derive_attr_index)?,
- derive_index,
- )
- }
- _ => return None,
- };
- let path = db
- .parse_macro_expansion(derive_attr)
- .value
- .0
- .syntax_node()
- .children()
- .nth(derive_index as usize)
- .and_then(<ast::Attr as AstNode>::cast)
- .and_then(|it| it.path())?;
- Some(InMacroFile { file_id: derive_attr, value: path })
+ match self.id {
+ AnyImplId::ImplId(id) => id.module(db).into(),
+ AnyImplId::BuiltinDeriveImplId(id) => id.module(db).into(),
+ }
}
pub fn check_orphan_rules(self, db: &dyn HirDatabase) -> bool {
- check_orphan_rules(db, self.id)
+ match self.id {
+ AnyImplId::ImplId(id) => check_orphan_rules(db, id),
+ AnyImplId::BuiltinDeriveImplId(_) => true,
+ }
}
fn all_macro_calls(&self, db: &dyn HirDatabase) -> Box<[(AstId<ast::Item>, MacroCallId)]> {
- self.id.impl_items(db).macro_calls.to_vec().into_boxed_slice()
+ match self.id {
+ AnyImplId::ImplId(id) => id.impl_items(db).macro_calls.to_vec().into_boxed_slice(),
+ AnyImplId::BuiltinDeriveImplId(_) => Box::default(),
+ }
}
}
@@ -5540,7 +5865,7 @@ impl<'db> Type<'db> {
else {
unreachable!("`Mode::MethodCall` can only return functions");
};
- let id = Function { id };
+ let id = Function { id: AnyFunctionId::FunctionId(id) };
match candidate.kind {
method_resolution::PickKind::InherentImplPick(_)
| method_resolution::PickKind::ObjectPick(..)
@@ -5564,7 +5889,7 @@ impl<'db> Type<'db> {
else {
unreachable!("`Mode::MethodCall` can only return functions");
};
- let id = Function { id };
+ let id = Function { id: AnyFunctionId::FunctionId(id) };
match candidate.candidate.kind {
method_resolution::CandidateKind::InherentImplCandidate {
..
@@ -5919,6 +6244,7 @@ enum Callee<'db> {
CoroutineClosure(InternedCoroutineId, GenericArgs<'db>),
FnPtr,
FnImpl(traits::FnTrait),
+ BuiltinDeriveImplMethod { method: BuiltinDeriveImplMethod, impl_: BuiltinDeriveImplId },
}
pub enum CallableKind<'db> {
@@ -5934,6 +6260,9 @@ impl<'db> Callable<'db> {
pub fn kind(&self) -> CallableKind<'db> {
match self.callee {
Callee::Def(CallableDefId::FunctionId(it)) => CallableKind::Function(it.into()),
+ Callee::BuiltinDeriveImplMethod { method, impl_ } => CallableKind::Function(Function {
+ id: AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ },
+ }),
Callee::Def(CallableDefId::StructId(it)) => CallableKind::TupleStruct(it.into()),
Callee::Def(CallableDefId::EnumVariantId(it)) => {
CallableKind::TupleEnumVariant(it.into())
@@ -5948,12 +6277,22 @@ impl<'db> Callable<'db> {
Callee::FnImpl(fn_) => CallableKind::FnImpl(fn_.into()),
}
}
+
+ fn as_function(&self) -> Option<Function> {
+ match self.callee {
+ Callee::Def(CallableDefId::FunctionId(it)) => Some(it.into()),
+ Callee::BuiltinDeriveImplMethod { method, impl_ } => {
+ Some(Function { id: AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } })
+ }
+ _ => None,
+ }
+ }
+
pub fn receiver_param(&self, db: &'db dyn HirDatabase) -> Option<(SelfParam, Type<'db>)> {
- let func = match self.callee {
- Callee::Def(CallableDefId::FunctionId(it)) if self.is_bound_method => it,
- _ => return None,
- };
- let func = Function { id: func };
+ if !self.is_bound_method {
+ return None;
+ }
+ let func = self.as_function()?;
Some((
func.self_param(db)?,
self.ty.derived(self.sig.skip_binder().inputs_and_output.inputs()[0]),
@@ -6350,7 +6689,12 @@ impl HasContainer for Module {
impl HasContainer for Function {
fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
- container_id_to_hir(self.id.lookup(db).container)
+ match self.id {
+ AnyFunctionId::FunctionId(id) => container_id_to_hir(id.lookup(db).container),
+ AnyFunctionId::BuiltinDeriveImplMethod { impl_, .. } => {
+ ItemContainer::Impl(Impl { id: AnyImplId::BuiltinDeriveImplId(impl_) })
+ }
+ }
}
}
@@ -6402,11 +6746,79 @@ impl HasContainer for ExternBlock {
}
}
+pub trait HasName {
+ fn name(&self, db: &dyn HirDatabase) -> Option<Name>;
+}
+
+macro_rules! impl_has_name {
+ ( $( $ty:ident ),* $(,)? ) => {
+ $(
+ impl HasName for $ty {
+ fn name(&self, db: &dyn HirDatabase) -> Option<Name> {
+ (*self).name(db).into()
+ }
+ }
+ )*
+ };
+}
+
+impl_has_name!(
+ ModuleDef,
+ Module,
+ Field,
+ Struct,
+ Union,
+ Enum,
+ Variant,
+ Adt,
+ VariantDef,
+ DefWithBody,
+ Function,
+ ExternCrateDecl,
+ Const,
+ Static,
+ Trait,
+ TypeAlias,
+ Macro,
+ ExternAssocItem,
+ AssocItem,
+ Local,
+ DeriveHelper,
+ ToolModule,
+ Label,
+ GenericParam,
+ TypeParam,
+ LifetimeParam,
+ ConstParam,
+ TypeOrConstParam,
+ InlineAsmOperand,
+);
+
+macro_rules! impl_has_name_no_db {
+ ( $( $ty:ident ),* $(,)? ) => {
+ $(
+ impl HasName for $ty {
+ fn name(&self, _db: &dyn HirDatabase) -> Option<Name> {
+ (*self).name().into()
+ }
+ }
+ )*
+ };
+}
+
+impl_has_name_no_db!(TupleField, StaticLifetime, BuiltinType, BuiltinAttr);
+
+impl HasName for Param<'_> {
+ fn name(&self, db: &dyn HirDatabase) -> Option<Name> {
+ self.name(db)
+ }
+}
+
fn container_id_to_hir(c: ItemContainerId) -> ItemContainer {
match c {
ItemContainerId::ExternBlockId(id) => ItemContainer::ExternBlock(ExternBlock { id }),
ItemContainerId::ModuleId(id) => ItemContainer::Module(Module { id }),
- ItemContainerId::ImplId(id) => ItemContainer::Impl(Impl { id }),
+ ItemContainerId::ImplId(id) => ItemContainer::Impl(id.into()),
ItemContainerId::TraitId(id) => ItemContainer::Trait(Trait { id }),
}
}
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index fcb97ab34e..485011c38d 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -13,7 +13,7 @@ use std::{
use base_db::FxIndexSet;
use either::Either;
use hir_def::{
- DefWithBodyId, FunctionId, MacroId, StructId, TraitId, VariantId,
+ DefWithBodyId, MacroId, StructId, TraitId, VariantId,
attrs::parse_extra_crate_attrs,
expr_store::{Body, ExprOrPatSource, HygieneId, path::Path},
hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat},
@@ -34,7 +34,7 @@ use hir_ty::{
diagnostics::{unsafe_operations, unsafe_operations_for_body},
infer_query_with_inspect,
next_solver::{
- DbInterner, Span,
+ AnyImplId, DbInterner, Span,
format_proof_tree::{ProofTreeData, dump_proof_tree_structured},
},
};
@@ -53,11 +53,11 @@ use syntax::{
};
use crate::{
- Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const, ConstParam,
- Crate, DefWithBody, DeriveHelper, Enum, Field, Function, GenericSubstitution, HasSource, Impl,
- InFile, InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef,
- Name, OverloadedDeref, ScopeDef, Static, Struct, ToolModule, Trait, TupleField, Type,
- TypeAlias, TypeParam, Union, Variant, VariantDef,
+ Adjust, Adjustment, Adt, AnyFunctionId, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const,
+ ConstParam, Crate, DefWithBody, DeriveHelper, Enum, Field, Function, GenericSubstitution,
+ HasSource, Impl, InFile, InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro,
+ Module, ModuleDef, Name, OverloadedDeref, ScopeDef, Static, Struct, ToolModule, Trait,
+ TupleField, Type, TypeAlias, TypeParam, Union, Variant, VariantDef,
db::HirDatabase,
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
source_analyzer::{SourceAnalyzer, resolve_hir_path},
@@ -106,7 +106,10 @@ impl PathResolution {
| PathResolution::DeriveHelper(_)
| PathResolution::ConstParam(_) => None,
PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())),
- PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())),
+ PathResolution::SelfType(impl_def) => match impl_def.id {
+ AnyImplId::ImplId(id) => Some(TypeNs::SelfType(id)),
+ AnyImplId::BuiltinDeriveImplId(_) => None,
+ },
}
}
}
@@ -345,23 +348,23 @@ impl<DB: HirDatabase + ?Sized> Semantics<'_, DB> {
}
pub fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<Function> {
- self.imp.resolve_await_to_poll(await_expr).map(Function::from)
+ self.imp.resolve_await_to_poll(await_expr)
}
pub fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option<Function> {
- self.imp.resolve_prefix_expr(prefix_expr).map(Function::from)
+ self.imp.resolve_prefix_expr(prefix_expr)
}
pub fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option<Function> {
- self.imp.resolve_index_expr(index_expr).map(Function::from)
+ self.imp.resolve_index_expr(index_expr)
}
pub fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option<Function> {
- self.imp.resolve_bin_expr(bin_expr).map(Function::from)
+ self.imp.resolve_bin_expr(bin_expr)
}
pub fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option<Function> {
- self.imp.resolve_try_expr(try_expr).map(Function::from)
+ self.imp.resolve_try_expr(try_expr)
}
pub fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantDef> {
@@ -1749,6 +1752,7 @@ impl<'db> SemanticsImpl<'db> {
func: Function,
subst: impl IntoIterator<Item = Type<'db>>,
) -> Option<Function> {
+ let AnyFunctionId::FunctionId(func) = func.id else { return Some(func) };
let interner = DbInterner::new_no_crate(self.db);
let mut subst = subst.into_iter();
let substs =
@@ -1757,7 +1761,12 @@ impl<'db> SemanticsImpl<'db> {
subst.next().expect("too few subst").ty.into()
});
assert!(subst.next().is_none(), "too many subst");
- Some(self.db.lookup_impl_method(env.env, func.into(), substs).0.into())
+ Some(match self.db.lookup_impl_method(env.env, func, substs).0 {
+ Either::Left(it) => it.into(),
+ Either::Right((impl_, method)) => {
+ Function { id: AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } }
+ }
+ })
}
fn resolve_range_pat(&self, range_pat: &ast::RangePat) -> Option<StructId> {
@@ -1768,23 +1777,23 @@ impl<'db> SemanticsImpl<'db> {
self.analyze(range_expr.syntax())?.resolve_range_expr(self.db, range_expr)
}
- fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<FunctionId> {
+ fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<Function> {
self.analyze(await_expr.syntax())?.resolve_await_to_poll(self.db, await_expr)
}
- fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option<FunctionId> {
+ fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option<Function> {
self.analyze(prefix_expr.syntax())?.resolve_prefix_expr(self.db, prefix_expr)
}
- fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option<FunctionId> {
+ fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option<Function> {
self.analyze(index_expr.syntax())?.resolve_index_expr(self.db, index_expr)
}
- fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option<FunctionId> {
+ fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option<Function> {
self.analyze(bin_expr.syntax())?.resolve_bin_expr(self.db, bin_expr)
}
- fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option<FunctionId> {
+ fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option<Function> {
self.analyze(try_expr.syntax())?.resolve_try_expr(self.db, try_expr)
}
@@ -1861,7 +1870,9 @@ impl<'db> SemanticsImpl<'db> {
}
pub fn get_unsafe_ops(&self, def: DefWithBody) -> FxHashSet<ExprOrPatSource> {
- let def = DefWithBodyId::from(def);
+ let Ok(def) = DefWithBodyId::try_from(def) else {
+ return FxHashSet::default();
+ };
let (body, source_map) = self.db.body_with_source_map(def);
let infer = InferenceResult::for_body(self.db, def);
let mut res = FxHashSet::default();
@@ -1877,7 +1888,9 @@ impl<'db> SemanticsImpl<'db> {
always!(block.unsafe_token().is_some());
let block = self.wrap_node_infile(ast::Expr::from(block));
let Some(def) = self.body_for(block.syntax()) else { return Vec::new() };
- let def = def.into();
+ let Ok(def) = def.try_into() else {
+ return Vec::new();
+ };
let (body, source_map) = self.db.body_with_source_map(def);
let infer = InferenceResult::for_body(self.db, def);
let Some(ExprOrPatId::ExprId(block)) = source_map.node_expr(block.as_ref()) else {
@@ -2023,16 +2036,22 @@ impl<'db> SemanticsImpl<'db> {
}
/// Search for a definition's source and cache its syntax tree
- pub fn source<Def: HasSource>(&self, def: Def) -> Option<InFile<Def::Ast>>
- where
- Def::Ast: AstNode,
- {
+ pub fn source<Def: HasSource>(&self, def: Def) -> Option<InFile<Def::Ast>> {
// FIXME: source call should go through the parse cache
let res = def.source(self.db)?;
self.cache(find_root(res.value.syntax()), res.file_id);
Some(res)
}
+ pub fn source_with_range<Def: HasSource>(
+ &self,
+ def: Def,
+ ) -> Option<InFile<(TextRange, Option<Def::Ast>)>> {
+ let res = def.source_with_range(self.db)?;
+ self.parse_or_expand(res.file_id);
+ Some(res)
+ }
+
pub fn body_for(&self, node: InFile<&SyntaxNode>) -> Option<DefWithBody> {
let container = self.with_ctx(|ctx| ctx.find_container(node))?;
@@ -2162,9 +2181,10 @@ impl<'db> SemanticsImpl<'db> {
let def = match &enclosing_item {
Either::Left(ast::Item::Fn(it)) if it.unsafe_token().is_some() => return true,
- Either::Left(ast::Item::Fn(it)) => {
- self.to_def(it).map(<_>::into).map(DefWithBodyId::FunctionId)
- }
+ Either::Left(ast::Item::Fn(it)) => (|| match self.to_def(it)?.id {
+ AnyFunctionId::FunctionId(id) => Some(DefWithBodyId::FunctionId(id)),
+ AnyFunctionId::BuiltinDeriveImplMethod { .. } => None,
+ })(),
Either::Left(ast::Item::Const(it)) => {
self.to_def(it).map(<_>::into).map(DefWithBodyId::ConstId)
}
@@ -2201,7 +2221,11 @@ impl<'db> SemanticsImpl<'db> {
}
pub fn impl_generated_from_derive(&self, impl_: Impl) -> Option<Adt> {
- let source = hir_def::src::HasSource::ast_ptr(&impl_.id.loc(self.db), self.db);
+ let id = match impl_.id {
+ AnyImplId::ImplId(id) => id,
+ AnyImplId::BuiltinDeriveImplId(id) => return Some(id.loc(self.db).adt.into()),
+ };
+ let source = hir_def::src::HasSource::ast_ptr(&id.loc(self.db), self.db);
let mut file_id = source.file_id;
let adt_ast_id = loop {
let macro_call = file_id.macro_file()?;
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 848ad33801..bf123e13f9 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -57,9 +57,9 @@ use syntax::{
use triomphe::Arc;
use crate::{
- Adt, AssocItem, BindingMode, BuiltinAttr, BuiltinType, Callable, Const, DeriveHelper, Field,
- Function, GenericSubstitution, Local, Macro, ModuleDef, Static, Struct, ToolModule, Trait,
- TupleField, Type, TypeAlias, Variant,
+ Adt, AnyFunctionId, AssocItem, BindingMode, BuiltinAttr, BuiltinType, Callable, Const,
+ DeriveHelper, Field, Function, GenericSubstitution, Local, Macro, ModuleDef, Static, Struct,
+ ToolModule, Trait, TupleField, Type, TypeAlias, Variant,
db::HirDatabase,
semantics::{PathResolution, PathResolutionPerNs},
};
@@ -431,7 +431,7 @@ impl<'db> SourceAnalyzer<'db> {
let expr_id = self.expr_id(call.clone().into())?.as_expr()?;
let (f_in_trait, substs) = self.infer()?.method_resolution(expr_id)?;
- Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs).into())
+ Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs))
}
pub(crate) fn resolve_method_call_fallback(
@@ -446,8 +446,8 @@ impl<'db> SourceAnalyzer<'db> {
let (fn_, subst) =
self.resolve_impl_method_or_trait_def_with_subst(db, f_in_trait, substs);
Some((
- Either::Left(fn_.into()),
- Some(GenericSubstitution::new(fn_.into(), subst, self.trait_environment(db))),
+ Either::Left(fn_),
+ GenericSubstitution::new_from_fn(fn_, subst, self.trait_environment(db)),
))
}
None => {
@@ -519,8 +519,8 @@ impl<'db> SourceAnalyzer<'db> {
None => inference_result.method_resolution(expr_id).map(|(f, substs)| {
let (f, subst) = self.resolve_impl_method_or_trait_def_with_subst(db, f, substs);
(
- Either::Right(f.into()),
- Some(GenericSubstitution::new(f.into(), subst, self.trait_environment(db))),
+ Either::Right(f),
+ GenericSubstitution::new_from_fn(f, subst, self.trait_environment(db)),
)
}),
}
@@ -569,7 +569,7 @@ impl<'db> SourceAnalyzer<'db> {
&self,
db: &'db dyn HirDatabase,
await_expr: &ast::AwaitExpr,
- ) -> Option<FunctionId> {
+ ) -> Option<Function> {
let mut ty = self.ty_of_expr(await_expr.expr()?)?;
let into_future_trait = self
@@ -605,7 +605,7 @@ impl<'db> SourceAnalyzer<'db> {
&self,
db: &'db dyn HirDatabase,
prefix_expr: &ast::PrefixExpr,
- ) -> Option<FunctionId> {
+ ) -> Option<Function> {
let (_op_trait, op_fn) = match prefix_expr.op_kind()? {
ast::UnaryOp::Deref => {
// This can be either `Deref::deref` or `DerefMut::deref_mut`.
@@ -650,7 +650,7 @@ impl<'db> SourceAnalyzer<'db> {
&self,
db: &'db dyn HirDatabase,
index_expr: &ast::IndexExpr,
- ) -> Option<FunctionId> {
+ ) -> Option<Function> {
let base_ty = self.ty_of_expr(index_expr.base()?)?;
let index_ty = self.ty_of_expr(index_expr.index()?)?;
@@ -679,7 +679,7 @@ impl<'db> SourceAnalyzer<'db> {
&self,
db: &'db dyn HirDatabase,
binop_expr: &ast::BinExpr,
- ) -> Option<FunctionId> {
+ ) -> Option<Function> {
let op = binop_expr.op_kind()?;
let lhs = self.ty_of_expr(binop_expr.lhs()?)?;
let rhs = self.ty_of_expr(binop_expr.rhs()?)?;
@@ -699,7 +699,7 @@ impl<'db> SourceAnalyzer<'db> {
&self,
db: &'db dyn HirDatabase,
try_expr: &ast::TryExpr,
- ) -> Option<FunctionId> {
+ ) -> Option<Function> {
let ty = self.ty_of_expr(try_expr.expr()?)?;
let op_fn = self.lang_items(db).TryTraitBranch?;
@@ -905,7 +905,7 @@ impl<'db> SourceAnalyzer<'db> {
subs,
self.trait_environment(db),
);
- (AssocItemId::from(f_in_trait), subst)
+ (AssocItem::Function(f_in_trait.into()), Some(subst))
}
Some(func_ty) => {
if let TyKind::FnDef(_fn_def, subs) = func_ty.kind() {
@@ -913,19 +913,19 @@ impl<'db> SourceAnalyzer<'db> {
.resolve_impl_method_or_trait_def_with_subst(
db, f_in_trait, subs,
);
- let subst = GenericSubstitution::new(
- fn_.into(),
+ let subst = GenericSubstitution::new_from_fn(
+ fn_,
subst,
self.trait_environment(db),
);
- (fn_.into(), subst)
+ (AssocItem::Function(fn_), subst)
} else {
let subst = GenericSubstitution::new(
f_in_trait.into(),
subs,
self.trait_environment(db),
);
- (f_in_trait.into(), subst)
+ (AssocItem::Function(f_in_trait.into()), Some(subst))
}
}
}
@@ -938,11 +938,11 @@ impl<'db> SourceAnalyzer<'db> {
subst,
self.trait_environment(db),
);
- (konst.into(), subst)
+ (AssocItem::Const(konst.into()), Some(subst))
}
};
- return Some((PathResolution::Def(AssocItem::from(assoc).into()), Some(subst)));
+ return Some((PathResolution::Def(assoc.into()), subst));
}
if let Some(VariantId::EnumVariantId(variant)) =
infer.variant_resolution_for_expr_or_pat(expr_id)
@@ -1401,7 +1401,7 @@ impl<'db> SourceAnalyzer<'db> {
db: &'db dyn HirDatabase,
func: FunctionId,
substs: GenericArgs<'db>,
- ) -> FunctionId {
+ ) -> Function {
self.resolve_impl_method_or_trait_def_with_subst(db, func, substs).0
}
@@ -1410,13 +1410,19 @@ impl<'db> SourceAnalyzer<'db> {
db: &'db dyn HirDatabase,
func: FunctionId,
substs: GenericArgs<'db>,
- ) -> (FunctionId, GenericArgs<'db>) {
+ ) -> (Function, GenericArgs<'db>) {
let owner = match self.resolver.body_owner() {
Some(it) => it,
- None => return (func, substs),
+ None => return (func.into(), substs),
};
let env = self.param_and(db.trait_environment_for_body(owner));
- db.lookup_impl_method(env, func, substs)
+ let (func, args) = db.lookup_impl_method(env, func, substs);
+ match func {
+ Either::Left(func) => (func.into(), args),
+ Either::Right((impl_, method)) => {
+ (Function { id: AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } }, args)
+ }
+ }
}
fn resolve_impl_const_or_trait_def_with_subst(
diff --git a/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/crates/ide-db/src/test_data/test_symbol_index_collection.txt
index 953bc73da9..7692a7d61a 100644
--- a/crates/ide-db/src/test_data/test_symbol_index_collection.txt
+++ b/crates/ide-db/src/test_data/test_symbol_index_collection.txt
@@ -734,11 +734,11 @@
FileSymbol {
name: "generic_impl_fn",
def: Function(
- Function {
- id: FunctionId(
+ FunctionId(
+ FunctionId(
6402,
),
- },
+ ),
),
loc: DeclarationLocation {
hir_file_id: FileId(
@@ -769,11 +769,11 @@
FileSymbol {
name: "impl_fn",
def: Function(
- Function {
- id: FunctionId(
+ FunctionId(
+ FunctionId(
6401,
),
- },
+ ),
),
loc: DeclarationLocation {
hir_file_id: FileId(
@@ -839,11 +839,11 @@
FileSymbol {
name: "main",
def: Function(
- Function {
- id: FunctionId(
+ FunctionId(
+ FunctionId(
6400,
),
- },
+ ),
),
loc: DeclarationLocation {
hir_file_id: FileId(
@@ -907,11 +907,11 @@
FileSymbol {
name: "trait_fn",
def: Function(
- Function {
- id: FunctionId(
+ FunctionId(
+ FunctionId(
6403,
),
- },
+ ),
),
loc: DeclarationLocation {
hir_file_id: FileId(
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index f57f2883b1..06ae0b1d73 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -767,14 +767,30 @@ fn label_of_ty(
)
});
+ let module_def_location = |label_builder: &mut InlayHintLabelBuilder<'_>,
+ def: ModuleDef,
+ name| {
+ let def = def.try_into();
+ if let Ok(def) = def {
+ label_builder.start_location_link(def);
+ }
+ #[expect(
+ clippy::question_mark,
+ reason = "false positive; replacing with `?` leads to 'type annotations needed' error"
+ )]
+ if let Err(err) = label_builder.write_str(name) {
+ return Err(err);
+ }
+ if def.is_ok() {
+ label_builder.end_location_link();
+ }
+ Ok(())
+ };
+
label_builder.write_str(LABEL_START)?;
- label_builder.start_location_link(ModuleDef::from(iter_trait).into());
- label_builder.write_str(LABEL_ITERATOR)?;
- label_builder.end_location_link();
+ module_def_location(label_builder, ModuleDef::from(iter_trait), LABEL_ITERATOR)?;
label_builder.write_str(LABEL_MIDDLE)?;
- label_builder.start_location_link(ModuleDef::from(item).into());
- label_builder.write_str(LABEL_ITEM)?;
- label_builder.end_location_link();
+ module_def_location(label_builder, ModuleDef::from(item), LABEL_ITEM)?;
label_builder.write_str(LABEL_MIDDLE2)?;
rec(sema, famous_defs, max_length, &ty, label_builder, config, display_target)?;
label_builder.write_str(LABEL_END)?;
diff --git a/crates/ide/src/inlay_hints/implicit_drop.rs b/crates/ide/src/inlay_hints/implicit_drop.rs
index 1317684a08..e5e4c899ec 100644
--- a/crates/ide/src/inlay_hints/implicit_drop.rs
+++ b/crates/ide/src/inlay_hints/implicit_drop.rs
@@ -34,9 +34,10 @@ pub(super) fn hints(
let def = sema.to_def(node)?;
let def: DefWithBody = def.into();
- let (hir, source_map) = sema.db.body_with_source_map(def.into());
+ let def = def.try_into().ok()?;
+ let (hir, source_map) = sema.db.body_with_source_map(def);
- let mir = sema.db.mir_body(def.into()).ok()?;
+ let mir = sema.db.mir_body(def).ok()?;
let local_to_binding = mir.local_to_binding_map();
diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs
index 020f235d3a..a271cac6fc 100644
--- a/crates/ide/src/navigation_target.rs
+++ b/crates/ide/src/navigation_target.rs
@@ -6,7 +6,7 @@ use arrayvec::ArrayVec;
use either::Either;
use hir::{
AssocItem, Crate, FieldSource, HasContainer, HasCrate, HasSource, HirDisplay, HirFileId,
- InFile, LocalSource, ModuleSource, Semantics, Symbol, db::ExpandDatabase, sym,
+ InFile, LocalSource, ModuleSource, Name, Semantics, Symbol, db::ExpandDatabase, sym,
symbols::FileSymbol,
};
use ide_db::{
@@ -204,6 +204,22 @@ impl NavigationTarget {
)
}
+ pub(crate) fn from_named_with_range(
+ db: &RootDatabase,
+ ranges: InFile<(TextRange, Option<TextRange>)>,
+ name: Option<Name>,
+ kind: SymbolKind,
+ ) -> UpmappingResult<NavigationTarget> {
+ let InFile { file_id, value: (full_range, focus_range) } = ranges;
+ let name = name.map(|name| name.symbol().clone()).unwrap_or_else(|| sym::underscore);
+
+ orig_range_with_focus_r(db, file_id, full_range, focus_range).map(
+ |(FileRange { file_id, range: full_range }, focus_range)| {
+ NavigationTarget::from_syntax(file_id, name.clone(), focus_range, full_range, kind)
+ },
+ )
+ }
+
pub(crate) fn from_syntax(
file_id: FileId,
name: Symbol,
@@ -414,7 +430,13 @@ impl ToNavFromAst for hir::Trait {
impl<D> TryToNav for D
where
- D: HasSource + ToNavFromAst + Copy + HasDocs + for<'db> HirDisplay<'db> + HasCrate,
+ D: HasSource
+ + ToNavFromAst
+ + Copy
+ + HasDocs
+ + for<'db> HirDisplay<'db>
+ + HasCrate
+ + hir::HasName,
D::Ast: ast::HasName,
{
fn try_to_nav(
@@ -422,11 +444,19 @@ where
sema: &Semantics<'_, RootDatabase>,
) -> Option<UpmappingResult<NavigationTarget>> {
let db = sema.db;
- let src = self.source(db)?;
+ let src = self.source_with_range(db)?;
Some(
- NavigationTarget::from_named(
+ NavigationTarget::from_named_with_range(
db,
- src.as_ref().map(|it| it as &dyn ast::HasName),
+ src.map(|(full_range, node)| {
+ (
+ full_range,
+ node.and_then(|node| {
+ Some(ast::HasName::name(&node)?.syntax().text_range())
+ }),
+ )
+ }),
+ self.name(db),
D::KIND,
)
.map(|mut res| {
@@ -477,16 +507,16 @@ impl TryToNav for hir::Impl {
sema: &Semantics<'_, RootDatabase>,
) -> Option<UpmappingResult<NavigationTarget>> {
let db = sema.db;
- let InFile { file_id, value } = self.source(db)?;
- let derive_path = self.as_builtin_derive_path(db);
-
- let (file_id, focus, syntax) = match &derive_path {
- Some(attr) => (attr.file_id.into(), None, attr.value.syntax()),
- None => (file_id, value.self_ty(), value.syntax()),
- };
+ let InFile { file_id, value: (full_range, source) } = self.source_with_range(db)?;
- Some(orig_range_with_focus(db, file_id, syntax, focus).map(
- |(FileRange { file_id, range: full_range }, focus_range)| {
+ Some(
+ orig_range_with_focus_r(
+ db,
+ file_id,
+ full_range,
+ source.and_then(|source| Some(source.self_ty()?.syntax().text_range())),
+ )
+ .map(|(FileRange { file_id, range: full_range }, focus_range)| {
NavigationTarget::from_syntax(
file_id,
sym::kw_impl,
@@ -494,8 +524,8 @@ impl TryToNav for hir::Impl {
full_range,
SymbolKind::Impl,
)
- },
- ))
+ }),
+ )
}
}
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 0738b7fadc..ffae7bf6c7 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -2503,7 +2503,7 @@ fn r#fn$0() {}
fn main() { r#fn(); }
"#,
expect![[r#"
- r#fn Function FileId(0) 0..12 3..7
+ fn Function FileId(0) 0..12 3..7
FileId(0) 25..29
"#]],
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index c562a9b30b..6cec912503 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -1679,11 +1679,11 @@ mod r#mod {
[
"(TestMod, NavigationTarget { file_id: FileId(0), full_range: 1..461, focus_range: 5..10, name: \"mod\", kind: Module, description: \"mod r#mod\" })",
"(Test, NavigationTarget { file_id: FileId(0), full_range: 17..41, focus_range: 32..36, name: \"r#fn\", kind: Function })",
- "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 47..84, name: \"r#for\", container_name: \"mod\" })",
- "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 90..146, name: \"r#struct\", container_name: \"mod\" })",
+ "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 47..84, name: \"for\", container_name: \"mod\" })",
+ "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 90..146, name: \"struct\", container_name: \"mod\" })",
"(DocTest, NavigationTarget { file_id: FileId(0), full_range: 152..266, focus_range: 189..205, name: \"impl\", kind: Impl })",
- "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 216..260, name: \"r#fn\" })",
- "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 323..367, name: \"r#fn\" })",
+ "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 216..260, name: \"fn\" })",
+ "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 323..367, name: \"fn\" })",
"(DocTest, NavigationTarget { file_id: FileId(0), full_range: 401..459, focus_range: 445..456, name: \"impl\", kind: Impl })",
]
"#]],
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs
index 6e9c6d26b5..3e325b2f99 100644
--- a/crates/intern/src/symbol/symbols.rs
+++ b/crates/intern/src/symbol/symbols.rs
@@ -525,5 +525,10 @@ define_symbols! {
arbitrary_self_types,
arbitrary_self_types_pointers,
supertrait_item_shadowing,
+ hash,
+ partial_cmp,
+ cmp,
+ CoerceUnsized,
+ DispatchFromDyn,
define_opaque,
}
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 9313600aad..503d5967bb 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -693,21 +693,24 @@ impl flags::AnalysisStats {
let mut sw = self.stop_watch();
let mut all = 0;
let mut fail = 0;
- for &body_id in bodies {
+ for &body in bodies {
bar.set_message(move || {
- format!("mir lowering: {}", full_name(db, body_id, body_id.module(db)))
+ format!("mir lowering: {}", full_name(db, body, body.module(db)))
});
bar.inc(1);
- if matches!(body_id, DefWithBody::Variant(_)) {
+ if matches!(body, DefWithBody::Variant(_)) {
continue;
}
- let module = body_id.module(db);
- if !self.should_process(db, body_id, module) {
+ let module = body.module(db);
+ if !self.should_process(db, body, module) {
continue;
}
all += 1;
- let Err(e) = db.mir_body(body_id.into()) else {
+ let Ok(body_id) = body.try_into() else {
+ continue;
+ };
+ let Err(e) = db.mir_body(body_id) else {
continue;
};
if verbosity.is_spammy() {
@@ -716,7 +719,7 @@ impl flags::AnalysisStats {
.into_iter()
.rev()
.filter_map(|it| it.name(db))
- .chain(Some(body_id.name(db).unwrap_or_else(Name::missing)))
+ .chain(Some(body.name(db).unwrap_or_else(Name::missing)))
.map(|it| it.display(db, Edition::LATEST).to_string())
.join("::");
bar.println(format!("Mir body for {full_name} failed due {e:?}"));
@@ -747,11 +750,12 @@ impl flags::AnalysisStats {
if self.parallel {
let mut inference_sw = self.stop_watch();
+ let bodies = bodies.iter().filter_map(|&body| body.try_into().ok()).collect::<Vec<_>>();
bodies
.par_iter()
.map_with(db.clone(), |snap, &body| {
- snap.body(body.into());
- InferenceResult::for_body(snap, body.into());
+ snap.body(body);
+ InferenceResult::for_body(snap, body);
})
.count();
eprintln!("{:<20} {}", "Parallel Inference:", inference_sw.elapsed());
@@ -769,6 +773,7 @@ impl flags::AnalysisStats {
let mut num_pat_type_mismatches = 0;
let mut panics = 0;
for &body_id in bodies {
+ let Ok(body_def_id) = body_id.try_into() else { continue };
let name = body_id.name(db).unwrap_or_else(Name::missing);
let module = body_id.module(db);
let display_target = module.krate(db).to_display_target(db);
@@ -807,9 +812,9 @@ impl flags::AnalysisStats {
bar.println(msg());
}
bar.set_message(msg);
- let body = db.body(body_id.into());
+ let body = db.body(body_def_id);
let inference_result =
- catch_unwind(AssertUnwindSafe(|| InferenceResult::for_body(db, body_id.into())));
+ catch_unwind(AssertUnwindSafe(|| InferenceResult::for_body(db, body_def_id)));
let inference_result = match inference_result {
Ok(inference_result) => inference_result,
Err(p) => {
@@ -826,7 +831,7 @@ impl flags::AnalysisStats {
}
};
// This query is LRU'd, so actually calling it will skew the timing results.
- let sm = || db.body_with_source_map(body_id.into()).1;
+ let sm = || db.body_with_source_map(body_def_id).1;
// region:expressions
let (previous_exprs, previous_unknown, previous_partially_unknown) =
@@ -1081,6 +1086,7 @@ impl flags::AnalysisStats {
let mut sw = self.stop_watch();
bar.tick();
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) {
continue;
@@ -1114,7 +1120,7 @@ impl flags::AnalysisStats {
bar.println(msg());
}
bar.set_message(msg);
- db.body(body_id.into());
+ db.body(body_def_id);
bar.inc(1);
}