Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crates/hir-def/src/attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: Meta) -> ControlFlow<Infal
"deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED),
"macro_export" => attr_flags.insert(AttrFlags::IS_MACRO_EXPORT),
"no_mangle" => attr_flags.insert(AttrFlags::NO_MANGLE),
"pointee" => attr_flags.insert(AttrFlags::IS_POINTEE),
"non_exhaustive" => attr_flags.insert(AttrFlags::NON_EXHAUSTIVE),
"ignore" => attr_flags.insert(AttrFlags::IS_IGNORE),
"bench" => attr_flags.insert(AttrFlags::IS_BENCH),
Expand Down Expand Up @@ -286,6 +287,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;
}
}

Expand Down
149 changes: 149 additions & 0 deletions crates/hir-def/src/builtin_derive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
//! Definition of builtin derive impls.
//!
//! To save time and memory, builtin derives are not really expanded. Instead, we record them
//! and create their impls based on lowered data, see crates/hir-ty/src/builtin_derive.rs.

use hir_expand::{InFile, builtin::BuiltinDeriveExpander, name::Name};
use intern::{Symbol, sym};
use tt::TextRange;

use crate::{
AdtId, BuiltinDeriveImplId, BuiltinDeriveImplLoc, FunctionId, HasModule, db::DefDatabase,
};

macro_rules! declare_enum {
( $( $trait:ident => [ $( $method:ident ),* ] ),* $(,)? ) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BuiltinDeriveImplTrait {
$( $trait, )*
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[allow(non_camel_case_types)]
pub enum BuiltinDeriveImplMethod {
$( $( $method, )* )*
}

impl BuiltinDeriveImplTrait {
#[inline]
pub fn name(self) -> Symbol {
match self {
$( Self::$trait => sym::$trait, )*
}
}

#[inline]
pub fn get_id(self, lang_items: &crate::lang_item::LangItems) -> Option<crate::TraitId> {
match self {
$( Self::$trait => lang_items.$trait, )*
}
}

#[inline]
pub fn get_method(self, method_name: &Symbol) -> Option<BuiltinDeriveImplMethod> {
match self {
$(
Self::$trait => {
match method_name {
$( _ if *method_name == sym::$method => Some(BuiltinDeriveImplMethod::$method), )*
_ => None,
}
}
)*
}
}

#[inline]
pub fn all_methods(self) -> &'static [BuiltinDeriveImplMethod] {
match self {
$( Self::$trait => &[ $(BuiltinDeriveImplMethod::$method),* ], )*
}
}
}

impl BuiltinDeriveImplMethod {
#[inline]
pub fn name(self) -> Symbol {
match self {
$( $( BuiltinDeriveImplMethod::$method => sym::$method, )* )*
}
}
}
};
}

declare_enum!(
Copy => [],
Clone => [clone],
Default => [default],
Debug => [fmt],
Hash => [hash],
Ord => [cmp],
PartialOrd => [partial_cmp],
Eq => [],
PartialEq => [eq],
CoerceUnsized => [],
DispatchFromDyn => [],
);

impl BuiltinDeriveImplMethod {
pub fn trait_method(
self,
db: &dyn DefDatabase,
impl_: BuiltinDeriveImplId,
) -> Option<FunctionId> {
let loc = impl_.loc(db);
let lang_items = crate::lang_item::lang_items(db, loc.krate(db));
let trait_ = impl_.loc(db).trait_.get_id(lang_items)?;
trait_.trait_items(db).method_by_name(&Name::new_symbol_root(self.name()))
}
}

pub(crate) fn with_derive_traits(
derive: BuiltinDeriveExpander,
mut f: impl FnMut(BuiltinDeriveImplTrait),
) {
let trait_ = match derive {
BuiltinDeriveExpander::Copy => BuiltinDeriveImplTrait::Copy,
BuiltinDeriveExpander::Clone => BuiltinDeriveImplTrait::Clone,
BuiltinDeriveExpander::Default => BuiltinDeriveImplTrait::Default,
BuiltinDeriveExpander::Debug => BuiltinDeriveImplTrait::Debug,
BuiltinDeriveExpander::Hash => BuiltinDeriveImplTrait::Hash,
BuiltinDeriveExpander::Ord => BuiltinDeriveImplTrait::Ord,
BuiltinDeriveExpander::PartialOrd => BuiltinDeriveImplTrait::PartialOrd,
BuiltinDeriveExpander::Eq => BuiltinDeriveImplTrait::Eq,
BuiltinDeriveExpander::PartialEq => BuiltinDeriveImplTrait::PartialEq,
BuiltinDeriveExpander::CoercePointee => {
f(BuiltinDeriveImplTrait::CoerceUnsized);
f(BuiltinDeriveImplTrait::DispatchFromDyn);
return;
}
};
f(trait_);
}

impl BuiltinDeriveImplLoc {
pub fn source(&self, db: &dyn DefDatabase) -> InFile<TextRange> {
let (adt_ast_id, module) = match self.adt {
AdtId::StructId(adt) => {
let adt_loc = adt.loc(db);
(adt_loc.id.upcast(), adt_loc.container)
}
AdtId::UnionId(adt) => {
let adt_loc = adt.loc(db);
(adt_loc.id.upcast(), adt_loc.container)
}
AdtId::EnumId(adt) => {
let adt_loc = adt.loc(db);
(adt_loc.id.upcast(), adt_loc.container)
}
};
let derive_range = self.derive_attr_id.find_derive_range(
db,
module.krate(db),
adt_ast_id,
self.derive_index,
);
adt_ast_id.with_value(derive_range)
}
}
22 changes: 17 additions & 5 deletions crates/hir-def/src/item_scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -159,6 +159,7 @@ pub struct ItemScope {
declarations: ThinVec<ModuleDefId>,

impls: ThinVec<ImplId>,
builtin_derive_impls: ThinVec<BuiltinDeriveImplId>,
extern_blocks: ThinVec<ExternBlockId>,
unnamed_consts: ThinVec<ConstId>,
/// Traits imported via `use Trait as _;`.
Expand Down Expand Up @@ -329,6 +330,10 @@ impl ItemScope {
self.impls.iter().copied()
}

pub fn builtin_derive_impls(&self) -> impl ExactSizeIterator<Item = BuiltinDeriveImplId> + '_ {
self.builtin_derive_impls.iter().copied()
}

pub fn all_macro_calls(&self) -> impl Iterator<Item = MacroCallId> + '_ {
self.macro_invocations.values().copied().chain(self.attr_macros.values().copied()).chain(
self.derive_macros.values().flat_map(|it| {
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -522,12 +531,13 @@ impl ItemScope {
adt: AstId<ast::Adt>,
attr_id: AttrId,
attr_call_id: MacroCallId,
len: usize,
mut derive_call_ids: SmallVec<[Option<MacroCallId>; 4]>,
) {
derive_call_ids.shrink_to_fit();
self.derive_macros.entry(adt).or_default().push(DeriveMacroInvocation {
attr_id,
attr_call_id,
derive_call_ids: smallvec![None; len],
derive_call_ids,
});
}

Expand Down Expand Up @@ -811,6 +821,7 @@ impl ItemScope {
unresolved,
declarations,
impls,
builtin_derive_impls,
unnamed_consts,
unnamed_trait_imports,
legacy_macros,
Expand All @@ -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();
Expand Down
51 changes: 50 additions & 1 deletion crates/hir-def/src/lang_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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)]
Expand Down Expand Up @@ -93,6 +94,10 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option<Box<LangIt
}
}

if matches!(krate.data(db).origin, base_db::CrateOrigin::Lang(base_db::LangCrateOrigin::Core)) {
lang_items.fill_non_lang_core_traits(db, crate_def_map);
}

if lang_items.is_empty() { None } else { Some(Box::new(lang_items)) }
}

Expand Down Expand Up @@ -135,6 +140,31 @@ impl LangItems {
}
}

fn resolve_core_trait(
db: &dyn DefDatabase,
core_def_map: &DefMap,
modules: &[Symbol],
name: Symbol,
) -> Option<TraitId> {
let mut current = &core_def_map[core_def_map.root];
for module in modules {
let Some((ModuleDefId::ModuleId(cur), _)) =
current.scope.type_(&Name::new_symbol_root(module.clone()))
else {
return None;
};
if cur.krate(db) != core_def_map.krate() || cur.block(db) != core_def_map.block_id() {
return None;
}
current = &core_def_map[cur];
}
let Some((ModuleDefId::TraitId(trait_), _)) = current.scope.type_(&Name::new_symbol_root(name))
else {
return None;
};
Some(trait_)
}

#[salsa::tracked(returns(as_deref))]
pub(crate) fn crate_notable_traits(db: &dyn DefDatabase, krate: Crate) -> Option<Box<[TraitId]>> {
let mut traits = Vec::new();
Expand Down Expand Up @@ -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)]
Expand All @@ -172,6 +206,9 @@ macro_rules! language_item_table {
$(#[$attr])*
pub $lang_item: Option<$target>,
)*
$(
pub $non_lang_trait: Option<TraitId>,
)*
}

impl LangItems {
Expand All @@ -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) {
Expand All @@ -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)]
Expand Down Expand Up @@ -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;
}
Loading