diff --git a/crates/hir-def/src/attrs.rs b/crates/hir-def/src/attrs.rs index febc794b5a05..7292962b3b60 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 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 000000000000..32385516ab58 --- /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 { + match self { + $( Self::$trait => lang_items.$trait, )* + } + } + + #[inline] + pub fn get_method(self, method_name: &Symbol) -> Option { + 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 { + 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 { + 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 3ffeebfaf2c9..9e7868b273ef 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, impls: ThinVec, + builtin_derive_impls: ThinVec, extern_blocks: ThinVec, unnamed_consts: ThinVec, /// Traits imported via `use Trait as _;`. @@ -329,6 +330,10 @@ impl ItemScope { self.impls.iter().copied() } + pub fn builtin_derive_impls(&self) -> impl ExactSizeIterator + '_ { + self.builtin_derive_impls.iter().copied() + } + pub fn all_macro_calls(&self) -> impl Iterator + '_ { 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, attr_id: AttrId, attr_call_id: MacroCallId, - len: usize, + mut derive_call_ids: SmallVec<[Option; 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 9fdfb5f5b32e..cbc9b3dcf8fe 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 Option { + 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> { let mut traits = Vec::new(); @@ -164,6 +194,10 @@ macro_rules! language_item_table { ( $LangItems:ident => $( $(#[$attr:meta])* $lang_item:ident, $module:ident :: $name:ident, $method:ident, $target:ident, $generics:expr; )* + + @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)] @@ -172,6 +206,9 @@ macro_rules! language_item_table { $(#[$attr])* pub $lang_item: Option<$target>, )* + $( + pub $non_lang_trait: Option, + )* } impl LangItems { @@ -182,6 +219,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) { @@ -196,6 +234,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)] @@ -431,4 +473,11 @@ language_item_table! { LangItems => String, sym::String, string, StructId, GenericRequirement::None; CStr, sym::CStr, c_str, StructId, GenericRequirement::None; Ordering, sym::Ordering, ordering, EnumId, GenericRequirement::None; + + @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 97af8ad93def..8d6c418d75dc 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; impl_intern!(UseId, UseLoc, intern_use, lookup_intern_use); @@ -660,6 +678,18 @@ impl_from!( for ModuleDefId ); +impl From 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/builtin_derive_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs index 0013c2a25679..814afdd16cf9 100644 --- a/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs +++ b/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs @@ -1,828 +1,833 @@ //! Tests for `builtin_derive_macro.rs` from `hir_expand`. -use expect_test::expect; - -use crate::macro_expansion_tests::{check, check_errors}; - -#[test] -fn test_copy_expand_simple() { - check( - r#" -//- minicore: derive, copy -#[derive(Copy)] -struct Foo; -"#, - expect![[r#" -#[derive(Copy)] -struct Foo; - -impl <> $crate::marker::Copy for Foo< > where {}"#]], - ); -} - -#[test] -fn test_copy_expand_in_core() { - check( - r#" -//- /lib.rs crate:core -#[rustc_builtin_macro] -macro derive {} -#[rustc_builtin_macro] -macro Copy {} -#[derive(Copy)] -struct Foo; -"#, - expect![[r#" -#[rustc_builtin_macro] -macro derive {} -#[rustc_builtin_macro] -macro Copy {} -#[derive(Copy)] -struct Foo; - -impl <> $crate::marker::Copy for Foo< > where {}"#]], - ); -} - -#[test] -fn test_copy_expand_with_type_params() { - check( - r#" -//- minicore: derive, copy -#[derive(Copy)] -struct Foo; -"#, - expect![[r#" -#[derive(Copy)] -struct Foo; - -impl $crate::marker::Copy for Foo where {}"#]], - ); -} - -#[test] -fn test_copy_expand_with_lifetimes() { - // We currently just ignore lifetimes - check( - r#" -//- minicore: derive, copy -#[derive(Copy)] -struct Foo; -"#, - expect![[r#" -#[derive(Copy)] -struct Foo; - -impl $crate::marker::Copy for Foo where {}"#]], - ); -} - -#[test] -fn test_clone_expand() { - check( - r#" -//- minicore: derive, clone -#[derive(Clone)] -enum Command { - Move { x: A, y: B }, - Do(&'static str), - Jump, -} -"#, - expect![[r#" -#[derive(Clone)] -enum Command { - Move { x: A, y: B }, - Do(&'static str), - Jump, -} - -impl $crate::clone::Clone for Command where { - fn clone(&self ) -> Self { - match self { - Command::Move { - x: x, y: y, - } - =>Command::Move { - x: x.clone(), y: y.clone(), - } - , Command::Do(f0, )=>Command::Do(f0.clone(), ), Command::Jump=>Command::Jump, - } - } -}"#]], - ); -} - -#[test] -fn test_clone_expand_with_associated_types() { - check( - r#" -//- minicore: derive, clone -trait Trait { - type InWc; - type InFieldQualified; - type InFieldShorthand; - type InGenericArg; -} -trait Marker {} -struct Vec(T); - -#[derive(Clone)] -struct Foo -where - ::InWc: Marker, -{ - qualified: ::InFieldQualified, - shorthand: T::InFieldShorthand, - generic: Vec, -} -"#, - expect![[r#" -trait Trait { - type InWc; - type InFieldQualified; - type InFieldShorthand; - type InGenericArg; -} -trait Marker {} -struct Vec(T); - -#[derive(Clone)] -struct Foo -where - ::InWc: Marker, -{ - qualified: ::InFieldQualified, - shorthand: T::InFieldShorthand, - generic: Vec, -} - -impl $crate::clone::Clone for Foo where ::InWc: Marker, T: Trait, T::InFieldShorthand: $crate::clone::Clone, T::InGenericArg: $crate::clone::Clone, { - fn clone(&self ) -> Self { - match self { - Foo { - qualified: qualified, shorthand: shorthand, generic: generic, - } - =>Foo { - qualified: qualified.clone(), shorthand: shorthand.clone(), generic: generic.clone(), - } - , - } - } -}"#]], - ); -} - -#[test] -fn test_clone_expand_with_const_generics() { - check( - r#" -//- minicore: derive, clone -#[derive(Clone)] -struct Foo(u32); -"#, - expect![[r#" -#[derive(Clone)] -struct Foo(u32); - -impl $crate::clone::Clone for Foo where { - fn clone(&self ) -> Self { - match self { - Foo(f0, )=>Foo(f0.clone(), ), - } - } -}"#]], - ); -} - -#[test] -fn test_default_expand() { - check( - r#" -//- minicore: derive, default -#[derive(Default)] -struct Foo { - field1: i32, - field2: (), -} -#[derive(Default)] -enum Bar { - Foo(u8), - #[default] - Bar, -} -"#, - expect![[r#" -#[derive(Default)] -struct Foo { - field1: i32, - field2: (), -} -#[derive(Default)] -enum Bar { - Foo(u8), - #[default] - Bar, -} - -impl <> $crate::default::Default for Foo< > where { - fn default() -> Self { - Foo { - field1: $crate::default::Default::default(), field2: $crate::default::Default::default(), - } - } -} -impl <> $crate::default::Default for Bar< > where { - fn default() -> Self { - Bar::Bar - } -}"#]], - ); -} - -#[test] -fn test_partial_eq_expand() { - check( - r#" -//- minicore: derive, eq -#[derive(PartialEq, Eq)] -enum Command { - Move { x: i32, y: i32 }, - Do(&'static str), - Jump, -} -"#, - expect![[r#" -#[derive(PartialEq, Eq)] -enum Command { - Move { x: i32, y: i32 }, - Do(&'static str), - Jump, -} - -impl <> $crate::cmp::PartialEq for Command< > where { - fn eq(&self , other: &Self ) -> bool { - match (self , other) { - (Command::Move { - x: x_self, y: y_self, - } - , Command::Move { - x: x_other, y: y_other, - } - )=>x_self.eq(x_other) && y_self.eq(y_other), (Command::Do(f0_self, ), Command::Do(f0_other, ))=>f0_self.eq(f0_other), (Command::Jump, Command::Jump)=>true , _unused=>false - } - } -} -impl <> $crate::cmp::Eq for Command< > where {}"#]], - ); -} - -#[test] -fn test_partial_eq_expand_with_derive_const() { - // FIXME: actually expand with const - check( - r#" -//- minicore: derive, eq -#[derive_const(PartialEq, Eq)] -enum Command { - Move { x: i32, y: i32 }, - Do(&'static str), - Jump, -} -"#, - expect![[r#" -#[derive_const(PartialEq, Eq)] -enum Command { - Move { x: i32, y: i32 }, - Do(&'static str), - Jump, -} - -impl <> $crate::cmp::PartialEq for Command< > where { - fn eq(&self , other: &Self ) -> bool { - match (self , other) { - (Command::Move { - x: x_self, y: y_self, - } - , Command::Move { - x: x_other, y: y_other, - } - )=>x_self.eq(x_other) && y_self.eq(y_other), (Command::Do(f0_self, ), Command::Do(f0_other, ))=>f0_self.eq(f0_other), (Command::Jump, Command::Jump)=>true , _unused=>false - } - } -} -impl <> $crate::cmp::Eq for Command< > where {}"#]], - ); -} - -#[test] -fn test_partial_ord_expand() { - check( - r#" -//- minicore: derive, ord -#[derive(PartialOrd, Ord)] -enum Command { - Move { x: i32, y: i32 }, - Do(&'static str), - Jump, -} -"#, - expect![[r#" -#[derive(PartialOrd, Ord)] -enum Command { - Move { x: i32, y: i32 }, - Do(&'static str), - Jump, -} - -impl <> $crate::cmp::PartialOrd for Command< > where { - fn partial_cmp(&self , other: &Self ) -> $crate::option::Option<$crate::cmp::Ordering> { - match $crate::intrinsics::discriminant_value(self ).partial_cmp(&$crate::intrinsics::discriminant_value(other)) { - $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> { - match (self , other) { - (Command::Move { - x: x_self, y: y_self, - } - , Command::Move { - x: x_other, y: y_other, - } - )=>match x_self.partial_cmp(&x_other) { - $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> { - match y_self.partial_cmp(&y_other) { - $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> { - $crate::option::Option::Some($crate::cmp::Ordering::Equal) - } - c=>return c, - } - } - c=>return c, - } - , (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.partial_cmp(&f0_other) { - $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> { - $crate::option::Option::Some($crate::cmp::Ordering::Equal) - } - c=>return c, - } - , (Command::Jump, Command::Jump)=>$crate::option::Option::Some($crate::cmp::Ordering::Equal), _unused=>$crate::option::Option::Some($crate::cmp::Ordering::Equal) - } - } - c=>return c, - } - } -} -impl <> $crate::cmp::Ord for Command< > where { - fn cmp(&self , other: &Self ) -> $crate::cmp::Ordering { - match $crate::intrinsics::discriminant_value(self ).cmp(&$crate::intrinsics::discriminant_value(other)) { - $crate::cmp::Ordering::Equal=> { - match (self , other) { - (Command::Move { - x: x_self, y: y_self, - } - , Command::Move { - x: x_other, y: y_other, - } - )=>match x_self.cmp(&x_other) { - $crate::cmp::Ordering::Equal=> { - match y_self.cmp(&y_other) { - $crate::cmp::Ordering::Equal=> { - $crate::cmp::Ordering::Equal - } - c=>return c, - } - } - c=>return c, - } - , (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.cmp(&f0_other) { - $crate::cmp::Ordering::Equal=> { - $crate::cmp::Ordering::Equal - } - c=>return c, - } - , (Command::Jump, Command::Jump)=>$crate::cmp::Ordering::Equal, _unused=>$crate::cmp::Ordering::Equal - } - } - c=>return c, - } - } -}"#]], - ); -} - -#[test] -fn test_hash_expand() { - check( - r#" -//- minicore: derive, hash -use core::hash::Hash; - -#[derive(Hash)] -struct Foo { - x: i32, - y: u64, - z: (i32, u64), -} -"#, - expect![[r#" -use core::hash::Hash; - -#[derive(Hash)] -struct Foo { - x: i32, - y: u64, - z: (i32, u64), -} - -impl <> $crate::hash::Hash for Foo< > where { - fn hash(&self , ra_expand_state: &mut H) { - match self { - Foo { - x: x, y: y, z: z, - } - => { - x.hash(ra_expand_state); - y.hash(ra_expand_state); - z.hash(ra_expand_state); - } - , - } - } -}"#]], - ); - check( - r#" -//- minicore: derive, hash -use core::hash::Hash; - -#[derive(Hash)] -enum Command { - Move { x: i32, y: i32 }, - Do(&'static str), - Jump, -} -"#, - expect![[r#" -use core::hash::Hash; - -#[derive(Hash)] -enum Command { - Move { x: i32, y: i32 }, - Do(&'static str), - Jump, -} - -impl <> $crate::hash::Hash for Command< > where { - fn hash(&self , ra_expand_state: &mut H) { - $crate::mem::discriminant(self ).hash(ra_expand_state); - match self { - Command::Move { - x: x, y: y, - } - => { - x.hash(ra_expand_state); - y.hash(ra_expand_state); - } - , Command::Do(f0, )=> { - f0.hash(ra_expand_state); - } - , Command::Jump=> {} - , - } - } -}"#]], - ); -} - -#[test] -fn test_debug_expand() { - check( - r#" -//- minicore: derive, fmt -use core::fmt::Debug; - -#[derive(Debug)] -enum Command { - Move { x: i32, y: i32 }, - Do(&'static str), - Jump, -} -"#, - expect![[r#" -use core::fmt::Debug; - -#[derive(Debug)] -enum Command { - Move { x: i32, y: i32 }, - Do(&'static str), - Jump, -} - -impl <> $crate::fmt::Debug for Command< > where { - fn fmt(&self , f: &mut $crate::fmt::Formatter) -> $crate::fmt::Result { - match self { - Command::Move { - x: x, y: y, - } - =>f.debug_struct("Move").field("x", &x).field("y", &y).finish(), Command::Do(f0, )=>f.debug_tuple("Do").field(&f0).finish(), Command::Jump=>f.write_str("Jump"), - } - } -}"#]], - ); -} -#[test] -fn test_debug_expand_with_cfg() { - check( - r#" - //- minicore: derive, fmt - use core::fmt::Debug; - - #[derive(Debug)] - struct HideAndShow { - #[cfg(never)] - always_hide: u32, - #[cfg(not(never))] - always_show: u32, - } - #[derive(Debug)] - enum HideAndShowEnum { - #[cfg(never)] - AlwaysHide, - #[cfg(not(never))] - AlwaysShow{ - #[cfg(never)] - always_hide: u32, - #[cfg(not(never))] - always_show: u32, - } - } - "#, - expect![[r#" -use core::fmt::Debug; - -#[derive(Debug)] -struct HideAndShow { - #[cfg(never)] - always_hide: u32, - #[cfg(not(never))] - always_show: u32, -} -#[derive(Debug)] -enum HideAndShowEnum { - #[cfg(never)] - AlwaysHide, - #[cfg(not(never))] - AlwaysShow{ - #[cfg(never)] - always_hide: u32, - #[cfg(not(never))] - always_show: u32, - } -} - -impl <> $crate::fmt::Debug for HideAndShow< > where { - fn fmt(&self , f: &mut $crate::fmt::Formatter) -> $crate::fmt::Result { - match self { - HideAndShow { - always_show: always_show, - } - =>f.debug_struct("HideAndShow").field("always_show", &always_show).finish() - } - } -} -impl <> $crate::fmt::Debug for HideAndShowEnum< > where { - fn fmt(&self , f: &mut $crate::fmt::Formatter) -> $crate::fmt::Result { - match self { - HideAndShowEnum::AlwaysShow { - always_show: always_show, - } - =>f.debug_struct("AlwaysShow").field("always_show", &always_show).finish(), - } - } -}"#]], - ); -} -#[test] -fn test_default_expand_with_cfg() { - check( - r#" -//- minicore: derive, default -#[derive(Default)] -struct Foo { - field1: i32, - #[cfg(never)] - field2: (), - #[cfg(feature = "never")] - field3: (), - #[cfg(not(feature = "never"))] - field4: (), -} -#[derive(Default)] -enum Bar { - Foo, - #[cfg_attr(not(never), default)] - Bar, -} -"#, - expect![[r##" -#[derive(Default)] -struct Foo { - field1: i32, - #[cfg(never)] - field2: (), - #[cfg(feature = "never")] - field3: (), - #[cfg(not(feature = "never"))] - field4: (), -} -#[derive(Default)] -enum Bar { - Foo, - #[cfg_attr(not(never), default)] - Bar, -} - -impl <> $crate::default::Default for Foo< > where { - fn default() -> Self { - Foo { - field1: $crate::default::Default::default(), field4: $crate::default::Default::default(), - } - } -} -impl <> $crate::default::Default for Bar< > where { - fn default() -> Self { - Bar::Bar - } -}"##]], - ); -} - -#[test] -fn coerce_pointee_expansion() { - check( - r#" -//- minicore: coerce_pointee - -use core::marker::CoercePointee; - -pub trait Trait {} - -#[derive(CoercePointee)] -#[repr(transparent)] -pub struct Foo<'a, T: ?Sized + Trait, #[pointee] U: ?Sized, const N: u32>(T) -where - U: Trait + ToString;"#, - expect![[r#" - -use core::marker::CoercePointee; - -pub trait Trait {} - -#[derive(CoercePointee)] -#[repr(transparent)] -pub struct Foo<'a, T: ?Sized + Trait, #[pointee] U: ?Sized, const N: u32>(T) -where - U: Trait + ToString; -impl $crate::ops::DispatchFromDyn> for Foo where U: Trait +ToString, T: Trait<__S>, __S: ?Sized, __S: Trait<__S> +ToString, U: ::core::marker::Unsize<__S>, T:?Sized+Trait, U:?Sized, {} -impl $crate::ops::CoerceUnsized> for Foo where U: Trait +ToString, T: Trait<__S>, __S: ?Sized, __S: Trait<__S> +ToString, U: ::core::marker::Unsize<__S>, T:?Sized+Trait, U:?Sized, {}"#]], - ); -} - -#[test] -fn coerce_pointee_errors() { - check_errors( - r#" -//- minicore: coerce_pointee - -use core::marker::CoercePointee; - -#[derive(CoercePointee)] -enum Enum {} - -#[derive(CoercePointee)] -struct Struct1; - -#[derive(CoercePointee)] -struct Struct2(); - -#[derive(CoercePointee)] -struct Struct3 {} - -#[derive(CoercePointee)] -struct Struct4(T); - -#[derive(CoercePointee)] -#[repr(transparent)] -struct Struct5(i32); - -#[derive(CoercePointee)] -#[repr(transparent)] -struct Struct6<#[pointee] T: ?Sized, #[pointee] U: ?Sized>(T, U); - -#[derive(CoercePointee)] -#[repr(transparent)] -struct Struct7(T, U); - -#[derive(CoercePointee)] -#[repr(transparent)] -struct Struct8<#[pointee] T, U: ?Sized>(T); - -#[derive(CoercePointee)] -#[repr(transparent)] -struct Struct9(T); - -#[derive(CoercePointee)] -#[repr(transparent)] -struct Struct9<#[pointee] T, U>(T) where T: ?Sized; -"#, - expect![[r#" - 35..72: `CoercePointee` can only be derived on `struct`s - 74..114: `CoercePointee` can only be derived on `struct`s with at least one field - 116..158: `CoercePointee` can only be derived on `struct`s with at least one field - 160..202: `CoercePointee` can only be derived on `struct`s with at least one field - 204..258: `CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]` - 260..326: `CoercePointee` can only be derived on `struct`s that are generic over at least one type - 328..439: only one type parameter can be marked as `#[pointee]` when deriving `CoercePointee` traits - 441..530: exactly one generic type parameter must be marked as `#[pointee]` to derive `CoercePointee` traits - 532..621: `derive(CoercePointee)` requires `T` to be marked `?Sized` - 623..690: `derive(CoercePointee)` requires `T` to be marked `?Sized`"#]], - ); -} - -#[test] -fn union_derive() { - check_errors( - r#" -//- minicore: clone, copy, default, fmt, hash, ord, eq, derive - -#[derive(Copy)] -union Foo1 { _v: () } -#[derive(Clone)] -union Foo2 { _v: () } -#[derive(Default)] -union Foo3 { _v: () } -#[derive(Debug)] -union Foo4 { _v: () } -#[derive(Hash)] -union Foo5 { _v: () } -#[derive(Ord)] -union Foo6 { _v: () } -#[derive(PartialOrd)] -union Foo7 { _v: () } -#[derive(Eq)] -union Foo8 { _v: () } -#[derive(PartialEq)] -union Foo9 { _v: () } - "#, - expect![[r#" - 78..118: this trait cannot be derived for unions - 119..157: this trait cannot be derived for unions - 158..195: this trait cannot be derived for unions - 196..232: this trait cannot be derived for unions - 233..276: this trait cannot be derived for unions - 313..355: this trait cannot be derived for unions"#]], - ); -} - -#[test] -fn default_enum_without_default_attr() { - check_errors( - r#" -//- minicore: default, derive - -#[derive(Default)] -enum Foo { - Bar, -} - "#, - expect!["1..41: `#[derive(Default)]` on enum with no `#[default]`"], - ); -} - -#[test] -fn generic_enum_default() { - check( - r#" -//- minicore: default, derive - -#[derive(Default)] -enum Foo { - Bar(T), - #[default] - Baz, -} -"#, - expect![[r#" - -#[derive(Default)] -enum Foo { - Bar(T), - #[default] - Baz, -} - -impl $crate::default::Default for Foo where { - fn default() -> Self { - Foo::Baz - } -}"#]], - ); -} +// FIXME: This file is commented out because due to the fast path for builtin derives, +// the macros do not really get expanded, and we cannot check their expansion. +// It's not removed because we still need to find some way to do that nevertheless. +// Maybe going through the list of registered derive calls in the def map? + +// use expect_test::expect; + +// use crate::macro_expansion_tests::{check, check_errors}; + +// #[test] +// fn test_copy_expand_simple() { +// check( +// r#" +// //- minicore: derive, copy +// #[derive(Copy)] +// struct Foo; +// "#, +// expect![[r#" +// #[derive(Copy)] +// struct Foo; + +// impl <> $crate::marker::Copy for Foo< > where {}"#]], +// ); +// } + +// #[test] +// fn test_copy_expand_in_core() { +// check( +// r#" +// //- /lib.rs crate:core +// #[rustc_builtin_macro] +// macro derive {} +// #[rustc_builtin_macro] +// macro Copy {} +// #[derive(Copy)] +// struct Foo; +// "#, +// expect![[r#" +// #[rustc_builtin_macro] +// macro derive {} +// #[rustc_builtin_macro] +// macro Copy {} +// #[derive(Copy)] +// struct Foo; + +// impl <> $crate::marker::Copy for Foo< > where {}"#]], +// ); +// } + +// #[test] +// fn test_copy_expand_with_type_params() { +// check( +// r#" +// //- minicore: derive, copy +// #[derive(Copy)] +// struct Foo; +// "#, +// expect![[r#" +// #[derive(Copy)] +// struct Foo; + +// impl $crate::marker::Copy for Foo where {}"#]], +// ); +// } + +// #[test] +// fn test_copy_expand_with_lifetimes() { +// // We currently just ignore lifetimes +// check( +// r#" +// //- minicore: derive, copy +// #[derive(Copy)] +// struct Foo; +// "#, +// expect![[r#" +// #[derive(Copy)] +// struct Foo; + +// impl $crate::marker::Copy for Foo where {}"#]], +// ); +// } + +// #[test] +// fn test_clone_expand() { +// check( +// r#" +// //- minicore: derive, clone +// #[derive(Clone)] +// enum Command { +// Move { x: A, y: B }, +// Do(&'static str), +// Jump, +// } +// "#, +// expect![[r#" +// #[derive(Clone)] +// enum Command { +// Move { x: A, y: B }, +// Do(&'static str), +// Jump, +// } + +// impl $crate::clone::Clone for Command where { +// fn clone(&self ) -> Self { +// match self { +// Command::Move { +// x: x, y: y, +// } +// =>Command::Move { +// x: x.clone(), y: y.clone(), +// } +// , Command::Do(f0, )=>Command::Do(f0.clone(), ), Command::Jump=>Command::Jump, +// } +// } +// }"#]], +// ); +// } + +// #[test] +// fn test_clone_expand_with_associated_types() { +// check( +// r#" +// //- minicore: derive, clone +// trait Trait { +// type InWc; +// type InFieldQualified; +// type InFieldShorthand; +// type InGenericArg; +// } +// trait Marker {} +// struct Vec(T); + +// #[derive(Clone)] +// struct Foo +// where +// ::InWc: Marker, +// { +// qualified: ::InFieldQualified, +// shorthand: T::InFieldShorthand, +// generic: Vec, +// } +// "#, +// expect![[r#" +// trait Trait { +// type InWc; +// type InFieldQualified; +// type InFieldShorthand; +// type InGenericArg; +// } +// trait Marker {} +// struct Vec(T); + +// #[derive(Clone)] +// struct Foo +// where +// ::InWc: Marker, +// { +// qualified: ::InFieldQualified, +// shorthand: T::InFieldShorthand, +// generic: Vec, +// } + +// impl $crate::clone::Clone for Foo where ::InWc: Marker, T: Trait, T::InFieldShorthand: $crate::clone::Clone, T::InGenericArg: $crate::clone::Clone, { +// fn clone(&self ) -> Self { +// match self { +// Foo { +// qualified: qualified, shorthand: shorthand, generic: generic, +// } +// =>Foo { +// qualified: qualified.clone(), shorthand: shorthand.clone(), generic: generic.clone(), +// } +// , +// } +// } +// }"#]], +// ); +// } + +// #[test] +// fn test_clone_expand_with_const_generics() { +// check( +// r#" +// //- minicore: derive, clone +// #[derive(Clone)] +// struct Foo(u32); +// "#, +// expect![[r#" +// #[derive(Clone)] +// struct Foo(u32); + +// impl $crate::clone::Clone for Foo where { +// fn clone(&self ) -> Self { +// match self { +// Foo(f0, )=>Foo(f0.clone(), ), +// } +// } +// }"#]], +// ); +// } + +// #[test] +// fn test_default_expand() { +// check( +// r#" +// //- minicore: derive, default +// #[derive(Default)] +// struct Foo { +// field1: i32, +// field2: (), +// } +// #[derive(Default)] +// enum Bar { +// Foo(u8), +// #[default] +// Bar, +// } +// "#, +// expect![[r#" +// #[derive(Default)] +// struct Foo { +// field1: i32, +// field2: (), +// } +// #[derive(Default)] +// enum Bar { +// Foo(u8), +// #[default] +// Bar, +// } + +// impl <> $crate::default::Default for Foo< > where { +// fn default() -> Self { +// Foo { +// field1: $crate::default::Default::default(), field2: $crate::default::Default::default(), +// } +// } +// } +// impl <> $crate::default::Default for Bar< > where { +// fn default() -> Self { +// Bar::Bar +// } +// }"#]], +// ); +// } + +// #[test] +// fn test_partial_eq_expand() { +// check( +// r#" +// //- minicore: derive, eq +// #[derive(PartialEq, Eq)] +// enum Command { +// Move { x: i32, y: i32 }, +// Do(&'static str), +// Jump, +// } +// "#, +// expect![[r#" +// #[derive(PartialEq, Eq)] +// enum Command { +// Move { x: i32, y: i32 }, +// Do(&'static str), +// Jump, +// } + +// impl <> $crate::cmp::PartialEq for Command< > where { +// fn eq(&self , other: &Self ) -> bool { +// match (self , other) { +// (Command::Move { +// x: x_self, y: y_self, +// } +// , Command::Move { +// x: x_other, y: y_other, +// } +// )=>x_self.eq(x_other) && y_self.eq(y_other), (Command::Do(f0_self, ), Command::Do(f0_other, ))=>f0_self.eq(f0_other), (Command::Jump, Command::Jump)=>true , _unused=>false +// } +// } +// } +// impl <> $crate::cmp::Eq for Command< > where {}"#]], +// ); +// } + +// #[test] +// fn test_partial_eq_expand_with_derive_const() { +// // FIXME: actually expand with const +// check( +// r#" +// //- minicore: derive, eq +// #[derive_const(PartialEq, Eq)] +// enum Command { +// Move { x: i32, y: i32 }, +// Do(&'static str), +// Jump, +// } +// "#, +// expect![[r#" +// #[derive_const(PartialEq, Eq)] +// enum Command { +// Move { x: i32, y: i32 }, +// Do(&'static str), +// Jump, +// } + +// impl <> $crate::cmp::PartialEq for Command< > where { +// fn eq(&self , other: &Self ) -> bool { +// match (self , other) { +// (Command::Move { +// x: x_self, y: y_self, +// } +// , Command::Move { +// x: x_other, y: y_other, +// } +// )=>x_self.eq(x_other) && y_self.eq(y_other), (Command::Do(f0_self, ), Command::Do(f0_other, ))=>f0_self.eq(f0_other), (Command::Jump, Command::Jump)=>true , _unused=>false +// } +// } +// } +// impl <> $crate::cmp::Eq for Command< > where {}"#]], +// ); +// } + +// #[test] +// fn test_partial_ord_expand() { +// check( +// r#" +// //- minicore: derive, ord +// #[derive(PartialOrd, Ord)] +// enum Command { +// Move { x: i32, y: i32 }, +// Do(&'static str), +// Jump, +// } +// "#, +// expect![[r#" +// #[derive(PartialOrd, Ord)] +// enum Command { +// Move { x: i32, y: i32 }, +// Do(&'static str), +// Jump, +// } + +// impl <> $crate::cmp::PartialOrd for Command< > where { +// fn partial_cmp(&self , other: &Self ) -> $crate::option::Option<$crate::cmp::Ordering> { +// match $crate::intrinsics::discriminant_value(self ).partial_cmp(&$crate::intrinsics::discriminant_value(other)) { +// $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> { +// match (self , other) { +// (Command::Move { +// x: x_self, y: y_self, +// } +// , Command::Move { +// x: x_other, y: y_other, +// } +// )=>match x_self.partial_cmp(&x_other) { +// $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> { +// match y_self.partial_cmp(&y_other) { +// $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> { +// $crate::option::Option::Some($crate::cmp::Ordering::Equal) +// } +// c=>return c, +// } +// } +// c=>return c, +// } +// , (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.partial_cmp(&f0_other) { +// $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> { +// $crate::option::Option::Some($crate::cmp::Ordering::Equal) +// } +// c=>return c, +// } +// , (Command::Jump, Command::Jump)=>$crate::option::Option::Some($crate::cmp::Ordering::Equal), _unused=>$crate::option::Option::Some($crate::cmp::Ordering::Equal) +// } +// } +// c=>return c, +// } +// } +// } +// impl <> $crate::cmp::Ord for Command< > where { +// fn cmp(&self , other: &Self ) -> $crate::cmp::Ordering { +// match $crate::intrinsics::discriminant_value(self ).cmp(&$crate::intrinsics::discriminant_value(other)) { +// $crate::cmp::Ordering::Equal=> { +// match (self , other) { +// (Command::Move { +// x: x_self, y: y_self, +// } +// , Command::Move { +// x: x_other, y: y_other, +// } +// )=>match x_self.cmp(&x_other) { +// $crate::cmp::Ordering::Equal=> { +// match y_self.cmp(&y_other) { +// $crate::cmp::Ordering::Equal=> { +// $crate::cmp::Ordering::Equal +// } +// c=>return c, +// } +// } +// c=>return c, +// } +// , (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.cmp(&f0_other) { +// $crate::cmp::Ordering::Equal=> { +// $crate::cmp::Ordering::Equal +// } +// c=>return c, +// } +// , (Command::Jump, Command::Jump)=>$crate::cmp::Ordering::Equal, _unused=>$crate::cmp::Ordering::Equal +// } +// } +// c=>return c, +// } +// } +// }"#]], +// ); +// } + +// #[test] +// fn test_hash_expand() { +// check( +// r#" +// //- minicore: derive, hash +// use core::hash::Hash; + +// #[derive(Hash)] +// struct Foo { +// x: i32, +// y: u64, +// z: (i32, u64), +// } +// "#, +// expect![[r#" +// use core::hash::Hash; + +// #[derive(Hash)] +// struct Foo { +// x: i32, +// y: u64, +// z: (i32, u64), +// } + +// impl <> $crate::hash::Hash for Foo< > where { +// fn hash(&self , ra_expand_state: &mut H) { +// match self { +// Foo { +// x: x, y: y, z: z, +// } +// => { +// x.hash(ra_expand_state); +// y.hash(ra_expand_state); +// z.hash(ra_expand_state); +// } +// , +// } +// } +// }"#]], +// ); +// check( +// r#" +// //- minicore: derive, hash +// use core::hash::Hash; + +// #[derive(Hash)] +// enum Command { +// Move { x: i32, y: i32 }, +// Do(&'static str), +// Jump, +// } +// "#, +// expect![[r#" +// use core::hash::Hash; + +// #[derive(Hash)] +// enum Command { +// Move { x: i32, y: i32 }, +// Do(&'static str), +// Jump, +// } + +// impl <> $crate::hash::Hash for Command< > where { +// fn hash(&self , ra_expand_state: &mut H) { +// $crate::mem::discriminant(self ).hash(ra_expand_state); +// match self { +// Command::Move { +// x: x, y: y, +// } +// => { +// x.hash(ra_expand_state); +// y.hash(ra_expand_state); +// } +// , Command::Do(f0, )=> { +// f0.hash(ra_expand_state); +// } +// , Command::Jump=> {} +// , +// } +// } +// }"#]], +// ); +// } + +// #[test] +// fn test_debug_expand() { +// check( +// r#" +// //- minicore: derive, fmt +// use core::fmt::Debug; + +// #[derive(Debug)] +// enum Command { +// Move { x: i32, y: i32 }, +// Do(&'static str), +// Jump, +// } +// "#, +// expect![[r#" +// use core::fmt::Debug; + +// #[derive(Debug)] +// enum Command { +// Move { x: i32, y: i32 }, +// Do(&'static str), +// Jump, +// } + +// impl <> $crate::fmt::Debug for Command< > where { +// fn fmt(&self , f: &mut $crate::fmt::Formatter) -> $crate::fmt::Result { +// match self { +// Command::Move { +// x: x, y: y, +// } +// =>f.debug_struct("Move").field("x", &x).field("y", &y).finish(), Command::Do(f0, )=>f.debug_tuple("Do").field(&f0).finish(), Command::Jump=>f.write_str("Jump"), +// } +// } +// }"#]], +// ); +// } +// #[test] +// fn test_debug_expand_with_cfg() { +// check( +// r#" +// //- minicore: derive, fmt +// use core::fmt::Debug; + +// #[derive(Debug)] +// struct HideAndShow { +// #[cfg(never)] +// always_hide: u32, +// #[cfg(not(never))] +// always_show: u32, +// } +// #[derive(Debug)] +// enum HideAndShowEnum { +// #[cfg(never)] +// AlwaysHide, +// #[cfg(not(never))] +// AlwaysShow{ +// #[cfg(never)] +// always_hide: u32, +// #[cfg(not(never))] +// always_show: u32, +// } +// } +// "#, +// expect![[r#" +// use core::fmt::Debug; + +// #[derive(Debug)] +// struct HideAndShow { +// #[cfg(never)] +// always_hide: u32, +// #[cfg(not(never))] +// always_show: u32, +// } +// #[derive(Debug)] +// enum HideAndShowEnum { +// #[cfg(never)] +// AlwaysHide, +// #[cfg(not(never))] +// AlwaysShow{ +// #[cfg(never)] +// always_hide: u32, +// #[cfg(not(never))] +// always_show: u32, +// } +// } + +// impl <> $crate::fmt::Debug for HideAndShow< > where { +// fn fmt(&self , f: &mut $crate::fmt::Formatter) -> $crate::fmt::Result { +// match self { +// HideAndShow { +// always_show: always_show, +// } +// =>f.debug_struct("HideAndShow").field("always_show", &always_show).finish() +// } +// } +// } +// impl <> $crate::fmt::Debug for HideAndShowEnum< > where { +// fn fmt(&self , f: &mut $crate::fmt::Formatter) -> $crate::fmt::Result { +// match self { +// HideAndShowEnum::AlwaysShow { +// always_show: always_show, +// } +// =>f.debug_struct("AlwaysShow").field("always_show", &always_show).finish(), +// } +// } +// }"#]], +// ); +// } +// #[test] +// fn test_default_expand_with_cfg() { +// check( +// r#" +// //- minicore: derive, default +// #[derive(Default)] +// struct Foo { +// field1: i32, +// #[cfg(never)] +// field2: (), +// #[cfg(feature = "never")] +// field3: (), +// #[cfg(not(feature = "never"))] +// field4: (), +// } +// #[derive(Default)] +// enum Bar { +// Foo, +// #[cfg_attr(not(never), default)] +// Bar, +// } +// "#, +// expect![[r##" +// #[derive(Default)] +// struct Foo { +// field1: i32, +// #[cfg(never)] +// field2: (), +// #[cfg(feature = "never")] +// field3: (), +// #[cfg(not(feature = "never"))] +// field4: (), +// } +// #[derive(Default)] +// enum Bar { +// Foo, +// #[cfg_attr(not(never), default)] +// Bar, +// } + +// impl <> $crate::default::Default for Foo< > where { +// fn default() -> Self { +// Foo { +// field1: $crate::default::Default::default(), field4: $crate::default::Default::default(), +// } +// } +// } +// impl <> $crate::default::Default for Bar< > where { +// fn default() -> Self { +// Bar::Bar +// } +// }"##]], +// ); +// } + +// #[test] +// fn coerce_pointee_expansion() { +// check( +// r#" +// //- minicore: coerce_pointee + +// use core::marker::CoercePointee; + +// pub trait Trait {} + +// #[derive(CoercePointee)] +// #[repr(transparent)] +// pub struct Foo<'a, T: ?Sized + Trait, #[pointee] U: ?Sized, const N: u32>(T) +// where +// U: Trait + ToString;"#, +// expect![[r#" + +// use core::marker::CoercePointee; + +// pub trait Trait {} + +// #[derive(CoercePointee)] +// #[repr(transparent)] +// pub struct Foo<'a, T: ?Sized + Trait, #[pointee] U: ?Sized, const N: u32>(T) +// where +// U: Trait + ToString; +// impl $crate::ops::DispatchFromDyn> for Foo where U: Trait +ToString, T: Trait<__S>, __S: ?Sized, __S: Trait<__S> +ToString, U: ::core::marker::Unsize<__S>, T:?Sized+Trait, U:?Sized, {} +// impl $crate::ops::CoerceUnsized> for Foo where U: Trait +ToString, T: Trait<__S>, __S: ?Sized, __S: Trait<__S> +ToString, U: ::core::marker::Unsize<__S>, T:?Sized+Trait, U:?Sized, {}"#]], +// ); +// } + +// #[test] +// fn coerce_pointee_errors() { +// check_errors( +// r#" +// //- minicore: coerce_pointee + +// use core::marker::CoercePointee; + +// #[derive(CoercePointee)] +// enum Enum {} + +// #[derive(CoercePointee)] +// struct Struct1; + +// #[derive(CoercePointee)] +// struct Struct2(); + +// #[derive(CoercePointee)] +// struct Struct3 {} + +// #[derive(CoercePointee)] +// struct Struct4(T); + +// #[derive(CoercePointee)] +// #[repr(transparent)] +// struct Struct5(i32); + +// #[derive(CoercePointee)] +// #[repr(transparent)] +// struct Struct6<#[pointee] T: ?Sized, #[pointee] U: ?Sized>(T, U); + +// #[derive(CoercePointee)] +// #[repr(transparent)] +// struct Struct7(T, U); + +// #[derive(CoercePointee)] +// #[repr(transparent)] +// struct Struct8<#[pointee] T, U: ?Sized>(T); + +// #[derive(CoercePointee)] +// #[repr(transparent)] +// struct Struct9(T); + +// #[derive(CoercePointee)] +// #[repr(transparent)] +// struct Struct9<#[pointee] T, U>(T) where T: ?Sized; +// "#, +// expect![[r#" +// 35..72: `CoercePointee` can only be derived on `struct`s +// 74..114: `CoercePointee` can only be derived on `struct`s with at least one field +// 116..158: `CoercePointee` can only be derived on `struct`s with at least one field +// 160..202: `CoercePointee` can only be derived on `struct`s with at least one field +// 204..258: `CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]` +// 260..326: `CoercePointee` can only be derived on `struct`s that are generic over at least one type +// 328..439: only one type parameter can be marked as `#[pointee]` when deriving `CoercePointee` traits +// 441..530: exactly one generic type parameter must be marked as `#[pointee]` to derive `CoercePointee` traits +// 532..621: `derive(CoercePointee)` requires `T` to be marked `?Sized` +// 623..690: `derive(CoercePointee)` requires `T` to be marked `?Sized`"#]], +// ); +// } + +// #[test] +// fn union_derive() { +// check_errors( +// r#" +// //- minicore: clone, copy, default, fmt, hash, ord, eq, derive + +// #[derive(Copy)] +// union Foo1 { _v: () } +// #[derive(Clone)] +// union Foo2 { _v: () } +// #[derive(Default)] +// union Foo3 { _v: () } +// #[derive(Debug)] +// union Foo4 { _v: () } +// #[derive(Hash)] +// union Foo5 { _v: () } +// #[derive(Ord)] +// union Foo6 { _v: () } +// #[derive(PartialOrd)] +// union Foo7 { _v: () } +// #[derive(Eq)] +// union Foo8 { _v: () } +// #[derive(PartialEq)] +// union Foo9 { _v: () } +// "#, +// expect![[r#" +// 78..118: this trait cannot be derived for unions +// 119..157: this trait cannot be derived for unions +// 158..195: this trait cannot be derived for unions +// 196..232: this trait cannot be derived for unions +// 233..276: this trait cannot be derived for unions +// 313..355: this trait cannot be derived for unions"#]], +// ); +// } + +// #[test] +// fn default_enum_without_default_attr() { +// check_errors( +// r#" +// //- minicore: default, derive + +// #[derive(Default)] +// enum Foo { +// Bar, +// } +// "#, +// expect!["1..41: `#[derive(Default)]` on enum with no `#[default]`"], +// ); +// } + +// #[test] +// fn generic_enum_default() { +// check( +// r#" +// //- minicore: default, derive + +// #[derive(Default)] +// enum Foo { +// Bar(T), +// #[default] +// Baz, +// } +// "#, +// expect![[r#" + +// #[derive(Default)] +// enum Foo { +// Bar(T), +// #[default] +// Baz, +// } + +// impl $crate::default::Default for Foo where { +// fn default() -> Self { +// Foo::Baz +// } +// }"#]], +// ); +// } 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 6f30ca04af8c..bf04a500a57d 100644 --- a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs +++ b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs @@ -122,16 +122,16 @@ struct Foo { v4: bool // No comma here } +#[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/collector.rs b/crates/hir-def/src/nameres/collector.rs index 08edf41c5694..de51e66ddeb4 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, 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, Vec>, } 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,84 @@ 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(¯o_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 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 +1567,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 +1603,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 +1803,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 +1851,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>, + ast_id: FileAstId, + 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 +2055,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 +2079,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 +2101,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 c8eb968b3587..a943f6f0ac01 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 6582f4b07574..c80519742585 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 { diff --git a/crates/hir-ty/src/builtin_derive.rs b/crates/hir-ty/src/builtin_derive.rs new file mode 100644 index 000000000000..b21008ca4cda --- /dev/null +++ b/crates/hir-ty/src/builtin_derive.rs @@ -0,0 +1,595 @@ +//! 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, DbInterner, EarlyBinder, GenericArgs, ParamEnv, 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_return_type))] +pub fn predicates<'db>( + db: &'db dyn HirDatabase, + impl_: BuiltinDeriveImplId, +) -> GenericPredicates<'db> { + 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(EarlyBinder::bind( + adt_predicates.explicit_predicates().iter_identity_copied().collect(), + )) + } 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(EarlyBinder::bind( + Box::default(), + )); + }; + 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(EarlyBinder::bind( + adt_predicates + .explicit_predicates() + .iter_identity_copied() + .chain(duplicated_bounds) + .chain(unsize_bound) + .collect(), + )) + } + } +} + +/// 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> 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>>( + 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<'db>, + trait_id: TraitId, +) -> GenericPredicates<'db> { + 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(EarlyBinder::bind( + adt_predicates + .explicit_predicates() + .iter_identity_copied() + .chain(extra_predicates) + .chain(assoc_type_bounds) + .collect(), + )) +} + +fn extend_assoc_type_bounds<'db>( + interner: DbInterner<'db>, + assoc_type_bounds: &mut Vec>, + fields: &ArenaMap>>, + trait_: TraitId, +) { + struct ProjectionFinder<'a, 'db> { + interner: DbInterner<'db>, + assoc_type_bounds: &'a mut Vec>, + trait_: TraitId, + } + + impl<'db> TypeVisitor> 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.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(*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(*const T); + +trait Trait {} + +#[derive(CoercePointee)] +struct MultiGenericParams<'a, T, #[pointee] U: ?Sized, const N: usize>(*const U) +where + T: Trait, + U: Trait; + "#, + 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 2dc937d76031..300996ced83e 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 f9523e7168de..4e5902cf46e3 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; @@ -94,7 +96,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { env: ParamEnvAndCrate<'db>, func: FunctionId, fn_subst: GenericArgs<'db>, - ) -> (FunctionId, GenericArgs<'db>); + ) -> (Either, GenericArgs<'db>); // endregion:mir diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index e1d62a9c7a3e..37e225e8003c 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(); } @@ -1978,6 +1978,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 3ae6451d6952..7b0c6f4e1387 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 5ebe87c5d5d6..756a512e30b9 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 dd34bbe2fd02..dc074f849529 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -1692,6 +1692,13 @@ impl<'db> GenericPredicates<'db> { } impl<'db> GenericPredicates<'db> { + #[inline] + pub(crate) fn from_explicit_own_predicates( + predicates: EarlyBinder<'db, Box<[Clause<'db>]>>, + ) -> Self { + Self { predicates, own_predicates_start: 0, is_trait: false, parent_is_trait: false } + } + #[inline] pub fn query(db: &'db dyn HirDatabase, def: GenericDefId) -> &'db GenericPredicates<'db> { &Self::query_with_diagnostics(db, def).0 @@ -1750,18 +1757,27 @@ pub(crate) fn trait_environment_for_body_query( db.trait_environment(def) } +pub(crate) fn param_env_from_predicates<'db>( + interner: DbInterner<'db>, + predicates: &GenericPredicates<'db>, +) -> 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_query<'db>( db: &'db dyn HirDatabase, def: GenericDefId, ) -> ParamEnv<'db> { 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. - ParamEnv { clauses } + param_env_from_predicates(interner, GenericPredicates::query(db, def)) } #[derive(Copy, Clone, Debug, PartialEq, Eq)] diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index 868ae00329b3..27ca990ceaf1 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, 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( @@ -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, 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); @@ -641,13 +661,13 @@ impl InherentImpls { #[derive(Debug, PartialEq)] struct OneTraitImpls { - non_blanket_impls: FxHashMap>, + non_blanket_impls: FxHashMap, Box<[BuiltinDeriveImplId]>)>, blanket_impls: Box<[ImplId]>, } #[derive(Default)] struct OneTraitImplsBuilder { - non_blanket_impls: FxHashMap>, + non_blanket_impls: FxHashMap, Vec)>, blanket_impls: Vec, } @@ -656,7 +676,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::>(); non_blanket_impls.shrink_to_fit(); let blanket_impls = self.blanket_impls.into_boxed_slice(); @@ -697,8 +719,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())) @@ -709,6 +732,7 @@ impl TraitImpls { fn collect( db: &dyn HirDatabase, def_map: &DefMap, + lang_items: &LangItems, map: &mut FxHashMap, ) { for (_module_id, module_data) in def_map.modules() { @@ -733,18 +757,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); } } } @@ -767,27 +802,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 6af47ab68bfc..0fd1c37d920f 100644 --- a/crates/hir-ty/src/method_resolution/probe.rs +++ b/crates/hir-ty/src/method_resolution/probe.rs @@ -999,7 +999,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( @@ -1488,7 +1488,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) } @@ -1522,7 +1522,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 3b4913cae3fb..f745f41ee470 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -76,12 +76,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)] pub struct VTableMap<'db> { @@ -2618,6 +2620,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 42c11113ee30..98fa4f9401a4 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 4c64a70a7a62..69fce0fc5639 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 b6167b4a097c..aa6caefc4a06 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 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 for DefWithBodyId { | SolverDefId::TraitId(_) | SolverDefId::TypeAliasId(_) | SolverDefId::ImplId(_) + | SolverDefId::BuiltinDeriveImplId(_) | SolverDefId::InternedClosureId(_) | SolverDefId::InternedCoroutineId(_) | SolverDefId::Ctor(Ctor::Struct(_)) @@ -216,6 +221,7 @@ impl TryFrom 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> for SolverDefId { fn as_local(self) -> Option { 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> 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 for SolverDefId { + #[inline] + fn from(value: AnyImplId) -> SolverDefId { + match value { + AnyImplId::ImplId(it) => it.into(), + AnyImplId::BuiltinDeriveImplId(it) => it.into(), + } + } +} +impl TryFrom for AnyImplId { + type Error = (); + #[inline] + fn try_from(value: SolverDefId) -> Result { + match value { + SolverDefId::ImplId(it) => Ok(it.into()), + SolverDefId::BuiltinDeriveImplId(it) => Ok(it.into()), + _ => Err(()), + } + } +} +impl<'db> inherent::DefId> for AnyImplId { + fn as_local(self) -> Option { + Some(self.into()) + } + fn is_local(self) -> bool { + true + } +} diff --git a/crates/hir-ty/src/next_solver/generics.rs b/crates/hir-ty/src/next_solver/generics.rs index 4d164a7e3bc5..a8288b4e8259 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, } +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 52ad410df6be..bd407fd15718 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, } @@ -395,7 +395,7 @@ fn to_selection<'db>(cand: InspectCandidate<'_, 'db>) -> Option> // 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 8b24a20a5bed..f323684c74a6 100644 --- a/crates/hir-ty/src/next_solver/interner.rs +++ b/crates/hir-ty/src/next_solver/interner.rs @@ -36,10 +36,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, util::explicit_item_bounds, + AdtIdWrapper, AnyImplId, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper, + CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, GeneralConstIdWrapper, OpaqueTypeKey, + RegionAssumptions, SimplifiedType, SolverContext, SolverDefIds, TraitIdWrapper, + TypeAliasIdWrapper, util::explicit_item_bounds, }, }; @@ -922,7 +922,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; @@ -1068,7 +1068,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 { @@ -1094,6 +1094,7 @@ impl<'db> Interner for DbInterner<'db> { | SolverDefId::TraitId(_) | SolverDefId::TypeAliasId(_) | SolverDefId::ImplId(_) + | SolverDefId::BuiltinDeriveImplId(_) | SolverDefId::InternedClosureId(_) | SolverDefId::InternedCoroutineId(_) => { return VariancesOf::new_from_iter(self, []); @@ -1234,6 +1235,7 @@ impl<'db> Interner for DbInterner<'db> { | SolverDefId::AdtId(_) | SolverDefId::TraitId(_) | SolverDefId::ImplId(_) + | SolverDefId::BuiltinDeriveImplId(_) | SolverDefId::EnumVariantId(..) | SolverDefId::Ctor(..) | SolverDefId::InternedOpaqueTyId(..) => panic!(), @@ -1352,8 +1354,7 @@ impl<'db> Interner for DbInterner<'db> { self, def_id: Self::DefId, ) -> EarlyBinder> { - 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)] @@ -1361,8 +1362,7 @@ impl<'db> Interner for DbInterner<'db> { self, def_id: Self::DefId, ) -> EarlyBinder> { - 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)] @@ -1407,32 +1407,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> { - 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| { @@ -1694,6 +1692,7 @@ impl<'db> Interner for DbInterner<'db> { SolverDefId::ConstId(_) | SolverDefId::FunctionId(_) | SolverDefId::ImplId(_) + | SolverDefId::BuiltinDeriveImplId(_) | SolverDefId::StaticId(_) | SolverDefId::InternedClosureId(_) | SolverDefId::InternedCoroutineId(_) @@ -1709,7 +1708,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()); } }, @@ -1831,7 +1835,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)] @@ -1839,14 +1846,24 @@ impl<'db> Interner for DbInterner<'db> { self, impl_id: Self::ImplId, ) -> EarlyBinder> { - 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 { @@ -2136,11 +2153,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 { @@ -2243,6 +2262,17 @@ impl<'db> DbInterner<'db> { } } +fn predicates_of<'db>( + db: &'db dyn HirDatabase, + def_id: SolverDefId, +) -> &'db GenericPredicates<'db> { + 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,)+) => { $( @@ -2285,7 +2315,7 @@ TrivialTypeTraversalImpls! { ClosureIdWrapper, CoroutineIdWrapper, AdtIdWrapper, - ImplIdWrapper, + AnyImplId, GeneralConstIdWrapper, Pattern<'db>, Safety, diff --git a/crates/hir-ty/src/next_solver/solver.rs b/crates/hir-ty/src/next_solver/solver.rs index 40a3f17cf169..0a27a0353ffd 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, trait_assoc_def_id: SolverDefId, - impl_id: ImplIdWrapper, + impl_id: AnyImplId, ) -> Result, 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 7b0c1d35442e..f4b2b94a17f0 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_shim", - "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_shim", + "impl_signature_shim", + "impl_signature_with_source_map_shim", "impl_self_ty_with_diagnostics_shim", "struct_signature_shim", "struct_signature_with_source_map_shim", diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs index d1056f31e19e..222ebff8062f 100644 --- a/crates/hir/src/attrs.rs +++ b/crates/hir/src/attrs.rs @@ -35,6 +35,7 @@ pub enum AttrsOwner { Field(FieldId), LifetimeParam(LifetimeParamId), TypeOrConstParam(TypeOrConstParamId), + Dummy, } impl AttrsOwner { @@ -123,7 +124,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 +136,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 +148,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 +163,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 +177,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 +202,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 +322,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 d0d8c4877d21..1f9af564c359 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, ¶ms, ¶ms_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, ¶ms, ¶ms_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 { + 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` 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` 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 bc025c5ef5cf..fc20f4b46bb9 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 for ModuleDef { } } -impl From for ModuleDefId { - fn from(id: ModuleDef) -> Self { - match id { +impl TryFrom for ModuleDefId { + type Error = (); + fn try_from(id: ModuleDef) -> Result { + 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 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 for DefWithBodyId { - fn from(def: DefWithBody) -> Self { - match def { - DefWithBody::Function(it) => DefWithBodyId::FunctionId(it.id), +impl TryFrom for DefWithBodyId { + type Error = (); + fn try_from(def: DefWithBody) -> Result { + 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 for AssocItem { } } -impl From 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 for GenericDefId { + type Error = (); + + fn try_from(def: GenericDef) -> Result { + def.id().ok_or(()) } } @@ -238,13 +241,17 @@ impl From for Field { } } -impl From for GenericDefId { - fn from(item: AssocItem) -> Self { - match item { - AssocItem::Function(f) => f.id.into(), +impl TryFrom for GenericDefId { + type Error = (); + fn try_from(item: AssocItem) -> Result { + 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 for ItemInNs { } } -impl From 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 for hir_def::item_scope::ItemInNs { + type Error = (); + fn try_from(it: ItemInNs) -> Result { + 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 for hir_def::builtin_type::BuiltinType { it.inner } } + +impl From for crate::Impl { + fn from(value: hir_def::ImplId) -> Self { + crate::Impl { id: AnyImplId::ImplId(value) } + } +} + +impl From for crate::Impl { + fn from(value: BuiltinDeriveImplId) -> Self { + crate::Impl { id: AnyImplId::BuiltinDeriveImplId(value) } + } +} + +impl From 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 1aa799400106..62fa59d4911d 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 fn source(self, db: &dyn HirDatabase) -> Option>; + + /// 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)>> { + 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> { - Some(self.id.lookup(db).source(db)) + match self.id { + AnyFunctionId::FunctionId(id) => Some(id.loc(db).source(db)), + // HACK: 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)>> { + 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> { - 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)>> { + 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> { 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> { - 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 a50a736ccd0e..2d3a24877a50 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}; 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 { 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 { 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 { 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 { - 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] @@ -1862,9 +1881,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), } } } @@ -1908,24 +1927,33 @@ impl DefWithBody { } } - fn id(&self) -> DefWithBodyId { - match self { - DefWithBody::Function(it) => it.id.into(), + fn id(&self) -> Option { + 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:?}"), @@ -1938,11 +1966,17 @@ impl DefWithBody { acc: &mut Vec>, 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) => { @@ -1957,11 +1991,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, @@ -1988,14 +2022,14 @@ impl DefWithBody { acc.push( TypeMismatch { expr_or_pat, - expected: Type::new(db, DefWithBodyId::from(self), mismatch.expected), - actual: Type::new(db, DefWithBodyId::from(self), mismatch.actual), + expected: Type::new(db, id, mismatch.expected), + actual: Type::new(db, id, mismatch.actual), } .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( @@ -2030,7 +2064,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 { @@ -2084,7 +2118,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) { @@ -2140,17 +2174,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()) } } @@ -2188,45 +2216,175 @@ 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(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Function { - pub(crate) id: FunctionId, + pub(crate) id: AnyFunctionId, } 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> 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 @@ -2235,30 +2393,41 @@ impl Function { db: &'db dyn HirDatabase, generics: impl Iterator>, ) -> 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>, + ) -> 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> { + 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() @@ -2270,31 +2439,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 { - 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> { - 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>> { @@ -2303,21 +2488,11 @@ impl Function { } pub fn params_without_self(self, db: &dyn HirDatabase) -> Vec> { - 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 @@ -2326,40 +2501,50 @@ impl Function { db: &'db dyn HirDatabase, generics: impl Iterator>, ) -> Vec> { - 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 { - 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, } } @@ -2392,33 +2577,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( @@ -2427,9 +2625,17 @@ impl Function { caller: Option, 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), @@ -2443,7 +2649,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 @@ -2456,12 +2662,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 { - 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( @@ -2469,13 +2681,18 @@ impl Function { db: &dyn HirDatabase, span_formatter: impl Fn(FileId, TextRange) -> String, ) -> Result> { + 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::new_from_iter(interner, []), 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), }, )?; let (result, output) = interpret_mir(db, body, false, None)?; @@ -2591,36 +2808,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(|¶m| 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(|¶m| 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 = callable_sig.inputs().as_slice()[0]; - Type { env: environment, ty } + let (env, sig) = self.func.fn_sig(db); + Type { env, ty: sig.skip_binder().inputs().get(0).unwrap() } } // FIXME: Find better API to also handle const generics @@ -2630,18 +2858,18 @@ impl SelfParam { generics: impl Iterator>, ) -> 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 = callable_sig.inputs().as_slice()[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, + } } } @@ -2862,7 +3090,7 @@ impl Trait { pub fn function(self, db: &dyn HirDatabase, name: impl PartialEq) -> Option { 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, }) } @@ -3143,15 +3371,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 { + 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, } } @@ -3189,6 +3417,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 { @@ -3267,7 +3498,10 @@ pub trait AsExternAssocItem { impl AsExternAssocItem for Function { fn as_extern_assoc_item(self, db: &dyn HirDatabase) -> Option { - 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) } } @@ -3295,7 +3529,7 @@ pub enum AssocItem { impl From 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 }), } } @@ -3313,7 +3547,10 @@ pub trait AsAssocItem { impl AsAssocItem for Function { fn as_assoc_item(self, db: &dyn HirDatabase) -> Option { - 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)), + } } } @@ -3442,7 +3679,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, }; @@ -3579,9 +3823,13 @@ impl_from!( impl GenericDef { pub fn params(self, db: &dyn HirDatabase) -> Vec { - 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), @@ -3595,39 +3843,51 @@ impl GenericDef { } pub fn lifetime_params(self, db: &dyn HirDatabase) -> Vec { - 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 { - 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 { + 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>) { - let def = self.id(); + let Some(def) = self.id() else { return }; let generics = db.generic_params(def); @@ -3700,6 +3960,17 @@ impl<'db> GenericSubstitution<'db> { Self { def, subst, env } } + fn new_from_fn( + def: Function, + subst: GenericArgs<'db>, + env: ParamEnvAndCrate<'db>, + ) -> Option { + 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), @@ -3812,7 +4083,9 @@ impl Local { pub fn as_self_param(self, db: &dyn HirDatabase) -> Option { 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, } } @@ -4300,7 +4573,7 @@ impl TypeOrConstParam { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Impl { - pub(crate) id: ImplId, + pub(crate) id: AnyImplId, } impl Impl { @@ -4312,6 +4585,7 @@ impl Impl { fn extend_with_def_map(db: &dyn HirDatabase, def_map: &DefMap, result: &mut Vec) { 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) { @@ -4323,7 +4597,7 @@ impl Impl { } pub fn all_in_module(db: &dyn HirDatabase, module: Module) -> Vec { - 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", @@ -4342,15 +4616,20 @@ impl Impl { else { return Vec::new(); }; - let mut extend_with_impls = - |impls: &[ImplId]| result.extend(impls.iter().copied().map(Impl::from)); - extend_with_impls(method_resolution::incoherent_inherent_impls(db, simplified_ty)); + 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)), + }; + extend_with_impls(Either::Left(method_resolution::incoherent_inherent_impls( + db, + simplified_ty, + ))); 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()) @@ -4372,7 +4651,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)); @@ -4386,75 +4668,118 @@ impl Impl { } pub fn trait_(self, db: &dyn HirDatabase) -> Option { - 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> { - 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 { - 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> { - 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(::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, 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(), + } } } @@ -5517,7 +5842,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(..) @@ -5541,7 +5866,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 { .. @@ -5895,6 +6220,7 @@ enum Callee<'db> { CoroutineClosure(InternedCoroutineId, GenericArgs<'db>), FnPtr, FnImpl(traits::FnTrait), + BuiltinDeriveImplMethod { method: BuiltinDeriveImplMethod, impl_: BuiltinDeriveImplId }, } pub enum CallableKind<'db> { @@ -5910,6 +6236,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()) @@ -5924,12 +6253,22 @@ impl<'db> Callable<'db> { Callee::FnImpl(fn_) => CallableKind::FnImpl(fn_.into()), } } + + fn as_function(&self) -> Option { + 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]), @@ -6326,7 +6665,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_) }) + } + } } } @@ -6378,11 +6722,79 @@ impl HasContainer for ExternBlock { } } +pub trait HasName { + fn name(&self, db: &dyn HirDatabase) -> Option; +} + +macro_rules! impl_has_name { + ( $( $ty:ident ),* $(,)? ) => { + $( + impl HasName for $ty { + fn name(&self, db: &dyn HirDatabase) -> Option { + (*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 { + (*self).name().into() + } + } + )* + }; +} + +impl_has_name_no_db!(TupleField, StaticLifetime, BuiltinType, BuiltinAttr); + +impl HasName for Param<'_> { + fn name(&self, db: &dyn HirDatabase) -> Option { + 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 ffb518b1e66f..ff498d02f069 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -12,7 +12,7 @@ use std::{ use either::Either; use hir_def::{ - DefWithBodyId, FunctionId, MacroId, StructId, TraitId, VariantId, + DefWithBodyId, MacroId, StructId, TraitId, VariantId, expr_store::{Body, ExprOrPatSource, HygieneId, path::Path}, hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat}, nameres::{ModuleOrigin, crate_def_map}, @@ -30,7 +30,7 @@ use hir_expand::{ use hir_ty::{ InferenceResult, diagnostics::{unsafe_operations, unsafe_operations_for_body}, - next_solver::DbInterner, + next_solver::{AnyImplId, DbInterner}, }; use intern::{Interned, Symbol, sym}; use itertools::Itertools; @@ -46,11 +46,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}, @@ -99,7 +99,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, + }, } } } @@ -325,23 +328,23 @@ impl Semantics<'_, DB> { } pub fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option { - 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 { - 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 { - 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 { - 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 { - 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 { @@ -1726,6 +1729,7 @@ impl<'db> SemanticsImpl<'db> { func: Function, subst: impl IntoIterator>, ) -> Option { + 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 = @@ -1734,7 +1738,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 { @@ -1745,23 +1754,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 { + fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option { self.analyze(await_expr.syntax())?.resolve_await_to_poll(self.db, await_expr) } - fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option { + fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option { self.analyze(prefix_expr.syntax())?.resolve_prefix_expr(self.db, prefix_expr) } - fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option { + fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option { self.analyze(index_expr.syntax())?.resolve_index_expr(self.db, index_expr) } - fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option { + fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option { self.analyze(bin_expr.syntax())?.resolve_bin_expr(self.db, bin_expr) } - fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option { + fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option { self.analyze(try_expr.syntax())?.resolve_try_expr(self.db, try_expr) } @@ -1838,7 +1847,9 @@ impl<'db> SemanticsImpl<'db> { } pub fn get_unsafe_ops(&self, def: DefWithBody) -> FxHashSet { - 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(); @@ -1854,7 +1865,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 { @@ -2000,16 +2013,22 @@ impl<'db> SemanticsImpl<'db> { } /// Search for a definition's source and cache its syntax tree - pub fn source(&self, def: Def) -> Option> - where - Def::Ast: AstNode, - { + pub fn source(&self, def: Def) -> Option> { // 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( + &self, + def: Def, + ) -> Option)>> { + 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 { let container = self.with_ctx(|ctx| ctx.find_container(node))?; @@ -2139,9 +2158,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) } @@ -2178,7 +2198,11 @@ impl<'db> SemanticsImpl<'db> { } pub fn impl_generated_from_derive(&self, impl_: Impl) -> Option { - 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 901c9e1575b2..603ecd3c94f6 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}, }; @@ -432,7 +432,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( @@ -447,8 +447,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 => { @@ -520,8 +520,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)), ) }), } @@ -570,7 +570,7 @@ impl<'db> SourceAnalyzer<'db> { &self, db: &'db dyn HirDatabase, await_expr: &ast::AwaitExpr, - ) -> Option { + ) -> Option { let mut ty = self.ty_of_expr(await_expr.expr()?)?; let into_future_trait = self @@ -607,7 +607,7 @@ impl<'db> SourceAnalyzer<'db> { &self, db: &'db dyn HirDatabase, prefix_expr: &ast::PrefixExpr, - ) -> Option { + ) -> Option { let (_op_trait, op_fn) = match prefix_expr.op_kind()? { ast::UnaryOp::Deref => { // This can be either `Deref::deref` or `DerefMut::deref_mut`. @@ -653,7 +653,7 @@ impl<'db> SourceAnalyzer<'db> { &self, db: &'db dyn HirDatabase, index_expr: &ast::IndexExpr, - ) -> Option { + ) -> Option { let base_ty = self.ty_of_expr(index_expr.base()?)?; let index_ty = self.ty_of_expr(index_expr.index()?)?; @@ -683,7 +683,7 @@ impl<'db> SourceAnalyzer<'db> { &self, db: &'db dyn HirDatabase, binop_expr: &ast::BinExpr, - ) -> Option { + ) -> Option { let op = binop_expr.op_kind()?; let lhs = self.ty_of_expr(binop_expr.lhs()?)?; let rhs = self.ty_of_expr(binop_expr.rhs()?)?; @@ -704,7 +704,7 @@ impl<'db> SourceAnalyzer<'db> { &self, db: &'db dyn HirDatabase, try_expr: &ast::TryExpr, - ) -> Option { + ) -> Option { let ty = self.ty_of_expr(try_expr.expr()?)?; let op_fn = self.lang_items(db).TryTraitBranch?; @@ -908,7 +908,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() { @@ -916,19 +916,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)) } } } @@ -941,11 +941,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) @@ -1404,7 +1404,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 } @@ -1413,13 +1413,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 cc879cf84fc6..602656e725a3 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 @@ -715,7 +715,9 @@ def: Function( Function { id: FunctionId( - 6402, + FunctionId( + 6402, + ), ), }, ), @@ -749,7 +751,9 @@ def: Function( Function { id: FunctionId( - 6401, + FunctionId( + 6401, + ), ), }, ), @@ -817,7 +821,9 @@ def: Function( Function { id: FunctionId( - 6400, + FunctionId( + 6400, + ), ), }, ), @@ -883,7 +889,9 @@ def: Function( Function { id: FunctionId( - 6403, + FunctionId( + 6403, + ), ), }, ), diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 03674978d53d..ca8e693d7a99 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -769,14 +769,34 @@ 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 951a672d4b79..b56e671d8119 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 67472507f66e..e17023aedefa 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)>, + name: Option, + kind: SymbolKind, + ) -> UpmappingResult { + 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 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> { 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| { @@ -478,16 +508,16 @@ impl TryToNav for hir::Impl { sema: &Semantics<'_, RootDatabase>, ) -> Option> { 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, @@ -495,8 +525,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 0738b7fadcc1..ffae7bf6c7c5 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 a61be93ea968..407179bda7f5 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs @@ -1683,11 +1683,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 462f1c69fa69..c8a4e6bb4c11 100644 --- a/crates/intern/src/symbol/symbols.rs +++ b/crates/intern/src/symbol/symbols.rs @@ -524,5 +524,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 f39ab1301f8f..67d0623bb9f7 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -691,21 +691,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() { @@ -714,7 +717,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:?}")); @@ -745,11 +748,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::>(); 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()); @@ -767,6 +771,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); @@ -805,9 +810,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) => { @@ -824,7 +829,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) = @@ -1079,6 +1084,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; @@ -1112,7 +1118,7 @@ impl flags::AnalysisStats { bar.println(msg()); } bar.set_message(msg); - db.body(body_id.into()); + db.body(body_def_id); bar.inc(1); }